Skip to content

Add Cuckoo Sandbox / CAPEv2 connector with dual API support#128

Merged
wrhalpin merged 1 commit intomainfrom
claude/create-gnat-admin-guide-BOSrp
Apr 19, 2026
Merged

Add Cuckoo Sandbox / CAPEv2 connector with dual API support#128
wrhalpin merged 1 commit intomainfrom
claude/create-gnat-admin-guide-BOSrp

Conversation

@wrhalpin
Copy link
Copy Markdown
Owner

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

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
Copilot AI review requested due to automatic review settings April 19, 2026 01:53
@wrhalpin wrhalpin merged commit 810767a into main Apr 19, 2026
3 of 25 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 CuckooClient with dual API prefix support (/api vs /apiv2) and STIX mappings for observed-data, malware, and indicators.
  • Register the new connector in CLIENT_REGISTRY and 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.

Comment on lines +140 to +156
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"
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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().

Copilot uses AI. Check for mistakes.
Comment on lines +157 to +159
except Exception: # noqa: BLE001
self._api_version = "2"
self._prefix = "/api"
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_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.

Suggested change
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

Copilot uses AI. Check for mistakes.
Comment on lines +20519 to +20530
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"

Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread README.md
Comment on lines +55 to 56
| **159 Connectors** | Uniform CRUD + bidirectional STIX 2.1 translation for every supported platform |
| **STIX 2.1 ORM** | Indicator, ThreatActor, Vulnerability, Malware, AttackPattern, Relationship, Observables |
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread CLAUDE.md
Comment on lines +34 to 35
├── connectors/ # 159 platform connectors (ThreatQ, CrowdStrike, Splunk, etc.)
├── ingest/ # Multi-source ingestion pipeline (14 readers, 12 mappers)
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants