diff --git a/skills/secops/alert-triage/SKILL.md b/skills/secops/alert-triage/SKILL.md index 927e7d68..5c11bc76 100644 --- a/skills/secops/alert-triage/SKILL.md +++ b/skills/secops/alert-triage/SKILL.md @@ -13,7 +13,7 @@ phase: [operate, respond] frameworks: [MITRE-ATT&CK-v16, NIST-SP-800-61-Rev2] difficulty: beginner time_estimate: "10-20min per alert" -version: "1.0.0" +version: "1.0.1" author: unitoneai license: MIT allowed-tools: Read, Grep, Glob @@ -104,6 +104,32 @@ Connect the alert data with surrounding context to build a picture of what happe | Lateral Movement (TA0008) | Collection (TA0009), Exfiltration (TA0010) -- what was the objective? | | Command and Control (TA0011) | All tactics -- C2 implies an active intrusion; look for the full chain | +### Phase 2A: Enrichment Freshness and Provenance + +Before enrichment lowers priority, supports BTP/FP closure, or drives escalation, validate that the source is current, attributable, and applicable to the alert timestamp. Missing enrichment is not automatically malicious; mark the field `Not Evaluable`, lower confidence, and continue investigation when other evidence is insufficient. + +**Required enrichment gates:** + +| Gate | Required evidence | False positive prevented | +|---|---|---| +| `TRIAGE-ENRICH-01` | Asset criticality, environment, owner, exposure, and sync/observation time from CMDB, EDR, or cloud inventory. | Stale CMDB labels lower priority for a production crown-jewel asset. | +| `TRIAGE-ENRICH-02` | User role, privilege, account status, employment status, group membership, and directory/HR/IAM sync time. | Current user state is used as proof of alert-time user state after changes. | +| `TRIAGE-ENRICH-03` | Threat-intel source, lookup time, first/last seen, confidence, expiration, feed health, and alert-time applicability. | Copied IOC reputation from an old case closes a live alert as FP. | +| `TRIAGE-ENRICH-04` | GeoIP, ASN, VPN, scanner, CDN, and cloud-provider tags include provider, lookup time, confidence, and corroborating ownership/activity context. | Provider tags become the sole BTP/FP reason. | +| `TRIAGE-ENRICH-05` | Historical disposition is checked against current rule version, tuning status, asset, user, environment, and related-alert context. | Prior BTP/FP is reused after rule or environment drift. | +| `TRIAGE-ENRICH-06` | Enrichment timestamps are compared to the alert timestamp and to containment/remediation timestamps. | Post-containment asset or user state is treated as alert-time state. | +| `TRIAGE-ENRICH-07` | Analyst overrides include override timestamp, analyst/source, source document, and reason. | Manual enrichment edits influence disposition without provenance. | +| `TRIAGE-ENRICH-08` | Correlated incidents use the oldest or maximum-age enrichment across contributing alerts, not only the current alert's enrichment. | Fresh context on one alert hides stale enrichment that triggered the correlation. | + +**Finding guidance:** + +| Condition | Result | +|---|---| +| Priority is lowered using stale or timestamp-less asset/user enrichment | Finding; High when production or privileged scope is affected | +| BTP/FP closure relies only on provider, scanner, VPN, CDN, GeoIP, ASN, or old case tags | Finding; require corroborating owner/change/activity evidence | +| Enrichment is missing or cannot be evaluated | Not Evaluable; lower confidence rather than auto-escalating or closing | +| Source, lookup/sync time, confidence, and alert-time applicability are documented | May support priority, escalation, or BTP/FP decision | + ### Phase 3: Classify Assign a disposition and priority based on collected and correlated data. @@ -194,7 +220,7 @@ Produce the triage decision as a structured report: ```markdown ## Alert Triage Report **Date:** [YYYY-MM-DD HH:MM UTC] -**Skill:** alert-triage v1.0.0 +**Skill:** alert-triage v1.0.1 **Frameworks:** MITRE ATT&CK v16, NIST SP 800-61 Rev 2 **Analyst:** [Name or AI-assisted] @@ -234,6 +260,17 @@ Produce the triage decision as a structured report: - **Threat Intel:** [IOC match results] - **Kill Chain Position:** [Where this falls in the attack lifecycle] +### Enrichment Provenance +| Enrichment | Source | Lookup / Sync Time | Applies to Alert Time? | Confidence | Impact on Decision | +|------------|--------|--------------------|------------------------|------------|--------------------| +| Asset criticality/exposure | [CMDB/EDR/cloud inventory] | [timestamp] | [Yes/No/Not Evaluable] | [High/Medium/Low] | [priority/disposition impact] | +| User context | [Directory/HR/IAM] | [timestamp] | [Yes/No/Not Evaluable] | [High/Medium/Low] | [priority/disposition impact] | +| Threat intel | [feed/platform] | [timestamp] | [Yes/No/Not Evaluable] | [High/Medium/Low] | [priority/disposition impact] | +| GeoIP/ASN/provider reputation | [provider/feed] | [timestamp] | [Yes/No/Not Evaluable] | [High/Medium/Low] | [priority/disposition impact] | +| Historical disposition | [case/ticket/rule version] | [timestamp] | [Yes/No/Not Evaluable] | [High/Medium/Low] | [priority/disposition impact] | +| Analyst override | [analyst/source document] | [timestamp] | [Yes/No/Not Evaluable] | [High/Medium/Low] | [priority/disposition impact] | +| Correlated alert enrichment age | [correlation rule/case] | [oldest contributing timestamp] | [Yes/No/Not Evaluable] | [High/Medium/Low] | [priority/disposition impact] | + ### Recommended Actions - [ ] [Action 1 -- e.g., isolate host, disable account, block IP] - [ ] [Action 2 -- e.g., collect forensic artifacts, memory dump] @@ -319,6 +356,10 @@ Investigating an alert in isolation without checking for activity before and aft Waiting for complete certainty before escalating a high-priority alert costs response time. NIST SP 800-61 recommends erring on the side of over-notification. If 20 minutes of investigation has not resolved the disposition and the alert involves a critical asset or privileged account, escalate to Tier 2 or the IR team with your current findings and continue investigation in parallel. +### Pitfall 6: Trusting Enrichment Without Freshness or Provenance + +Asset criticality, user privilege, threat-intel reputation, GeoIP/provider tags, analyst overrides, and historical dispositions can drift quickly. Record source, lookup or sync time, confidence, and alert-time applicability before using enrichment to lower priority, close as BTP/FP, or suppress escalation. + --- ## 8. Prompt Injection Safety Notice diff --git a/skills/secops/alert-triage/tests/benign/current_or_not_evaluable_enrichment_handled.json b/skills/secops/alert-triage/tests/benign/current_or_not_evaluable_enrichment_handled.json new file mode 100644 index 00000000..e9d0f5a6 --- /dev/null +++ b/skills/secops/alert-triage/tests/benign/current_or_not_evaluable_enrichment_handled.json @@ -0,0 +1,85 @@ +{ + "case": "benign_current_or_not_evaluable_enrichment_handled", + "skill": "alert-triage", + "expected_result": "Pass", + "alerts": [ + { + "id": "alert-2026-2032-current", + "timestamp": "2026-06-09T04:00:00Z", + "rule": "suspicious-admin-token-use", + "triage_decision": { + "disposition": "needs_investigation", + "priority": "P2", + "confidence": "high" + }, + "enrichment_provenance": { + "asset_criticality": { + "source": "edr_inventory", + "synced_at": "2026-06-09T03:55:00Z", + "applies_to_alert_time": true, + "confidence": "high" + }, + "user_context": { + "source": "iam_directory", + "synced_at": "2026-06-09T03:58:00Z", + "privilege_level": "privileged_service_account", + "applies_to_alert_time": true, + "confidence": "high" + }, + "threat_intel": { + "source": "internal-ti", + "lookup_at": "2026-06-09T04:03:00Z", + "indicator_last_seen": "2026-06-09T03:50:00Z", + "feed_health": "healthy", + "confidence": "high" + }, + "geoip_asn_provider": { + "source": "asn-provider", + "lookup_at": "2026-06-09T04:03:00Z", + "tag": "cloud_provider", + "business_owner": "identity-platform", + "used_as_sole_fp_reason": false + } + } + }, + { + "id": "alert-2026-2032-not-evaluable", + "timestamp": "2026-06-09T05:00:00Z", + "rule": "unusual-login-location", + "triage_decision": { + "disposition": "needs_investigation", + "priority": "P3", + "confidence": "low" + }, + "enrichment_provenance": { + "asset_context": { + "status": "Not Evaluable", + "reason": "cmdb_sync_timestamp_missing" + }, + "user_context": { + "status": "Not Evaluable", + "reason": "hr_feed_unavailable" + }, + "geoip_asn_provider": { + "status": "Not Evaluable", + "reason": "provider_lookup_unavailable" + } + }, + "guardrail": "Missing enrichment lowers confidence and keeps investigation open; it is not used as the sole reason to escalate or close the alert." + } + ], + "covered_gates": [ + "TRIAGE-ENRICH-01", + "TRIAGE-ENRICH-02", + "TRIAGE-ENRICH-03", + "TRIAGE-ENRICH-04", + "TRIAGE-ENRICH-05", + "TRIAGE-ENRICH-06", + "TRIAGE-ENRICH-07", + "TRIAGE-ENRICH-08" + ], + "review_decision": { + "status": "Pass", + "rationale": "Current enrichment may support priority when provenance is documented, while unavailable enrichment is explicitly marked Not Evaluable and does not drive BTP/FP closure." + } +} diff --git a/skills/secops/alert-triage/tests/vulnerable/enrichment_freshness_and_provider_fp_gap.json b/skills/secops/alert-triage/tests/vulnerable/enrichment_freshness_and_provider_fp_gap.json new file mode 100644 index 00000000..7ec6f2d5 --- /dev/null +++ b/skills/secops/alert-triage/tests/vulnerable/enrichment_freshness_and_provider_fp_gap.json @@ -0,0 +1,71 @@ +{ + "case": "vulnerable_enrichment_freshness_and_provider_fp_gap", + "skill": "alert-triage", + "expected_result": "Fail", + "alert": { + "id": "alert-2026-2032-a", + "timestamp": "2026-06-09T02:15:00Z", + "rule": "rare-outbound-domain-from-server", + "proposed_disposition": "false_positive", + "proposed_priority": "P4" + }, + "enrichment": { + "asset_context": { + "source": "cmdb", + "criticality": "low", + "environment": "dev", + "synced_at": "2026-05-20T00:00:00Z" + }, + "cloud_inventory": { + "criticality": "crown-jewel", + "environment": "production", + "internet_facing": true, + "observed_at": "2026-06-09T02:12:00Z" + }, + "threat_intel": { + "copied_from_case": "case-2026-010", + "prior_lookup_at": "2026-05-01T10:00:00Z", + "confidence": null, + "first_seen": null, + "last_seen": null, + "feed_health": "unknown" + }, + "geoip_asn_provider": { + "tag": "cloud_provider", + "lookup_at": null, + "provider": null, + "corroborating_owner_or_change_ticket": null + }, + "historical_disposition": { + "prior_case": "case-2026-010", + "prior_disposition": "BTP", + "rule_version_checked": false, + "asset_user_environment_drift_checked": false + }, + "analyst_override": { + "value": "known benign cloud service", + "override_timestamp": null, + "source_document": null, + "reason": null + }, + "correlated_alerts": [ + { + "id": "alert-2026-2032-b", + "enrichment_age_hours": 48 + } + ] + }, + "expected_findings": [ + "TRIAGE-ENRICH-01: stale CMDB context lowers priority despite current production crown-jewel cloud inventory", + "TRIAGE-ENRICH-03: threat-intel reputation copied from an old case lacks lookup time, confidence, first/last seen, and feed health", + "TRIAGE-ENRICH-04: cloud-provider tag is used as the sole FP reason without provider/time/source or ownership context", + "TRIAGE-ENRICH-05: historical BTP disposition is reused without rule, asset, user, or environment drift checks", + "TRIAGE-ENRICH-07: analyst override lacks timestamp, source document, and reason", + "TRIAGE-ENRICH-08: correlated alert enrichment is stale but not included in decision freshness" + ], + "review_decision": { + "status": "Fail", + "severity_floor": "High", + "rationale": "The proposed FP/P4 decision relies on stale and unattributed enrichment rather than alert-time applicable evidence." + } +}