From 1636c4119b1e7d0c24f6485a0cde0d7ab81b04c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matev=C5=BE=20Er=C5=BEen?= <matevz.erzen@xlab.si>
Date: Mon, 22 Nov 2021 08:46:47 +0000
Subject: [PATCH] Updated gRPC message structure

---
 MANIFEST                                      |   2 +-
 README.md                                     |  61 +++++---
 evidence/evidence.py                          |  21 ---
 evidence/evidence_pb2.py                      |  45 +++---
 evidence/evidence_store_pb2.py                |  10 +-
 evidence/generate_evidence.py                 |  34 +++++
 forward_evidence/forward_evidence.py          |  26 +---
 proto/evidence.proto                          |  59 +++++---
 proto/evidence_store.proto                    |  76 ++++++----
 requirements.txt                              |  11 +-
 scheduler/scheduler.py                        |   2 +-
 .../wazuh_evidence_collector.py               | 137 ++++++++----------
 12 files changed, 257 insertions(+), 227 deletions(-)
 delete mode 100644 evidence/evidence.py
 create mode 100644 evidence/generate_evidence.py

diff --git a/MANIFEST b/MANIFEST
index ec626c8..f6a67d7 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,2 +1,2 @@
-VERSION=v0.0.2
+VERSION=v0.0.3
 SERVICE=evidence-collector
\ No newline at end of file
diff --git a/README.md b/README.md
index 43e990d..f06663c 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # Evidence Collector
 
-This project includes modules for collecting evidence regarding Wazuh and VAT.
+This project includes modules for collecting evidence regarding Wazuh and VAT and sending it to [Clouditor](https://github.com/clouditor/clouditor) for further processing.
 
 ## Wazuh evidence collector
 
@@ -19,13 +19,13 @@ Wazuh evidence collector uses [Wazuh's API](https://documentation.wazuh.com/curr
 3. Build Docker image:
 
 ```
-docker build -t evidence-collector .
+$ docker build -t evidence-collector .
 ```
 
 4. Run the image:
 
 ```
-docker run evidence-collector
+$ docker run evidence-collector
 ```
 
 > Note: Current simple image runs code from `test.py`. If you wish to test anything else, change this file or edit `Dockerfile`.
@@ -39,15 +39,15 @@ docker run evidence-collector
 3. Install dependencies:
 
 ```
-pip install -r requirements.txt
+$ pip install -r requirements.txt
 
-sudo apt-get install jq
+$ sudo apt-get install jq
 ```
 
 4. a) Install Redis server locally:
 
 ```
-sudo apt-get install redis-server
+$ sudo apt-get install redis-server
 ```
 
 > Note: To stop Redis server use `/etc/init.d/redis-server stop`.
@@ -55,7 +55,7 @@ sudo apt-get install redis-server
 4. b) Run Redis server in Docker container:
 
 ```
-docker run --name my-redis-server -p 6379:6379 -d redis
+$ docker run --name my-redis-server -p 6379:6379 -d redis
 ```
 
 In this case also comment-out server start command in `entrypoint.sh`:
@@ -67,13 +67,26 @@ In this case also comment-out server start command in `entrypoint.sh`:
 5. Run `entrypoint.sh`:
 
 ```
-./entrypoint.sh
+$ ./entrypoint.sh
 ```
 
 > Note: This repository consists of multiple Python modules. When running Python code manually, use of `-m` flag might be necessary.
 
 ## Component configuration 
 
+### Generate gRPC code from `.proto` files
+
+```
+$ pip3 install grpcio-tools
+$ python3 -m grpc_tools.protoc --proto_path=proto evidence.proto --python_out=evidence --grpc_python_out=evidence
+```
+
+As we are interacting with Clouditor, .proto files are taken from [there](https://github.com/clouditor/clouditor/tree/main/proto).
+
+> Note:
+> since we are running the code as a package, we have to modify imports in newly generated code:
+> `import evidence_pb2 as evidence__pb2` --> `import evidence.evidence_pb2 as evidence__pb2`
+
 ### API User authentication
 
 Current implementation has disabled SSL certificate verification & uses simple username/password verification (defined inside `/constants/constants.py`). Production version should change this with cert verification.
@@ -83,15 +96,15 @@ Current implementation has disabled SSL certificate verification & uses simple u
 Example command for testing the API via CLI: 
 
 ```
-curl --user admin:changeme --insecure -X GET "https://192.168.33.10:9200/wazuh-alerts*/_search?pretty" -H 'Content-Type: application/json' -d'
-{"query": {
-  "bool": {
-    "must": [{"match": {"predecoder.program_name": "clamd"}},
-             {"match": {"rule.description": "Clamd restarted"}},
-             {"match": {"agent.id": "001"}}]
+$ curl --user admin:changeme --insecure -X GET "https://192.168.33.10:9200/wazuh-alerts*/_search?pretty" -H 'Content-Type: application/json' -d'
+  {"query": {
+    "bool": {
+      "must": [{"match": {"predecoder.program_name": "clamd"}},
+              {"match": {"rule.description": "Clamd restarted"}},
+              {"match": {"agent.id": "001"}}]
+      }
     }
-  }
-}'
+  }'
 ```
 
 ### Running [RQ](https://github.com/rq/rq) and [RQ-scheduler](https://github.com/rq/rq-scheduler) localy
@@ -99,9 +112,9 @@ curl --user admin:changeme --insecure -X GET "https://192.168.33.10:9200/wazuh-a
 1. Install (if needed) and run `redis-server`:
 
 ```
-sudo apt-get install redis-server
+$ sudo apt-get install redis-server
 
-redis-server
+$ redis-server
 ```
 
 > Note: By default, server listens on port `6379`. Take this into consideration when starting other components.
@@ -109,17 +122,17 @@ redis-server
 2. Install RQ and RQ-scheduler:
 
 ```
-pip install rq
+$ pip install rq
 
-pip install rq-scheduler
+$ pip install rq-scheduler
 ```
 
 3. Run both components in 2 terminals:
 
 ```
-rqworker low
+$ rqworker low
 
-rqscheduler --host localhost --port 6379
+$ rqscheduler --host localhost --port 6379
 ```
 
 > Note: `low` in the first command references task queue worker will use.
@@ -127,7 +140,7 @@ rqscheduler --host localhost --port 6379
 4. Run Python script containing RQ commands as usual:
 
 ```
-python3 ...
+$ python3 -m wazuh_evidence_collector.wazuh_evidence_collector
 ```
 
 ## Known issues
@@ -143,5 +156,5 @@ elasticsearch.exceptions.UnsupportedProductError: The client noticed that the se
 To resolve this, downgrade to older package version: 
 
 ```
-pip install 'elasticsearch<7.14.0'
+$ pip install 'elasticsearch<7.14.0'
 ```
diff --git a/evidence/evidence.py b/evidence/evidence.py
deleted file mode 100644
index 2633435..0000000
--- a/evidence/evidence.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import json 
-
-class Evidence:
-
-    def __init__(self, evidence_id, timestamp, resource_id, tool, resource_type, feature_type, feature_property, measurement_result, raw):
-        self.evidence_id = evidence_id
-        self.timestamp = timestamp
-        self.resource_id = resource_id
-        self.tool = tool
-        self.resource_type = resource_type
-        self.feature_type = feature_type
-        self.feature_property = feature_property
-        self.measurement_result = measurement_result
-        self.raw = raw
-
-    def toJson(self):
-        return json.dumps(self.__dict__)
-
-def simple_evidence(evidence_id, timestamp, resource_id, feature_property, measurement_result, raw):
-    return Evidence(evidence_id, timestamp, resource_id, None, None, None, feature_property, measurement_result, raw)
-    
\ No newline at end of file
diff --git a/evidence/evidence_pb2.py b/evidence/evidence_pb2.py
index 8d19a46..ff09aaf 100644
--- a/evidence/evidence_pb2.py
+++ b/evidence/evidence_pb2.py
@@ -17,11 +17,11 @@ from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__
 
 DESCRIPTOR = _descriptor.FileDescriptor(
   name='evidence.proto',
-  package='',
+  package='clouditor',
   syntax='proto3',
-  serialized_options=b'Z\010evidence',
+  serialized_options=b'Z\014api/evidence',
   create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\x0e\x65vidence.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xc1\x01\n\x08\x45vidence\x12\n\n\x02id\x18\x01 \x01(\t\x12\x12\n\nservice_id\x18\x02 \x01(\t\x12\x13\n\x0bresource_id\x18\x03 \x01(\t\x12-\n\ttimestamp\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x1a\n\x12\x61pplicable_metrics\x18\x05 \x03(\x05\x12\x0b\n\x03raw\x18\x06 \x01(\t\x12(\n\x08resource\x18\x07 \x01(\x0b\x32\x16.google.protobuf.ValueB\nZ\x08\x65videnceb\x06proto3'
+  serialized_pb=b'\n\x0e\x65vidence.proto\x12\tclouditor\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xa1\x01\n\x08\x45vidence\x12\n\n\x02id\x18\x01 \x01(\t\x12-\n\ttimestamp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x12\n\nservice_id\x18\x03 \x01(\t\x12\x0f\n\x07tool_id\x18\x04 \x01(\t\x12\x0b\n\x03raw\x18\x05 \x01(\t\x12(\n\x08resource\x18\x06 \x01(\x0b\x32\x16.google.protobuf.ValueB\x0eZ\x0c\x61pi/evidenceb\x06proto3'
   ,
   dependencies=[google_dot_protobuf_dot_struct__pb2.DESCRIPTOR,google_dot_protobuf_dot_timestamp__pb2.DESCRIPTOR,])
 
@@ -30,57 +30,50 @@ DESCRIPTOR = _descriptor.FileDescriptor(
 
 _EVIDENCE = _descriptor.Descriptor(
   name='Evidence',
-  full_name='Evidence',
+  full_name='clouditor.Evidence',
   filename=None,
   file=DESCRIPTOR,
   containing_type=None,
   create_key=_descriptor._internal_create_key,
   fields=[
     _descriptor.FieldDescriptor(
-      name='id', full_name='Evidence.id', index=0,
+      name='id', full_name='clouditor.Evidence.id', index=0,
       number=1, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=b"".decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
     _descriptor.FieldDescriptor(
-      name='service_id', full_name='Evidence.service_id', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
+      name='timestamp', full_name='clouditor.Evidence.timestamp', index=1,
+      number=2, type=11, cpp_type=10, label=1,
+      has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
     _descriptor.FieldDescriptor(
-      name='resource_id', full_name='Evidence.resource_id', index=2,
+      name='service_id', full_name='clouditor.Evidence.service_id', index=2,
       number=3, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=b"".decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
     _descriptor.FieldDescriptor(
-      name='timestamp', full_name='Evidence.timestamp', index=3,
-      number=4, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='applicable_metrics', full_name='Evidence.applicable_metrics', index=4,
-      number=5, type=5, cpp_type=1, label=3,
-      has_default_value=False, default_value=[],
+      name='tool_id', full_name='clouditor.Evidence.tool_id', index=3,
+      number=4, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
     _descriptor.FieldDescriptor(
-      name='raw', full_name='Evidence.raw', index=5,
-      number=6, type=9, cpp_type=9, label=1,
+      name='raw', full_name='clouditor.Evidence.raw', index=4,
+      number=5, type=9, cpp_type=9, label=1,
       has_default_value=False, default_value=b"".decode('utf-8'),
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
     _descriptor.FieldDescriptor(
-      name='resource', full_name='Evidence.resource', index=6,
-      number=7, type=11, cpp_type=10, label=1,
+      name='resource', full_name='clouditor.Evidence.resource', index=5,
+      number=6, type=11, cpp_type=10, label=1,
       has_default_value=False, default_value=None,
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
@@ -97,8 +90,8 @@ _EVIDENCE = _descriptor.Descriptor(
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=82,
-  serialized_end=275,
+  serialized_start=93,
+  serialized_end=254,
 )
 
 _EVIDENCE.fields_by_name['timestamp'].message_type = google_dot_protobuf_dot_timestamp__pb2._TIMESTAMP
@@ -109,7 +102,7 @@ _sym_db.RegisterFileDescriptor(DESCRIPTOR)
 Evidence = _reflection.GeneratedProtocolMessageType('Evidence', (_message.Message,), {
   'DESCRIPTOR' : _EVIDENCE,
   '__module__' : 'evidence_pb2'
-  # @@protoc_insertion_point(class_scope:Evidence)
+  # @@protoc_insertion_point(class_scope:clouditor.Evidence)
   })
 _sym_db.RegisterMessage(Evidence)
 
diff --git a/evidence/evidence_store_pb2.py b/evidence/evidence_store_pb2.py
index a472270..69bacf0 100644
--- a/evidence/evidence_store_pb2.py
+++ b/evidence/evidence_store_pb2.py
@@ -19,9 +19,9 @@ DESCRIPTOR = _descriptor.FileDescriptor(
   name='evidence_store.proto',
   package='clouditor',
   syntax='proto3',
-  serialized_options=b'Z\010evidence',
+  serialized_options=b'Z\014api/evidence',
   create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\x14\x65vidence_store.proto\x12\tclouditor\x1a\x0e\x65vidence.proto\x1a\x1bgoogle/protobuf/empty.proto\"\'\n\x15StoreEvidenceResponse\x12\x0e\n\x06status\x18\x01 \x01(\x08\"\x16\n\x14ListEvidencesRequest\"5\n\x15ListEvidencesResponse\x12\x1c\n\tevidences\x18\x01 \x03(\x0b\x32\t.Evidence2\xd8\x01\n\rEvidenceStore\x12<\n\rStoreEvidence\x12\t.Evidence\x1a .clouditor.StoreEvidenceResponse\x12\x35\n\x0eStoreEvidences\x12\t.Evidence\x1a\x16.google.protobuf.Empty(\x01\x12R\n\rListEvidences\x12\x1f.clouditor.ListEvidencesRequest\x1a .clouditor.ListEvidencesResponseB\nZ\x08\x65videnceb\x06proto3'
+  serialized_pb=b'\n\x14\x65vidence_store.proto\x12\tclouditor\x1a\x0e\x65vidence.proto\x1a\x1bgoogle/protobuf/empty.proto\"\'\n\x15StoreEvidenceResponse\x12\x0e\n\x06status\x18\x01 \x01(\x08\"\x16\n\x14ListEvidencesRequest\"?\n\x15ListEvidencesResponse\x12&\n\tevidences\x18\x01 \x03(\x0b\x32\x13.clouditor.Evidence2\xec\x01\n\rEvidenceStore\x12\x46\n\rStoreEvidence\x12\x13.clouditor.Evidence\x1a .clouditor.StoreEvidenceResponse\x12?\n\x0eStoreEvidences\x12\x13.clouditor.Evidence\x1a\x16.google.protobuf.Empty(\x01\x12R\n\rListEvidences\x12\x1f.clouditor.ListEvidencesRequest\x1a .clouditor.ListEvidencesResponseB\x0eZ\x0c\x61pi/evidenceb\x06proto3'
   ,
   dependencies=[evidence__pb2.DESCRIPTOR,google_dot_protobuf_dot_empty__pb2.DESCRIPTOR,])
 
@@ -113,7 +113,7 @@ _LISTEVIDENCESRESPONSE = _descriptor.Descriptor(
   oneofs=[
   ],
   serialized_start=145,
-  serialized_end=198,
+  serialized_end=208,
 )
 
 _LISTEVIDENCESRESPONSE.fields_by_name['evidences'].message_type = evidence__pb2._EVIDENCE
@@ -153,8 +153,8 @@ _EVIDENCESTORE = _descriptor.ServiceDescriptor(
   index=0,
   serialized_options=None,
   create_key=_descriptor._internal_create_key,
-  serialized_start=201,
-  serialized_end=417,
+  serialized_start=211,
+  serialized_end=447,
   methods=[
   _descriptor.MethodDescriptor(
     name='StoreEvidence',
diff --git a/evidence/generate_evidence.py b/evidence/generate_evidence.py
new file mode 100644
index 0000000..1c0c97d
--- /dev/null
+++ b/evidence/generate_evidence.py
@@ -0,0 +1,34 @@
+import json 
+from evidence.evidence_pb2 import Evidence
+
+# Used if user doesn't provide other
+_default_resource_type = ["VirtualMachine", "Compute", "Resource"]
+
+def create_resource(id, name, type, property_list):
+    resource = {
+        "id": str(id),
+        "name": str(name),
+        "type": type if type is not None else _default_resource_type
+    }
+
+    if property_list is not None:
+        resource.update(property_list)
+
+    return resource
+
+def create_evidence(id, service_id, tool_id, raw, resource):
+    evidence = Evidence()
+
+    evidence.id = str(id)
+    evidence.timestamp.GetCurrentTime()
+    evidence.timestamp.nanos = 0
+    evidence.service_id = service_id
+    evidence.tool_id = tool_id
+    evidence.raw = json.dumps(raw)
+    evidence.resource.struct_value.update(resource)
+
+    return evidence
+
+def print_evidence(evidence):
+    evidence.raw = evidence.raw[:50] + "..."
+    print(evidence)    
\ No newline at end of file
diff --git a/forward_evidence/forward_evidence.py b/forward_evidence/forward_evidence.py
index a51a8be..9b42835 100644
--- a/forward_evidence/forward_evidence.py
+++ b/forward_evidence/forward_evidence.py
@@ -1,38 +1,22 @@
 from evidence.evidence_store_pb2_grpc import EvidenceStoreStub
 from evidence.evidence_pb2 import Evidence
-from google.protobuf.struct_pb2 import Value
 import grpc
 import json
 
-f = open('constants.json',)
-constants = json.load(f)
-f.close()
-
-def create_grpc_message(ev):
-    ev_grpc = Evidence()
-
-    ev_grpc.id = ev.evidence_id
-    ev_grpc.timestamp.GetCurrentTime()
-    ev_grpc.resource_id = ev.resource_id
-    ev_grpc.service_id = str(ev.tool)
-    ev_grpc.resource.string_value = str(ev.resource_type)
-    ev_grpc.applicable_metrics.extend([1] if ev.measurement_result else [0])
-    ev_grpc.raw = ''.join(map(str, ev.raw))
-
-    return ev_grpc
-
 class ForwardEvidence(object):
 
     def __init__(self):
+        f = open('constants.json',)
+        constants = json.load(f)
+        f.close()
+
         self.channel = grpc.insecure_channel('{}:{}'.format(constants['clouditor']['host'], constants['clouditor']['port']))
         
         self.stub = EvidenceStoreStub(self.channel)
 
     def send_evidence(self, evidence):
-        grpc_evidence = create_grpc_message(evidence)
-
         try:
-            response = self.stub.StoreEvidence(grpc_evidence)  
+            response = self.stub.StoreEvidence(evidence)  
             print('gRPC evidence forwarded: ' + str(response))
         except grpc.RpcError as err:
             print(err)
diff --git a/proto/evidence.proto b/proto/evidence.proto
index e9ed94f..145d767 100644
--- a/proto/evidence.proto
+++ b/proto/evidence.proto
@@ -1,30 +1,47 @@
+/*
+ * Copyright 2021 Fraunhofer AISEC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *           $$\                           $$\ $$\   $$\
+ *           $$ |                          $$ |\__|  $$ |
+ *  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
+ * $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
+ * $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
+ * $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
+ * \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
+ *  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
+ *
+ * This file is part of Clouditor Community Edition.
+ */
+// ToDo(all): Change name of file to resources.proto or similar? (It is not
+// containing evidences only)
 syntax = "proto3";
 
+package clouditor;
+
 import "google/protobuf/struct.proto";
 import "google/protobuf/timestamp.proto";
 
-option go_package = "evidence";
+option go_package = "api/evidence";
 
-// TODO: Addapt to the final Evidence structure..
-// Copied from https://github.com/clouditor/clouditor/blob/main/proto/evidence.proto
+// An evidence resource
 message Evidence {
-  string id = 1;
-
-  string service_id = 2;
-
-  string resource_id = 3;
-
+  string id = 1; // the ID in a uuid format
   // TODO: replace with google/type/date.proto timestamp.proto or date.proto?
-  google.protobuf.Timestamp timestamp = 4;
-
-  repeated int32 applicable_metrics = 5;
-
-  // "raw" evidence (for the auditor), for example the raw JSON response from
-  // the API. This does not follow a defined schema
-  string raw = 6;
-
-  // optional; a semantic representation of the Cloud resource according to our
-  // defined ontology. a JSON serialized node of our semantic graph. This may be
-  // Clouditor-specific.
-  google.protobuf.Value resource = 7;
+  google.protobuf.Timestamp timestamp = 2; // time of evidence creation
+  string service_id = 3; // Reference to a service this evidence was gathered from
+  string tool_id = 4; // Reference to the tool which provided the evidence
+  string raw = 5; // evidence in its original form without following a defined schema, e.g. the raw JSON
+  google.protobuf.Value resource = 6; // semantic representation of the Cloud resource according to our defined ontology
 }
\ No newline at end of file
diff --git a/proto/evidence_store.proto b/proto/evidence_store.proto
index 08de2b2..8181628 100644
--- a/proto/evidence_store.proto
+++ b/proto/evidence_store.proto
@@ -1,25 +1,51 @@
-syntax = "proto3";
-
-package clouditor;
-
-import "evidence.proto";
-import "google/protobuf/empty.proto";
-
-option go_package = "evidence";
-
-// Manages the storage of evidences
-service EvidenceStore {
-  // Stores an evidence to the evidence storage
-  rpc StoreEvidence(Evidence) returns (StoreEvidenceResponse);
-
-  // Stores a stream of evidences to the evidence storage
-  rpc StoreEvidences(stream Evidence) returns (google.protobuf.Empty);
-
-  // Returns the evidences lying in the evidence storage
-  rpc ListEvidences(ListEvidencesRequest) returns (ListEvidencesResponse);
-}
-
-message StoreEvidenceResponse { bool status = 1; }
-
-message ListEvidencesRequest {}
-message ListEvidencesResponse { repeated Evidence evidences = 1; }
\ No newline at end of file
+/*
+ * Copyright 2021 Fraunhofer AISEC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *           $$\                           $$\ $$\   $$\
+ *           $$ |                          $$ |\__|  $$ |
+ *  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
+ * $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
+ * $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
+ * $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
+ * \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
+ *  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
+ *
+ * This file is part of Clouditor Community Edition.
+ */
+ syntax = "proto3";
+
+ package clouditor;
+ 
+ import "evidence.proto";
+ import "google/protobuf/empty.proto";
+ 
+ option go_package = "api/evidence";
+ 
+ // Manages the storage of evidences
+ service EvidenceStore {
+   // Stores an evidence to the evidence storage
+   rpc StoreEvidence(Evidence) returns (StoreEvidenceResponse);
+ 
+   // Stores a stream of evidences to the evidence storage
+   rpc StoreEvidences(stream Evidence) returns (google.protobuf.Empty);
+ 
+   // Returns the evidences lying in the evidence storage
+   rpc ListEvidences(ListEvidencesRequest) returns (ListEvidencesResponse);
+ }
+ 
+ message StoreEvidenceResponse { bool status = 1; }
+ 
+ message ListEvidencesRequest {}
+ message ListEvidencesResponse { repeated Evidence evidences = 1; }
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index c1025f1..77f9652 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,9 +1,10 @@
+grpcio==1.41.1
+rq==1.2.2
+elasticsearch==7.13.4
 redis==3.3.11
-rq_scheduler==0.11.0
 urllib3==1.25.8
-rq==1.2.2
 elasticsearch_dsl==7.4.0
-grpcio==1.41.1
-elasticsearch==7.13.4
 protobuf==3.19.1
-click==7.1.2
\ No newline at end of file
+rq_scheduler==0.11.0
+click==7.1.2
+configparser==5.1.0
\ No newline at end of file
diff --git a/scheduler/scheduler.py b/scheduler/scheduler.py
index 42feb47..bcfc9fa 100644
--- a/scheduler/scheduler.py
+++ b/scheduler/scheduler.py
@@ -29,7 +29,7 @@ remove_jobs(scheduler)
 # Should probably be "0 0 * * * ".
 scheduler.cron(
     '* * * * * ',               
-    func=wazuh_evidence_collector.run_full_check,                 
+    func=wazuh_evidence_collector.run_collector,                 
     args=[],
     repeat=None,
     queue_name=constants['redis']['queue'],
diff --git a/wazuh_evidence_collector/wazuh_evidence_collector.py b/wazuh_evidence_collector/wazuh_evidence_collector.py
index 862e932..0843b52 100644
--- a/wazuh_evidence_collector/wazuh_evidence_collector.py
+++ b/wazuh_evidence_collector/wazuh_evidence_collector.py
@@ -3,11 +3,9 @@ from wazuh_evidence_collector.wazuh_client import WazuhClient
 from elasticsearch import Elasticsearch
 from elasticsearch_dsl import Search
 from forward_evidence.forward_evidence import ForwardEvidence
-from evidence.evidence import Evidence, simple_evidence
-from random import randint
-from sys import maxsize
-from datetime import datetime
-import pprint
+from evidence.generate_evidence import create_resource, create_evidence, print_evidence
+import uuid
+import configparser
 
 f = open('constants.json',)
 constants = json.load(f)
@@ -25,60 +23,66 @@ es = Elasticsearch(
         ssl_show_warn=False,
     )
 
-# TODO: Get real data.
-# Get (temporary) ID
-def get_id(reqId):
-    return reqId + '-' + str(randint(0, maxsize))
+# Get ID (UUID)
+def get_id():
+    id = uuid.uuid1()
 
-# Get timestamp (can be changed according to our preferences)
-def get_timestamp():
-    ts = datetime.utcnow()
+    return id
 
-    return ts.strftime('%Y-%m-%dT%H:%M:%SZ')
+# Get tool ID (SERVICE:VERSION format)
+def get_tool_id():
+    with open('MANIFEST', 'r') as f:
+        config_string = '[clouditor]\n' + f.read()
 
-# Wrapepr function that runs all the checks (for every manager/agent)
-def run_full_check():
+    config = configparser.ConfigParser()
+    config.read_string(config_string)
+
+    version = '{}:{}'.format(config.get('clouditor', 'SERVICE'), config.get('clouditor', 'VERSION'))
+
+    return version
+
+# Wrapper function that runs all the checks (for every manager/agent)
+def run_collector():
 
     # Get list of all agent ids (including manager's)
     def get_agents(wc):
         body = wc.req('GET', 'agents')
         
-        agents_ids = []
+        agent_list = []
         for agent in body['data']['affected_items']:
-            agents_ids.append(agent['id'])
+            agent_list.append([agent['id'], agent['name']])
 
-        return body, agents_ids
+        return body, agent_list
 
-    body, agents_ids = get_agents(wc)
+    body, agent_list = get_agents(wc)
 
-    agent_evidences = []
+    evidence_list = []
 
-    for agent in agents_ids:
-        agent_evidences.append(wazuh_monitoring_enabled(wc, agent))
-        agent_evidences.append(malvare_protection_enabled(wc, es, agent))
+    for agent in agent_list:
+        evidence_list.append(generate_evidence(wc, es, agent))
 
     # TODO:
     forwarder = ForwardEvidence()
-    for evidence in agent_evidences:
+    for evidence in evidence_list:
         forwarder.send_evidence(evidence)
-        pprint.pprint(evidence.__dict__)
+        print_evidence(evidence)
 
-    return agent_evidences
+    return evidence_list
 
-# Check Wazuh's configuration
-def wazuh_monitoring_enabled(wc, agent_id):
+# Run checks and generate evidence
+def generate_evidence(wc, es, agent):
 
     # Check if syscheck enabled
-    def check_syscheck(wc, agent_id):
-        body = wc.req('GET', 'agents/' + agent_id + '/config/syscheck/syscheck')
+    def check_syscheck(wc, agent):
+        body = wc.req('GET', 'agents/' + agent[0] + '/config/syscheck/syscheck')
 
         measurement_result = body['data']['syscheck']['disabled'] == 'no'
 
         return body, measurement_result
 
     # Check if rootcheck enabled
-    def check_rootcheck(wc, agent_id):
-        body = wc.req('GET', 'agents/' + agent_id + '/config/syscheck/rootcheck')
+    def check_rootcheck(wc, agent):
+        body = wc.req('GET', 'agents/' + agent[0] + '/config/syscheck/rootcheck')
 
         measurement_result = body['data']['rootcheck']['disabled'] == 'no'
 
@@ -113,25 +117,6 @@ def wazuh_monitoring_enabled(wc, agent_id):
 
         return body, measurement_result
 
-    raw_evidence = []
-
-    evidence, result_syscheck = check_syscheck(wc, agent_id)
-    raw_evidence.append(evidence)
-
-    evidence, result_rootcheck = check_rootcheck(wc, agent_id)
-    raw_evidence.append(evidence)
-    
-    evidence, result_aler_integration = check_alert_integrations(wc)
-    raw_evidence.append(evidence)
-
-    if result_syscheck and result_rootcheck and result_aler_integration:
-        return simple_evidence(get_id('05.3'), get_timestamp(), agent_id, "wazuh_monitoring_enabled", "true", raw_evidence)
-    else: 
-        return simple_evidence(get_id('05.3'), get_timestamp(), agent_id, "wazuh_monitoring_enabled", "false", raw_evidence)
-
-# Check if agent uses ClamAV or VirusTotal
-def malvare_protection_enabled(wc, es, agent_id):
-
     # Check for VirusTotal integration
     def check_virus_total_integration(wc):
         body = wc.req('GET', 'manager/configuration')
@@ -152,8 +137,8 @@ def malvare_protection_enabled(wc, es, agent_id):
         return body, measurement_result
 
     # Check if ClamAV daemon process running
-    def check_clamd_process(wc, agent_id):
-        body = wc.req('GET', 'syscollector/' + agent_id + '/processes')
+    def check_clamd_process(wc, agent):
+        body = wc.req('GET', 'syscollector/' + agent[0] + '/processes')
 
         measurement_result = False
 
@@ -165,11 +150,11 @@ def malvare_protection_enabled(wc, es, agent_id):
         return body, measurement_result
 
     # Check ClamAV logs in Elasticsearch
-    def check_clamd_logs_elastic(es, agent_id):
+    def check_clamd_logs_elastic(es, agent):
         s = Search(using=es, index="wazuh-alerts-*") \
         .query("match", predecoder__program_name="clamd") \
         .query("match", rule__descrhosttion="Clamd restarted") \
-        .query("match", agent__id=agent_id) 
+        .query("match", agent__id=agent[0]) 
 
         body = s.execute().to_dict()
 
@@ -179,36 +164,34 @@ def malvare_protection_enabled(wc, es, agent_id):
 
     raw_evidence = []
 
-    evidence, result_virus_total = check_virus_total_integration(wc)
+    evidence, result_syscheck = check_syscheck(wc, agent)
     raw_evidence.append(evidence)
 
-    evidence, result_lamd_process = check_clamd_process(wc, agent_id)
+    evidence, result_rootcheck = check_rootcheck(wc, agent)
     raw_evidence.append(evidence)
     
-    evidence, result_clamd_logs = check_clamd_logs_elastic(es, agent_id)
+    evidence, result_aler_integration = check_alert_integrations(wc)
     raw_evidence.append(evidence)
 
-    if result_virus_total or (result_lamd_process and result_clamd_logs):
-        return simple_evidence(get_id('05.4'), get_timestamp(), agent_id, "malvare_protection_enabled", "true", raw_evidence)
-    else: 
-        return simple_evidence(get_id('05.4'), get_timestamp(), agent_id, "malvare_protection_enabled", "false", raw_evidence)
-
-# Check last Syscheck & Rootcheck scan times
-# TODO: When producing 'real' evidence, make sure to provide differentiation between Syscheck and Rootcheck outputs.
-def check_last_scan_time(wc, agent_id):
-    body = wc.req('GET', 'syscheck/' + agent_id + '/last_scan')
-
-    measurement_result = body['data']['affected_items'][0]['end']
-
-    evidence1 = simple_evidence(get_id('05.4'), get_timestamp(), "last_scan", measurement_result, body)
-
-    body = wc.req('GET', 'rootcheck/' + agent_id + '/last_scan')
+    evidence, result_virus_total = check_virus_total_integration(wc)
+    raw_evidence.append(evidence)
 
-    measurement_result = body['data']['affected_items'][0]['end']
+    evidence, result_lamd_process = check_clamd_process(wc, agent)
+    raw_evidence.append(evidence)
+    
+    evidence, result_clamd_logs = check_clamd_logs_elastic(es, agent)
+    raw_evidence.append(evidence)
 
-    evidence2 = simple_evidence(get_id('05.4'), get_timestamp(), "last_scan", measurement_result, body)
+    # TODO: 
+    if result_syscheck and result_rootcheck and result_aler_integration and \
+        (result_virus_total or (result_lamd_process and result_clamd_logs)):
+        malware_protection = { "malwareProtection": { "enabled": True }}
+    else: 
+        malware_protection = { "malwareProtection": { "enabled": False }}
 
-    return evidence1, evidence2
+    # TODO: change ID
+    resource = create_resource(agent[0], agent[1], None, malware_protection)
+    return create_evidence(get_id(), "evidence_collector_service", get_tool_id(), raw_evidence, resource)
 
 if __name__ == "__main__":
-    run_full_check()
\ No newline at end of file
+    run_collector()
\ No newline at end of file
-- 
GitLab