Skip to the content.

GNAT Connector Reference

GNATConnector publishes SenseGNAT findings and narratives into GNAT via TAXII 2.1. It converts Finding objects to STIX 2.1 Indicator dicts and Narrative objects to STIX 2.1 Note dicts, then POSTs them as a STIX bundle to the GNAT TAXII collection endpoint.

Module: sensegnat/connectors/gnat_connector.py


GNATConnector

Constructor

GNATConnector(
    base_url:   str = "",
    api_key:    str = "",
    workspace:  str = "gnat",
    tlp:        str = "white",
    confidence: int = 75,
    timeout:    int = 30,
)
Parameter Type Default Description
base_url str "" Root URL of the GNAT server, e.g. "https://gnat.example.com". Trailing slash is stripped.
api_key str "" Bearer token for GNAT API authentication.
workspace str "gnat" TAXII collection/workspace name used in the endpoint path.
tlp str "white" TLP marking applied to all STIX objects.
confidence int 75 STIX confidence score (0–100) on all Indicator objects.
timeout int 30 HTTP request timeout in seconds.

When base_url or api_key is empty, all push methods log a warning and return an empty PushResult without making network calls. to_record() and narrative_to_record() still function normally.


Methods

finding_to_stix(finding: Finding) -> dict

Converts a Finding to a STIX 2.1 Indicator dict. No network call.

See STIX Indicator fields below for the complete output schema.


narrative_to_stix(narrative: Narrative) -> dict

Converts a Narrative to a STIX 2.1 Note dict. No network call.

See STIX Note fields below for the complete output schema.


push_findings(findings: list[Finding]) -> PushResult

Converts each Finding to a STIX Indicator via finding_to_stix, wraps all objects in a STIX bundle, and POSTs the bundle to the TAXII endpoint.

Returns an empty PushResult immediately if findings is empty.


push_narratives(narratives: list[Narrative]) -> PushResult

Converts each Narrative to a STIX Note via narrative_to_stix, wraps all objects in a STIX bundle, and POSTs the bundle to the TAXII endpoint.

Returns an empty PushResult immediately if narratives is empty.


to_record(finding: Finding) -> dict

Alias for finding_to_stix. Returns the STIX Indicator dict without making any network call. Provided for backwards compatibility.


narrative_to_record(narrative: Narrative) -> dict

Alias for narrative_to_stix. Returns the STIX Note dict without making any network call. Provided for backwards compatibility.


TAXII Transport

Endpoint

POST {base_url}/taxii2/roots/gnat/collections/{workspace}/objects/

Request headers

Header Value
Authorization Bearer {api_key}
Content-Type application/stix+json;version=2.1
Accept application/taxii+json;version=2.1

Bundle envelope

All objects are wrapped in a STIX 2.1 bundle before posting:

{
  "type": "bundle",
  "id": "bundle--{uuid4}",
  "spec_version": "2.1",
  "objects": [ ... ]
}

PushResult

@dataclass
class PushResult:
    pushed: int       = 0
    errors: list[str] = field(default_factory=list)

    @property
    def ok(self) -> bool:
        return not self.errors
Attribute Type Description
pushed int Number of objects in a successful push. 0 on failure or when no objects were sent.
errors list[str] Error messages. Empty on success.
ok bool (property) True if errors is empty.

Error conditions

Condition errors content
HTTP error (4xx, 5xx) "HTTP {code}: {reason}"
Network/OS error str(exc)
No base_url/api_key configured No error recorded; push is silently skipped.

STIX Indicator fields (for findings)

Produced by finding_to_stix(finding).

Standard STIX 2.1 fields

Field Type Value
type str "indicator"
spec_version str "2.1"
id str "indicator--{uuid4}" (fresh UUID per call)
created str ISO 8601 UTC timestamp at time of serialization
modified str Same as created
name str "sensegnat:{finding_type}:{subject_id}"
pattern str See pattern rules below
pattern_type str "stix"
valid_from str finding.seen_at.isoformat()
indicator_types list[str] ["anomalous-activity"]
confidence int Configured confidence value

GNAT custom fields

Field Type Value
x_gnat_sensor_type str "ids_alert"
x_gnat_sensor_id str "sensegnat"
x_gnat_signature str finding.finding_type
x_gnat_tags list[str] [finding.subject_id]
x_gnat_tlp str Configured tlp value

SenseGNAT custom fields

Field Type Value
x_sensegnat_finding_id str finding.finding_id
x_sensegnat_score float finding.score
x_sensegnat_severity str finding.severity
x_sensegnat_summary str finding.summary
x_sensegnat_evidence dict[str, str] finding.evidence
x_sensegnat_subject_id str finding.subject_id

Pattern rules

if "destination" in finding.evidence:
    pattern = f"[ipv4-addr:value = '{finding.evidence['destination']}']"
else:
    pattern = f"[x-sensegnat-subject:id = '{finding.subject_id}']"

Detectors that populate evidence["destination"] (all except TimeWindowDriftDetector) produce an IPv4 pattern. TimeWindowDriftDetector produces a subject-keyed pattern.


STIX Note fields (for narratives)

Produced by narrative_to_stix(narrative).

Standard STIX 2.1 fields

Field Type Value
type str "note"
spec_version str "2.1"
id str "note--{uuid4}" (fresh UUID per call)
created str ISO 8601 UTC timestamp at time of serialization
modified str Same as created
content str narrative.summary
object_refs list [] (empty — no specific STIX objects referenced)

GNAT / SenseGNAT custom fields

Field Type Value
x_gnat_sensor_id str "sensegnat"
x_gnat_tlp str Configured tlp value
x_sensegnat_subject_id str narrative.subject_id
x_sensegnat_finding_count int narrative.finding_count
x_sensegnat_severity str narrative.severity
x_sensegnat_score float narrative.score
x_sensegnat_finding_types list[str] list(narrative.finding_types)

Example STIX Indicator

{
  "type": "indicator",
  "spec_version": "2.1",
  "id": "indicator--a3b4c5d6-e7f8-9012-abcd-ef1234567890",
  "created": "2024-01-15T12:05:00.123456+00:00",
  "modified": "2024-01-15T12:05:00.123456+00:00",
  "name": "sensegnat:rare-destination:alice",
  "pattern": "[ipv4-addr:value = '198.51.100.99']",
  "pattern_type": "stix",
  "valid_from": "2024-01-15T12:05:00.123456+00:00",
  "indicator_types": ["anomalous-activity"],
  "confidence": 75,
  "x_gnat_sensor_type": "ids_alert",
  "x_gnat_sensor_id": "sensegnat",
  "x_gnat_signature": "rare-destination",
  "x_gnat_tags": ["alice"],
  "x_gnat_tlp": "white",
  "x_sensegnat_finding_id": "f1234567-89ab-cdef-0123-456789abcdef",
  "x_sensegnat_score": 0.65,
  "x_sensegnat_severity": "medium",
  "x_sensegnat_summary": "alice contacted a rare destination 198.51.100.99",
  "x_sensegnat_evidence": {
    "destination": "198.51.100.99",
    "port": "8080",
    "protocol": "tcp"
  },
  "x_sensegnat_subject_id": "alice"
}

Example STIX Note

{
  "type": "note",
  "spec_version": "2.1",
  "id": "note--b4c5d6e7-f8a9-0123-bcde-f12345678901",
  "created": "2024-01-15T12:05:01.000000+00:00",
  "modified": "2024-01-15T12:05:01.000000+00:00",
  "content": "alice: 4 finding(s) — rare-destination ×3, peer-deviation. Severity: medium, peak score: 0.70.",
  "object_refs": [],
  "x_gnat_sensor_id": "sensegnat",
  "x_gnat_tlp": "white",
  "x_sensegnat_subject_id": "alice",
  "x_sensegnat_finding_count": 4,
  "x_sensegnat_severity": "medium",
  "x_sensegnat_score": 0.70,
  "x_sensegnat_finding_types": ["rare-destination", "peer-deviation"]
}