diff --git a/external-import/crowdstrike/README.md b/external-import/crowdstrike/README.md index c76c4298ddb..d6e59b48a9a 100644 --- a/external-import/crowdstrike/README.md +++ b/external-import/crowdstrike/README.md @@ -78,7 +78,7 @@ There are a number of configuration options, which are set either in `docker-com | Client ID | crowdstrike.client_id | `CROWDSTRIKE_CLIENT_ID` | | Yes | CrowdStrike API Client ID. | | Client Secret | crowdstrike.client_secret | `CROWDSTRIKE_CLIENT_SECRET` | | Yes | CrowdStrike API Client Secret. | | TLP | crowdstrike.tlp | `CROWDSTRIKE_TLP` | amber+strict | No | TLP marking for imported data. | -| Create Observables | crowdstrike.create_observables | `CROWDSTRIKE_CREATE_OBSERVABLES` | true | No | Create observables from indicators. | +| Create Observables | crowdstrike.create_observables | `CROWDSTRIKE_CREATE_OBSERVABLES` | true | No | Create observables from indicators and from optional report IOC extraction. | | Create Indicators | crowdstrike.create_indicators | `CROWDSTRIKE_CREATE_INDICATORS` | true | No | Create indicators. | | Scopes | crowdstrike.scopes | `CROWDSTRIKE_SCOPES` | actor,report,indicator,yara_master | No | Comma-separated list of data types to import. | | MITRE ATT&CK Version | crowdstrike.attack_version | `CROWDSTRIKE_ATTACK_VERSION` | 17.1 | No | MITRE ATT&CK Enterprise version used to resolve label-derived ATT&CK techniques to canonical MITRE IDs. Should match the version imported by the MITRE ATT&CK external import connector. | @@ -94,7 +94,7 @@ There are a number of configuration options, which are set either in `docker-com | Report Target Industries | crowdstrike.report_target_industries | `CROWDSTRIKE_REPORT_TARGET_INDUSTRIES` | | No | Filter reports by target industry. | | Report Guess Malware | crowdstrike.report_guess_malware | `CROWDSTRIKE_REPORT_GUESS_MALWARE` | false | No | Use report tags to guess malware. | | Report Guess Relations | crowdstrike.report_guess_relations | `CROWDSTRIKE_REPORT_GUESS_RELATIONS` | false | No | Auto-create relationships between entities. | -| Report Extract IOCs | crowdstrike.report_extract_iocs | `CROWDSTRIKE_REPORT_EXTRACT_IOCS` | | No | Comma-separated list of IOC types to extract from report text as Observables. Supported: `ipv4`, `ipv6`, `domain`, `url`, `md5`, `sha1`, `sha256`. Empty to disable. | +| Report Extract IOCs | crowdstrike.report_extract_iocs | `CROWDSTRIKE_REPORT_EXTRACT_IOCS` | | No | Comma-separated list of IOC types to extract from report text as Observables. Supported: `ipv4`, `ipv6`, `domain`, `url`, `md5`, `sha1`, `sha256`. Empty to disable. Requires `CROWDSTRIKE_CREATE_OBSERVABLES=true`. | | Indicator Start Timestamp | crowdstrike.indicator_start_timestamp | `CROWDSTRIKE_INDICATOR_START_TIMESTAMP` | (30 days ago) | No | Unix timestamp. Empty = 30 days ago. 0 = ALL indicators. | | Indicator Exclude Types | crowdstrike.indicator_exclude_types | `CROWDSTRIKE_INDICATOR_EXCLUDE_TYPES` | hash_ion,hash_md5,hash_sha1,password,username | No | Comma-separated indicator types to exclude. | | Default Score | crowdstrike.default_x_opencti_score | `CROWDSTRIKE_DEFAULT_X_OPENCTI_SCORE` | 50 | No | Default x_opencti_score for indicators. | diff --git a/external-import/crowdstrike/__metadata__/CONNECTOR_CONFIG_DOC.md b/external-import/crowdstrike/__metadata__/CONNECTOR_CONFIG_DOC.md index f9a5ce20554..220de29f756 100644 --- a/external-import/crowdstrike/__metadata__/CONNECTOR_CONFIG_DOC.md +++ b/external-import/crowdstrike/__metadata__/CONNECTOR_CONFIG_DOC.md @@ -17,7 +17,7 @@ Below is an exhaustive enumeration of all configurable parameters available, eac | CONNECTOR_DURATION_PERIOD | `string` | | Format: [`duration`](https://json-schema.org/understanding-json-schema/reference/string#built-in-formats) | | `"PT1H"` | ISO8601 Duration format starting with 'P' for Period (e.g., 'PT30M' for 30 minutes). | | CROWDSTRIKE_BASE_URL | `string` | | Format: [`uri`](https://json-schema.org/understanding-json-schema/reference/string#built-in-formats) | | `"https://api.crowdstrike.com/"` | CrowdStrike API base URL. | | CROWDSTRIKE_TLP | `string` | | `red` `amber+strict` `amber` `green` `clear` `white` | | `"amber+strict"` | Default Traffic Light Protocol (TLP) marking for imported data. | -| CROWDSTRIKE_CREATE_OBSERVABLES | `boolean` | | boolean | | `true` | Whether to create observables in OpenCTI. | +| CROWDSTRIKE_CREATE_OBSERVABLES | `boolean` | | boolean | | `true` | Whether to create observables in OpenCTI (indicator-derived observables and report IOC extraction observables). | | CROWDSTRIKE_CREATE_INDICATORS | `boolean` | | boolean | | `true` | Whether to create indicators in OpenCTI. | | CROWDSTRIKE_SCOPES | `array` | | string | | `["actor", "report", "indicator", "malware", "yara_master", "snort_suricata_master"]` | Comma-separated list of scopes to enable. Available: actor, report, indicator, malware, vulnerability, yara_master, snort_suricata_master. | | CROWDSTRIKE_ATTACK_VERSION | `string` | | string | | `"17.1"` | MITRE ATT&CK Enterprise version to use for technique ID resolution (e.g., 17.1). Should match the version imported by the MITRE ATT&CK external import connector. | @@ -31,7 +31,7 @@ Below is an exhaustive enumeration of all configurable parameters available, eac | CROWDSTRIKE_REPORT_TARGET_INDUSTRIES | `array` | | string | | `[]` | Comma-separated list of target industries to filter reports. | | CROWDSTRIKE_REPORT_GUESS_MALWARE | `boolean` | | boolean | | `false` | Whether to use report tags to guess related malware. | | CROWDSTRIKE_REPORT_GUESS_RELATIONS | `boolean` | | boolean | | `false` | Whether to automatically guess and create relationships in reports. | -| CROWDSTRIKE_REPORT_EXTRACT_IOCS | `array` | | string | | `[]` | Comma-separated list of IOC types to extract from report text content via regex. Extracted IOCs are created as Observables (not Indicators) and linked to the report. Supported types: ipv4, ipv6, domain, url, md5, sha1, sha256. Leave empty to disable. | +| CROWDSTRIKE_REPORT_EXTRACT_IOCS | `array` | | string | | `[]` | Comma-separated list of IOC types to extract from report text content via regex. Extracted IOCs are created as Observables (not Indicators) and linked to the report. Supported types: ipv4, ipv6, domain, url, md5, sha1, sha256. Leave empty to disable. Requires CROWDSTRIKE_CREATE_OBSERVABLES=true. | | CROWDSTRIKE_INDICATOR_START_TIMESTAMP | `integer` | | integer | | | Unix timestamp from which to start importing indicators. Default is 30 days ago. BEWARE: 0 means ALL indicators! | | CROWDSTRIKE_INDICATOR_EXCLUDE_TYPES | `array` | | string | | `["hash_ion", "hash_md5", "hash_sha1", "password", "username"]` | Comma-separated list of indicator types to exclude from import. | | CROWDSTRIKE_INDICATOR_IP_MAX_AGE | `string` | | Format: [`duration`](https://json-schema.org/understanding-json-schema/reference/string#built-in-formats) | | `null` | ISO8601 Duration format starting with 'P' for Period (e.g., 'P90D' for 90 days). Covers both IPv4 and IPv6. | diff --git a/external-import/crowdstrike/src/crowdstrike_feeds_connector/report/importer.py b/external-import/crowdstrike/src/crowdstrike_feeds_connector/report/importer.py index 4c895409a26..c2fb3de6529 100644 --- a/external-import/crowdstrike/src/crowdstrike_feeds_connector/report/importer.py +++ b/external-import/crowdstrike/src/crowdstrike_feeds_connector/report/importer.py @@ -486,6 +486,9 @@ def _create_report_bundle( def _extract_iocs_from_report(self, report) -> list[_Observable]: """Extract IOCs from report text content and create STIX observables.""" + if not self.indicator_config.get("create_observables", True): + return [] + if not self.report_extract_iocs: return [] diff --git a/external-import/crowdstrike/tests/ioc_extraction/test_report_ioc_extraction.py b/external-import/crowdstrike/tests/ioc_extraction/test_report_ioc_extraction.py index a4c7c469449..5320a394e78 100644 --- a/external-import/crowdstrike/tests/ioc_extraction/test_report_ioc_extraction.py +++ b/external-import/crowdstrike/tests/ioc_extraction/test_report_ioc_extraction.py @@ -199,3 +199,54 @@ def test_report_with_iocs_in_description_produces_observables( assert "ipv4-addr" in bundle_types assert "domain-name" in bundle_types assert "file" in bundle_types # SHA-256 creates a File observable + + +def _build_importer_for_ioc_extraction( + author_identity: Identity, tlp_marking: MarkingDefinition, create_observables: bool +) -> ReportImporter: + importer = ReportImporter.__new__(ReportImporter) + importer.author = author_identity + importer.tlp_marking = tlp_marking + importer.indicator_config = {"create_observables": create_observables} + importer.report_extract_iocs = ["ipv4", "domain", "sha256"] + importer._info = lambda *args, **kwargs: None + importer._warning = lambda *args, **kwargs: None + return importer + + +def test_extract_iocs_respects_create_observables_disabled( + author_identity, tlp_marking +): + importer = _build_importer_for_ioc_extraction( + author_identity, tlp_marking, create_observables=False + ) + + report = { + "description": ( + "45.33.32.156 evil.example.com " + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ) + } + + observables = importer._extract_iocs_from_report(report) + assert observables == [] + + +def test_extract_iocs_when_create_observables_enabled(author_identity, tlp_marking): + importer = _build_importer_for_ioc_extraction( + author_identity, tlp_marking, create_observables=True + ) + + report = { + "description": ( + "45.33.32.156 evil.example.com " + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ) + } + + observables = importer._extract_iocs_from_report(report) + observable_types = {observable.type for observable in observables} + + assert "ipv4-addr" in observable_types + assert "domain-name" in observable_types + assert "file" in observable_types