prohori 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prohori-0.1.0/.gitignore +70 -0
- prohori-0.1.0/CHANGELOG.md +145 -0
- prohori-0.1.0/LICENSE +21 -0
- prohori-0.1.0/PKG-INFO +376 -0
- prohori-0.1.0/README.md +331 -0
- prohori-0.1.0/pyproject.toml +200 -0
- prohori-0.1.0/src/prohori/__init__.py +9 -0
- prohori-0.1.0/src/prohori/__main__.py +6 -0
- prohori-0.1.0/src/prohori/cli/__init__.py +5 -0
- prohori-0.1.0/src/prohori/cli/app.py +85 -0
- prohori-0.1.0/src/prohori/cli/demo.py +120 -0
- prohori-0.1.0/src/prohori/cli/detect.py +225 -0
- prohori-0.1.0/src/prohori/cli/enrich.py +91 -0
- prohori-0.1.0/src/prohori/cli/exit_codes.py +14 -0
- prohori-0.1.0/src/prohori/cli/incidents.py +251 -0
- prohori-0.1.0/src/prohori/cli/parse.py +310 -0
- prohori-0.1.0/src/prohori/cli/report.py +135 -0
- prohori-0.1.0/src/prohori/cli/rules.py +180 -0
- prohori-0.1.0/src/prohori/cli/simulate.py +91 -0
- prohori-0.1.0/src/prohori/cli/triage.py +295 -0
- prohori-0.1.0/src/prohori/config.py +1 -0
- prohori-0.1.0/src/prohori/enrich/__init__.py +34 -0
- prohori-0.1.0/src/prohori/enrich/abuseipdb.py +59 -0
- prohori-0.1.0/src/prohori/enrich/addresses.py +97 -0
- prohori-0.1.0/src/prohori/enrich/cache.py +137 -0
- prohori-0.1.0/src/prohori/enrich/config.py +118 -0
- prohori-0.1.0/src/prohori/enrich/data/offline.json +109 -0
- prohori-0.1.0/src/prohori/enrich/enricher.py +207 -0
- prohori-0.1.0/src/prohori/enrich/geolite.py +92 -0
- prohori-0.1.0/src/prohori/enrich/http.py +150 -0
- prohori-0.1.0/src/prohori/enrich/offline.py +39 -0
- prohori-0.1.0/src/prohori/enrich/provider.py +97 -0
- prohori-0.1.0/src/prohori/enrich/ratelimit.py +117 -0
- prohori-0.1.0/src/prohori/enrich/virustotal.py +58 -0
- prohori-0.1.0/src/prohori/incidents/__init__.py +34 -0
- prohori-0.1.0/src/prohori/incidents/attack.py +71 -0
- prohori-0.1.0/src/prohori/incidents/builder.py +421 -0
- prohori-0.1.0/src/prohori/incidents/events.py +56 -0
- prohori-0.1.0/src/prohori/incidents/grouping.py +131 -0
- prohori-0.1.0/src/prohori/incidents/novelty.py +126 -0
- prohori-0.1.0/src/prohori/incidents/provenance.py +52 -0
- prohori-0.1.0/src/prohori/incidents/runbooks.py +94 -0
- prohori-0.1.0/src/prohori/incidents/scoring.py +231 -0
- prohori-0.1.0/src/prohori/incidents/summary.py +83 -0
- prohori-0.1.0/src/prohori/llm/__init__.py +1 -0
- prohori-0.1.0/src/prohori/logging.py +1 -0
- prohori-0.1.0/src/prohori/ml/__init__.py +1 -0
- prohori-0.1.0/src/prohori/parsers/__init__.py +39 -0
- prohori-0.1.0/src/prohori/parsers/access_log.py +191 -0
- prohori-0.1.0/src/prohori/parsers/app_json.py +123 -0
- prohori-0.1.0/src/prohori/parsers/auth_log.py +459 -0
- prohori-0.1.0/src/prohori/parsers/base.py +216 -0
- prohori-0.1.0/src/prohori/parsers/detect.py +69 -0
- prohori-0.1.0/src/prohori/parsers/generic_json.py +209 -0
- prohori-0.1.0/src/prohori/parsers/registry.py +41 -0
- prohori-0.1.0/src/prohori/parsers/util.py +54 -0
- prohori-0.1.0/src/prohori/pipeline/__init__.py +1 -0
- prohori-0.1.0/src/prohori/py.typed +0 -0
- prohori-0.1.0/src/prohori/reports/__init__.py +30 -0
- prohori-0.1.0/src/prohori/reports/make.py +46 -0
- prohori-0.1.0/src/prohori/reports/redact.py +190 -0
- prohori-0.1.0/src/prohori/reports/render.py +359 -0
- prohori-0.1.0/src/prohori/reports/templates/report.html.j2 +208 -0
- prohori-0.1.0/src/prohori/reports/templates/report.md.j2 +146 -0
- prohori-0.1.0/src/prohori/rules/__init__.py +48 -0
- prohori-0.1.0/src/prohori/rules/attack.py +80 -0
- prohori-0.1.0/src/prohori/rules/builtin/README.md +8 -0
- prohori-0.1.0/src/prohori/rules/builtin/abuse-bursts.tests.yml +65 -0
- prohori-0.1.0/src/prohori/rules/builtin/abuse-bursts.yml +23 -0
- prohori-0.1.0/src/prohori/rules/builtin/account-lockouts.tests.yml +46 -0
- prohori-0.1.0/src/prohori/rules/builtin/account-lockouts.yml +22 -0
- prohori-0.1.0/src/prohori/rules/builtin/admin-role-granted.tests.yml +30 -0
- prohori-0.1.0/src/prohori/rules/builtin/admin-role-granted.yml +17 -0
- prohori-0.1.0/src/prohori/rules/builtin/admin-unusual-hours.tests.yml +38 -0
- prohori-0.1.0/src/prohori/rules/builtin/admin-unusual-hours.yml +24 -0
- prohori-0.1.0/src/prohori/rules/builtin/authorization-enumeration.tests.yml +59 -0
- prohori-0.1.0/src/prohori/rules/builtin/authorization-enumeration.yml +24 -0
- prohori-0.1.0/src/prohori/rules/builtin/brute-force.tests.yml +65 -0
- prohori-0.1.0/src/prohori/rules/builtin/brute-force.yml +21 -0
- prohori-0.1.0/src/prohori/rules/builtin/injection-attempts.tests.yml +58 -0
- prohori-0.1.0/src/prohori/rules/builtin/injection-attempts.yml +61 -0
- prohori-0.1.0/src/prohori/rules/builtin/password-spraying.tests.yml +65 -0
- prohori-0.1.0/src/prohori/rules/builtin/password-spraying.yml +23 -0
- prohori-0.1.0/src/prohori/rules/builtin/refresh-token-reuse.tests.yml +31 -0
- prohori-0.1.0/src/prohori/rules/builtin/refresh-token-reuse.yml +18 -0
- prohori-0.1.0/src/prohori/rules/builtin/scanner-user-agents.tests.yml +79 -0
- prohori-0.1.0/src/prohori/rules/builtin/scanner-user-agents.yml +43 -0
- prohori-0.1.0/src/prohori/rules/builtin/sensitive-account-change.tests.yml +54 -0
- prohori-0.1.0/src/prohori/rules/builtin/sensitive-account-change.yml +33 -0
- prohori-0.1.0/src/prohori/rules/builtin/sensitive-file-probes.tests.yml +61 -0
- prohori-0.1.0/src/prohori/rules/builtin/sensitive-file-probes.yml +41 -0
- prohori-0.1.0/src/prohori/rules/builtin/ssh-invalid-user-storm.tests.yml +62 -0
- prohori-0.1.0/src/prohori/rules/builtin/ssh-invalid-user-storm.yml +28 -0
- prohori-0.1.0/src/prohori/rules/builtin/success-after-failures.tests.yml +60 -0
- prohori-0.1.0/src/prohori/rules/builtin/success-after-failures.yml +26 -0
- prohori-0.1.0/src/prohori/rules/builtin/web-scanning.tests.yml +85 -0
- prohori-0.1.0/src/prohori/rules/builtin/web-scanning.yml +22 -0
- prohori-0.1.0/src/prohori/rules/compiler.py +224 -0
- prohori-0.1.0/src/prohori/rules/condition.py +231 -0
- prohori-0.1.0/src/prohori/rules/engine.py +531 -0
- prohori-0.1.0/src/prohori/rules/loader.py +208 -0
- prohori-0.1.0/src/prohori/rules/matching.py +370 -0
- prohori-0.1.0/src/prohori/rules/model.py +220 -0
- prohori-0.1.0/src/prohori/rules/regex_safety.py +77 -0
- prohori-0.1.0/src/prohori/rules/testing.py +331 -0
- prohori-0.1.0/src/prohori/runbooks/README.md +25 -0
- prohori-0.1.0/src/prohori/runbooks/abuse-bursts.md +34 -0
- prohori-0.1.0/src/prohori/runbooks/account-lockouts.md +32 -0
- prohori-0.1.0/src/prohori/runbooks/admin-role-granted.md +32 -0
- prohori-0.1.0/src/prohori/runbooks/admin-unusual-hours.md +33 -0
- prohori-0.1.0/src/prohori/runbooks/authorization-enumeration.md +31 -0
- prohori-0.1.0/src/prohori/runbooks/brute-force.md +35 -0
- prohori-0.1.0/src/prohori/runbooks/injection-attempts.md +33 -0
- prohori-0.1.0/src/prohori/runbooks/password-spraying.md +33 -0
- prohori-0.1.0/src/prohori/runbooks/refresh-token-reuse.md +32 -0
- prohori-0.1.0/src/prohori/runbooks/scanner-user-agents.md +31 -0
- prohori-0.1.0/src/prohori/runbooks/sensitive-account-change.md +35 -0
- prohori-0.1.0/src/prohori/runbooks/sensitive-file-probes.md +32 -0
- prohori-0.1.0/src/prohori/runbooks/ssh-invalid-user-storm.md +32 -0
- prohori-0.1.0/src/prohori/runbooks/success-after-failures.md +34 -0
- prohori-0.1.0/src/prohori/runbooks/web-scanning.md +32 -0
- prohori-0.1.0/src/prohori/schema/__init__.py +70 -0
- prohori-0.1.0/src/prohori/schema/alert.py +55 -0
- prohori-0.1.0/src/prohori/schema/event.py +98 -0
- prohori-0.1.0/src/prohori/schema/fields.py +77 -0
- prohori-0.1.0/src/prohori/schema/ids.py +35 -0
- prohori-0.1.0/src/prohori/schema/incident.py +165 -0
- prohori-0.1.0/src/prohori/schema/serialization.py +35 -0
- prohori-0.1.0/src/prohori/schema/types.py +95 -0
- prohori-0.1.0/src/prohori/simulate/__init__.py +23 -0
- prohori-0.1.0/src/prohori/simulate/addresses.py +57 -0
- prohori-0.1.0/src/prohori/simulate/background.py +394 -0
- prohori-0.1.0/src/prohori/simulate/evaluate.py +197 -0
- prohori-0.1.0/src/prohori/simulate/formats.py +79 -0
- prohori-0.1.0/src/prohori/simulate/model.py +125 -0
- prohori-0.1.0/src/prohori/simulate/run.py +127 -0
- prohori-0.1.0/src/prohori/simulate/scenarios.py +613 -0
- prohori-0.1.0/src/prohori/version.py +3 -0
- prohori-0.1.0/tests/__init__.py +0 -0
- prohori-0.1.0/tests/fixtures/.gitkeep +0 -0
- prohori-0.1.0/tests/fixtures/rules/invalid/aggregate.yml +13 -0
- prohori-0.1.0/tests/fixtures/rules/invalid/alias-bomb.yml +11 -0
- prohori-0.1.0/tests/fixtures/rules/invalid/bad-condition.yml +8 -0
- prohori-0.1.0/tests/fixtures/rules/invalid/metadata.yml +10 -0
- prohori-0.1.0/tests/fixtures/rules/invalid/modifiers.yml +12 -0
- prohori-0.1.0/tests/fixtures/rules/invalid/python-tag.yml +5 -0
- prohori-0.1.0/tests/fixtures/rules/invalid/unknown-field.yml +8 -0
- prohori-0.1.0/tests/fixtures/rules/invalid/unsafe-regex.yml +8 -0
- prohori-0.1.0/tests/fixtures/rules/invalid/unused-selection.yml +10 -0
- prohori-0.1.0/tests/fixtures/rules/valid/brute-force.tests.yml +49 -0
- prohori-0.1.0/tests/fixtures/rules/valid/brute-force.yml +17 -0
- prohori-0.1.0/tests/fixtures/rules/valid/error-ratio.tests.yml +30 -0
- prohori-0.1.0/tests/fixtures/rules/valid/error-ratio.yml +16 -0
- prohori-0.1.0/tests/fixtures/rules/valid/password-spraying.tests.yml +36 -0
- prohori-0.1.0/tests/fixtures/rules/valid/password-spraying.yml +17 -0
- prohori-0.1.0/tests/fixtures/rules/valid/success-after-failures.tests.yml +36 -0
- prohori-0.1.0/tests/fixtures/rules/valid/success-after-failures.yml +17 -0
- prohori-0.1.0/tests/fixtures/rules/valid/web-scanning.tests.yml +38 -0
- prohori-0.1.0/tests/fixtures/rules/valid/web-scanning.yml +20 -0
- prohori-0.1.0/tests/golden/.gitkeep +0 -0
- prohori-0.1.0/tests/golden/cli/demo.txt +15 -0
- prohori-0.1.0/tests/golden/cli/detect-access-log.txt +27 -0
- prohori-0.1.0/tests/golden/cli/detect-auth-log.txt +20 -0
- prohori-0.1.0/tests/golden/cli/incidents-takeover.txt +55 -0
- prohori-0.1.0/tests/golden/cli/parse-app.txt +26 -0
- prohori-0.1.0/tests/golden/cli/rules-list.txt +49 -0
- prohori-0.1.0/tests/golden/cli/simulate-list.txt +59 -0
- prohori-0.1.0/tests/golden/cli/triage-auth-log-summary.md +9 -0
- prohori-0.1.0/tests/golden/detect/access-log.jsonl +2 -0
- prohori-0.1.0/tests/golden/detect/app-security.jsonl +3 -0
- prohori-0.1.0/tests/golden/detect/auth-log.jsonl +1 -0
- prohori-0.1.0/tests/golden/incidents/access-log.jsonl +2 -0
- prohori-0.1.0/tests/golden/incidents/account-takeover.jsonl +1 -0
- prohori-0.1.0/tests/golden/incidents/app-security.jsonl +3 -0
- prohori-0.1.0/tests/golden/incidents/auth-log.jsonl +1 -0
- prohori-0.1.0/tests/golden/reports/takeover-redacted.md +169 -0
- prohori-0.1.0/tests/golden/reports/takeover.csv +18 -0
- prohori-0.1.0/tests/golden/reports/takeover.html +229 -0
- prohori-0.1.0/tests/golden/reports/takeover.json +487 -0
- prohori-0.1.0/tests/golden/reports/takeover.md +170 -0
- prohori-0.1.0/tests/golden/simulate/eval-seed1.json +457 -0
- prohori-0.1.0/tests/golden/simulate/hashes.json +18 -0
- prohori-0.1.0/tests/integration/__init__.py +0 -0
- prohori-0.1.0/tests/integration/test_pipeline.py +67 -0
- prohori-0.1.0/tests/unit/__init__.py +0 -0
- prohori-0.1.0/tests/unit/cli/__init__.py +0 -0
- prohori-0.1.0/tests/unit/cli/test_cli_golden.py +81 -0
- prohori-0.1.0/tests/unit/cli/test_detect_command.py +173 -0
- prohori-0.1.0/tests/unit/cli/test_incidents_command.py +192 -0
- prohori-0.1.0/tests/unit/cli/test_parse_command.py +180 -0
- prohori-0.1.0/tests/unit/cli/test_report_command.py +150 -0
- prohori-0.1.0/tests/unit/cli/test_rules_command.py +155 -0
- prohori-0.1.0/tests/unit/cli/test_simulate_command.py +82 -0
- prohori-0.1.0/tests/unit/cli/test_triage_demo.py +219 -0
- prohori-0.1.0/tests/unit/enrich/__init__.py +0 -0
- prohori-0.1.0/tests/unit/enrich/conftest.py +42 -0
- prohori-0.1.0/tests/unit/enrich/test_addresses.py +120 -0
- prohori-0.1.0/tests/unit/enrich/test_cache_ratelimit.py +167 -0
- prohori-0.1.0/tests/unit/enrich/test_cli.py +118 -0
- prohori-0.1.0/tests/unit/enrich/test_enricher.py +238 -0
- prohori-0.1.0/tests/unit/enrich/test_geolite_offline_config.py +217 -0
- prohori-0.1.0/tests/unit/enrich/test_http_providers.py +318 -0
- prohori-0.1.0/tests/unit/incidents/conftest.py +5 -0
- prohori-0.1.0/tests/unit/incidents/factories.py +83 -0
- prohori-0.1.0/tests/unit/incidents/test_attack_runbooks.py +140 -0
- prohori-0.1.0/tests/unit/incidents/test_builder.py +263 -0
- prohori-0.1.0/tests/unit/incidents/test_events_novelty.py +137 -0
- prohori-0.1.0/tests/unit/incidents/test_grouping.py +138 -0
- prohori-0.1.0/tests/unit/incidents/test_scoring.py +206 -0
- prohori-0.1.0/tests/unit/parsers/__init__.py +0 -0
- prohori-0.1.0/tests/unit/parsers/test_access_log.py +178 -0
- prohori-0.1.0/tests/unit/parsers/test_auth_log.py +663 -0
- prohori-0.1.0/tests/unit/parsers/test_base.py +141 -0
- prohori-0.1.0/tests/unit/parsers/test_json_parsers.py +361 -0
- prohori-0.1.0/tests/unit/reports/__init__.py +0 -0
- prohori-0.1.0/tests/unit/reports/conftest.py +90 -0
- prohori-0.1.0/tests/unit/reports/test_redact.py +179 -0
- prohori-0.1.0/tests/unit/reports/test_render.py +177 -0
- prohori-0.1.0/tests/unit/rules/__init__.py +0 -0
- prohori-0.1.0/tests/unit/rules/test_builtin_rules.py +124 -0
- prohori-0.1.0/tests/unit/rules/test_compiler_loader.py +414 -0
- prohori-0.1.0/tests/unit/rules/test_condition.py +148 -0
- prohori-0.1.0/tests/unit/rules/test_engine.py +556 -0
- prohori-0.1.0/tests/unit/rules/test_matching.py +249 -0
- prohori-0.1.0/tests/unit/rules/test_regex_safety.py +88 -0
- prohori-0.1.0/tests/unit/rules/test_rule_testing.py +233 -0
- prohori-0.1.0/tests/unit/rules/test_verify_attack_script.py +72 -0
- prohori-0.1.0/tests/unit/schema/__init__.py +0 -0
- prohori-0.1.0/tests/unit/schema/strategies.py +80 -0
- prohori-0.1.0/tests/unit/schema/test_alert_incident.py +156 -0
- prohori-0.1.0/tests/unit/schema/test_event.py +191 -0
- prohori-0.1.0/tests/unit/schema/test_fields_serialization.py +114 -0
- prohori-0.1.0/tests/unit/simulate/__init__.py +0 -0
- prohori-0.1.0/tests/unit/simulate/test_evaluate.py +114 -0
- prohori-0.1.0/tests/unit/simulate/test_simulate.py +169 -0
- prohori-0.1.0/tests/unit/test_docs.py +25 -0
- prohori-0.1.0/tests/unit/test_release_notes.py +43 -0
- prohori-0.1.0/tests/unit/test_skeleton.py +61 -0
prohori-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# --- Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
*.egg
|
|
9
|
+
|
|
10
|
+
# --- Virtual environments
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# --- Tooling caches and reports
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
.hypothesis/
|
|
20
|
+
.coverage
|
|
21
|
+
.coverage.*
|
|
22
|
+
coverage.xml
|
|
23
|
+
htmlcov/
|
|
24
|
+
.tox/
|
|
25
|
+
.nox/
|
|
26
|
+
|
|
27
|
+
# --- Secrets and local configuration
|
|
28
|
+
.env
|
|
29
|
+
.env.*
|
|
30
|
+
!.env.example
|
|
31
|
+
*.local.toml
|
|
32
|
+
*.local.yaml
|
|
33
|
+
prohori.local.*
|
|
34
|
+
|
|
35
|
+
# --- Datasets, models and generated artefacts (never committed)
|
|
36
|
+
data/*
|
|
37
|
+
!data/README.md
|
|
38
|
+
results/checkpoints/
|
|
39
|
+
models/
|
|
40
|
+
*.joblib
|
|
41
|
+
*.pkl
|
|
42
|
+
*.pickle
|
|
43
|
+
*.onnx
|
|
44
|
+
*.parquet
|
|
45
|
+
*.mmdb
|
|
46
|
+
*.sqlite
|
|
47
|
+
*.sqlite3
|
|
48
|
+
*.db
|
|
49
|
+
|
|
50
|
+
# --- Notebooks
|
|
51
|
+
.ipynb_checkpoints/
|
|
52
|
+
|
|
53
|
+
# --- Pipeline runtime state
|
|
54
|
+
pipeline/.volumes/
|
|
55
|
+
pipeline/dbt/target/
|
|
56
|
+
pipeline/dbt/logs/
|
|
57
|
+
pipeline/dbt/dbt_packages/
|
|
58
|
+
pipeline/airflow/logs/
|
|
59
|
+
|
|
60
|
+
# --- Editors and OS
|
|
61
|
+
.vscode/
|
|
62
|
+
.idea/
|
|
63
|
+
*.swp
|
|
64
|
+
.DS_Store
|
|
65
|
+
Thumbs.db
|
|
66
|
+
|
|
67
|
+
# Simulator output written by examples in the docs
|
|
68
|
+
/out/
|
|
69
|
+
/prohori-demo/
|
|
70
|
+
/triage/
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
6
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-10-27
|
|
11
|
+
|
|
12
|
+
First public release: the batch core, from a log file to scored incidents and shareable reports,
|
|
13
|
+
offline by default.
|
|
14
|
+
|
|
15
|
+
### Highlights
|
|
16
|
+
|
|
17
|
+
- `prohori demo`: simulated attacks triaged offline in seconds, with no configuration.
|
|
18
|
+
- `prohori triage FILE`: parse, detect, enrich, build incidents and write a report per incident.
|
|
19
|
+
- Parsers for Linux `auth.log`, nginx/Apache access logs and JSON Lines (app layout or a field
|
|
20
|
+
mapping), into one typed event schema.
|
|
21
|
+
- 15 YAML detection rules with fixtures, runbooks and ATT&CK v20.0 tags, run by a stateful engine
|
|
22
|
+
(thresholds, windows, sequences, time of day).
|
|
23
|
+
- Optional IP enrichment (GeoLite2, AbuseIPDB, VirusTotal) that never sends private or
|
|
24
|
+
documentation addresses, with caching and rate limits.
|
|
25
|
+
- Incidents with a points-based score and its breakdown, confidence and limitations; reports in
|
|
26
|
+
Markdown, self-contained HTML, JSON and CSV, with fail-closed redaction.
|
|
27
|
+
- A labelled attack simulator and published per-rule precision and recall, including held-out
|
|
28
|
+
variants that show where the rules miss.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- Release workflow (`.github/workflows/release.yml`): tag-triggered build, `twine check`, wheel
|
|
33
|
+
smoke tests on Linux and Windows, TestPyPI, PyPI after a maintainer's approval (trusted
|
|
34
|
+
publishing, no tokens), a GitHub release with the changelog section, and an install-from-PyPI
|
|
35
|
+
check. Documented in `docs/RELEASING.md`.
|
|
36
|
+
- `prohori triage FILE|-`: parse, detect, enrich (offline unless `--online`), build incidents and
|
|
37
|
+
write `incidents.jsonl`, one report per incident (`--format md|html|json|csv`) and
|
|
38
|
+
`summary.md`; `--redact` / `--redact-ips` redact every file with one key per run.
|
|
39
|
+
- `prohori demo`: simulates an account takeover, an SSH brute force and a web scan, triages them
|
|
40
|
+
offline with no configuration and shows whether every expected rule fired.
|
|
41
|
+
- `prohori version` shows the ATT&CK release and a fingerprint of the built-in rule set.
|
|
42
|
+
- Command-line reference `docs/CLI.md`, sample reports in `docs/examples/`, a terminal capture of
|
|
43
|
+
the demo (`docs/images/demo.svg`) and `scripts/generate_examples.py` to regenerate them.
|
|
44
|
+
- Golden tests for the table output of the main commands, a `--help` test for every command and
|
|
45
|
+
an end-to-end test (simulator, triage, incidents) against the simulator's ground truth.
|
|
46
|
+
- Log simulator (`prohori.simulate`) and `prohori simulate SCENARIO --seed N --out DIR
|
|
47
|
+
[--variant default|heldout] [--set NAME=VALUE] [--list]`: eight labelled attack scenarios
|
|
48
|
+
(SSH brute force, password spraying, credential stuffing, refresh-token reuse, account takeover
|
|
49
|
+
with a payout change, web scanning, injection attempts, admin misuse) in realistic benign
|
|
50
|
+
background activity, as `auth.log`, access-log or app-JSON files with per-line labels and a
|
|
51
|
+
manifest. Byte-identical for the same seed; documentation IP ranges only.
|
|
52
|
+
- Rule evaluation: `scripts/evaluate_rules.py` writes per-rule precision and recall for the
|
|
53
|
+
default and held-out variants to `results/rules_eval.json`; `scripts/generate_results_table.py`
|
|
54
|
+
puts the tables in `docs/SIMULATOR.md`.
|
|
55
|
+
- Time-of-day matching in rules: a `timezone` key and the `time.hour`, `time.weekday` and
|
|
56
|
+
`time.minute_of_day` fields.
|
|
57
|
+
- Built-in rule PRH-ADMIN-002: admin actions between 22:00 and 07:00 (Asia/Dhaka), with fixtures
|
|
58
|
+
and a runbook (15 built-in rules in total).
|
|
59
|
+
- Runbook actions starting with `For SSH,` or `For the web app,` are shown only for incidents
|
|
60
|
+
with that kind of event.
|
|
61
|
+
- Incident reports (`prohori.reports`) and `prohori report INCIDENT [-f md|html|json|csv]`:
|
|
62
|
+
header, summary, timeline, indicators with enrichment, entities, evidence, ATT&CK mapping,
|
|
63
|
+
score breakdown, recommended actions, confidence and limitations, and an appendix with input
|
|
64
|
+
and rule-file SHA-256 hashes and versions. HTML is one self-contained file with a strict
|
|
65
|
+
Content-Security-Policy; Markdown escapes every value; CSV cells cannot become formulas.
|
|
66
|
+
Documented in `docs/REPORTS.md`.
|
|
67
|
+
- `--redact` and `--redact-ips`: account names, ids, email hashes, emails and optionally IP
|
|
68
|
+
addresses become keyed pseudonyms (HMAC-SHA256, random key per run); the command refuses to
|
|
69
|
+
write a report that would still contain an original value.
|
|
70
|
+
- `Incident.provenance` (tool version, input files with SHA-256, rules fired with rule-file
|
|
71
|
+
SHA-256, enrichment mode), filled in by `prohori incidents`; `TimelineEntry.account` and
|
|
72
|
+
`TimelineEntry.source_ip`.
|
|
73
|
+
- Sample incident `samples/incidents/INC-ee7c8818fbb34989.json`.
|
|
74
|
+
- `jinja2` is now a core dependency.
|
|
75
|
+
- Incident builder (`prohori.incidents`) and `prohori incidents FILE|-`: alerts that share an IP,
|
|
76
|
+
account, request or session within a time gap become one incident, with a points-based score
|
|
77
|
+
and its breakdown, severity, confidence, ATT&CK mapping (with the ATT&CK version), a timeline,
|
|
78
|
+
enriched indicators, recommended actions from the runbooks, and limitations. Sign-in novelty
|
|
79
|
+
(a new IP or client for an account) is incident context from a bounded per-account history.
|
|
80
|
+
Shared IP addresses stop linking alerts after 10 accounts. Enrichment is offline unless
|
|
81
|
+
`--online` is given. Documented in `docs/INCIDENTS.md`.
|
|
82
|
+
- `Incident.attack_version` and `prohori.schema.incident_id()` (deterministic incident ids).
|
|
83
|
+
- Sample `samples/logs/account-takeover.jsonl`: brute force, sign-in, 2FA disabled, payout change.
|
|
84
|
+
- IP enrichment (`prohori.enrich`) and `prohori enrich IP... [--offline]`: providers for local
|
|
85
|
+
MaxMind GeoLite2 databases (`prohori[geoip]` extra), AbuseIPDB and VirusTotal, a bundled
|
|
86
|
+
synthetic fixture for offline runs, a SQLite cache with TTLs, daily budgets, a token bucket,
|
|
87
|
+
bounded retries and timeouts. Private, reserved and documentation addresses are never sent to
|
|
88
|
+
external services. Documented in `docs/ENRICHMENT.md`; third-party terms in `docs/LICENSES.md`.
|
|
89
|
+
- `httpx` is now a core dependency.
|
|
90
|
+
- `prohori detect FILE|-`: parse a log file and run the built-in rules plus any `--rules PATH`,
|
|
91
|
+
with table or JSON Lines output, out-of-order tolerance (`--lateness`) and exit code 3 when
|
|
92
|
+
alerts are found (`--exit-zero` to disable).
|
|
93
|
+
- Built-in rules PRH-ABUSE-001, PRH-ADMIN-001, PRH-WEB-001 to 004 and PRH-SSH-001, with fixtures
|
|
94
|
+
and runbooks (14 built-in rules in total).
|
|
95
|
+
- `urldecode` rule modifier: percent-decode a field before matching.
|
|
96
|
+
- Built-in rules PRH-AUTH-001 to 004, PRH-SESS-001, PRH-ACCT-001 and PRH-AUTHZ-001, each with
|
|
97
|
+
fixtures (including real log lines through the parsers) and a runbook in `prohori/runbooks/`.
|
|
98
|
+
- ATT&CK tags verified against Enterprise ATT&CK v20.0 (`prohori.rules.attack`), with
|
|
99
|
+
`scripts/verify_attack.py` to re-check the list against the official STIX data.
|
|
100
|
+
- Stateful rule engine (`prohori.rules.engine`): single-pass evaluation of stateless rules,
|
|
101
|
+
`aggregate` thresholds (count, distinct, ratio; sliding and tumbling windows) and `sequence`
|
|
102
|
+
rules, with a reorder buffer for out-of-order events, protection against bogus future
|
|
103
|
+
timestamps, and bounded state with eviction counters.
|
|
104
|
+
- Rule test harness and `prohori rules test`: each rule runs against its `<rule>.tests.yml`
|
|
105
|
+
fixtures (generated event timelines or real log lines through a parser).
|
|
106
|
+
- Detection rule format (`prohori.rules`): YAML rules with selections, field modifiers
|
|
107
|
+
(`contains`, `startswith`, `endswith`, `re`, `cidr`, `gt`/`gte`/`lt`/`lte`, `exists`, `all`,
|
|
108
|
+
`cased`), wildcards, a boolean `condition` language, and `aggregate` / `sequence` blocks that are
|
|
109
|
+
validated now and evaluated by the upcoming stateful engine. Documented in `docs/RULES.md`.
|
|
110
|
+
- Safe rule loading: size, depth and node limits, safe YAML only, no anchors or aliases, and every
|
|
111
|
+
error reported with file, line and path.
|
|
112
|
+
- Rule regular expressions run on RE2 (`google-re2`), so matching time is linear in the input and
|
|
113
|
+
no pattern can hang a run.
|
|
114
|
+
- `prohori rules list` and `prohori rules validate`.
|
|
115
|
+
- `prohori parse FILE|-`: parse and normalise logs with format detection, table or JSON Lines
|
|
116
|
+
output, a parse summary with error examples, and exit codes (`0` ok, `1` every line failed,
|
|
117
|
+
`2` usage error).
|
|
118
|
+
- `access_log` parser for nginx and Apache combined/common log formats, with custom patterns
|
|
119
|
+
and query strings kept in `extra.query`.
|
|
120
|
+
- `app_json` parser for application security events in JSON Lines.
|
|
121
|
+
- `generic_json` parser driven by a validated YAML field-mapping file.
|
|
122
|
+
- Sample access, application and generic JSON logs, and a sample mapping file.
|
|
123
|
+
- `pyyaml` and `rich` as core dependencies.
|
|
124
|
+
- Parser framework (`prohori.parsers`): bounded binary line reading, sanitising of control,
|
|
125
|
+
ANSI and invisible characters, per-run statistics with error examples, and a parser registry.
|
|
126
|
+
- `auth_log` parser for Linux `auth.log` / `secure`: sshd logins, invalid users, too many
|
|
127
|
+
authentication failures, disconnects, `pam_unix` and sudo lines; RFC 3164 and RFC 3339
|
|
128
|
+
timestamps with year inference, New Year rollover and configurable timezone.
|
|
129
|
+
Documented in `docs/PARSERS.md`.
|
|
130
|
+
- Synthetic sample logs in `samples/logs/`.
|
|
131
|
+
- Atheris fuzz harness for the parser, run in CI.
|
|
132
|
+
- `tzdata` dependency on Windows for IANA timezone support.
|
|
133
|
+
- Common schema (`prohori.schema`): immutable `Event`, `Alert` and `Incident` models with UTC-only
|
|
134
|
+
timestamps, IPv4/IPv6 validation, truncation of untrusted text, deterministic ids, dotted-path
|
|
135
|
+
field access and JSON / JSON Lines helpers. Documented in `docs/SCHEMA.md`.
|
|
136
|
+
- `pydantic` as a core dependency.
|
|
137
|
+
- Project skeleton: `src/` layout package with typed, importable subpackages.
|
|
138
|
+
- `prohori` command-line entry point with `--version` and `version`.
|
|
139
|
+
- Tooling: uv, Ruff, mypy (strict), pytest with coverage, pre-commit with secret scanning.
|
|
140
|
+
- Continuous integration on Linux and Windows for Python 3.11 to 3.14, CodeQL and Dependabot.
|
|
141
|
+
- Contributor, security and code-of-conduct documents.
|
|
142
|
+
- OpenSSF Scorecard workflow, README status badges and a project wiki.
|
|
143
|
+
|
|
144
|
+
[Unreleased]: https://github.com/UtshaBasak/Prohori/compare/v0.1.0...HEAD
|
|
145
|
+
[0.1.0]: https://github.com/UtshaBasak/Prohori/releases/tag/v0.1.0
|
prohori-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Utsha Basak
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
prohori-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: prohori
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A laptop-sized mini-SOC: from raw logs to detections, enriched incidents and analyst-ready reports.
|
|
5
|
+
Project-URL: Homepage, https://github.com/UtshaBasak/Prohori
|
|
6
|
+
Project-URL: Repository, https://github.com/UtshaBasak/Prohori
|
|
7
|
+
Project-URL: Issues, https://github.com/UtshaBasak/Prohori/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/UtshaBasak/Prohori/blob/main/CHANGELOG.md
|
|
9
|
+
Author: Utsha Basak
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: detection-engineering,incident-response,log-analysis,security,siem,soc,threat-detection
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Information Technology
|
|
16
|
+
Classifier: Intended Audience :: System Administrators
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Topic :: Security
|
|
25
|
+
Classifier: Topic :: System :: Logging
|
|
26
|
+
Classifier: Topic :: System :: Monitoring
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: >=3.11
|
|
29
|
+
Requires-Dist: google-re2>=1.1
|
|
30
|
+
Requires-Dist: httpx>=0.28
|
|
31
|
+
Requires-Dist: jinja2>=3.1
|
|
32
|
+
Requires-Dist: pydantic>=2.10
|
|
33
|
+
Requires-Dist: pyyaml>=6.0
|
|
34
|
+
Requires-Dist: rich>=13.0
|
|
35
|
+
Requires-Dist: typer>=0.15
|
|
36
|
+
Requires-Dist: tzdata>=2024.1
|
|
37
|
+
Provides-Extra: all
|
|
38
|
+
Requires-Dist: geoip2>=5.0; extra == 'all'
|
|
39
|
+
Provides-Extra: geoip
|
|
40
|
+
Requires-Dist: geoip2>=5.0; extra == 'geoip'
|
|
41
|
+
Provides-Extra: llm
|
|
42
|
+
Provides-Extra: ml
|
|
43
|
+
Provides-Extra: pipeline
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
# Prohori (প্রহরী)
|
|
47
|
+
|
|
48
|
+
<!-- Build and security -->
|
|
49
|
+
[](https://github.com/UtshaBasak/Prohori/actions/workflows/ci.yml)
|
|
50
|
+
[](https://github.com/UtshaBasak/Prohori/actions/workflows/codeql.yml)
|
|
51
|
+
[](https://scorecard.dev/viewer/?uri=github.com/UtshaBasak/Prohori)
|
|
52
|
+
[](https://github.com/gitleaks/gitleaks)
|
|
53
|
+
[](https://github.com/UtshaBasak/Prohori/blob/main/.github/dependabot.yml)
|
|
54
|
+
|
|
55
|
+
<!-- Package -->
|
|
56
|
+
[](https://pypi.org/project/prohori/)
|
|
57
|
+
[](https://pypi.org/project/prohori/)
|
|
58
|
+
[](https://pypi.org/project/prohori/#files)
|
|
59
|
+
[](https://pypistats.org/packages/prohori)
|
|
60
|
+
[](https://github.com/UtshaBasak/Prohori/releases)
|
|
61
|
+
[](https://github.com/UtshaBasak/Prohori/actions/workflows/release.yml)
|
|
62
|
+

|
|
63
|
+

|
|
64
|
+
[](https://github.com/UtshaBasak/Prohori/blob/main/LICENSE)
|
|
65
|
+
|
|
66
|
+
<!-- Tooling and conventions -->
|
|
67
|
+
[](https://github.com/astral-sh/uv)
|
|
68
|
+
[](https://github.com/astral-sh/ruff)
|
|
69
|
+
[](https://mypy-lang.org/)
|
|
70
|
+
[](https://github.com/pre-commit/pre-commit)
|
|
71
|
+
[](https://www.conventionalcommits.org/)
|
|
72
|
+
[](https://github.com/UtshaBasak/Prohori/blob/main/CODE_OF_CONDUCT.md)
|
|
73
|
+
|
|
74
|
+
<!-- Repository -->
|
|
75
|
+
[](https://github.com/UtshaBasak/Prohori/commits/main)
|
|
76
|
+
[](https://github.com/UtshaBasak/Prohori/graphs/commit-activity)
|
|
77
|
+
[](https://github.com/UtshaBasak/Prohori/issues)
|
|
78
|
+
[](https://github.com/UtshaBasak/Prohori/pulls)
|
|
79
|
+
[](https://github.com/UtshaBasak/Prohori/wiki)
|
|
80
|
+
[](https://github.com/UtshaBasak/Prohori/blob/main/CONTRIBUTING.md)
|
|
81
|
+
|
|
82
|
+
**A laptop-sized mini-SOC: from raw logs to detections, enriched incidents and analyst-ready
|
|
83
|
+
reports, with honest machine-learning evaluation and a streaming data pipeline.**
|
|
84
|
+
|
|
85
|
+
*Prohori* (প্রহরী) is Bengali for "guard". It is an open-source, defensive security-operations
|
|
86
|
+
toolkit in Python that runs on a single laptop with free tools only, offline by default.
|
|
87
|
+
|
|
88
|
+
> **Status: alpha (v0.1.0).** The batch core is complete: one command goes from a log file to
|
|
89
|
+
> scored incidents and shareable reports, and a labelled attack simulator measures the rules.
|
|
90
|
+
> Machine learning, the streaming pipeline and the LLM analyst follow in later releases. See the
|
|
91
|
+
> [CHANGELOG](https://github.com/UtshaBasak/Prohori/blob/main/CHANGELOG.md) and the [roadmap](#roadmap).
|
|
92
|
+
|
|
93
|
+
## 60-second demo
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pipx install prohori # or: pip install prohori
|
|
97
|
+
prohori demo
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+

|
|
101
|
+
|
|
102
|
+
The demo simulates an account takeover, an SSH brute force and a web scan, triages each one
|
|
103
|
+
offline and writes an HTML report per incident to `prohori-demo/`. It needs no configuration and
|
|
104
|
+
sends nothing over the network. Sample reports: [Markdown](https://github.com/UtshaBasak/Prohori/blob/main/docs/examples/account-takeover.md),
|
|
105
|
+
[HTML](https://github.com/UtshaBasak/Prohori/blob/main/docs/examples/account-takeover.html) (download and open it) and
|
|
106
|
+
[redacted](https://github.com/UtshaBasak/Prohori/blob/main/docs/examples/account-takeover-redacted.md).
|
|
107
|
+
|
|
108
|
+
## Contents
|
|
109
|
+
|
|
110
|
+
- [What it does](#what-it-does)
|
|
111
|
+
- [Install](#install)
|
|
112
|
+
- [Quick start](#quick-start)
|
|
113
|
+
- [Example: an account takeover](#example-an-account-takeover)
|
|
114
|
+
- [Commands](#commands)
|
|
115
|
+
- [Built-in detection rules](#built-in-detection-rules)
|
|
116
|
+
- [How well the rules work](#how-well-the-rules-work)
|
|
117
|
+
- [Privacy and safety](#privacy-and-safety)
|
|
118
|
+
- [Documentation](#documentation)
|
|
119
|
+
- [Limitations](#limitations)
|
|
120
|
+
- [Roadmap](#roadmap)
|
|
121
|
+
- [Development](#development)
|
|
122
|
+
|
|
123
|
+
## What it does
|
|
124
|
+
|
|
125
|
+
```mermaid
|
|
126
|
+
flowchart LR
|
|
127
|
+
A[Log files<br>auth.log, nginx, app JSON] --> B[parse<br>common event schema]
|
|
128
|
+
B --> C[detect<br>15 YAML rules]
|
|
129
|
+
C --> D[incidents<br>group, score, ATT&CK]
|
|
130
|
+
E[enrich<br>GeoLite2, AbuseIPDB, VirusTotal] --> D
|
|
131
|
+
D --> F[report<br>Markdown, HTML, JSON, CSV]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
| Stage | Today |
|
|
135
|
+
|-------|-------|
|
|
136
|
+
| **Parse** | Linux `auth.log` (sshd, sudo, PAM), nginx/Apache access logs, application security events (JSON Lines) and any JSON Lines layout through a YAML field mapping, into one typed event schema. Format detection, streaming with bounded memory, hostile input handled (oversized lines, bad encodings, control characters). |
|
|
137
|
+
| **Detect** | 15 readable YAML rules (Sigma-inspired) with field modifiers, thresholds, distinct counts, ratios, sliding and tumbling windows and ordered sequences. Every rule ships with test fixtures and a runbook; regular expressions run on RE2, so no pattern can hang a run. |
|
|
138
|
+
| **Enrich** | IP geolocation, network owner and reputation from local GeoLite2 files, AbuseIPDB and VirusTotal, all optional. SQLite cache, daily budgets, rate limiting and bounded retries. Private and documentation addresses never leave the machine. |
|
|
139
|
+
| **Incidents** | Related alerts (same IP, account, request or session within a time gap) become one incident with a points-based score you can read line by line, severity, confidence, MITRE ATT&CK v20.0 mapping, a timeline, recommended actions and stated limitations. Detects sign-ins from an IP or app new to the account. |
|
|
140
|
+
| **Simulate** | Eight labelled attack scenarios (SSH brute force, password spraying, credential stuffing, token reuse, account takeover, web scanning, injection, admin misuse) hidden in realistic background activity, byte-identical for a seed, documentation IP addresses only. Used to measure every rule's precision and recall. |
|
|
141
|
+
| **Report** | Markdown, a self-contained HTML page, JSON or a CSV timeline, with an appendix of input and rule-file SHA-256 hashes. `--redact` replaces account names, ids and emails (and optionally IPs) with keyed pseudonyms, and refuses to write a report that would still leak one. |
|
|
142
|
+
|
|
143
|
+
Prohori only **recommends** actions. It never blocks addresses, disables accounts or touches
|
|
144
|
+
remote systems.
|
|
145
|
+
|
|
146
|
+
## Install
|
|
147
|
+
|
|
148
|
+
Prohori needs Python 3.11 or newer. CI tests it on Linux and Windows; macOS should work but is
|
|
149
|
+
not tested.
|
|
150
|
+
|
|
151
|
+
- **From PyPI:** `pipx install prohori` puts the `prohori` command in its own environment;
|
|
152
|
+
`pip install prohori` works in any virtual environment. Local GeoLite2 support is an extra:
|
|
153
|
+
`pip install "prohori[geoip]"`.
|
|
154
|
+
- **From source:** install [uv](https://docs.astral.sh/uv/), then
|
|
155
|
+
`git clone https://github.com/UtshaBasak/Prohori.git && cd Prohori && uv sync`, and run
|
|
156
|
+
commands as `uv run prohori ...`. The sample files used below are in the repository.
|
|
157
|
+
|
|
158
|
+
## Quick start
|
|
159
|
+
|
|
160
|
+
Everything below runs offline on the synthetic samples in the repository (run from a clone).
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
uv run prohori triage samples/logs/account-takeover.jsonl -o triage/ # the whole pipeline
|
|
164
|
+
uv run prohori parse samples/logs/auth.log # normalise a log
|
|
165
|
+
uv run prohori detect samples/logs/auth.log # run the 15 rules
|
|
166
|
+
uv run prohori enrich 203.0.113.7 --offline # look up an IP (demo data)
|
|
167
|
+
uv run prohori incidents samples/logs/account-takeover.jsonl # group alerts into incidents
|
|
168
|
+
uv run prohori report samples/incidents/INC-ee7c8818fbb34989.json -f html -o report.html
|
|
169
|
+
uv run prohori simulate account-takeover --seed 1 --out out/ # your own labelled attack
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`prohori triage` writes `triage/summary.md`, `triage/incidents.jsonl` and an HTML report per
|
|
173
|
+
incident. Open a report in a browser: one file, no scripts, no external resources. For your own
|
|
174
|
+
logs, `prohori triage /var/log/auth.log --year 2026 -o triage/` works the same way (add
|
|
175
|
+
`--redact-ips` before sharing the output).
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
## Example: an account takeover
|
|
179
|
+
|
|
180
|
+
`samples/logs/account-takeover.jsonl` is a synthetic attack on a web shop account: twelve wrong
|
|
181
|
+
passwords from one IP in 33 seconds, a successful sign-in, two-step verification switched off and
|
|
182
|
+
the bKash payout number changed. Prohori raises four alerts and joins them into **one critical
|
|
183
|
+
incident**:
|
|
184
|
+
|
|
185
|
+
```text
|
|
186
|
+
$ uv run prohori incidents samples/logs/account-takeover.jsonl # first lines
|
|
187
|
+
INC-ee7c8818fbb34989 critical score 100
|
|
188
|
+
+-------------------------------------------------------------------------------------------------+
|
|
189
|
+
| title | Successful sign-in after many failures from one IP and 3 related alerts |
|
|
190
|
+
| time (UTC) | 2026-10-24 21:10:00 - 2026-10-24 21:13:10 |
|
|
191
|
+
| confidence | high |
|
|
192
|
+
| rules | PRH-ACCT-001, PRH-AUTH-001, PRH-AUTH-003 |
|
|
193
|
+
| entities | source.ip: 203.0.113.7 |
|
|
194
|
+
| | user.email_hash: 0cc175b9c0f1b6a831c399e269772661 |
|
|
195
|
+
| | user.id: u-2001 |
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Every point of the score is explained (from `prohori report`):
|
|
199
|
+
|
|
200
|
+
| Points | Reason |
|
|
201
|
+
|-------:|--------|
|
|
202
|
+
| +50 | Most severe alert: PRH-AUTH-003 (critical) |
|
|
203
|
+
| +20 | 2 more rules fired: PRH-ACCT-001, PRH-AUTH-001 |
|
|
204
|
+
| +20 | A sign-in succeeded after failed attempts |
|
|
205
|
+
| +15 | Sensitive account or admin change: account.2fa.disabled, account.bkash.changed |
|
|
206
|
+
| +20 | Reputation of 203.0.113.7: abuse confidence 97; 9 vendors flag it as malicious (from bundled demo data) |
|
|
207
|
+
| +10 | Sign-in to u-2001 from 203.0.113.7, an IP not seen for this account before (1 earlier IP) |
|
|
208
|
+
| +5 | Sign-in to u-2001 with client web, not seen for this account before |
|
|
209
|
+
| -40 | Capped at 100 |
|
|
210
|
+
| **100** | **Total** |
|
|
211
|
+
|
|
212
|
+
The report adds the timeline, the IP's enrichment, the ATT&CK techniques (T1110 Brute Force,
|
|
213
|
+
T1078 Valid Accounts, T1098 Account Manipulation, T1556.006 Multi-Factor Authentication),
|
|
214
|
+
recommended actions from the runbooks and the limitations, such as "single log source" and
|
|
215
|
+
"enrichment is bundled demo data".
|
|
216
|
+
|
|
217
|
+
## Commands
|
|
218
|
+
|
|
219
|
+
| Command | What it does | Docs |
|
|
220
|
+
|---------|--------------|------|
|
|
221
|
+
| `prohori demo` | Simulate attacks, triage them and show the results; no configuration, offline | [CLI.md](https://github.com/UtshaBasak/Prohori/blob/main/docs/CLI.md#prohori-demo) |
|
|
222
|
+
| `prohori triage FILE` | Log in, incidents and a report per incident out (`--format`, `--redact`, `--online`) | [CLI.md](https://github.com/UtshaBasak/Prohori/blob/main/docs/CLI.md#prohori-triage) |
|
|
223
|
+
| `prohori parse FILE` | Parse and normalise a log; table or JSON Lines | [PARSERS.md](https://github.com/UtshaBasak/Prohori/blob/main/docs/PARSERS.md) |
|
|
224
|
+
| `prohori detect FILE` | Run the 15 built-in rules (and `--rules DIR`); exit code 3 when alerts are found | [RULES.md](https://github.com/UtshaBasak/Prohori/blob/main/docs/RULES.md) |
|
|
225
|
+
| `prohori rules list` / `validate` / `test` | Show, check and test detection rules | [RULES.md](https://github.com/UtshaBasak/Prohori/blob/main/docs/RULES.md) |
|
|
226
|
+
| `prohori enrich IP...` | Geolocation and reputation; `--offline` never uses the network | [ENRICHMENT.md](https://github.com/UtshaBasak/Prohori/blob/main/docs/ENRICHMENT.md) |
|
|
227
|
+
| `prohori incidents FILE` | Parse, detect and group alerts into scored incidents; offline enrichment unless `--online` | [INCIDENTS.md](https://github.com/UtshaBasak/Prohori/blob/main/docs/INCIDENTS.md) |
|
|
228
|
+
| `prohori report INCIDENT` | Render an incident as `md`, `html`, `json` or `csv`; `--redact`, `--redact-ips` | [REPORTS.md](https://github.com/UtshaBasak/Prohori/blob/main/docs/REPORTS.md) |
|
|
229
|
+
| `prohori simulate SCENARIO --out DIR` | Write a labelled attack scenario (`--list`, `--seed`, `--variant heldout`, `--set`) | [SIMULATOR.md](https://github.com/UtshaBasak/Prohori/blob/main/docs/SIMULATOR.md) |
|
|
230
|
+
| `prohori version` | The version, ATT&CK release and built-in rule set | |
|
|
231
|
+
|
|
232
|
+
Commands that take a file also read standard input with `-`, and every command has `--help`.
|
|
233
|
+
Exit codes are the same everywhere: `0` nothing found, `3` alerts or incidents found
|
|
234
|
+
(`--exit-zero` turns this off for CI pipelines), `1` nothing could be processed and `2` invalid
|
|
235
|
+
usage. The full reference is [docs/CLI.md](https://github.com/UtshaBasak/Prohori/blob/main/docs/CLI.md).
|
|
236
|
+
|
|
237
|
+
## Built-in detection rules
|
|
238
|
+
|
|
239
|
+
| Rule | Detects | Severity | ATT&CK |
|
|
240
|
+
|------|---------|----------|--------|
|
|
241
|
+
| PRH-AUTH-001 | Brute-force sign-in attempts from one IP | high | T1110, T1110.001 |
|
|
242
|
+
| PRH-AUTH-002 | Password spraying or credential stuffing from one IP | high | T1110.003, T1110.004 |
|
|
243
|
+
| PRH-AUTH-003 | Successful sign-in after many failures from one IP | critical | T1110, T1078 |
|
|
244
|
+
| PRH-AUTH-004 | Account lockouts across many accounts | high | T1110 |
|
|
245
|
+
| PRH-SESS-001 | Refresh-token reuse | high | T1528, T1550.001 |
|
|
246
|
+
| PRH-ACCT-001 | Sensitive account change shortly after sign-in | high | T1098, T1556.006 |
|
|
247
|
+
| PRH-AUTHZ-001 | Authorisation denials across many resources | medium | T1595.003 |
|
|
248
|
+
| PRH-ABUSE-001 | Rate-limit bursts or bot-check failures from one IP | medium | T1499.002 |
|
|
249
|
+
| PRH-ADMIN-001 | Admin role granted | high | T1098 |
|
|
250
|
+
| PRH-ADMIN-002 | Admin action at an unusual hour (22:00-07:00 local time) | medium | T1078 |
|
|
251
|
+
| PRH-WEB-001 | Web scanning from one IP | medium | T1595.003 |
|
|
252
|
+
| PRH-WEB-002 | Injection patterns in request paths | medium | T1190 |
|
|
253
|
+
| PRH-WEB-003 | Scanner or empty user agent at a high request rate | medium | T1595.002 |
|
|
254
|
+
| PRH-WEB-004 | Probes for sensitive files and admin tools | medium | T1595.003 |
|
|
255
|
+
| PRH-SSH-001 | SSH invalid-user storm or repeated authentication-limit disconnects | high | T1110, T1110.001 |
|
|
256
|
+
|
|
257
|
+
ATT&CK tags are verified against the official Enterprise ATT&CK v20.0 data. Each rule has a
|
|
258
|
+
runbook in [`src/prohori/runbooks/`](https://github.com/UtshaBasak/Prohori/blob/main/src/prohori/runbooks/). Writing your own rules is covered in
|
|
259
|
+
[docs/RULES.md](https://github.com/UtshaBasak/Prohori/blob/main/docs/RULES.md).
|
|
260
|
+
|
|
261
|
+
## How well the rules work
|
|
262
|
+
|
|
263
|
+
`prohori simulate` writes each attack scenario in two variants: **default**, the attack the rules
|
|
264
|
+
were designed for, and **held-out**, the same attack done the way a careful attacker would
|
|
265
|
+
(slower, from more addresses, with a browser user agent, obfuscated, or at another hour).
|
|
266
|
+
`scripts/evaluate_rules.py` runs all rules over five seeds of each and compares the alerts with
|
|
267
|
+
the line labels.
|
|
268
|
+
|
|
269
|
+
<!-- results:start -->
|
|
270
|
+
| | Default | Held-out |
|
|
271
|
+
|--|--------:|---------:|
|
|
272
|
+
| Attacks caught by at least one expected rule | 40/40 | 35/40 |
|
|
273
|
+
|
|
274
|
+
Expected rules that missed in the held-out variants (runs missed out of runs):
|
|
275
|
+
|
|
276
|
+
- `account-takeover`: PRH-ACCT-001 3/5, PRH-AUTH-001 5/5
|
|
277
|
+
- `admin-misuse`: PRH-ADMIN-002 5/5
|
|
278
|
+
- `credential-stuffing`: PRH-AUTH-001 5/5, PRH-AUTH-002 5/5, PRH-AUTH-003 5/5
|
|
279
|
+
- `password-spraying`: PRH-AUTH-001 5/5
|
|
280
|
+
- `ssh-bruteforce`: PRH-AUTH-001 5/5
|
|
281
|
+
- `web-scanning`: PRH-WEB-003 5/5
|
|
282
|
+
<!-- results:end -->
|
|
283
|
+
|
|
284
|
+
PRH-ACCT-001 has low precision on purpose: it flags every sensitive change soon after a sign-in,
|
|
285
|
+
including the owners' own. These are synthetic results written by the rules' author; the full
|
|
286
|
+
per-rule table and caveats are in [docs/SIMULATOR.md](https://github.com/UtshaBasak/Prohori/blob/main/docs/SIMULATOR.md#results).
|
|
287
|
+
|
|
288
|
+
## Privacy and safety
|
|
289
|
+
|
|
290
|
+
- **Offline by default.** Nothing leaves the machine unless you configure API keys and, for
|
|
291
|
+
incidents, pass `--online`. Tests block real network access.
|
|
292
|
+
- **Private, reserved and documentation IP addresses are never sent** to outside services, even
|
|
293
|
+
when keys are configured. VirusTotal keeps the addresses it is asked about, so querying is a
|
|
294
|
+
deliberate choice.
|
|
295
|
+
- **Logs are untrusted input.** Parsers cap line lengths and strip control characters; reports
|
|
296
|
+
escape every value for their format (HTML, Markdown, CSV formulas) and are tested with hostile
|
|
297
|
+
input.
|
|
298
|
+
- **Shareable reports.** `--redact` / `--redact-ips` use keyed pseudonyms and fail closed.
|
|
299
|
+
- **No secrets in the repository.** API keys live in the environment or a git-ignored `.env`;
|
|
300
|
+
gitleaks runs on every commit and in CI.
|
|
301
|
+
- **Samples are synthetic** and use documentation address ranges only.
|
|
302
|
+
|
|
303
|
+
Third-party data terms (MITRE ATT&CK, GeoLite2, AbuseIPDB, VirusTotal) are summarised in
|
|
304
|
+
[docs/LICENSES.md](https://github.com/UtshaBasak/Prohori/blob/main/docs/LICENSES.md).
|
|
305
|
+
|
|
306
|
+
## Documentation
|
|
307
|
+
|
|
308
|
+
| Document | Contents |
|
|
309
|
+
|----------|----------|
|
|
310
|
+
| [CLI reference](https://github.com/UtshaBasak/Prohori/blob/main/docs/CLI.md) | Every command, the shared options, formats and exit codes |
|
|
311
|
+
| [Architecture](https://github.com/UtshaBasak/Prohori/blob/main/docs/ARCHITECTURE.md) | Components, data flow and run modes |
|
|
312
|
+
| [Event schema](https://github.com/UtshaBasak/Prohori/blob/main/docs/SCHEMA.md) | Event, Alert and Incident fields |
|
|
313
|
+
| [Parsers](https://github.com/UtshaBasak/Prohori/blob/main/docs/PARSERS.md) | Supported formats, options and field mappings |
|
|
314
|
+
| [Rules](https://github.com/UtshaBasak/Prohori/blob/main/docs/RULES.md) | Rule format, evaluation semantics, testing and the built-in rules |
|
|
315
|
+
| [Enrichment](https://github.com/UtshaBasak/Prohori/blob/main/docs/ENRICHMENT.md) | Providers, privacy, cache, limits and configuration |
|
|
316
|
+
| [Incidents](https://github.com/UtshaBasak/Prohori/blob/main/docs/INCIDENTS.md) | Grouping, the scoring table, confidence and limitations |
|
|
317
|
+
| [Reports](https://github.com/UtshaBasak/Prohori/blob/main/docs/REPORTS.md) | Formats, sections, redaction and output safety |
|
|
318
|
+
| [Simulator](https://github.com/UtshaBasak/Prohori/blob/main/docs/SIMULATOR.md) | Attack scenarios, labels, and rule precision and recall |
|
|
319
|
+
| [Decisions](https://github.com/UtshaBasak/Prohori/blob/main/docs/DECISIONS.md) | Architecture decision records |
|
|
320
|
+
| [Releasing](https://github.com/UtshaBasak/Prohori/blob/main/docs/RELEASING.md) | How releases are built, tested and published |
|
|
321
|
+
| [Third-party terms](https://github.com/UtshaBasak/Prohori/blob/main/docs/LICENSES.md) | Data and service licences |
|
|
322
|
+
| [Wiki](https://github.com/UtshaBasak/Prohori/wiki) | Installation, quick start and guides |
|
|
323
|
+
|
|
324
|
+
## Limitations
|
|
325
|
+
|
|
326
|
+
Prohori is honest about what it does not do:
|
|
327
|
+
|
|
328
|
+
- **It reads logs after the fact.** There is no live monitoring yet (the streaming pipeline is
|
|
329
|
+
planned for v0.3.0), and it never blocks, disables or changes anything.
|
|
330
|
+
- **Rules see only what the logs say.** Request bodies, headers and encrypted traffic are not
|
|
331
|
+
inspected; the web rules use the request line only.
|
|
332
|
+
- **Thresholds can be evaded.** The held-out evaluation shows that slow or widely distributed
|
|
333
|
+
attacks stay under the brute-force and spraying thresholds; see
|
|
334
|
+
[How well the rules work](#how-well-the-rules-work).
|
|
335
|
+
- **Some rules are noisy by design.** PRH-ACCT-001 flags every sensitive change shortly after a
|
|
336
|
+
sign-in, including the owner's own; incidents list such benign causes as limitations.
|
|
337
|
+
- **The evaluation is synthetic** and written by the rules' author. Test the rules on your own
|
|
338
|
+
logs before relying on them.
|
|
339
|
+
- **Enrichment free tiers** are small and non-commercial, and reputation data is evidence, not
|
|
340
|
+
proof.
|
|
341
|
+
- **Supported formats** are `auth.log`, nginx/Apache access logs and JSON Lines (built-in app
|
|
342
|
+
layout or a field mapping). Windows event logs and cloud audit logs are not supported yet.
|
|
343
|
+
|
|
344
|
+
## Roadmap
|
|
345
|
+
|
|
346
|
+
| Release | Contents | State |
|
|
347
|
+
|---------|----------|-------|
|
|
348
|
+
| v0.1.0 | Batch core: parsers, rules, enrichment, incidents, reports, log simulator with rule evaluation, `triage` and `demo` commands | Released |
|
|
349
|
+
| v0.2.0 | Machine learning: leakage-controlled intrusion detection on public flow datasets, log-derived anomaly detection, model cards | Planned |
|
|
350
|
+
| v0.3.0 | Pipeline: streaming core, Redpanda and PostgreSQL in Docker, dbt models, dashboard, orchestration | Planned |
|
|
351
|
+
| v1.0.0 | Optional LLM analyst (opt-in, redacted input, validated output), final polish | Planned |
|
|
352
|
+
|
|
353
|
+
## Development
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
uv sync # create .venv with the package and dev tools
|
|
357
|
+
uv run pre-commit install # optional: run checks on every commit
|
|
358
|
+
|
|
359
|
+
uv run ruff check . # lint
|
|
360
|
+
uv run ruff format --check . # formatting
|
|
361
|
+
uv run mypy # strict type check
|
|
362
|
+
uv run pytest --cov # tests with coverage (kept at 85 % or more)
|
|
363
|
+
uv run prohori rules test # run every rule against its fixtures
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
CI runs on Linux and Windows with Python 3.11 to 3.14, plus CodeQL, secret scanning, parser
|
|
367
|
+
fuzzing and the OpenSSF Scorecard. All GitHub Actions are pinned to commit SHAs. See
|
|
368
|
+
[CONTRIBUTING.md](https://github.com/UtshaBasak/Prohori/blob/main/CONTRIBUTING.md) for conventions.
|
|
369
|
+
|
|
370
|
+
## Security
|
|
371
|
+
|
|
372
|
+
To report a vulnerability, see [SECURITY.md](https://github.com/UtshaBasak/Prohori/blob/main/SECURITY.md).
|
|
373
|
+
|
|
374
|
+
## License
|
|
375
|
+
|
|
376
|
+
[MIT](https://github.com/UtshaBasak/Prohori/blob/main/LICENSE). Third-party data has its own terms; see [docs/LICENSES.md](https://github.com/UtshaBasak/Prohori/blob/main/docs/LICENSES.md).
|