Skip to content

Reproduce

Every result in Evaluation comes from one of the commands below. Runtimes were measured on the author's laptop (Intel i5-13500H, 16 GB RAM, Windows 11, Python 3.14); CI runs the fast ones on every push. Everything is seeded and deterministic: re-running a step rewrites its result files byte for byte (only the timing columns of rows.csv, scale.json and the runtime_s fields change).

0. Setup

git clone https://github.com/rakshit-737/linchpin-attack-path-analysis && cd linchpin-attack-path-analysis
pip install -e ".[dev,api,ml,bench,docs]"
python -m pytest -q          # real-data, live-Neo4j and browser tests skip without the datasets / server / Chromium

1. Data (outside the repo)

command time output
python scripts/download_data.py a few minutes (about 270 MB) ../../datasets/linchpin/: NVD feeds, EPSS (current and 2022-12-01), KEV, sample exports, MANIFEST.sha256
linchpin intel-build --data-dir ../../datasets/linchpin about 5 min derived/cve_intel.csv.gz (379,082 CVEs)
python scripts/build_cve_pool.py under 1 min src/linchpin/synth/data/cve_pool.csv (3,000 CVEs, seed 7; committed)
python scripts/build_cpe_index.py --data-dir ../../datasets/linchpin 94 s src/linchpin/intel/data/cpe_index.csv.gz (27,095 version ranges; committed)

The benchmark and ablation need only the committed CVE pool, not the downloads.

2. Results

command time writes expect
python benchmarks/run_benchmark.py --seeds 50 --budget 3 --workers 8 11.5 min on the laptop (8 workers); 3.7 min in CI (4 workers) summary.{json,md}, rows.csv, strategies.png pooled disconnect 100% LINCHPIN and MILP, 97% greedy interdiction, 95% best patch-only plan, 39% betweenness, 3-10% score queues and random; none: 0.83 of the optimal cost gain
python benchmarks/ablation.py --seeds 50 --budget 3 --workers 8 14 min on the laptop (8 workers); 9 min in CI (4 workers) ablation.{json,md}, ablation_rows.csv ad + multi: fused 100/100, no identity 5/100; pooled: fused 100%, no identity 37%, identity only 18%, KEV->EPSS 3%
python benchmarks/repro_epss.py --data-dir ../../datasets/linchpin about 5 min (paired and ratio bootstraps) repro_epss.{json,md} paper-like: CVSS 7+ effort 58.2% (paper 58.1%); effort ratio 0.701 [0.695, 0.824]; prospective: 20 vs 5 discordant, McNemar p = 0.0041
python benchmarks/ml_exploitability.py 45-70 min (three trainings with 200-replicate bootstraps) ml_exploitability.{json,md}, derived/exploit_model.npz test CVEs without exploitation-status phrases: ROC-AUC 0.836, AP 0.028 vs 0.011 for CVSS
python benchmarks/ml_paired.py --data-dir ../../datasets/linchpin about 25 min on a busy laptop (1,000 paired replicates; no training) ml_paired.{json,md} AUC difference +0.082 [0.065, 0.100]; AP ratio 2.43x [1.90, 3.24]
python benchmarks/case_study.py --data-dir ../../datasets/linchpin about 1 min case_study.{json,md} 128 nodes, 224 edges; first fix: rotate ADMINISTRATOR@TESTLAB.LOCAL; without identity findings: no path seen, no fix
python benchmarks/lab_case_study.py --scans benchmarks/results/lab 4 s lab/lab_case_study.{json,md}, lab/topology.yaml 5 of 6 services matched, 393 CVEs (7 KEV); 1 fix (upgrade Tomcat 9.0.30)
python benchmarks/scale.py --reps 5 about 4 min scale.{json,png} about 1.1 s at 467 nodes on the laptop (one session's medians)
python scripts/make_figures.py 10 s docs/img/how/*.png, copies the result figures into docs/img/results/
python scripts/build_static_demo.py --data-dir ../../datasets/linchpin 39 s docs/demo/ five snapshots, budgets 1-10
python scripts/gen_cli_reference.py 1 s docs/reference/cli.md (a test fails when it is stale)

make bench, make casestudy, make ml, make scale and make demo wrap the same commands. The results workflow (Actions tab, run manually) runs the benchmark and the ablation on a clean Linux runner and uploads their result files; the committed tables come from its run 37089520295. Every result JSON records the commit (and whether code had uncommitted changes) or the CI run that produced it.

3. Live environments (GitHub Actions only)

These need Docker and run on ephemeral ubuntu-24.04 runners; locally, the committed artefacts can be replayed.

CI job what it does committed artefact
lab-scan lab/up.sh starts httpd 2.4.49, nginx 1.16.1, tomcat 9.0.30, redis 5.0.7 and mysql 5.5.62 on two --internal networks; lab/scan.sh runs nmap -sV (no scripts) from a container inside each; benchmarks/lab_case_study.py plans and checks benchmarks/results/lab/ (run 37091866743, recorded in lab_case_study.json)
neo4j-gds Neo4j 5.26 Community with GDS 2.13.13; Yen in GDS vs NetworkX on 250 and 500 hosts, k = 10 and 100 benchmarks/results/gds_crosscheck.json (run 37091866743, recorded in the file)
neo4j live push / pull round trip
ui Playwright against the live API and the built static demo
paper builds paper/linchpin.pdf from LaTeX and fails if the committed PDF's text differs from the fresh build the PDF

4. Docs

mkdocs build --strict && grep -q LINCHPIN_STATIC site/demo/index.html   # what CI's docs job checks
npm install --no-save mermaid@11.17.2 jsdom@26.1.0 && node scripts/check_mermaid.mjs   # every mermaid block parses