HQ-Axiom/axiom-pentest-core

GitHub: HQ-Axiom/axiom-pentest-core

一款自托管、离线优先的渗透测试报告与 AppSec 证据控制平台,支持多源扫描结果导入、确定性去重、合规映射和防篡改审计日志。

Stars: 0 | Forks: 0

# AXIOM-Pentest-Core Autonomous, evidence-driven security-testing platform for **authorized** targets, local labs, and CTFs. ![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg) ![TypeScript](https://img.shields.io/badge/TypeScript-5.9-3178c6.svg) ![Node](https://img.shields.io/badge/Node-%3E%3D20.10-339933.svg) ![Status](https://img.shields.io/badge/status-beta-blue.svg) ![Tests](https://img.shields.io/badge/tests-1478%20passing-success.svg) ![Version](https://img.shields.io/badge/version-0.7.0-blue.svg) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/41355ebfc1181029.svg)](https://github.com/HQ-Axiom/axiom-pentest-core/actions/workflows/ci.yml) ## TL;DR (30 seconds) - **What:** Self-hosted workspace for authorized pentest engagements. Runs your existing OSS scanners (Semgrep / Nuclei / Trivy / gitleaks / osv-scanner) under one `authorization.yml`, imports SARIF / Burp / GHAS / GitLab findings, dedupes deterministically, triages with a hash-chained audit log, exports MD / HTML / SARIF / JSON reports. - **Why interesting:** Authorization is enforced by the engine (kill-switch, RPS limit, scope, hard refusals — in code, not policy). Fully offline, no telemetry, no phone-home. Apache-2.0 public core; private overlays for PDF + ticket bundles + portfolio + metrics + signed-artefact issuance. - **Community (free):** every scanner, every importer, dedupe, triage, risk scoring, Markdown / HTML / SARIF / JSON reports, §5.4 compliance mapping (OWASP / ASVS / PCI / ISO), local Console, `axiom verify` for signed artefacts. - **Pro / Enterprise (private overlay):** PDF executive reports, Jira / GitHub / GitLab / CSV ticket bundles, cross-workspace Portfolio rollup, Trend / MTTR metrics, signed-artefact *issuance* (cosign Mode B/C + Rekor double-ack). - **Try it in 60 seconds:** git clone https://github.com/HQ-Axiom/axiom-pentest-core.git cd axiom-pentest-core pnpm install && pnpm -r build node packages/cli/bin/axiom.mjs demo --json node packages/cli/bin/axiom.mjs report -w demo-juice -f md - **Status:** v0.7.0 beta. **Public source is on GitHub at [`github.com/HQ-Axiom/axiom-pentest-core`](https://github.com/HQ-Axiom/axiom-pentest-core)** as of 2026-06-07 (P5.8). Not on npm. Not on Docker Hub / GHCR. No GitHub Release uploaded yet (release-artefact upload is gated on a production signing key). Pro overlay (PDF + ticket bundles + portfolio + metrics + signing) remains a private overlay repo. Three editions live in the same monorepo; community is fully usable today. - **Looking for:** 3–5 pilot partners (DACH / EU boutique pentest firms, internal AppSec teams that can't use SaaS). 2-week pilot, no purchase obligation, no data upload. See [`docs/PILOT_OUTREACH_PACK.md`](docs/PILOT_OUTREACH_PACK.md). ## Why AXIOM? You already run Burp, Snyk, Semgrep, Trivy, CodeQL, or some subset. You triage the same SQL injection three times — once per tool — into a spreadsheet that resets every re-scan. AXIOM is not a replacement for any of them. AXIOM is the layer above: - **Runs** OSS scanners (Semgrep, Nuclei, Trivy, gitleaks, osv-scanner) under one `authorization.yml` with scope, RPS limit, kill-switch, and hard refusals enforced by the engine — not by convention. - **Imports** SARIF / XML / JSON from the commercial tools you already pay for. `axiom import sarif` (any v2.1.0 producer: Snyk, CodeQL, Semgrep Pro, gitleaks…), `axiom import burp` (Burp Pro XML + Burp Enterprise JSON), `axiom import ghas --source code-scanning|dependabot` (GitHub Advanced Security file exports), `axiom import gitlab --type sast|dependency|secret|dast` (GitLab Security reports). `axiom import status` lists imported batches and provenance. All importers are file-local: AXIOM never calls GitHub or GitLab APIs itself — operators feed `gh api …` / `glab api …` outputs, which keeps imports offline-testable. - **Maps to compliance frameworks** OWASP Top 10 2021, ASVS L1/L2, PCI-DSS 6.5 / 4.0, ISO 27001:2022 — report §5.4 *Compliance Coverage* in both Markdown and HTML. Anti-hallucination: unknown stays `unmapped`, never guessed. Editable via `packages/reporter/src/compliance/mappings.yml`. - **Dedupes** findings deterministically across tools via a 5-key selector tuple. 54.5 % noise reduction on Juice Shop. - **Triages** with a 5-status workflow (`open`, `triaged`, `confirmed`, `false_positive`, `wont_fix`) that **carries decisions forward across re-scans**. Reviewers see only new deltas. - **Audits** every state change in a SHA-256 hash-chained log. `axiom audit verify` walks the chain. Triage exports ship with a tamper-evident `_integrity` block. - **Reports** Markdown, HTML, and SARIF v2.1.0 with `partialFingerprints`, `kind`, `guid`, and `suppressions[]` for GitHub Code Scanning. One PR check. - **Tracks trend over time** (Phase E4 — v0.4.0). Immutable workspace snapshots, eight-class diff between any two snapshots, time-series trend (day/week/month UTC buckets), MTTR analytics (time-to-triage + time-to-resolve, p50/p90/max per band), cross-workspace portfolio rollup ("which workspaces are improving, which are drifting?"), and safe garbage collection with `--confirm`-gated deletes. `axiom metrics {snapshot|list|show|diff|trend|mttr|prune}` on the CLI; a new Trend tab in the Local Console with the same data behind `/api/metrics/*` under the existing `authorise()` chokepoint. - **Signs artefacts** (Phase E5 — v0.5.0). Detached Ed25519 signatures for reports, SARIF exports, ticket bundles and release manifests (`axiom keygen` / `sign` / `verify`); optional local **cosign** mode (`--mode cosign`, fully offline); and a **Rekor** transparency-log upload that runs **only** with an explicit D21 double-ack (`--rekor-upload` **and** `AXIOM_ALLOW_OUTBOUND_REKOR=1`, Enterprise-gated). `sign` is Enterprise-gated; `verify` is ungated and offline in every edition; the public key is always pinned out-of-band. No automatic outbound — D21 enforced in code. Self-hosted, single-binary-chain, zero telemetry, zero cloud calls, Apache-2.0. No license key, no SaaS lock-in. What AXIOM does **not** claim: it does not replace Burp's Repeater or Snyk's vuln database or Semgrep's rule engine or Nuclei's templates. See [`docs/COMPETITIVE_POSITIONING.md`](docs/COMPETITIVE_POSITIONING.md) for the eleven-tool honest comparison and the five anti-claims. ## New to AXIOM? 👉 **Tutorial:** [`docs/GETTING_STARTED.md`](docs/GETTING_STARTED.md) — von 0 auf ersten Report in 10 Minuten, mit copy-paste Befehlen für jeden Schritt. Empfohlen für jeden, der AXIOM zum ersten Mal aufsetzt. This repository is **public on GitHub** at [`github.com/HQ-Axiom/axiom-pentest-core`](https://github.com/HQ-Axiom/axiom-pentest-core). Clone via: git clone https://github.com/HQ-Axiom/axiom-pentest-core.git cd axiom-pentest-core Not yet published to **npm** or **Docker Hub / GHCR** — those steps remain operator-gated. The **Pro overlay** (PDF reports, Jira/GitHub ticket bundles, portfolio rollups, metrics, signed-release issuance) is a separate **private** overlay repo and is not part of this public source tree. ## Quickstart (60 seconds) # Inside the repo: pnpm install && pnpm -r build node packages/cli/bin/axiom.mjs doctor # pre-flight check node packages/cli/bin/axiom.mjs setup --workspace my-first --skip-auth-hint node packages/cli/bin/axiom.mjs demo # canned workspace with 6 findings + 2 snapshots node packages/cli/bin/axiom.mjs report -f md -w demo-juice Every command runs offline. `axiom doctor --json` reports the resolved edition, feature availability, and the auth-store snapshot (no secrets). ### Attach evidence to findings (P5.5) Attach screenshots, openssl/curl output, Burp dumps, HTTP captures, or any small text/log/json file to a finding. The file is copied into the workspace (your source is untouched), hashed with SHA-256, and recorded with a hash-chained audit event. Metadata flows into MD / HTML / JSON / SARIF reports and the local dashboard. # Attach node packages/cli/bin/axiom.mjs evidence add -w my-first \ --finding \ --file \ --kind terminal|screenshot|http|text|json|log|other \ --title "..." --description "..." --json # List + inspect node packages/cli/bin/axiom.mjs evidence list -w my-first --finding node packages/cli/bin/axiom.mjs evidence show -w my-first --evidence Storage: `workspaces//evidence/attachments//-`. Default size cap 10 MiB (`--max-bytes` to override). Symlinks always refused; directories refused; path traversal stripped. **No upload, no remote storage, no automatic screenshot.** See `docs/EVIDENCE_ATTACHMENTS_USAGE.md`. ### Distribution status ### Community vs Pro vs Enterprise AXIOM ships under three editions, gated through the local `AXIOM_EDITION` env var (default: `community`) or a signed `~/.axiom/license.json` (Pro / Enterprise — offline, no phone-home). The split: - **Community** (free, forever): every scanner, importer, dedupe, triage, risk scoring, Markdown / HTML / SARIF / JSON reports, **§5.4 Compliance Coverage section** (OWASP / ASVS / PCI / ISO mapping), single-user Local Console (loopback-only, multi-user auth wired in but no role gating in Community). - **Pro** (code-gated via `assertFeature(...)`, refuse with exit **40** in Community): PDF executive report, **ticket bundle exports** (Jira / GitHub / GitLab / CSV), cross-workspace **Portfolio** rollup, **Trend & Metrics** (snapshots / diff / MTTR / prune / portfolio rollup). - **Enterprise** (code-gated via `assertFeature("signed_artefacts")`): the signing side of cosign/Sigstore (Mode B/C + optional Rekor double-ack). Verification stays in Community in every edition (D24). - **Reserved vocabulary** (feature flag exists in doctor `--json` but no code-gate yet — fully available in every edition today): `rbac_console`, `compliance_report`, `triage_portable_integrity`, `custom_compliance`, `siem_export`, `byok_secrets`. These are placeholders for upcoming overlay extractions; the doctor flag is `false` in Community, but the underlying functionality is shipped under Apache-2.0 in v0.7.0. Treat them as "reserved", not "Pro-exclusive today". See [`docs/productization-design.md`](docs/productization-design.md) for the full feature matrix + license-file format. Pro features in Community mode refuse cleanly with exit code **40** + a `FEATURE_REQUIRES_PRO` error pointing at the docs. To run them locally against an `axiom-pro` overlay checkout (P5.7 — one command, no more symlink-dance): # Activate the local Pro overlay once node packages/cli/bin/axiom.mjs pro activate --overlay ~/axiom-pro # Then any Pro command — same binary, AXIOM_EDITION=pro prefix AXIOM_EDITION=pro node packages/cli/bin/axiom.mjs report -w demo-juice -f pdf -o report.pdf AXIOM_EDITION=pro node packages/cli/bin/axiom.mjs export tickets -w demo-juice -f jira-json -o tickets.json AXIOM_EDITION=pro node packages/cli/bin/axiom.mjs metrics trend -w demo-juice --window all # Clean up when done (refuses non-symlinks; idempotent) node packages/cli/bin/axiom.mjs pro deactivate node packages/cli/bin/axiom.mjs pro status ### Offline license activation (P1.6) A paid edition can also be activated by a **signed, offline** license file — Ed25519, no network, no keyserver, no phone-home. The issuer signs the canonical claims; AXIOM verifies against an out-of-band public key. `AXIOM_EDITION` always wins over a license file (operator intent). # Inspect the active license (ungated; never prints the signature or keys): axiom license inspect # Point AXIOM at a specific license file: AXIOM_LICENSE_FILE=/path/license.json axiom --version AXIOM_LICENSE_FILE=/path/license.json axiom doctor --json # → edition.signature_status # Issuer-only (needs the private key; the key is never printed or stored): axiom license issue --edition pro --organization "Acme" \ --expires 2027-01-01 --out license.json --key issuer.key A bad/expired/tampered/missing license **fails safe to Community with a warning** — it never crashes. Only `axiom license inspect --require-valid` turns a non-valid license into a hard error (exit **41**, `LICENSE_INVALID`). **Production trust root + rotation (P1.7).** The shipped trust root is a **dev/test placeholder** (`doctor`/`inspect` warn loudly while it is in use). Production deployments configure a real trust root, in precedence order: AXIOM_LICENSE_PUBLIC_KEY_PEM='-----BEGIN PUBLIC KEY-----…' # inline AXIOM_LICENSE_PUBLIC_KEY=/etc/axiom/trust.pem # PEM file AXIOM_LICENSE_TRUST_ROOTS_FILE=/etc/axiom/trust-roots.json # multi-key, rotation-ready axiom license inspect --json # → trust_root_mode / production_trust_root / matched_key_id axiom doctor --json | jq .edition The `axiom-license-trust-roots/v1` file carries multiple public keys with `status: active|retiring|revoked` + `not_before`/`not_after` windows: `active`/`retiring` keys verify inside their window, `revoked` never. A broken production trust source **fails closed to Community** — it never silently falls back to the dev key. Full operator runbook (issue, rotate, emergency-revoke, DR, acceptance checklist): [`docs/license-trust-root-runbook.md`](docs/license-trust-root-runbook.md). Schema + trust model: [`docs/productization-design.md`](docs/productization-design.md) §§ 3.6–3.7. ## Local packaging P1.4a ships a Dockerfile + a local release script. Nothing is published — all artefacts stay on the build host. # Plan-only manifest (verifies version, computes SHA-256, writes no artefacts): node scripts/make-release.mjs --dry-run --json # Real local release — runs pnpm -r build, pnpm -r test, then pnpm pack the CLI: node scripts/make-release.mjs # Optional: build a container image (Node 22 Debian-slim, multi-stage, ~750 MB): docker build -t axiom-local:0.7.0 . docker run --rm -v "$PWD/workspaces:/app/workspaces" axiom-local:0.7.0 --version Or use the wrapper `Makefile`: make image # docker build -t axiom-local: . make image-smoke # build + axiom --version + doctor --json + demo with mounted volume make release-dry # manifest-only dry run make release # full local release (build + test + pnpm pack) make verdaccio-smoke # local Verdaccio registry-install proof (skipped without Verdaccio) make registry-install-smoke # alias for verdaccio-smoke make help # list all targets ### Container runtime contract The image is loopback-by-design (D9 / D13): the Local Console refuses to bind on a non-loopback host. CLI commands work fully inside the container. For a smoke run: docker run --rm -v "$PWD/workspaces:/app/workspaces" axiom-local:0.7.0 demo --json docker run --rm -e AXIOM_EDITION=pro \ -v "$PWD/workspaces:/app/workspaces" \ axiom-local:0.7.0 portfolio summary --json Mounting the host's `workspaces/` directory makes the container's writes visible to subsequent `axiom` calls (on host or container). The auth DB lives at `/root/.axiom` — mount a separate host directory to persist users + sessions across container restarts. Console-mode inside the container is **deferred to a later P1 step** — the dashboard's hard loopback-bind guard would need a deliberate `AXIOM_DASHBOARD_ALLOW_EXTERNAL_BIND=1` env-path before published-port `docker run -p 18888:18888` makes sense. CLI surface (every `axiom` subcommand) works fine inside the container today. Output lands in `release-artifacts/` (git-ignored): one `.tgz` per packed workspace + `release-manifest.json` with SHA-256 hashes, the resolved version, git commit, and a paranoia-pass that refuses any artefact path matching `.env` / `.sqlite` / `workspaces/` / `.axiom/`. No `npm publish`, no `docker push`, no tag — the operator decides what happens next. Container volumes (operator-mounted, not VOLUME-declared so `docker run --rm` works cleanly): - `/app/workspaces` — per-workspace SQLite ledgers - `/root/.axiom` — global auth DB + optional `license.json` for Pro/Enterprise ### Verifying signed releases **v0.5.1 is the first AXIOM release that officially documents and versions the P1.5 signed-release flow.** P1.5 turns `make-release.mjs --sign` into a self-describing, versioned **signed release directory** (default `dist/releases/axiom-v/`, override with `--out `): # Enterprise-gated signing (Mode A, offline Ed25519 — no Rekor, no network): axiom keygen # one-time, private key 0600 make release # or: node scripts/make-release.mjs AXIOM_EDITION=enterprise \ node scripts/make-release.mjs --sign --signer "you@org" \ --out dist/releases/axiom-v0.7.0 The folder contains the tarball, the signed `release-manifest.json` + its sidecar, `SHA256SUMS`, detached raw Ed25519 `.sig` files, the **public** key (`axiom-pub.pem`), `provenance.json` and a `README_VERIFY.md`. Consumers verify **fully offline** (verification is ungated — works in Community), three independent ways: # 1. AXIOM-native (the cryptographic root of trust): axiom verify release --manifest release-manifest.json --key axiom-pub.pem # 2. Tool-agnostic integrity: sha256sum -c SHA256SUMS # 3. Defense-in-depth with stock OpenSSL: base64 -d SHA256SUMS.sig > /tmp/s.sig openssl pkeyutl -verify -pubin -inkey axiom-pub.pem -rawin -in SHA256SUMS -sigfile /tmp/s.sig Pin `axiom-pub.pem` **out of band** — AXIOM never trusts a key embedded in a sidecar. None of these paths contacts a network or a transparency log. Full detail + the honest reproducibility caveat: [`docs/SIGNED_RELEASES.md`](docs/SIGNED_RELEASES.md). ### Public/Private productization wave (v0.6.0 → v0.7.0) The productization wave spans v0.6.0 (P1.8: documented public/private repo split + private-release trust-root profile scaffold + reproducible offline release smoke) through v0.7.0 (P1.9–P1.17: five Pro/Enterprise overlay packages extracted, two further enterprise namespaces vocabulary-reserved). At v0.7.0 the public-core ships **zero `@axiom/(pro|ent)-*` implementation code** — the boundary is enforced by audit-guard tests, by the `scripts/public-split-audit.mjs` (`status:"ok"`, 0 fails / 0 warns), and by the overlay's `overlay-install-from-tarballs.mjs` smoke. **At v0.7.0**, the private `~/axiom-pro/` overlay repo holds **five** extracted packages: `@axiom/pro-reporter` (P1.9, PDF executive report), `@axiom/pro-ticket-bundle` (P1.11, cross-workspace ticket bundle exporter), `@axiom/pro-metrics` (P1.12 + P1.13, portfolio metrics + analytics engines), `@axiom/pro-portfolio-export` (P1.14, JSON/CSV portfolio renderers), and `@axiom/ent-signing` (P1.15, the entire sign-side surface for releases / reports / SARIF / ticket bundles). `axiom verify *` deliberately stays in public-core (D24 packaging boundary), so any consumer can verify an AXIOM-signed artefact offline without an Enterprise license. Community users continue to get MD / HTML / SARIF reports out of the box; Pro/Enterprise features each fail cleanly with exit code **40** + `FEATURE_REQUIRES_PRO` until the matching overlay is installed. Acceptance docs: [`docs/p1_9-external-overlay-acceptance.md`](docs/p1_9-external-overlay-acceptance.md). #### Private overlay development Local-skeleton model (link: into the public-core sibling): # Both repos checked out under the same parent dir cd ~/axiom-pro # private overlay repo (no remote) pnpm install # link: into ../axiom-pentest-core/packages/ pnpm -r build && pnpm -r test # five overlay packages, 129 tests green The overlay repo carries **no** GitHub remote by default. A distribution-mode install (npm-pack tarballs into a public-core consumer) is also supported via `scripts/overlay-install-from-tarballs.mjs` in the overlay repo and is asserted by the v0.7.0 release smoke. #### Public OSS Distribution readiness (released in v0.6.2) As of v0.6.2, every public package is verifiably packable into a clean npm tarball — proven on every release-cut by `make public-distribution-smoke`: # Per-package pack + invariant scan (zero Pro code, zero secrets, # zero operator data; SHA-256 recorded). Tarballs land in # dist/public-pack-audit/ (git-ignored). make public-pack-audit # → status: ok (fails=0, warns=0) # → 13 packages, 9.7 KB–199.6 KB each # Full end-to-end smoke (split-audit + private-profile-check + # pack-audit; add --docker if axiom-local:0.7.0 image is built). make public-distribution-smoke # Direct invocations (with JSON output) are also supported: node scripts/public-npm-pack-audit.mjs --json node scripts/public-distribution-smoke.mjs --json # Optional Docker public-image smoke (community-only behaviour, # proves PDF refuses with FEATURE_REQUIRES_PRO and the image has # zero pro-reporter artefacts on disk). make image # if not already built make public-image-smoke The full **What is** and **What isn't** in each tarball is contracted in [`docs/public-oss-distribution.md`](docs/public-oss-distribution.md). Every public `package.json` still carries `"private": true` — flipping that flag is the single deliberate operator gesture that authorises a public `npm publish`. **Public source is live on GitHub ([`HQ-Axiom/axiom-pentest-core`](https://github.com/HQ-Axiom/axiom-pentest-core), since 2026-06-07). No `npm publish` has happened. No Docker image published. No GitHub Release uploaded yet.** # Public release smoke (ephemeral signing key in $TMPDIR, no trust-roots): make release-smoke-public # Private release smoke (bundles release/private/production-trust-roots.example.json): make release-smoke-private # Both profiles + customer-verify simulation + leak scan in one command: make release-smoke `make release-smoke` emits signed releases into `dist/release-smoke*/` (git-ignored), runs `axiom verify release` against each, asserts that the private manifest's `trust_roots.sha256` matches the shipped `production-trust-roots.json`, runs `axiom doctor --json` against the private trust-roots (expects `production_trust_root:true`), then re-verifies in a sandboxed `$TMPDIR` (third-party simulation). Schema: `axiom-release-profile-smoke/v1`. **No network, no Rekor, no push, no publish.** Full 20-step acceptance gate: [`docs/release-profile-acceptance-gate.md`](docs/release-profile-acceptance-gate.md). All five Pro/Enterprise overlay packages — `@axiom/pro-reporter` (P1.9), `@axiom/pro-ticket-bundle` (P1.11), `@axiom/pro-metrics` (P1.12 + P1.13), `@axiom/pro-portfolio-export` (P1.14), and `@axiom/ent-signing` (P1.15) — have been **physically externalised** out of this repository and live in a separate private `axiom-pro` overlay repo (no remote, no publish). Public-core is **Pro-code-free**: zero `packages/pro-*/` or `packages/ent-*/` directories, zero `@axiom/(pro|ent)-*` reference in any public `package.json` dependency bucket; enforced by `scripts/public-split-audit.mjs` (`status:"ok"`, 0 fails, 0 warns) and by the overlay's `overlay-install-from-tarballs.mjs` smoke. Two further enterprise namespaces (`@axiom/ent-rbac` P1.16, `@axiom/ent-compliance` P1.17) are vocabulary-reserved with prospective audit-guard tests; no overlay package on disk yet (assessment docs in [`docs/rbac-extraction-assessment.md`](docs/rbac-extraction-assessment.md) and [`docs/custom-compliance-extraction-assessment.md`](docs/custom-compliance-extraction-assessment.md)). ## Authorization is non-optional ## What AXIOM is A coherent pipeline that combines SAST (repo indexer, route mapper, dataflow-light, secrets, SCA), DAST (passive crawler + active-safe probes for reflection / SQL-error / SSRF / path-traversal / open-redirect), a Playwright-driven browser verifier (XSS reflection, cookie hygiene, login recording), session-aware authenticated crawling, declarative business-logic invariants, and a unified reporting engine (Markdown, HTML, SARIF, JSON) — all gated by a single authorization manifest and recorded in a per-workspace SQLite ledger with full HTTP evidence and an append-only audit log. AXIOM is a **TypeScript monorepo** of 13 packages built with pnpm workspaces. State lives in each workspace's `.axiom/db.sqlite`; nothing is uploaded anywhere. ## Easiest mode — AXIOM Local Console If this is your first time, skip the per-stage CLI and use the **Local Console** — a loopback-only browser wizard that drives the whole pipeline (index → routes → scan → verify → report) from a 12-step UI with explicit authorization checkboxes. Same safety guarantees as the CLI; nothing binds outside `127.0.0.1`. AX="$(pwd)/packages/cli/bin/axiom.mjs" node "$AX" console --open See [`docs/gui-quickstart.md`](docs/gui-quickstart.md) for the full walkthrough. For unattended / CI runs, the equivalent one-liner is `axiom scan-wizard --url --profile quick --confirm-authorization`. ## Quick start ### Prerequisites - **Node 20.10+** — `nvm use` if you have nvm; otherwise see https://nodejs.org - **pnpm 9+** — `corepack enable && corepack prepare pnpm@latest --activate` - **Linux build tools** for `better-sqlite3` (apt: `build-essential python3`; macOS: `xcode-select --install`) - **Optional:** chromium for browser-verify — `pnpm exec playwright install chromium` The Quickstart below sets `$AX` to the absolute path of the CLI entry point so subsequent examples work regardless of `cwd`. For convenience you can also `alias axiom="node $AX"`. Every later example in this README assumes `$AX` is set. git clone https://github.com/HQ-Axiom/axiom-pentest-core.git cd axiom-pentest-core nvm use && corepack enable && pnpm install && pnpm -r build pnpm -r test # 1478 tests across 13 packages # optional: install chromium for browser-agent verification (~250 MB, one-time) pnpm exec playwright install chromium AX="$(pwd)/packages/cli/bin/axiom.mjs" node "$AX" doctor # env self-check `axiom doctor` validates Node version, pnpm, the native `better-sqlite3` binding, YAML support, write permissions, and (warn-only) the chromium binary. A green doctor is a precondition for every other command. When a check fails, doctor now prints an `install hint:` line with the platform-specific command — paste that line and run it. ## First scan against OWASP Juice Shop (E2E) End-to-end run against a local Juice Shop with authenticated crawling and browser verification: # One-time: clone the Juice Shop source so the indexer / route-mapper have code to walk git clone https://github.com/juice-shop/juice-shop.git ~/juice-shop docker run --rm -d --name juice-shop -p 3000:3000 docker.io/bkimminich/juice-shop node "$AX" workspace create juice-shop-local --seed-auth # Edit workspaces/juice-shop-local/authorization.yml — confirm scope, owner, window node "$AX" auth show -w juice-shop-local # HTTP-only login: POSTs credentials, extracts JWT from response body, # synthesizes a Playwright-compatible storage state. Never persists secrets. node "$AX" auth login -w juice-shop-local --role admin \ -u http://127.0.0.1:3000/rest/user/login \ --field email=admin@juice-sh.op --field password=admin123 \ --bearer-path authentication.token # Full pipeline: index -> routes -> secrets -> http_tester -> reporter node "$AX" run -w juice-shop-local \ -r ~/juice-shop -b http://127.0.0.1:3000 \ --active-safe --as admin --no-sca # Browser-verify every reflection finding (single Chromium pass) node "$AX" browser verify-xss -w juice-shop-local --all-reflections # Render a Markdown report node "$AX" report -w juice-shop-local -f md docker rm -f juice-shop Authenticated crawl reaches **+60 % more 2xx routes** and **-70 % 401s** vs. anonymous baseline on Juice Shop. Browser verification reduces reflection false-positives by **~87.5 %** on the same target. **Shortcut:** the bash script `scripts/demo-juice-shop.sh` automates all of the above — see `docs/checkpoints.md` for the Phase 6 entry. ## CLI surface 40+ top-level commands, in workflow order (run `axiom --help` for the authoritative list; the table below covers the orchestrating commands; the 5 standalone external-adapter commands `axiom semgrep / nuclei / trivy / gitleaks / osv-scanner` are documented under §"Adapters" — they're each a thin wrapper around the matching binary, used for adapter-specific debugging; the unified entry-point is `axiom adapters run-all`): | Command | Purpose | |---|---| | `axiom init` | First-run helper; prints next steps and links to docs | | `axiom doctor` | Environment self-check (Node, pnpm, sqlite, yaml, chromium) | | `axiom workspace create / list / info` | Create or inspect a workspace (DB, audit log, evidence dir) | | `axiom auth init / show / inspect` | Seed and validate an `authorization.yml` manifest | | `axiom auth login` | HTTP-only login: capture session + bearer for a role | | `axiom auth session list / show / remove` | Manage captured per-role storage states | | `axiom index run / list / show` | Walk a repo: files, projects, frameworks, entry points, deps | | `axiom routes run / list / show` | Extract HTTP routes (Express, FastAPI, OpenAPI) from indexed code | | `axiom http crawl` | Authorized HTTP crawl, optionally `--as `, `--active-safe` | | `axiom http requests` | Inspect captured requests / responses (filter by role, status) | | `axiom scan` | Single-shot: scope-check + crawl + active-safe probes | | `axiom run` | Full agent pipeline: index -> routes -> (secrets, sca) -> http -> report | | `axiom secrets scan` | Local secret scanner (always masks values before persistence) | | `axiom sca` | Dependency CVE check via OSV.dev (npm, PyPI, Go, Maven, ...) | | `axiom dataflow scan` | SAST-light: source-to-sink taint heuristics (JS / TS / Python) | | `axiom invariants validate / run` | Declarative business-logic rules over captured responses | | `axiom hypotheses idor` | Two-role response diff for IDOR / BOLA detection | | `axiom browser status / screenshot` | Chromium status; on-demand screenshot of an authorized URL | | `axiom browser record-login` | Headed Playwright session-capture (storage state per role) | | `axiom browser verify-xss [] [--all-reflections]` | Live-DOM XSS / reflection verifier | | `axiom correlate` | Link findings back to mapped routes (path-anchored regex) | | `axiom coverage` | Route-coverage report (matched vs. uncovered routes) | | `axiom findings list / show / export` | Inspect / filter findings; export md / json / sarif | | `axiom findings dedupe / clusters / cluster {show,split,triage,triage-bulk}` | Cluster duplicate findings (5 deterministic keys), reversible split, MAX-severity policy | | `axiom findings triage / triage-bulk / triage-history` | Per-finding decisions: open / accepted / dismissed / false_positive / wont_fix; cluster propagation; audit-only history | | `axiom findings triage-policy {validate,apply}` | Declarative auto-triage rules (YAML v1) — protects local direct decisions by default | | `axiom findings triage-export / triage-import` | Portable JSON for sharing decisions across workspaces (stable selectors, --force override) | | `axiom findings scans-list / diff / triage-carryover` | Cross-run delta + propagate triage from earlier runs (CI re-scan workflow) | | `axiom adapters {status, run-all}` | Unified entry point for 5 OSS scanners + native HTTP crawl. `--profile {quick,full,deep,ci}` presets, `--parallel`, `--fail-on ` exit-code policy | | `axiom replay ` | Emit a curl (or Playwright fetch stub) to reproduce a finding | | `axiom report -f md\|html` | Full 12-section report (incl. 5.1 Consolidated Findings, 5.2 Triage Status) with evidence appendix | | `axiom dashboard` | Read-only loopback dashboard (zero-dep Node + embedded SPA) | | `axiom audit verify -w [--json]` | Verify the workspace's hash-chained NDJSON audit log; non-zero exit on a broken chain | | `axiom kill [--clear]` | Drop / clear the kill-switch sentinel — refuses subsequent runs | Run any command with `--help` for full options. ## Architecture +---------------------------+ | axiom CLI (commander) | +-------------+-------------+ | +----------------+----------------+ | | +----------v----------+ +------------v-----------+ | @axiom/orchestr. | | AuthGate / KillSw | | topo + budgets | | ScopeEnforcer | +----------+----------+ +------------+-----------+ | | +-----------+----------+----------+-----------+----------+ | | | | | | v v v v v v +-----+ +--------+ +--------+ +---------+ +---------+ +--------+ |index| |route- | |dataflow| |sca- | |http- | |browser-| |er | |mapper | |-light | |secrets | |tester | |agent | +-----+ +--------+ +--------+ +---------+ +----+----+ +---+----+ | | +----------+---------+ v +---------+ +---------+ | reporter|<--|business-| | md/html/| |logic | | sarif | +---------+ +----+----+ | +------v-------+ | dashboard | loopback-only | (zero-dep) | +--------------+ All packages persist through @axiom/core (SQLite via better-sqlite3) and share types through @axiom/shared. 13 packages: `shared`, `core`, `cli`, `orchestrator`, `indexer`, `route-mapper`, `http-tester`, `browser-agent`, `sca-secrets`, `dataflow-light`, `business-logic`, `reporter`, `dashboard`. See `docs/decisions.md` for ADRs and `docs/checkpoints.md` for the per-phase delivery log. ## Security model - **Authorization manifest** — Zod-validated YAML with target metadata, scope allowlists, allowed test types, rate / budget limits, scan window, hard refusals, and operator attestation. The AuthGate snapshots the parsed manifest into the DB at scan start. - **Scope enforcement** — Domain (literal + leading-wildcard), IPv4 / IPv6 / CIDR allowlists, port allowlist, default-deny on private ranges, hard block on cloud-metadata endpoints (`169.254.169.254`, `fd00:ec2::254`, `metadata.google.internal`, ...). - **Kill switch** — Presence of `.axiom/KILL_SWITCH` halts every running and future agent immediately. `axiom kill` toggles it; `axiom kill --clear` removes it. - **RPS limiter** — Per-host token bucket (default `max_rps_per_host: 5`). Hard cap on total requests per scan via `max_total_requests`. - **Budget cap & timeouts** — Per-agent budget, global deadline, hard 2x-budget timeout enforced by the orchestrator (`runPlan`). - **Audit log** — Append-only NDJSON at `.axiom/audit.log`. Every agent start, scope decision, finding write, kill-switch event, and report generation is recorded. - **Secret masking** — Secret-scanner findings always mask the value before persistence (`AKIA****...****MPLE`). `axiom auth login` records field **names** only, never values or tokens. - **Loopback-only dashboard** — Refuses to bind `0.0.0.0` unless `--allow-external`. Sets `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, `Referrer-Policy: no-referrer`. No CDN, no remote scripts, no remote fonts. - **Hermetic marker probes** — Reflection / open-redirect canaries use the unresolvable `axiom-canary.invalid` domain; markers carry per-finding `axm_` IDs so they can be unambiguously attributed in the live DOM later. ## Findings and verification Each finding carries a `severity` (`info | low | medium | high | critical`), a `confidence` (`unconfirmed | low | high | verified`), and — once browser-verified — a `verification_status`: | `verification_status` | Meaning | |---|---| | `verified_xss` | Marker rendered in script context (inline `