Add Cuckoo Sandbox / CAPEv2 connector with dual API support#128
Add Cuckoo Sandbox / CAPEv2 connector with dual API support#128
Conversation
New gnat/connectors/cuckoo/ connector supporting both Cuckoo 2.x (/api/) and CAPEv2/3.x (/apiv2/) APIs with auto-detection at authenticate() time. Bearer token auth, sandbox_report_envelope() for STIX observed-data, IOC extraction from network/dropped/signatures sections, score-based verdict mapping, and domain helpers for file/URL submission. Platform count: 158 → 159. 22 new tests. https://claude.ai/code/session_01H5UbjsuiiGya5n1eUCxoaR
There was a problem hiding this comment.
Pull request overview
Adds a new cuckoo connector to GNAT to integrate with Cuckoo Sandbox 2.x and CAPEv2/3.x, including STIX translation and IOC extraction, plus docs/config/registry wiring so it becomes an officially supported platform.
Changes:
- Introduce
CuckooClientwith dual API prefix support (/apivs/apiv2) and STIX mappings for observed-data, malware, and indicators. - Register the new connector in
CLIENT_REGISTRYand add example config + documentation/changelog updates (158 → 159). - Add unit tests covering authentication, CRUD-ish reads, submission helpers, STIX conversions, and IOC extraction.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
gnat/connectors/cuckoo/client.py |
New connector client implementation (auth/version detection, CRUD read helpers, STIX/IOC logic). |
gnat/connectors/cuckoo/__init__.py |
Exposes CuckooClient for package import. |
gnat/clients/__init__.py |
Registers/imports the new cuckoo connector for runtime instantiation. |
config/config.ini.example |
Adds [cuckoo] configuration example. |
tests/unit/connectors/test_connectors.py |
Adds TestCuckooClient test coverage for the new connector. |
README.md |
Updates connector count and lists cuckoo among supported platforms. |
CLAUDE.md |
Updates repo documentation to include the new connector and updated count. |
CHANGELOG.md |
Documents the addition and features of the connector. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def authenticate(self) -> None: | ||
| if not self.api_key: | ||
| raise GNATClientError( | ||
| "Cuckoo connector requires api_key in config." | ||
| ) | ||
| self._auth_headers["Authorization"] = f"Bearer {self.api_key}" | ||
|
|
||
| if self._api_version is None: | ||
| self._detect_version() | ||
| else: | ||
| self._prefix = "/apiv2" if self._api_version == "3" else "/api" | ||
|
|
||
| def _detect_version(self) -> None: | ||
| try: | ||
| self.get("/apiv2/cuckoo/status/") | ||
| self._api_version = "3" | ||
| self._prefix = "/apiv2" |
There was a problem hiding this comment.
authenticate() calls _detect_version(), and _detect_version() uses self.get(...). With BaseClient, any get/post/... call when _authenticated is still False will re-enter authenticate(), causing infinite recursion on the first real request when api_version is omitted. Fix by ensuring the client marks itself authenticated (or otherwise bypasses BaseClient’s auth gate) before performing version-probe HTTP calls, e.g., set _authenticated prior to probing or issue the probe via the underlying pool manager instead of self.get().
| except Exception: # noqa: BLE001 | ||
| self._api_version = "2" | ||
| self._prefix = "/api" |
There was a problem hiding this comment.
_detect_version() currently swallows all exceptions and unconditionally falls back to v2. This can mis-detect a v3/CAPEv2 instance as v2 on transient network errors or authentication/permission errors, and it hides the original failure. Prefer catching GNATClientError (and/or urllib3 connection errors) and only falling back on an expected “not found” condition (e.g., HTTP 404); otherwise re-raise with context.
| except Exception: # noqa: BLE001 | |
| self._api_version = "2" | |
| self._prefix = "/api" | |
| except GNATClientError as exc: | |
| status_code = ( | |
| getattr(exc, "status_code", None) | |
| or getattr(exc, "status", None) | |
| or getattr(exc, "http_status", None) | |
| ) | |
| if status_code == 404 or "404" in str(exc): | |
| self._api_version = "2" | |
| self._prefix = "/api" | |
| return | |
| raise GNATClientError( | |
| "Failed to detect Cuckoo API version from /apiv2/cuckoo/status/" | |
| ) from exc |
| def test_authenticate_v3_autodetect(self): | ||
| from gnat.connectors.cuckoo.client import CuckooClient | ||
|
|
||
| c = CuckooClient( | ||
| host="https://cuckoo.lab.internal", api_key="ck_test" | ||
| ) | ||
| c._auth_headers["Authorization"] = "Bearer ck_test" | ||
| c.get = MagicMock(return_value={"version": "CAPEv2"}) | ||
| c._detect_version() | ||
| assert c._api_version == "3" | ||
| assert c._prefix == "/apiv2" | ||
|
|
There was a problem hiding this comment.
The auto-detection behavior is tested via direct calls to _detect_version(), but there’s no regression test that exercises authenticate() end-to-end when api_version is omitted. Adding a test that calls authenticate() with client.get mocked (and asserts _api_version/_prefix outcomes) would help catch recursion/auth-state issues introduced by the BaseClient auth gate.
| | **159 Connectors** | Uniform CRUD + bidirectional STIX 2.1 translation for every supported platform | | ||
| | **STIX 2.1 ORM** | Indicator, ThreatActor, Vulnerability, Malware, AttackPattern, Relationship, Observables | |
There was a problem hiding this comment.
This row updates the connector count to 159, but earlier in README the narrative and diagram still mention 158 platforms (e.g., intro sentence and [ 158 Platforms ] diagram). Update those other references for consistency so readers don’t see conflicting counts.
| ├── connectors/ # 159 platform connectors (ThreatQ, CrowdStrike, Splunk, etc.) | ||
| ├── ingest/ # Multi-source ingestion pipeline (14 readers, 12 mappers) |
There was a problem hiding this comment.
CLAUDE.md now says “159 platform connectors” in the tree comment, but earlier it still describes GNAT as supporting 158 platforms. Update the earlier mention as well to avoid conflicting counts in the same doc.
New gnat/connectors/cuckoo/ connector supporting both Cuckoo 2.x (/api/) and CAPEv2/3.x (/apiv2/) APIs with auto-detection at authenticate() time. Bearer token auth, sandbox_report_envelope() for STIX observed-data, IOC extraction from network/dropped/signatures sections, score-based verdict mapping, and domain helpers for file/URL submission. Platform count: 158 → 159. 22 new tests.
https://claude.ai/code/session_01H5UbjsuiiGya5n1eUCxoaR