import json, os, sys S = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}")) I = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}")) def out(value): print(json.dumps(value, default=str)) def fail(message, **extra): payload = {"error": message} payload.update(extra) print(json.dumps(payload, default=str)) sys.exit(1) def as_bool(v): return v if isinstance(v, bool) else str(v).lower() in ("1", "true", "yes") def as_int(v, default=None): try: return int(v) except (TypeError, ValueError): return default def as_list(v): if isinstance(v, list): return v if v in (None, ""): return [] return [x.strip() for x in str(v).split(",") if x.strip()] try: from pycti import OpenCTIApiClient except ImportError as e: fail("The 'pycti' Python library is required for the OpenCTI integration. " "Install it on the execution host (engine): pip install pycti", detail=str(e)) def client(): base = str(S.get("base_url", "")).strip().rstrip("/") api_key = S.get("api_key") or (S.get("credentials") or {}).get("password") return OpenCTIApiClient(base, api_key, ssl_verify=not as_bool(S.get("insecure")), log_level="error") TYPES_TO_OPENCTI = { "account": "User-Account", "domain": "Domain-Name", "email": "Email-Addr", "file-md5": "StixFile", "file-sha1": "StixFile", "file-sha256": "StixFile", "file": "StixFile", "host": "X-OpenCTI-Hostname", "ip": "IPv4-Addr", "ipv6": "IPv6-Addr", "registry key": "Windows-Registry-Key", "url": "Url", } OBSERVABLE_TYPE_TO_STIX_PATTERN = { "IPv4-Addr": "[ipv4-addr:value = '{{indicator}}']", "IPv6-Addr": "[ipv6-addr:value = '{{indicator}}']", "Domain-Name": "[domain-name:value = '{{indicator}}']", "Url": "[url:value = '{{indicator}}']", "Email-Addr": "[email-addr:value = '{{indicator}}']", "StixFile": "[file:hashes.'SHA-256' = '{{indicator}}']", "Process": "[process:pid = '{{indicator}}']", "User-Account": "[user-account:user_id = '{{indicator}}']", "Windows-Registry-Key": "[windows-registry-key:key = '{{indicator}}']", } def build_stix_pattern(indicator, observable_type): if observable_type not in OBSERVABLE_TYPE_TO_STIX_PATTERN: fail(f"Invalid observable type: {observable_type}") return OBSERVABLE_TYPE_TO_STIX_PATTERN[observable_type].replace("{{indicator}}", indicator) name = I.get("name") indicator = I.get("indicator") main_observable_type = TYPES_TO_OPENCTI.get(str(I.get("main_observable_type", "")).lower(), I.get("main_observable_type")) description = I.get("description") valid_from = I.get("valid_from") valid_until = I.get("valid_until") created_by = I.get("created_by") label_id = I.get("label_id") marking_id = I.get("marking_id") external_references_id = I.get("external_references_id") pattern = build_stix_pattern(indicator, main_observable_type) try: result = client().indicator.create( name=name, description=description, pattern=pattern, pattern_type="stix", x_opencti_main_observable_type=main_observable_type, indicator_types=as_list(I.get("indicator_types")), confidence=as_int(I.get("confidence"), 50), x_opencti_score=as_int(I.get("score"), 50), valid_from=valid_from, valid_until=valid_until, createdBy=created_by, objectLabel=label_id, objectMarking=marking_id, externalReferences=external_references_id, x_opencti_create_observables=as_bool(I.get("create_observables")), ) except Exception as e: fail("Can't create indicator in OpenCTI.", detail=str(e)) out({"id": result.get("id")})