|
| 1 | +# OpenCTI Connectors Repository - Copilot Instructions |
| 2 | + |
| 3 | +## Repository Overview |
| 4 | + |
| 5 | +This is the **OpenCTI connectors** monorepo, containing 200+ Python-based connectors that integrate the OpenCTI threat intelligence platform with external tools and data sources. The repository uses a multi-connector architecture with shared utilities and strict validation pipelines. |
| 6 | + |
| 7 | +**Key Statistics:** |
| 8 | +- **Language:** Python 3.11-3.12 (Alpine-based Docker images) |
| 9 | +- **Connector Types:** 128 external-import, 53 internal-enrichment, 28 stream, 6 internal-export-file, 6 internal-import-file |
| 10 | +- **Build System:** CircleCI with dynamic pipeline generation |
| 11 | +- **Testing:** pytest with isolated virtual environments per connector |
| 12 | + |
| 13 | +## Critical Build & Validation Requirements |
| 14 | + |
| 15 | +### Code Formatting (ALWAYS REQUIRED) |
| 16 | + |
| 17 | +**Before committing any Python code changes, you MUST run both formatters:** |
| 18 | + |
| 19 | +```bash |
| 20 | +# Install formatting tools (if not already installed) |
| 21 | +pip install isort==7.0.0 black==25.12.0 --user |
| 22 | + |
| 23 | +# Run isort first |
| 24 | +isort --profile black --line-length 88 . |
| 25 | + |
| 26 | +# Then run black |
| 27 | +black . |
| 28 | +``` |
| 29 | + |
| 30 | +**Note:** The CI will fail if code is not properly formatted. These commands MUST be run before pushing code. |
| 31 | + |
| 32 | +### Linting Requirements |
| 33 | + |
| 34 | +**1. Flake8 (Basic Linting)** |
| 35 | +```bash |
| 36 | +pip install flake8 --user |
| 37 | +flake8 --ignore=E,W . |
| 38 | +``` |
| 39 | + |
| 40 | +**2. Custom Pylint Plugin (STIX ID Validation - CRITICAL)** |
| 41 | + |
| 42 | +This custom checker ensures STIX2 objects use deterministic IDs. **ALWAYS run this before committing connector code:** |
| 43 | + |
| 44 | +```bash |
| 45 | +cd shared/pylint_plugins/check_stix_plugin |
| 46 | +pip install -r requirements.txt |
| 47 | + |
| 48 | +# Run on your connector directory (example for external-import/mycconnector) |
| 49 | +PYTHONPATH=. python -m pylint ../../../external-import/myconnector \ |
| 50 | + --disable=all \ |
| 51 | + --enable=no_generated_id_stix,no-value-for-parameter,unused-import \ |
| 52 | + --load-plugins linter_stix_id_generator |
| 53 | +``` |
| 54 | + |
| 55 | +**Common Issue:** If you create STIX2 objects, you MUST use deterministic ID generation via pycti or the new connectors-sdk models. Never let stix2 library auto-generate IDs. |
| 56 | + |
| 57 | +### Running Tests |
| 58 | + |
| 59 | +**Test Structure:** Each connector with tests has a `tests/test-requirements.txt` file. Tests run in isolated virtual environments. |
| 60 | + |
| 61 | +**To run tests for a specific connector:** |
| 62 | + |
| 63 | +```bash |
| 64 | +# Run test script with specific test-requirements.txt |
| 65 | +bash run_test.sh ./external-import/myconnector/tests/test-requirements.txt |
| 66 | +``` |
| 67 | + |
| 68 | +**Important Notes:** |
| 69 | +- The test script (`run_test.sh`) checks for changes from `master` branch |
| 70 | +- Tests only run if connector or connectors-sdk has changes (on non-master branches) |
| 71 | +- Test script installs latest pycti from GitHub master branch |
| 72 | +- If connector depends on connectors-sdk, it installs local version |
| 73 | +- Test output goes to `test_outputs/` directory |
| 74 | +- Tests use pytest with JUnit XML output |
| 75 | + |
| 76 | +**Test Dependencies Common Pattern:** |
| 77 | +``` |
| 78 | +pytest |
| 79 | +pycti |
| 80 | +connectors-sdk @ git+https://github.com/OpenCTI-Platform/connectors.git@master#subdirectory=connectors-sdk |
| 81 | +``` |
| 82 | + |
| 83 | +## Repository Structure |
| 84 | + |
| 85 | +### Top-Level Directories |
| 86 | + |
| 87 | +``` |
| 88 | +├── .circleci/ # CI/CD configuration |
| 89 | +│ ├── config.yml # Main CircleCI workflow |
| 90 | +│ ├── scripts/ # Dynamic pipeline generation (generate_ci.py) |
| 91 | +│ └── vars.yml # Connector-specific build configurations |
| 92 | +├── .github/ # GitHub workflows & templates |
| 93 | +├── connectors-sdk/ # Shared SDK for connector development (Python 3.11+) |
| 94 | +├── external-import/ # 128 connectors for importing external threat intel |
| 95 | +├── internal-enrichment/ # 53 connectors for enriching existing data |
| 96 | +├── internal-export-file/ # 6 connectors for exporting data files |
| 97 | +├── internal-import-file/ # 6 connectors for importing data files |
| 98 | +├── stream/ # 28 connectors for streaming data |
| 99 | +├── shared/ # Shared utilities |
| 100 | +│ ├── pylint_plugins/ # Custom pylint plugins (STIX ID checker) |
| 101 | +│ └── tools/ # Manifest/schema generation scripts |
| 102 | +├── templates/ # Connector templates for each type |
| 103 | +└── tests/ # Repository-level tests |
| 104 | +``` |
| 105 | + |
| 106 | +### Standard Connector Structure |
| 107 | + |
| 108 | +Every connector follows this pattern: |
| 109 | + |
| 110 | +``` |
| 111 | +external-import/myconnector/ |
| 112 | +├── __metadata__/ |
| 113 | +│ ├── connector_manifest.json # Connector metadata (title, description, etc.) |
| 114 | +│ ├── connector_config_schema.json # Config JSON schema (auto-generated) |
| 115 | +│ └── logo.png # Connector logo |
| 116 | +├── src/ |
| 117 | +│ ├── connector/ # Main logic |
| 118 | +│ │ ├── connector.py # Core connector class |
| 119 | +│ │ ├── converter_to_stix.py # STIX conversion logic |
| 120 | +│ │ └── settings.py # Config validation |
| 121 | +│ ├── main.py # Entry point |
| 122 | +│ └── requirements.txt # Python dependencies |
| 123 | +├── tests/ |
| 124 | +│ ├── tests_connector/ # Test modules |
| 125 | +│ ├── conftest.py # pytest configuration |
| 126 | +│ └── test-requirements.txt # Test dependencies |
| 127 | +├── .env.sample # Environment variable template |
| 128 | +├── docker-compose.yml # Docker deployment config |
| 129 | +├── Dockerfile # Container build |
| 130 | +├── entrypoint.sh # Container entrypoint |
| 131 | +└── README.md # Connector documentation |
| 132 | +``` |
| 133 | + |
| 134 | +## Creating New Connectors |
| 135 | + |
| 136 | +**Use the provided script to scaffold a new connector:** |
| 137 | + |
| 138 | +```bash |
| 139 | +cd templates |
| 140 | +sh create_connector_dir.sh -t <TYPE> -n <NAME> |
| 141 | +``` |
| 142 | + |
| 143 | +**Available types:** `external-import`, `internal-enrichment`, `stream`, `internal-import-file`, `internal-export-file` |
| 144 | + |
| 145 | +**After creating, update these files:** |
| 146 | +1. Replace all `Template`/`template` references with your connector name |
| 147 | +2. Update `__metadata__/connector_manifest.json` with accurate information |
| 148 | +3. Configure environment variables in `.env.sample` |
| 149 | +4. Implement connector logic in `src/connector/connector.py` |
| 150 | + |
| 151 | +## Key Configuration Files |
| 152 | + |
| 153 | +### Root Level |
| 154 | + |
| 155 | +- **`.flake8`** - Flake8 configuration (ignores: E203, E266, E501, W503, F403, F401) |
| 156 | +- **`.pre-commit-config.yaml`** - Pre-commit hooks (black, flake8, isort, GPG signing) |
| 157 | +- **`.pylintrc`** - Pylint configuration |
| 158 | +- **`ci-requirements.txt`** - CI dependencies: `isort==7.0.0 black==25.12.0 pytest==8.4.2` |
| 159 | +- **`Makefile`** - Manifest and schema generation commands |
| 160 | +- **`run_test.sh`** - Test execution script (checks for changes, runs pytest) |
| 161 | +- **`manifest.json`** - Global manifest (auto-generated from all connector manifests) |
| 162 | + |
| 163 | +### CircleCI Pipeline |
| 164 | + |
| 165 | +**Workflow Steps:** |
| 166 | +1. **ensure_formatting** - Runs isort and black checks (Python 3.12) |
| 167 | +2. **base_linter** - Runs flake8 with `--ignore=E,W` |
| 168 | +3. **linter** - Runs custom pylint plugin for STIX ID validation |
| 169 | +4. **test** - Runs pytest for changed connectors (parallelism: 4, Python 3.11) |
| 170 | +5. **build_manifest** - Generates manifest.json and config schemas |
| 171 | +6. **build** - Builds Docker images for changed connectors |
| 172 | + |
| 173 | +**Important:** Tests and builds only run for connectors with changes (unless on master or connectors-sdk changed). |
| 174 | + |
| 175 | +## Connectors SDK |
| 176 | + |
| 177 | +**Location:** `connectors-sdk/` |
| 178 | + |
| 179 | +**Purpose:** Provides models, exceptions, and utilities for building connectors. |
| 180 | + |
| 181 | +**Installation:** |
| 182 | +```bash |
| 183 | +pip install "connectors-sdk @ git+https://github.com/OpenCTI-Platform/connectors.git@master#subdirectory=connectors-sdk" |
| 184 | +``` |
| 185 | + |
| 186 | +**Key Features:** |
| 187 | +- STIX2.1 compliant models with deterministic IDs |
| 188 | +- Pre-built classes for IOCs, Authors, Markings, Relationships |
| 189 | +- Exception handling utilities |
| 190 | +- Pydantic-based configuration validation |
| 191 | + |
| 192 | +**Example Usage:** |
| 193 | +```python |
| 194 | +from connectors_sdk.models import IPV4Address, OrganizationAuthor, TLPMarking |
| 195 | +from connectors_sdk.models.octi import related_to |
| 196 | + |
| 197 | +author = OrganizationAuthor(name="Example Author") |
| 198 | +ip = IPV4Address(value="127.0.0.1", author=author, markings=[TLPMarking(level="amber+strict")]) |
| 199 | +stix_object = ip.to_stix2_object() # Deterministic ID generated |
| 200 | +``` |
| 201 | + |
| 202 | +## Dockerfile Pattern |
| 203 | + |
| 204 | +All connectors use Alpine-based Python images: |
| 205 | + |
| 206 | +```dockerfile |
| 207 | +FROM python:3.12-alpine |
| 208 | +ENV CONNECTOR_TYPE=EXTERNAL_IMPORT |
| 209 | + |
| 210 | +COPY src /opt/opencti-connector-name |
| 211 | + |
| 212 | +RUN apk --no-cache add git build-base libmagic libffi-dev && \ |
| 213 | + cd /opt/opencti-connector-name && \ |
| 214 | + pip3 install --no-cache-dir -r requirements.txt && \ |
| 215 | + apk del git build-base |
| 216 | + |
| 217 | +COPY entrypoint.sh / |
| 218 | +RUN chmod +x /entrypoint.sh |
| 219 | +ENTRYPOINT ["/entrypoint.sh"] |
| 220 | +``` |
| 221 | + |
| 222 | +**Note:** Some connectors use Python 3.11 (see `.circleci/vars.yml` for exceptions). |
| 223 | + |
| 224 | +## Common Issues & Workarounds |
| 225 | + |
| 226 | +### Issue: Test Script Fails with "fatal: Not a valid object name origin/master" |
| 227 | +**Workaround:** The script expects `origin/master` remote ref. In CI/local environments with shallow clones, you may need to fetch master branch first. |
| 228 | + |
| 229 | +### Issue: Tests Not Running |
| 230 | +**Cause:** Test script only runs tests for connectors with changes (detected via git diff). |
| 231 | +**Workaround:** Set `CIRCLE_BRANCH=master` to force all tests to run, or make a change in the connector directory. |
| 232 | + |
| 233 | +### Issue: Pylint Plugin Fails |
| 234 | +**Common Cause:** Creating STIX2 objects without deterministic IDs. |
| 235 | +**Fix:** Use pycti's `generate_id()` methods or connectors-sdk models. |
| 236 | + |
| 237 | +### Issue: Import/Dependency Errors During Tests |
| 238 | +**Cause:** Tests install latest pycti from GitHub, which may have breaking changes. |
| 239 | +**Workaround:** Pin specific pycti version in test-requirements.txt if needed. |
| 240 | + |
| 241 | +## Manifest & Schema Generation |
| 242 | + |
| 243 | +**Commands (defined in Makefile):** |
| 244 | + |
| 245 | +```bash |
| 246 | +# Generate single connector manifest |
| 247 | +make connector_manifest |
| 248 | + |
| 249 | +# Generate all connector manifests |
| 250 | +make connectors_manifests |
| 251 | + |
| 252 | +# Generate single connector config schema |
| 253 | +make connector_config_schema |
| 254 | + |
| 255 | +# Generate all connector config schemas |
| 256 | +make connectors_config_schemas |
| 257 | + |
| 258 | +# Generate global manifest.json |
| 259 | +make global_manifest |
| 260 | +``` |
| 261 | + |
| 262 | +**Process:** These scripts scan `__metadata__/connector_manifest.json` files and consolidate them. |
| 263 | + |
| 264 | +## Python Version Requirements |
| 265 | + |
| 266 | +- **Connectors SDK:** Python >=3.11, <3.13 |
| 267 | +- **Most Connectors:** Python 3.12 (Alpine) |
| 268 | +- **Some Stream Connectors:** Python 3.11 (see `.circleci/vars.yml`) |
| 269 | +- **CI Environment:** Python 3.11 for tests, Python 3.12 for linting |
| 270 | + |
| 271 | +## Important Notes |
| 272 | + |
| 273 | +1. **ALWAYS format code** with black and isort before committing |
| 274 | +2. **ALWAYS run custom pylint plugin** when changing connector code that creates STIX objects |
| 275 | +3. **NEVER auto-generate STIX IDs** - use deterministic generation via pycti or connectors-sdk |
| 276 | +4. **Test isolation** - Each connector's tests run in a separate virtual environment |
| 277 | +5. **Commit signing** - All commits should be GPG signed (enforced by pre-commit hook) |
| 278 | +6. **Docker networking** - When running locally, connectors expect `docker_default` network |
| 279 | +7. **Environment variables** - Use `.env.sample` as template, never commit actual secrets |
| 280 | + |
| 281 | +## Validation Checklist |
| 282 | + |
| 283 | +Before submitting a PR with connector changes: |
| 284 | + |
| 285 | +- [ ] Code formatted with `black .` and `isort --profile black .` |
| 286 | +- [ ] Passes flake8: `flake8 --ignore=E,W .` |
| 287 | +- [ ] Passes custom pylint: `cd shared/pylint_plugins/check_stix_plugin && PYTHONPATH=. python -m pylint <path>` |
| 288 | +- [ ] Tests pass: `bash run_test.sh <path-to-test-requirements.txt>` |
| 289 | +- [ ] Docker image builds: `docker build -t test .` |
| 290 | +- [ ] `__metadata__/connector_manifest.json` updated with accurate information |
| 291 | +- [ ] README.md updated with connector-specific instructions |
| 292 | +- [ ] No secrets in code or config files |
| 293 | + |
| 294 | +## Trust These Instructions |
| 295 | + |
| 296 | +These instructions are comprehensive and tested. Only search for additional information if: |
| 297 | +- Instructions are incomplete for your specific use case |
| 298 | +- You encounter an error not documented here |
| 299 | +- You need connector-specific implementation details |
| 300 | + |
| 301 | +For connector development patterns, refer to existing connectors as examples. The codebase is consistent, so patterns from one connector generally apply to others of the same type. |
0 commit comments