Source overview
panther-labs/panther-analysis
Python-native Detection-as-Code for Panther — AWS, GCP, Azure, Okta, GitHub, endpoint.
panther-labs-panther-analysis
· Panther Python
· upstream repo ↗
· Apache 2.0
How detections work here
Panther detections are Python, not a rule DSL. Each detection is a .py file
defining a rule(event) function that returns a boolean, paired with a .yml
file carrying the metadata — ID, log types, severity, ATT&CK mapping, enabled
flag.
Panther streams normalized log events through these functions. Because the logic
is ordinary Python, it can do things a declarative rule cannot: look up
dictionaries, walk nested structures with event.deep_get(...), compute a
severity from the event's own contents. The same property means a Panther
detection cannot be mechanically translated to another SIEM — there is no query
to port.
How rules are written
Beyond rule(), a detection may define helper functions that Panther calls when
the rule returns true:
title(event)— the alert headline, usually interpolating event fields.severity(event)— overrides the static YAML severity per event.dedup(event)— returns a grouping key; events sharing it collapse into one alert rather than many.alert_context(event)— extra fields attached to the alert.
The YAML side declares LogTypes, Enabled, Severity, and ATT&CK under
Reports: MITRE ATT&CK: in TA0001:T1566 form — tactic and technique paired,
which is why this source's ATT&CK coverage is unusually complete.
What fires an alert
rule() returning True, subject to dedup() collapsing repeats. Severity is
whichever of the YAML value or the severity() function applies.
Content divides into four kinds, and they are not interchangeable: rule
(streaming detections, the bulk), policy (cloud resource misconfiguration
checks evaluated against state rather than events), scheduled_rule (queries
run on a timer), and correlation_rule (detections over other detections'
signals). Filter by content type on this page — a policy is a compliance
check, not a threat detection.
Reading a rule on this site
The Python body is shown as the rule's logic. Read rule() first; the helper
functions shape the alert, not the match.
Runbook from the YAML becomes triage notes on a rule's page. Because severity
can be computed at runtime by severity(), the severity shown here is the
static YAML declaration and may not be what a given event actually produces.
Gotchas
- A detection whose
rule()is triviallyreturn Trueis not broken — it is a passthrough for a source that has already done the detecting (a vendor alert feed), and the real work is intitle()andseverity(). Enabled: falsein the YAML means upstream ships it off by default. That flag does not map to our status field, so check it on the rule before assuming it runs.
Rungs
Reaches TTPs — 31 rules, 3.0% of the ruleset. A rung is claimed at 20 rules and 1.0% of the source, so that one rule cannot speak for a small ruleset and a rounding error cannot speak for a large one.
- Atomic IOCs 0
- Network & Host Artifacts 0
- Tools 0
- TTPs 31
- Not a detection 119
- Pending classification 874
Shares are of all 1024 live rules in this source, including the atomic-IOC and non-detection content the count above hides by default. The pending band is drawn to scale for the same reason: it is the width of what no classifier has read, and a bar without it would look more complete than the index is. What the rungs mean.
Severity
- Critical 26
- High 288
- Medium 374
- Low 147
- Informational 189
Status
- Stable 883
- Experimental 141
Content types
Recently modified
all →| AWS S3 Object Copied to External Account Bucket | 2026-10-09 |
| AWS S3 Object Exfiltration WITH Object Deletion | 2026-10-09 |
| AWS RDS Instance Encryption | 2026-08-19 |
| SentinelOne Alert Passthrough | 2026-07-23 |
| AWS Console GetSigninToken Potential Abuse | 2026-07-07 |