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.
| Property | Value |
|---|---|
| Schedule | Daily 08:00 UTC |
| Timeout | 30 minutes |
| CPU/Memory | 256 / 512 MB |
Feed
| Property | Value |
|---|---|
| URL | https://rules.emergingthreats.net/open/snort-2.9.0/emerging-all.rules |
| Auth | None — fully public |
| Format | Snort 2.9.0 rule syntax (one rule per line, ~40 MB) |
| Language | English |
| Items | ~30,000+ rules total, subset with CVE references |
| License | BSD (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
| Field | Maps To | Notes |
|---|---|---|
sid | Exploit.exploitId | Unique rule identifier |
msg | Exploit.title | Human-readable alert message |
classtype | CVEProblemType (CWE mapping) | e.g., web-application-attack → CWE-20 |
reference:cve | ExploitCVE junction + CVEMetadata | Multiple allowed per rule |
reference:url | CVEMetadataReferences | External advisory/documentation |
reference:bugtraq | CVEMetadataReferences | SecurityFocus BID links |
metadata.created_at | Exploit.datePublished | YYYY_MM_DD format |
metadata.confidence | CVEDescription (remediation text) | Low/Medium/High |
metadata.signature_severity | CVEDescription (remediation text) | Minor/Major/Critical |
metadata.mitre_technique_id | CVEImpact + CVEImpactDescription | MITRE ATT&CK mapping |
metadata.affected_product | Exploit.platform | Product 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
| Table | Created By | Key |
|---|---|---|
| Exploit | One per rule with CVEs | (SID, emergingthreats) |
| ExploitCVE | Junction per (rule, CVE) | (exploitUuid, cveId, vulnetix) |
| CVEMetadata | One per unique CVE, source=vulnetix | (cveId, vulnetix) |
| CVEDescription | Combined remediation (all rules for a CVE) | per (cveId, vulnetix) |
| CVEMetadataReferences | Rule URL + external refs | per (cveId, vulnetix) |
| CVEProblemType | classtype → CWE mapping | per (cveId, vulnetix) |
| CVEImpact | MITRE ATT&CK technique (when metadata present) | per (cveId, vulnetix) |
| CVEImpactDescription | MITRE ATT&CK narrative | per impact |
CVE Source Resolution
For each CVE referenced by a rule, the processor ensures a CVEMetadata record
exists with source="vulnetix":
- Exists check:
SELECT 1 FROM "CVEMetadata" WHERE cveId=$1 AND source='vulnetix' - Clone from priority source: cve.org → nist-nvd → github → anchore_adp → any other
- 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
--forceor--all) - The rules file is always re-downloaded (no ETag/If-Modified-Since)
Flags
| Flag | Default | Description |
|---|---|---|
--all | false | Reprocess all rules (bypass SID skip set) |
--limit | 0 | Max rules to process (0 = unlimited) |
--force | false | Force reprocessing of already-seen SIDs |
ECS Schedule
Daily at 08:00 UTC. CPU: 256, Memory: 512 MB.
Key Files
| File | Purpose |
|---|---|
cmd/emergingthreats-snort-processor/main.go | Processor entry point, CVE resolution, batch loop |
internal/emergingthreats/types.go | SnortRule, RuleMetadata, ClasstypeInfo structs |
internal/emergingthreats/parser_v2.go | Snort 2.x rule parser (ParseV2Rules, ParseV2Rule) |
internal/emergingthreats/classtype.go | classtype-to-CWE mapping table (~25 entries) |
internal/emergingthreats/mapper.go | Rule → DB record mapping (Exploit, Description, References, etc.) |
internal/emergingthreats/parser_v2_test.go | Parser unit tests (10 test cases) |
internal/db/cveimpact.go | InsertImpacts 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)