feat(stairwell): add Stairwell internal-enrichment connector (#7095) - #7097
feat(stairwell): add Stairwell internal-enrichment connector (#7095)#7097zachdaniel-SW wants to merge 3 commits into
Conversation
Contributor License AgreementHey @zachdaniel-SW! Thank you for your contribution to Filigran! Before we can merge this pull request, we need you to sign our Contributor License Agreement (CLA). Why do we need a CLA?The CLA helps protect both you and Filigran. It ensures that:
How to signYou can sign the CLA using either of these methods:
Once signed, this comment will be automatically updated. ❌ CLA not signed yet This is an automated message from the Filigran CLA Bot. If you have questions, please contact the maintainers. |
|
Video of connector - https://fathom.video/share/GYDfsKAdPCe722ar7Uvyqzo6dAP-EZMt |
There was a problem hiding this comment.
Pull request overview
Adds a new internal-enrichment/stairwell connector implementing Stairwell-based enrichment for OpenCTI observables (files, domains, IPs, ASNs), emitting results via STIX 2.1 bundles with notes/relationships/custom properties.
Changes:
- Introduces a Stairwell API client + enrichment dispatchers for file/domain/ip/asn observables.
- Adds STIX bundle/object construction utilities and scoring policy (monotonic OpenCTI score mapping).
- Adds connector packaging (Dockerfile, compose, entrypoint, metadata) and a comprehensive unit test suite.
Reviewed changes
Copilot reviewed 27 out of 29 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| internal-enrichment/stairwell/src/tests/test-requirements.txt | Test dependency list for Stairwell connector tests. |
| internal-enrichment/stairwell/src/tests/test_stix_builder.py | Unit tests for STIX object/bundle builder helpers. |
| internal-enrichment/stairwell/src/tests/test_score_policy.py | Unit tests for MalEval probability→score mapping and monotonic scoring. |
| internal-enrichment/stairwell/src/tests/test_network_enrichers.py | Unit tests for domain/IP/ASN enrichment logic and emitted STIX shape. |
| internal-enrichment/stairwell/src/tests/test_file_enricher.py | Unit tests for file enrichment behavior (notes, variants, sightings, exclusions). |
| internal-enrichment/stairwell/src/tests/conftest.py | pytest path setup so connector.* imports resolve in tests. |
| internal-enrichment/stairwell/src/requirements.txt | Runtime dependencies for the Stairwell connector. |
| internal-enrichment/stairwell/src/main.py | Connector entrypoint wiring settings → helper → connector run loop. |
| internal-enrichment/stairwell/src/connector/stix_builder.py | STIX 2.1 ID + object constructors and bundle serialization. |
| internal-enrichment/stairwell/src/connector/stairwell/score.py | MalEval bucket scoring utilities. |
| internal-enrichment/stairwell/src/connector/stairwell/client.py | Stairwell HTTP client (V1/V2 endpoints + retries + UI deep links). |
| internal-enrichment/stairwell/src/connector/stairwell/init.py | Stairwell subpackage exports. |
| internal-enrichment/stairwell/src/connector/settings.py | Pydantic/connectors-sdk settings models for connector + Stairwell config. |
| internal-enrichment/stairwell/src/connector/ip_enricher.py | IP enrichment implementation (geo/cloud/asn/whois + note emission). |
| internal-enrichment/stairwell/src/connector/file_enricher.py | File enrichment implementation (metadata, notes, variants, sightings, related IOCs). |
| internal-enrichment/stairwell/src/connector/enricher.py | Dispatcher routing OpenCTI entity types to the correct enricher. |
| internal-enrichment/stairwell/src/connector/domain_enricher.py | Domain enrichment implementation (DNS resolutions + whitelist + note emission). |
| internal-enrichment/stairwell/src/connector/connector.py | OpenCTI connector wrapper (TLP gating + message processing + dispatch). |
| internal-enrichment/stairwell/src/connector/asn_enricher.py | ASN enrichment implementation (WHOIS parsing + note emission). |
| internal-enrichment/stairwell/src/connector/init.py | Package marker for connector module. |
| internal-enrichment/stairwell/src/config.yml.sample | Sample YAML configuration for local runs. |
| internal-enrichment/stairwell/README.md | Connector documentation (capabilities, configuration, behavior, install/test). |
| internal-enrichment/stairwell/entrypoint.sh | Container entrypoint script. |
| internal-enrichment/stairwell/Dockerfile | Container build definition for the connector. |
| internal-enrichment/stairwell/docker-compose.yml | Example compose service definition for deployment. |
| internal-enrichment/stairwell/.env.test.sample | Sample env file for local manual testing. |
| internal-enrichment/stairwell/.dockerignore | Docker ignore rules for packaging. |
| internal-enrichment/stairwell/metadata/connector_manifest.json | Connector metadata manifest for the OpenCTI connectors catalog. |
| make_relationship( | ||
| source_id=source_entity_id, | ||
| target_id=sco["id"], | ||
| relationship_type="derived-from", | ||
| tlp=self.tlp, | ||
| ) |
| def stix_id(stix_type: str, seed: str) -> str: | ||
| return f"{stix_type}--{uuid.uuid5(_STIX_NAMESPACE, seed)}" |
There was a problem hiding this comment.
Thanks — this is intentional. We mint deterministic UUIDv5 SCO IDs from the STIX 2.1 namespace so re-enrichment and cross-run dedup stay stable. OpenCTI recomputes each observable's canonical standard_id from its value on ingest, so duplicates by value shouldn't occur regardless of the incoming id. We're confirming that in live end-to-end testing on 6.8.x; if any duplication surfaces, we'll switch SCO ID generation to STIX2 canonicalization (per the rst-whois example) before requesting final review.
| | Connector name | `connector.name` | `CONNECTOR_NAME` | yes | `Stairwell` | Display name in OpenCTI | | ||
| | Connector scope | `connector.scope` | `CONNECTOR_SCOPE` | yes | `StixFile,Domain-Name,IPv4-Addr,IPv6-Addr,Autonomous-System` | Observable types this connector handles | | ||
| | Auto enrich | `connector.auto` | `CONNECTOR_AUTO` | no | `false` | Enrich every newly-created in-scope observable | | ||
| | Log level | `connector.log_level` | `CONNECTOR_LOG_LEVEL` | no | `info` | `debug`, `info`, `warn`, `error` | |
Proposed changes
Adds
internal-enrichment/stairwell, an INTERNAL_ENRICHMENT connector thatenriches StixFile, Domain-Name, IPv4-Addr, IPv6-Addr, and Autonomous-System
observables with Stairwell intelligence (MalEval verdict + monotonic score, AI
File Triage note, file metadata, YARA matches, variant discovery with
derived-from links, per-asset sightings, DNS history, IP intel, WHOIS).
Built to the current template: connectors-sdk + Pydantic settings, manifest,
entrypoint, thin main.py, playbook-compatible. STIX emitted via bundles with
deterministic IDs; no direct API writes. Detonation data intentionally excluded.
Related issues
Close #7095
Checklist
Further comments
Unit tests: 41 passing (
pytest tests/). Config layer verified against realpycti 7.260722.0 + connectors-sdk. Live end-to-end validation on OpenCTI 6.8.x
in progress — will confirm the "tested" box before requesting final review.