Emerging Threats Snort Processor — Design

Overview

Fetches Snort IDS rules from the Emerging Threats open ruleset, parses CVE references, and creates Exploit records (source=emergingthreats) linked to CVEMetadata records (source=vulnetix). The Snort rule text is stored as a remediation description in a markdown code block in CVEDescription.

Only rules with at least one reference:cve are processed. Disabled rules (lines starting with #alert, #drop, etc.) are parsed with Disabled=true.

The parser is version-specific: this processor uses the Snort 2.x parser (parser_v2.go). Snort 3.x (Lua-based) rules will need a separate parser_v3.go.

PropertyValue
ScheduleDaily 08:00 UTC
Timeout30 minutes
CPU/Memory256 / 512 MB

Feed

PropertyValue
URLhttps://rules.emergingthreats.net/open/snort-2.9.0/emerging-all.rules
AuthNone — fully public
FormatSnort 2.9.0 rule syntax (one rule per line, ~40 MB)
LanguageEnglish
Items~30,000+ rules total, subset with CVE references
LicenseBSD (SID 2000000+ range)

Snort Rule Format (v2.9.0)

action protocol src_ip src_port -> dst_ip dst_port (options)

Example with CVE references:

alert tcp $EXTERNAL_NET $HTTP_PORTS -> $HOME_NET any (
  msg:"ET WEB_SERVER Cisco ASA Clientless SSL VPN XSS";
  flow:to_client,established;
  reference:url,tools.cisco.com/security/center/viewAlert.x?alertId=18442;
  reference:cve,2009-1201;
  reference:cve,2009-1202;
  classtype:web-application-attack;
  sid:2010730; rev:4;
  metadata:affected_product Web_Server_Applications, attack_target Web_Server,
    created_at 2010_07_30, confidence Medium, signature_severity Major,
    tag XSS, tag Cross_Site_Scripting, updated_at 2010_09_08;
)

Key Fields Extracted

FieldMaps ToNotes
sidExploit.exploitIdUnique rule identifier
msgExploit.titleHuman-readable alert message
classtypeCVEProblemType (CWE mapping)e.g., web-application-attack → CWE-20
reference:cveExploitCVE junction + CVEMetadataMultiple allowed per rule
reference:urlCVEMetadataReferencesExternal advisory/documentation
reference:bugtraqCVEMetadataReferencesSecurityFocus BID links
metadata.created_atExploit.datePublishedYYYY_MM_DD format
metadata.confidenceCVEDescription (remediation text)Low/Medium/High
metadata.signature_severityCVEDescription (remediation text)Minor/Major/Critical
metadata.mitre_technique_idCVEImpact + CVEImpactDescriptionMITRE ATT&CK mapping
metadata.affected_productExploit.platformProduct category
metadata.tag(logged, not stored separately)Classification tags

Parsing

Comment vs. Disabled Rule Detection

  • # Copyright ... → pure comment, skip
  • #alert tcp ... → disabled rule, parse with Disabled=true

Detection: after stripping #, check if first word is a valid Snort action (alert, drop, pass, log, activate, dynamic, sdrop).

Body Tokenization

The rule body is split on ; while respecting:

  • Double-quoted strings ("...")
  • Hex blocks (|xx xx|)

This prevents false splits in content patterns like content:"|4e 3d;2c|".

Storage

erDiagram Exploit ||--o{ ExploitCVE : "has" ExploitCVE }o--|| CVEMetadata : "links to" CVEMetadata ||--o{ CVEDescription : "has" CVEMetadata ||--o{ CVEMetadataReferences : "has" CVEMetadata ||--o{ CVEProblemType : "has" CVEMetadata ||--o{ CVEImpact : "has" CVEImpact ||--o{ CVEImpactDescription : "has" Exploit { string exploitId "SID" string source "emergingthreats" string title "msg field" string category "classtype" string platform "affected_product" string cveIds "JSON array" } CVEMetadata { string cveId "CVE-YYYY-XXXX" string source "vulnetix" } CVEDescription { string value "Markdown remediation" string containerType "cna" }
TableCreated ByKey
ExploitOne per rule with CVEs(SID, emergingthreats)
ExploitCVEJunction per (rule, CVE)(exploitUuid, cveId, vulnetix)
CVEMetadataOne per unique CVE, source=vulnetix(cveId, vulnetix)
CVEDescriptionCombined remediation (all rules for a CVE)per (cveId, vulnetix)
CVEMetadataReferencesRule URL + external refsper (cveId, vulnetix)
CVEProblemTypeclasstype → CWE mappingper (cveId, vulnetix)
CVEImpactMITRE ATT&CK technique (when metadata present)per (cveId, vulnetix)
CVEImpactDescriptionMITRE ATT&CK narrativeper impact

CVE Source Resolution

For each CVE referenced by a rule, the processor ensures a CVEMetadata record exists with source="vulnetix":

  1. Exists check: SELECT 1 FROM "CVEMetadata" WHERE cveId=$1 AND source='vulnetix'
  2. Clone from priority source: cve.org → nist-nvd → github → anchore_adp → any other
  3. Fallback: Create minimal record (state=PUBLISHED, dataVersion=5.1)

Incremental Strategy

  • Resume key: Exploit.exploitId (SID) for source=“emergingthreats”
  • At startup, load all processed SIDs via LoadProcessedExploitIDs()
  • Skip rules whose SID is already in the set (unless --force or --all)
  • The rules file is always re-downloaded (no ETag/If-Modified-Since)

Flags

FlagDefaultDescription
--allfalseReprocess all rules (bypass SID skip set)
--limit0Max rules to process (0 = unlimited)
--forcefalseForce reprocessing of already-seen SIDs

ECS Schedule

Daily at 08:00 UTC. CPU: 256, Memory: 512 MB.

Key Files

FilePurpose
cmd/emergingthreats-snort-processor/main.goProcessor entry point, CVE resolution, batch loop
internal/emergingthreats/types.goSnortRule, RuleMetadata, ClasstypeInfo structs
internal/emergingthreats/parser_v2.goSnort 2.x rule parser (ParseV2Rules, ParseV2Rule)
internal/emergingthreats/classtype.goclasstype-to-CWE mapping table (~25 entries)
internal/emergingthreats/mapper.goRule → DB record mapping (Exploit, Description, References, etc.)
internal/emergingthreats/parser_v2_test.goParser unit tests (10 test cases)
internal/db/cveimpact.goInsertImpacts function (new, shared)

Verification

-- Exploit records
SELECT COUNT(*) FROM "Exploit" WHERE source = 'emergingthreats';

-- ExploitCVE junctions
SELECT COUNT(*) FROM "ExploitCVE" ec
JOIN "Exploit" e ON ec."exploitUuid" = e.uuid
WHERE e.source = 'emergingthreats';

-- CVEMetadata vulnetix records
SELECT COUNT(*) FROM "CVEMetadata" WHERE source = 'vulnetix';

-- Remediation descriptions
SELECT LEFT("value", 200) FROM "CVEDescription"
WHERE source = 'vulnetix' AND "value" LIKE '%Snort IDS%' LIMIT 3;

S3 Persistence

Not used. This processor does not currently archive payloads or quarantine failures to S3. Per the S3 Persistence Contract this is non-compliant — see the compliance matrix for the implementation roadmap.

Expected paths when implemented:

  • Archive: emerging-threats/files/{sha256}/{filename}
  • Quarantine: failed-feeds/emergingthreats-snort-processor/{YYYY-MM-DD}/{reason}/{filename}
  • Likely reasons: (none documented)