Repository navigation
Releases: rakshit-737/rootline-provenance-forensics
Releases · rakshit-737/rootline-provenance-forensics
Release list
rootline v1.1.2
Changed
- The repository was renamed to
rakshit-737/rootline-provenance-forensics. Docs now live at
https://rakshit-737.github.io/rootline-provenance-forensics/ and the container image is
ghcr.io/rakshit-737/rootline-provenance-forensics. Older entries below keep the old names.
rootline v1.1.1
Added
rootline.stats: standard-library Wilson, Student-t and log-cluster bootstrap intervals, a
paired t-test, exact sign and sign-flip tests, and p-value formatting that never prints 0.rootline.provenance: every result file written byscripts/bench.pyand
scripts/ablation.pycarries a provenance block (script, version, commit, dirty flag,
GitHub workflow and run id, Python, platform);results/RESULTS.mdnames the run of each
section.- Manual
benchworkflow: regeneratesresults/atlas.json,ablation.jsonand
log4shell.jsonin CI, plus an in-sample re-score of dev/dev2 at HEAD
(results/coverage_head.json). The committed files come from run 37092501921. scripts/live/collect_repeatability.py: one row per live-ebpf CI job (checks, root causes,
story size, lost events) from the run artefacts, for a stated run-id window.scripts/verify_freeze.py --ref REF(hashgit show REF:path),--ast BASE
(docstring-insensitive logic check) and--files; afreeze-recordCI job asserts that
the freeze holds at 153eade and 60e3561, that the rule logic at HEAD is unchanged, and that
freeze_v03.jsonandresults/coverage.jsonare untouched since scoring.scripts/render_tables.py --checkand a "Headline results" block with a source and run-id
column; tests keep the README and docs-home headline tables identical to it, and the
README's quickstart output identical to the real CLI output.rootline.bench.sealed_excluding_v03_targets()recomputes coverage.json's derived sealed
key from the committed rows (it matches).results/figures/ablation_stops.png: the session-root stop effect per held-out log.download_data.py --only DEST...;docs/protocol.md"State after scoring"; a References
section in the README.
Changed
- Ablation statistics are over logs, not pivots: exact sign and sign-flip tests and a paired t
over per-log means, p-values to 4 significant digits; S1-S4 is reported as per-log ranges
with a t(3) interval on the difference, because four logs cannot support a
distribution-free test. render_tables.pycomputes every Wilson interval once from the counts, gives the
alert-precision proxy its pre-registered interval, adds across-log intervals and paired
tests to the IOC-pivot and anomaly tables, the M2/M4 (new exploit) split, ATLAS's own
cleaned list under our scorer as the like-for-like reference, 5-seed ranges for the LSTM
reproduction, and separate rows for the detailed live run and the repeatability window.scripts/bench.pyruns atlas + log4shell by default. Rule coverage runs only with
--only coverage, behind the freeze check;--allow-unfrozenscores dev/dev2 into
coverage_head.json. Parts that were not run print "reused ... (not re-run)".- The replay UI routes edges around vertex labels and shows each IOC once; the static demo
serves Mermaid and Cypher exports as text. Hero screenshot refreshed. - Supply chain: first-party actions pinned by SHA, the base and Neo4j images by digest, and the
image installs its dependencies from the hash-lockeddocker/requirements.lock
(--require-hashes), which pip-audit also checks; the lock sits outside Dependabot's pip
scope, where single-pin bumps (PR #6, pydantic-core) would break it. httpx2joins theapianddevextras (starlette's TestClient prefers it).- Docstrings on every public function and class outside the frozen sources; ruff D101-D103
in lint. build_demo.py,render_tables.pyandverify_freeze.pyhave--help.
Fixed
- A JSONL capture that is not UTF-8 text crashed
analyze/verifywith a traceback and made
POST /api/analyzereturn 500; it is now a one-line error and HTTP 422. bench.py --only atlasandablation.pyoverwrote their results with empty files when the
ATLAS data was missing; they now exit with an error and write nothing.bench.py --only log4shellfinds the OTRF captures where the downloader puts them.- Full-method held-out precision is 0.62 (0.6249), not 0.63; Wilson bounds rounded twice are
corrected (27/45 is [0.45, 0.73], 4/64 is [0.02, 0.15]; ADR 0009 erratum). - Live eBPF repeatability: the window missed run 36998890924 and over-counted the Dependabot
runs. Over 15 pushes, 71 of 72 completed jobs pass (Wilson [0.925, 0.998]) and the chain is
recovered in 72 of 72; the 10 Dependabot runs (50/50 jobs) are listed by id. - Wording: hit@3 is 0.40-0.50; time + stops is the highest held-out F1 point estimate, not a
significant winner; the paper's Table 3 event counts equal the+lines for 9 of 10
attacks; all of Table 4's starting entities are listed; the LSTM setup is attributed to
ATLAS's released code.
Documented
- The sealed numbers belong to commit 60e3561: the normaliser, graph and Sysmon/auditd loaders
changed in logic after scoring (f1e71e6, bc6984d), and docstrings changed in frozen rule files
(284c125, 580470b). Re-scoring dev/dev2 with the current code reproduces all 73 rows. - 8 of the 12 held-out ATLAS logs replay an S1-S4 exploit; the stop effect also holds on the
four logs with a new exploit (M2, M4: +0.12 precision, 4 of 4 higher). - Roadmap entries for the live multi-sensor ablation, the leave-one-scenario-out redesign with
LSTM retraining on M2-M6, and the 1M-event story demo. - The v1.1.0 tag produced duplicate release runs (37003642235, 37003644268) that failed with
"a release with the same tag name already exists"; the release workflow was fixed in
2aad2d0 (serialised per tag, tolerates an existing release).
Results that got worse (published as is)
- Held-out full-method precision is reported as 0.62 instead of 0.63.
- The S1-S4 intervals are wider: full F1 0.66 is now given with the per-log range 0.42-0.78
and ΔF1 t(3) interval [0.24, 0.58] instead of a 4-log bootstrap [0.51, 0.78], and the
pivot-level p-values (printed as p=0 or p < 1e-6) are replaced by log-level ones
(stops: p = 0.0005; full vs naive F1 on held-out logs: p = 0.28). - IsolationForest vs the user-dir heuristic: the heuristic's advantage is p = 0.039 by sign
test with a mean difference CI that includes 0 ([-0.02, 0.33]).
rootline v1.1.0
Added
- Live eBPF in CI (
live-ebpfjob, five runs per push): the bpftrace probe runs with
sudo on the ubuntu-24.04 runner kernel while a benign chain runs entirely inside the
runner (TEST-NET server on a dummy interface, throwawayHOME, dummy files); one
query must recover the download socket as a root cause plus every chain step.
Results inresults/live_ebpf.json; ADR 0008 supersedes ADR 0007. - v0.3 rules RL-019..024 (sudo/doas/setuid abuse, kernel modules, library
hijacking and related techniques), written from thedevanddev2captures. - dev / dev2 / sealed rule protocol (
docs/protocol.md, ADR 0009): 160 sealed
Splunk captures pinned atattack_data@b4573ed3by LFS SHA-256, a freeze check
(scripts/verify_freeze.py) and the manualsealed-coverageworkflow that scored
them once (run 36994398382). Wilson 95 % intervals and an alert-precision proxy. - Ablation study (
rootline.ablation,scripts/ablation.py, ADR 0010):
time-respecting traversal, session-root stops, causal spine and "accessed"
expansion switched off one at a time, every ground-truth entity as a pivot,
ATLAS S1-S4 (design data) vs the 12 M1-M6 host logs (held out), scenario-cluster
bootstrap CIs and sign tests. - ATLAS reproduction (
repro/atlas_repro.py, manualatlas-reproworkflow):
PyTorch reimplementation of the ATLAS LSTM under the paper's setup, S1-S4 x 5 seeds,
with ATLAS's shipped artefacts scored the same way (results/atlas_repro.json). - IsolationForest baselines: a user-directory heuristic and a run without the
exec_user_dirfeature (IForestTagger(drop=...),userdir_ranking). - ATLAS M1 (multi-host) logs in the benchmarks; all ATLAS zips pinned to commit
e46096dwith SHA-256 and size. - Docs: How it works, Evaluation, Reproduce and protocol pages; generated CLI
reference;docs/hooks.pyso a plainmkdocs build --strictworks. - Repo: issue/PR templates, CODEOWNERS, Dependabot, CITATION.cff; CI on Python
3.10-3.14; private vulnerability reporting and secret scanning enabled.
Changed
- Probe v1.2: self filtering by PID instead of process name; process creation
fromwake_up_new_taskfor new thread groups only (threads no longer appear as
processes);-f jsonoutput with a fixed record layout; fd maps keyed by process
start time; dup2/dup3 path tracking;tsns=timestamps. - The default rule set is v0.3 (RL-001..024). The v1.0
holdoutis renameddev2
and is in-sample for v0.3. - IOCs:
ipholds all external addresses,ipv4/ipv6split them by family;
loopback, unspecified and link-local addresses and/dev,/proc,/sys,/run
paths are never IOCs; IPv6 sockets are labelled[addr]:port. - API: loopback-only Host allowlist,
X-Rootline: 1required on POST, streamed
upload cap, parsing in a worker thread, bounded story store, docs endpoints off
by default;servewarns when bound off loopback. - CLI:
verifyreads any format and fails on zero parsed events; one-line errors for
missing files;--iforestchecks for its extra first; help text on every option. - Downloads: Splunk URLs pinned to a commit, size check, fail closed without a
SHA-256; default data directory~/.cache/rootline. - Packaging: SPDX licence string (setuptools >= 77), tests shipped in the sdist,
reproextra, uvicorn indev. Actions bumped to their Node-24 majors; release
actions pinned by SHA.
Fixed
- Cypher export: a root-cause path containing a newline could escape a
//comment
and inject a statement. Telemetry text is now only ever a quoted literal. - Mermaid labels escape quotes and control characters.
- Sysmon
<Event>extraction was quadratic on unterminated tags (ReDoS); it is now
linear with a 1 MB line cap. - Malformed numbers, deep JSON and non-finite timestamps no longer crash ingestion.
- The
/demo/URL served a docs page instead of the replay UI (page renamed to
live-demo.md, plus a CI guard). - Release notes extraction from this file (awk escape bug); the v1.0.0 release body
was corrected. mean_ciclips the 95 % t-interval to the metric's valid range (recall in
[0, 1], hits in [0, min(k, malicious)], first-hit rank >= 1), so results no
longer report impossible bounds such as recall@10 CI [0.837, 1.063].
scripts/bench.py --render-onlyre-renders RESULTS.md from cached JSON.- The live chain no longer writes into the real
~/.awsand~/.configof whoever
runs it; it refuses to run outside CI unlessROOTLINE_LAB=1.
Results that got worse (published as is)
- Rule coverage on the sealed split: v0.3 detects 4 of 64 parsed captures (Wilson
95 % [0.03, 0.15]); 96 of 160 sealed captures are not parsed by the frozen loaders. - The ATLAS LSTM reproduction does not reach the paper's numbers (entity F1 0.24-0.75
vs 0.89-1.00). - On held-out ATLAS hosts the full reconstructor's F1 gain over naive reachability is
not significant (recall 0.72), and from the analyst IOC the story collapses on 5 of 6
second-hop hosts. - IsolationForest process ranking is beaten by a one-line user-directory heuristic
(new baseline), and falls to recall@10 0.49 without itsexec_user_dirfeature.
rootline v1.0.0
Docs site, static demo, release pipeline, and statistically honest anomaly numbers.
Added
- MkDocs Material documentation site on GitHub Pages
(https://rakshit-737.github.io/rootline/), with architecture, datasets,
benchmarks, CLI, Python API (mkdocstrings) and HTTP API reference, the threat
model, ADRs and limitations. It is built withmkdocs build --strict. - A static build of the attack-replay UI under
/demo/
(scripts/build_demo.py). It is rendered from the committed Log4Shell excerpt
and one synthetic intrusion. - Release workflow: on a
v*tag it pushes the image to
ghcr.io/rakshit-737/rootlineand creates a GitHub Release with the wheel and
sdist. - CI: a Docker image job that checks the preloaded story is served, and a
docker-compose job that imports the Cypher export into Neo4j. - Committed benchmark results (
results/): ATLAS S1-S4, Splunk dev/holdout
coverage, OTRF Log4Shell fusion, plus figures. rootline serve --fuseloads several sensor captures as one story.Dockerfileanddocker-compose.yml, with a Neo4j service for Cypher
imports.
Changed
- The IsolationForest benchmark now runs 10 seeds and reports the mean with a
95 % t-interval, instead of a single seed.
Fixed
reduction_statsrebuilt the attack-sequence set for every edge (O(E*N)).
ATLAS S3 took about 10 minutes and now takes under a second. Each ATLAS graph
is also built once and shared across benchmarks.