Metadata-Version: 2.5
Name: aisoc-detections
Version: 0.1.0
Summary: AiSOC Python detection framework — write detections as def rule(event), unit-tested against inline fixtures
Project-URL: Homepage, https://github.com/beenuar/AiSOC
Project-URL: Repository, https://github.com/beenuar/AiSOC
Author-email: Beenu Arora <beenu@cyble.com>
License: MIT
Keywords: aisoc,detection,detection-as-code,security,soc
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Requires-Python: >=3.11
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff<0.17,>=0.16.8; extra == 'dev'
Description-Content-Type: text/markdown

# aisoc-detections — Python detection framework

Write detections as Python, unit-tested against their own fixtures. Complements
the YAML/Sigma corpus in [`detections/`](../../detections/) — use Python when a
detection needs real logic (thresholds, correlation, stateful decisions) that is
awkward in a declarative DSL.

## A detection

A detection is a `.py` module with a `rule(event) -> bool` and metadata:

```python
ID = "py-okta-mfa-fatigue"
TITLE = "Okta MFA fatigue (push bombing)"
SEVERITY = "high"  # info | low | medium | high | critical
MITRE = ["T1621"]
DESCRIPTION = "Many MFA push challenges to one user in a short window."


def rule(event: dict) -> bool:
    return event.get("eventType") == "user.mfa.attempt" and event.get("attempts", 0) >= 5


# Optional: def title(event) -> str, def dedup(event) -> str

TESTS = [
    {"name": "fires on 6 attempts", "event": {"eventType": "user.mfa.attempt", "attempts": 6}, "expect": True},
    {"name": "quiet on 1 attempt", "event": {"eventType": "user.mfa.attempt", "attempts": 1}, "expect": False},
]
```

Every detection **must** ship at least one positive and one negative `TESTS`
case — the harness fails a blind rule (misses a positive) or a noisy one (fires
on a negative), the same non-circular guarantee the YAML corpus gets.

## Running the fixture gate

```bash
# from packages/aisoc-detections/
python -m aisoc_detections.runner detections    # aka `aisoc-detections`
PYTHONPATH=. python -m pytest tests/
```

CI runs this on every PR ([`.github/workflows/python-detections.yml`](../../.github/workflows/python-detections.yml)).

## Safety

Rules are first-party and reviewed via PR. `evaluate()` is fail-closed: a rule
that raises returns `False` (never fires, never crashes the batch).
