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.






[](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 `