We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Error KeyError: 'value' in Shodan-InternetDB connector
## Description

We get the following error in Shodan-InternetDB connector for every IP we try to process:

INFO:root:Reading StixCyberObservable {f14b0557-269b-478c-822d-dd206ce88060}.
ERROR:root:Error in message processing, reporting error to API
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/dist-packages/pycti/connector/opencti_connector_helper.py", line 181, in _data_handler
    message = self.callback(json_data["event"])
  File "/opt/opencti/connectors/internal-enrichment/shodan-internetdb/src/shodan_internetdb/connector.py", line 103, in _process_message
    value = observable["value"]
KeyError: 'value'
INFO:root:Reporting work update_received work_6cbd1a73-9cfb-4825-9554-929cc42df702_2023-04-21T11:35:40.994Z
INFO:root:Message (delivery_tag=1) processed, thread terminated

## Environment

1. OS (where OpenCTI server runs): Ubuntu 22
2. OpenCTI version: 5.7.2
3. OpenCTI client: python


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `internal-enrichment/shodan-internetdb/src/shodan_internetdb/connector.py`
Content:
```
1 """Shodan InternetDB connector"""
2 
3 from __future__ import annotations
4 
5 import logging
6 from datetime import datetime, timedelta
7 from pathlib import Path
8 from typing import Any, Dict, List, Union
9 
10 import pycti
11 import stix2
12 import validators
13 import yaml
14 from pycti.connector.opencti_connector_helper import OpenCTIConnectorHelper
15 from requests.exceptions import RequestException
16 
17 from .client import ShodanInternetDbClient, ShodanResult
18 from .config import RootConfig
19 
20 __all__ = [
21     "ShodanInternetDBConnector",
22 ]
23 
24 log = logging.getLogger(__name__)
25 
26 
27 class ShodanInternetDBConnector:
28     """Shodan InternetDB connector"""
29 
30     def __init__(self):
31         """Constructor"""
32         config_path = Path(__file__).parent.parent.joinpath("config.yml")
33         config = (
34             yaml.load(config_path.open(), Loader=yaml.SafeLoader)
35             if config_path.is_file()
36             else {}
37         )
38 
39         self._config = RootConfig.parse_obj(config)
40         self._helper = OpenCTIConnectorHelper(config)
41 
42         self._identity = self._helper.api.identity.create(
43             type="Organization",
44             name="Shodan",
45             description="Shodan is a search engine for Internet-connected devices.",
46         )
47         self._identity_id = self._identity["standard_id"]
48         self._object_marking_id = stix2.TLP_WHITE["id"]
49 
50         self._client = ShodanInternetDbClient(verify=self._config.shodan.ssl_verify)
51 
52     def start(self) -> None:
53         """
54         Start the connector
55         :return: None
56         """
57         self._helper.listen(self._process_message)
58 
59     def _process_message(self, data: Dict[str, Any]) -> str:
60         """
61         Process the data message
62         :param data: Entity data
63         :return: None
64         """
65         # Fetch the observable being processed
66         entity_id = data["entity_id"]
67 
68         custom_attributes = """
69             id
70             entity_type
71             objectMarking {
72               edges {
73                 node {
74                   id
75                   definition_type
76                   definition
77                 }
78               }
79             }
80             observable_value
81         """
82         observable = self._helper.api.stix_cyber_observable.read(
83             id=entity_id, customAttributes=custom_attributes
84         )
85 
86         if observable is None:
87             log.error("Observable not found with entity_id %s", entity_id)
88             return "Observable not found"
89 
90         # Check TLP markings, do not submit higher than the max allowed
91         tlps = ["TLP:CLEAR"]
92         for marking_definition in observable.get("objectMarking", []):
93             if marking_definition["definition_type"] == "TLP":
94                 tlps.append(marking_definition["definition"])
95 
96         for tlp in tlps:
97             max_tlp_name = self._config.shodan.max_tlp.name
98             if not OpenCTIConnectorHelper.check_max_tlp(tlp, max_tlp_name):
99                 log.debug("Skipping observable, TLP is greater than the MAX TLP")
100                 return "Skipping observable (TLP)"
101 
102         # Process the observable value
103         value = observable["value"]
104         if not validators.ipv4(value):
105             log.error("Observable value is not an IPv4 address")
106             return "Skipping observable (ipv4 validation)"
107 
108         try:
109             result = self._client.query(value)
110         except RequestException:
111             log.exception("Shodan API error")
112             return "Skipping observable (Shodan API error)"
113 
114         if result is None:
115             log.debug("No information available on %s", value)
116             return "Skipping observable (Shodan 404)"
117 
118         # Process the result
119         log.debug("Processing %s", value)
120         self._process_domains(observable, result)
121         self._process_tags(observable, result)
122         self._process_vulns(observable, result)
123         self._process_note(observable, result)
124 
125         return "Success"
126 
127     def _process_note(
128         self,
129         observable: Dict[str, Any],
130         result: ShodanResult,
131     ) -> None:
132         """
133         Add an enrichment note to the observable
134         :param observable: Observable data
135         :param result: Shodan data
136         :return: None
137         """
138 
139         def format_list(alist: List[Union[str, int]]) -> str:
140             """Format a list of primitives into a Markdown list"""
141             return "".join(f"\n- {name}" for name in alist) or "n/a"
142 
143         value = observable["value"]
144         abstract = f"Shodan InternetDB enrichment of {value}"
145         content = f"""```
146 Shodan InternetDB:
147 ------------------
148 Hostnames: {format_list(result.hostnames)}
149 ------------------
150 Software: {format_list(result.cpes)}
151 ------------------
152 Vulnerabilities: {format_list(result.vulns)}
153 ------------------
154 Ports: {format_list(result.ports)}
155 ------------------
156 ```
157 """
158 
159         self._helper.api.note.create(
160             stix_id=pycti.Note.generate_id(datetime.now().isoformat(), content),
161             createdBy=self._identity_id,
162             objectMarking=[self._object_marking_id],
163             confidence=self._helper.connect_confidence_level,
164             objects=[observable["id"]],
165             authors=[self._identity_id],
166             abstract=abstract,
167             content=content,
168         )
169 
170     def _process_domains(
171         self,
172         observable: Dict[str, Any],
173         result: ShodanResult,
174     ) -> None:
175         """
176         Add additional domains to the observable
177         :param observable: Observable data
178         :param result: Shodan data
179         :return: None
180         """
181 
182         markings = observable["objectMarkingIds"]
183         for name in result.hostnames:
184             log.debug("Adding domain %s", name)
185             domain = self._helper.api.stix_cyber_observable.create(
186                 observableData=dict(
187                     type="Domain-Name",
188                     value=name,
189                 ),
190                 objectMarking=markings,
191                 createdBy=self._identity_id,
192                 update=True,
193             )
194 
195             log.debug("Creating domain relationship")
196             self._helper.api.stix_nested_ref_relationship.create(
197                 fromId=domain["id"],
198                 toId=observable["id"],
199                 relationship_type="resolves-to",
200                 createdBy=self._identity_id,
201                 objectMarking=markings,
202                 confidence=self._helper.connect_confidence_level,
203                 update=True,
204             )
205 
206     def _process_tags(
207         self,
208         observable: Dict[str, Any],
209         result: ShodanResult,
210     ) -> None:
211         """
212         Add additional tags to the observable
213         :param observable: Observable data
214         :param result: Shodan data
215         :return: None
216         """
217 
218         for name in result.tags:
219             log.debug("Creating label %s", name)
220             label = self._helper.api.label.create(value=name)
221 
222             log.debug("Adding to observable")
223             self._helper.api.stix_cyber_observable.add_label(
224                 id=observable["id"],
225                 label_id=label["id"],
226             )
227 
228     def _process_vulns(
229         self,
230         observable: Dict[str, Any],
231         result: ShodanResult,
232     ) -> None:
233         """
234         Add additional vulnerabilities to the observable
235         :param observable: Observable data
236         :param result: Shodan data
237         :return: None
238         """
239         now = datetime.utcnow()
240         vuln_eol = now + timedelta(days=60)
241 
242         for name in result.vulns:
243             log.debug("Creating vulnerability %s", name)
244             vuln = self._helper.api.vulnerability.create(
245                 stix_id=pycti.Vulnerability.generate_id(name),
246                 name=name,
247                 createdBy=self._identity_id,
248                 objectMarking=[self._object_marking_id],
249                 confidence=self._helper.connect_confidence_level,
250                 update=True,
251             )
252 
253             log.debug("Creating vulnerability relationship")
254             self._helper.api.stix_core_relationship.create(
255                 fromId=observable["id"],
256                 toId=vuln["id"],
257                 relationship_type="related-to",
258                 createdBy=self._identity_id,
259                 start_time=now.strftime("%Y-%m-%dT%H:%M:%SZ"),
260                 stop_time=vuln_eol.strftime("%Y-%m-%dT%H:%M:%SZ"),
261                 confidence=self._helper.connect_confidence_level,
262                 objectMarking=[self._object_marking_id],
263                 update=True,
264             )
265
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/internal-enrichment/shodan-internetdb/src/shodan_internetdb/connector.py b/internal-enrichment/shodan-internetdb/src/shodan_internetdb/connector.py
--- a/internal-enrichment/shodan-internetdb/src/shodan_internetdb/connector.py
+++ b/internal-enrichment/shodan-internetdb/src/shodan_internetdb/connector.py
@@ -100,7 +100,7 @@
                 return "Skipping observable (TLP)"
 
         # Process the observable value
-        value = observable["value"]
+        value = observable["observable_value"]
         if not validators.ipv4(value):
             log.error("Observable value is not an IPv4 address")
             return "Skipping observable (ipv4 validation)"
@@ -140,7 +140,7 @@
             """Format a list of primitives into a Markdown list"""
             return "".join(f"\n- {name}" for name in alist) or "n/a"
 
-        value = observable["value"]
+        value = observable["observable_value"]
         abstract = f"Shodan InternetDB enrichment of {value}"
         content = f"""```
 Shodan InternetDB:

{"golden_diff": "diff --git a/internal-enrichment/shodan-internetdb/src/shodan_internetdb/connector.py b/internal-enrichment/shodan-internetdb/src/shodan_internetdb/connector.py\n--- a/internal-enrichment/shodan-internetdb/src/shodan_internetdb/connector.py\n+++ b/internal-enrichment/shodan-internetdb/src/shodan_internetdb/connector.py\n@@ -100,7 +100,7 @@\n                 return \"Skipping observable (TLP)\"\n \n         # Process the observable value\n-        value = observable[\"value\"]\n+        value = observable[\"observable_value\"]\n         if not validators.ipv4(value):\n             log.error(\"Observable value is not an IPv4 address\")\n             return \"Skipping observable (ipv4 validation)\"\n@@ -140,7 +140,7 @@\n             \"\"\"Format a list of primitives into a Markdown list\"\"\"\n             return \"\".join(f\"\\n- {name}\" for name in alist) or \"n/a\"\n \n-        value = observable[\"value\"]\n+        value = observable[\"observable_value\"]\n         abstract = f\"Shodan InternetDB enrichment of {value}\"\n         content = f\"\"\"```\n Shodan InternetDB:\n", "issue": "Error KeyError: 'value' in Shodan-InternetDB connector\n## Description\r\n\r\nWe get the following error in Shodan-InternetDB connector for every IP we try to process:\r\n\r\nINFO:root:Reading StixCyberObservable {f14b0557-269b-478c-822d-dd206ce88060}.\r\nERROR:root:Error in message processing, reporting error to API\r\nTraceback (most recent call last):\r\n  File \"/usr/local/lib/python3.10/dist-packages/pycti/connector/opencti_connector_helper.py\", line 181, in _data_handler\r\n    message = self.callback(json_data[\"event\"])\r\n  File \"/opt/opencti/connectors/internal-enrichment/shodan-internetdb/src/shodan_internetdb/connector.py\", line 103, in _process_message\r\n    value = observable[\"value\"]\r\nKeyError: 'value'\r\nINFO:root:Reporting work update_received work_6cbd1a73-9cfb-4825-9554-929cc42df702_2023-04-21T11:35:40.994Z\r\nINFO:root:Message (delivery_tag=1) processed, thread terminated\r\n\r\n## Environment\r\n\r\n1. OS (where OpenCTI server runs): Ubuntu 22\r\n2. OpenCTI version: 5.7.2\r\n3. OpenCTI client: python\r\n\n", "before_files": [{"content": "\"\"\"Shodan InternetDB connector\"\"\"\n\nfrom __future__ import annotations\n\nimport logging\nfrom datetime import datetime, timedelta\nfrom pathlib import Path\nfrom typing import Any, Dict, List, Union\n\nimport pycti\nimport stix2\nimport validators\nimport yaml\nfrom pycti.connector.opencti_connector_helper import OpenCTIConnectorHelper\nfrom requests.exceptions import RequestException\n\nfrom .client import ShodanInternetDbClient, ShodanResult\nfrom .config import RootConfig\n\n__all__ = [\n    \"ShodanInternetDBConnector\",\n]\n\nlog = logging.getLogger(__name__)\n\n\nclass ShodanInternetDBConnector:\n    \"\"\"Shodan InternetDB connector\"\"\"\n\n    def __init__(self):\n        \"\"\"Constructor\"\"\"\n        config_path = Path(__file__).parent.parent.joinpath(\"config.yml\")\n        config = (\n            yaml.load(config_path.open(), Loader=yaml.SafeLoader)\n            if config_path.is_file()\n            else {}\n        )\n\n        self._config = RootConfig.parse_obj(config)\n        self._helper = OpenCTIConnectorHelper(config)\n\n        self._identity = self._helper.api.identity.create(\n            type=\"Organization\",\n            name=\"Shodan\",\n            description=\"Shodan is a search engine for Internet-connected devices.\",\n        )\n        self._identity_id = self._identity[\"standard_id\"]\n        self._object_marking_id = stix2.TLP_WHITE[\"id\"]\n\n        self._client = ShodanInternetDbClient(verify=self._config.shodan.ssl_verify)\n\n    def start(self) -> None:\n        \"\"\"\n        Start the connector\n        :return: None\n        \"\"\"\n        self._helper.listen(self._process_message)\n\n    def _process_message(self, data: Dict[str, Any]) -> str:\n        \"\"\"\n        Process the data message\n        :param data: Entity data\n        :return: None\n        \"\"\"\n        # Fetch the observable being processed\n        entity_id = data[\"entity_id\"]\n\n        custom_attributes = \"\"\"\n            id\n            entity_type\n            objectMarking {\n              edges {\n                node {\n                  id\n                  definition_type\n                  definition\n                }\n              }\n            }\n            observable_value\n        \"\"\"\n        observable = self._helper.api.stix_cyber_observable.read(\n            id=entity_id, customAttributes=custom_attributes\n        )\n\n        if observable is None:\n            log.error(\"Observable not found with entity_id %s\", entity_id)\n            return \"Observable not found\"\n\n        # Check TLP markings, do not submit higher than the max allowed\n        tlps = [\"TLP:CLEAR\"]\n        for marking_definition in observable.get(\"objectMarking\", []):\n            if marking_definition[\"definition_type\"] == \"TLP\":\n                tlps.append(marking_definition[\"definition\"])\n\n        for tlp in tlps:\n            max_tlp_name = self._config.shodan.max_tlp.name\n            if not OpenCTIConnectorHelper.check_max_tlp(tlp, max_tlp_name):\n                log.debug(\"Skipping observable, TLP is greater than the MAX TLP\")\n                return \"Skipping observable (TLP)\"\n\n        # Process the observable value\n        value = observable[\"value\"]\n        if not validators.ipv4(value):\n            log.error(\"Observable value is not an IPv4 address\")\n            return \"Skipping observable (ipv4 validation)\"\n\n        try:\n            result = self._client.query(value)\n        except RequestException:\n            log.exception(\"Shodan API error\")\n            return \"Skipping observable (Shodan API error)\"\n\n        if result is None:\n            log.debug(\"No information available on %s\", value)\n            return \"Skipping observable (Shodan 404)\"\n\n        # Process the result\n        log.debug(\"Processing %s\", value)\n        self._process_domains(observable, result)\n        self._process_tags(observable, result)\n        self._process_vulns(observable, result)\n        self._process_note(observable, result)\n\n        return \"Success\"\n\n    def _process_note(\n        self,\n        observable: Dict[str, Any],\n        result: ShodanResult,\n    ) -> None:\n        \"\"\"\n        Add an enrichment note to the observable\n        :param observable: Observable data\n        :param result: Shodan data\n        :return: None\n        \"\"\"\n\n        def format_list(alist: List[Union[str, int]]) -> str:\n            \"\"\"Format a list of primitives into a Markdown list\"\"\"\n            return \"\".join(f\"\\n- {name}\" for name in alist) or \"n/a\"\n\n        value = observable[\"value\"]\n        abstract = f\"Shodan InternetDB enrichment of {value}\"\n        content = f\"\"\"```\nShodan InternetDB:\n------------------\nHostnames: {format_list(result.hostnames)}\n------------------\nSoftware: {format_list(result.cpes)}\n------------------\nVulnerabilities: {format_list(result.vulns)}\n------------------\nPorts: {format_list(result.ports)}\n------------------\n```\n\"\"\"\n\n        self._helper.api.note.create(\n            stix_id=pycti.Note.generate_id(datetime.now().isoformat(), content),\n            createdBy=self._identity_id,\n            objectMarking=[self._object_marking_id],\n            confidence=self._helper.connect_confidence_level,\n            objects=[observable[\"id\"]],\n            authors=[self._identity_id],\n            abstract=abstract,\n            content=content,\n        )\n\n    def _process_domains(\n        self,\n        observable: Dict[str, Any],\n        result: ShodanResult,\n    ) -> None:\n        \"\"\"\n        Add additional domains to the observable\n        :param observable: Observable data\n        :param result: Shodan data\n        :return: None\n        \"\"\"\n\n        markings = observable[\"objectMarkingIds\"]\n        for name in result.hostnames:\n            log.debug(\"Adding domain %s\", name)\n            domain = self._helper.api.stix_cyber_observable.create(\n                observableData=dict(\n                    type=\"Domain-Name\",\n                    value=name,\n                ),\n                objectMarking=markings,\n                createdBy=self._identity_id,\n                update=True,\n            )\n\n            log.debug(\"Creating domain relationship\")\n            self._helper.api.stix_nested_ref_relationship.create(\n                fromId=domain[\"id\"],\n                toId=observable[\"id\"],\n                relationship_type=\"resolves-to\",\n                createdBy=self._identity_id,\n                objectMarking=markings,\n                confidence=self._helper.connect_confidence_level,\n                update=True,\n            )\n\n    def _process_tags(\n        self,\n        observable: Dict[str, Any],\n        result: ShodanResult,\n    ) -> None:\n        \"\"\"\n        Add additional tags to the observable\n        :param observable: Observable data\n        :param result: Shodan data\n        :return: None\n        \"\"\"\n\n        for name in result.tags:\n            log.debug(\"Creating label %s\", name)\n            label = self._helper.api.label.create(value=name)\n\n            log.debug(\"Adding to observable\")\n            self._helper.api.stix_cyber_observable.add_label(\n                id=observable[\"id\"],\n                label_id=label[\"id\"],\n            )\n\n    def _process_vulns(\n        self,\n        observable: Dict[str, Any],\n        result: ShodanResult,\n    ) -> None:\n        \"\"\"\n        Add additional vulnerabilities to the observable\n        :param observable: Observable data\n        :param result: Shodan data\n        :return: None\n        \"\"\"\n        now = datetime.utcnow()\n        vuln_eol = now + timedelta(days=60)\n\n        for name in result.vulns:\n            log.debug(\"Creating vulnerability %s\", name)\n            vuln = self._helper.api.vulnerability.create(\n                stix_id=pycti.Vulnerability.generate_id(name),\n                name=name,\n                createdBy=self._identity_id,\n                objectMarking=[self._object_marking_id],\n                confidence=self._helper.connect_confidence_level,\n                update=True,\n            )\n\n            log.debug(\"Creating vulnerability relationship\")\n            self._helper.api.stix_core_relationship.create(\n                fromId=observable[\"id\"],\n                toId=vuln[\"id\"],\n                relationship_type=\"related-to\",\n                createdBy=self._identity_id,\n                start_time=now.strftime(\"%Y-%m-%dT%H:%M:%SZ\"),\n                stop_time=vuln_eol.strftime(\"%Y-%m-%dT%H:%M:%SZ\"),\n                confidence=self._helper.connect_confidence_level,\n                objectMarking=[self._object_marking_id],\n                update=True,\n            )\n", "path": "internal-enrichment/shodan-internetdb/src/shodan_internetdb/connector.py"}], "after_files": [{"content": "\"\"\"Shodan InternetDB connector\"\"\"\n\nfrom __future__ import annotations\n\nimport logging\nfrom datetime import datetime, timedelta\nfrom pathlib import Path\nfrom typing import Any, Dict, List, Union\n\nimport pycti\nimport stix2\nimport validators\nimport yaml\nfrom pycti.connector.opencti_connector_helper import OpenCTIConnectorHelper\nfrom requests.exceptions import RequestException\n\nfrom .client import ShodanInternetDbClient, ShodanResult\nfrom .config import RootConfig\n\n__all__ = [\n    \"ShodanInternetDBConnector\",\n]\n\nlog = logging.getLogger(__name__)\n\n\nclass ShodanInternetDBConnector:\n    \"\"\"Shodan InternetDB connector\"\"\"\n\n    def __init__(self):\n        \"\"\"Constructor\"\"\"\n        config_path = Path(__file__).parent.parent.joinpath(\"config.yml\")\n        config = (\n            yaml.load(config_path.open(), Loader=yaml.SafeLoader)\n            if config_path.is_file()\n            else {}\n        )\n\n        self._config = RootConfig.parse_obj(config)\n        self._helper = OpenCTIConnectorHelper(config)\n\n        self._identity = self._helper.api.identity.create(\n            type=\"Organization\",\n            name=\"Shodan\",\n            description=\"Shodan is a search engine for Internet-connected devices.\",\n        )\n        self._identity_id = self._identity[\"standard_id\"]\n        self._object_marking_id = stix2.TLP_WHITE[\"id\"]\n\n        self._client = ShodanInternetDbClient(verify=self._config.shodan.ssl_verify)\n\n    def start(self) -> None:\n        \"\"\"\n        Start the connector\n        :return: None\n        \"\"\"\n        self._helper.listen(self._process_message)\n\n    def _process_message(self, data: Dict[str, Any]) -> str:\n        \"\"\"\n        Process the data message\n        :param data: Entity data\n        :return: None\n        \"\"\"\n        # Fetch the observable being processed\n        entity_id = data[\"entity_id\"]\n\n        custom_attributes = \"\"\"\n            id\n            entity_type\n            objectMarking {\n              edges {\n                node {\n                  id\n                  definition_type\n                  definition\n                }\n              }\n            }\n            observable_value\n        \"\"\"\n        observable = self._helper.api.stix_cyber_observable.read(\n            id=entity_id, customAttributes=custom_attributes\n        )\n\n        if observable is None:\n            log.error(\"Observable not found with entity_id %s\", entity_id)\n            return \"Observable not found\"\n\n        # Check TLP markings, do not submit higher than the max allowed\n        tlps = [\"TLP:CLEAR\"]\n        for marking_definition in observable.get(\"objectMarking\", []):\n            if marking_definition[\"definition_type\"] == \"TLP\":\n                tlps.append(marking_definition[\"definition\"])\n\n        for tlp in tlps:\n            max_tlp_name = self._config.shodan.max_tlp.name\n            if not OpenCTIConnectorHelper.check_max_tlp(tlp, max_tlp_name):\n                log.debug(\"Skipping observable, TLP is greater than the MAX TLP\")\n                return \"Skipping observable (TLP)\"\n\n        # Process the observable value\n        value = observable[\"observable_value\"]\n        if not validators.ipv4(value):\n            log.error(\"Observable value is not an IPv4 address\")\n            return \"Skipping observable (ipv4 validation)\"\n\n        try:\n            result = self._client.query(value)\n        except RequestException:\n            log.exception(\"Shodan API error\")\n            return \"Skipping observable (Shodan API error)\"\n\n        if result is None:\n            log.debug(\"No information available on %s\", value)\n            return \"Skipping observable (Shodan 404)\"\n\n        # Process the result\n        log.debug(\"Processing %s\", value)\n        self._process_domains(observable, result)\n        self._process_tags(observable, result)\n        self._process_vulns(observable, result)\n        self._process_note(observable, result)\n\n        return \"Success\"\n\n    def _process_note(\n        self,\n        observable: Dict[str, Any],\n        result: ShodanResult,\n    ) -> None:\n        \"\"\"\n        Add an enrichment note to the observable\n        :param observable: Observable data\n        :param result: Shodan data\n        :return: None\n        \"\"\"\n\n        def format_list(alist: List[Union[str, int]]) -> str:\n            \"\"\"Format a list of primitives into a Markdown list\"\"\"\n            return \"\".join(f\"\\n- {name}\" for name in alist) or \"n/a\"\n\n        value = observable[\"observable_value\"]\n        abstract = f\"Shodan InternetDB enrichment of {value}\"\n        content = f\"\"\"```\nShodan InternetDB:\n------------------\nHostnames: {format_list(result.hostnames)}\n------------------\nSoftware: {format_list(result.cpes)}\n------------------\nVulnerabilities: {format_list(result.vulns)}\n------------------\nPorts: {format_list(result.ports)}\n------------------\n```\n\"\"\"\n\n        self._helper.api.note.create(\n            stix_id=pycti.Note.generate_id(datetime.now().isoformat(), content),\n            createdBy=self._identity_id,\n            objectMarking=[self._object_marking_id],\n            confidence=self._helper.connect_confidence_level,\n            objects=[observable[\"id\"]],\n            authors=[self._identity_id],\n            abstract=abstract,\n            content=content,\n        )\n\n    def _process_domains(\n        self,\n        observable: Dict[str, Any],\n        result: ShodanResult,\n    ) -> None:\n        \"\"\"\n        Add additional domains to the observable\n        :param observable: Observable data\n        :param result: Shodan data\n        :return: None\n        \"\"\"\n\n        markings = observable[\"objectMarkingIds\"]\n        for name in result.hostnames:\n            log.debug(\"Adding domain %s\", name)\n            domain = self._helper.api.stix_cyber_observable.create(\n                observableData=dict(\n                    type=\"Domain-Name\",\n                    value=name,\n                ),\n                objectMarking=markings,\n                createdBy=self._identity_id,\n                update=True,\n            )\n\n            log.debug(\"Creating domain relationship\")\n            self._helper.api.stix_nested_ref_relationship.create(\n                fromId=domain[\"id\"],\n                toId=observable[\"id\"],\n                relationship_type=\"resolves-to\",\n                createdBy=self._identity_id,\n                objectMarking=markings,\n                confidence=self._helper.connect_confidence_level,\n                update=True,\n            )\n\n    def _process_tags(\n        self,\n        observable: Dict[str, Any],\n        result: ShodanResult,\n    ) -> None:\n        \"\"\"\n        Add additional tags to the observable\n        :param observable: Observable data\n        :param result: Shodan data\n        :return: None\n        \"\"\"\n\n        for name in result.tags:\n            log.debug(\"Creating label %s\", name)\n            label = self._helper.api.label.create(value=name)\n\n            log.debug(\"Adding to observable\")\n            self._helper.api.stix_cyber_observable.add_label(\n                id=observable[\"id\"],\n                label_id=label[\"id\"],\n            )\n\n    def _process_vulns(\n        self,\n        observable: Dict[str, Any],\n        result: ShodanResult,\n    ) -> None:\n        \"\"\"\n        Add additional vulnerabilities to the observable\n        :param observable: Observable data\n        :param result: Shodan data\n        :return: None\n        \"\"\"\n        now = datetime.utcnow()\n        vuln_eol = now + timedelta(days=60)\n\n        for name in result.vulns:\n            log.debug(\"Creating vulnerability %s\", name)\n            vuln = self._helper.api.vulnerability.create(\n                stix_id=pycti.Vulnerability.generate_id(name),\n                name=name,\n                createdBy=self._identity_id,\n                objectMarking=[self._object_marking_id],\n                confidence=self._helper.connect_confidence_level,\n                update=True,\n            )\n\n            log.debug(\"Creating vulnerability relationship\")\n            self._helper.api.stix_core_relationship.create(\n                fromId=observable[\"id\"],\n                toId=vuln[\"id\"],\n                relationship_type=\"related-to\",\n                createdBy=self._identity_id,\n                start_time=now.strftime(\"%Y-%m-%dT%H:%M:%SZ\"),\n                stop_time=vuln_eol.strftime(\"%Y-%m-%dT%H:%M:%SZ\"),\n                confidence=self._helper.connect_confidence_level,\n                objectMarking=[self._object_marking_id],\n                update=True,\n            )\n", "path": "internal-enrichment/shodan-internetdb/src/shodan_internetdb/connector.py"}]}