Read-only, local-first AI security audit for any LLM-using codebase. Maps every finding to the OWASP LLM Top 10.
Audithex reads your code, finds problems, and reports them. It never modifies user code, never auto-fixes, and never sends data to a third party. Output is always a notification: "here is a problem, here is how to fix it."
The scanner is polyglot. It ships native TypeScript Compiler API parsers for .ts/.tsx/.js/.jsx/.mjs/.cjs and regex-based detectors for Python, PHP, Go, Java, and Ruby. Standalone prompt files (.md/.txt) are also recognised. The set of extensions, languages, ignored directories, and rule severities is controlled from .env and .audithex/config.json — never hard-coded inside the scanner.
- CLI commands:
scan,update,selftest,history,ui,user,init,version - Optional MongoDB persistence — point
MONGODB_URIat any Mongo and every scan is saved to thescan_runscollection for review throughaudithex historyand the local web UI. The CLI runs fully without MongoDB; persistence is purely opt-in. - Local web UI —
audithex uiboots a single-user dashboard onhttp://localhost:7777(Next.js 16 + React 19 + Tailwind 3, bcrypt-signed cookie auth, Cypress-covered). Includes scan history, finding detail, scan-to-scan diff, read-only settings, on-demand "Explain how to fix" answers from Claude (cached in Mongo), and one-click PDF export of any scan. - 16 rules (R001 – R016) covering OWASP LLM01, LLM02, LLM03, LLM05, LLM06, LLM07, LLM10, mapped to CWE-22, 78, 79, 89, 94, 502, 798, 918 — see the coverage table below
- 20 secret patterns for OpenAI, Anthropic, Google, Cohere, Mistral, Hugging Face, Replicate, GitHub, GitLab, Slack, Discord, AWS, Stripe, Twilio, SendGrid
- 3 rule engines:
regex-in-code,regex-in-prompt,artifact-property - 6 extractors: SDK imports, model strings, system prompts, tool definitions, RAG config, secret candidates
- AST-confidence detection for
.ts/.tsx/.js/.jsx/.mjs/.cjsSDK imports, tool literals, and code-embedded system prompts. Rule evaluation itself is regex-based — full data-flow / taint analysis is on the roadmap. - Package-scoped AI gating — web-vulnerability rules (R005 eval, R006 file-write, R007 exec, R008 fetch, R009 SQL, R010 innerHTML) fire only inside npm packages that contain at least one LLM SDK import, so generic SSRF/XSS in unrelated code does not produce LLM05 findings.
- Inline suppression pragmas —
// audithex-ignore-line,// audithex-ignore-next-line,// audithex-ignore: R008, R010for the handful of cases that legitimately need a single-line waiver. - Self-evaluating engine with a
fixture-banking-botground-truth fixture (16 expected findings, precision ≥ 0.95, recall ≥ 0.9) - Git-based rules-pack update channel with
git pull --ff-onlyandgit reset --hardrollback when the new pack's selftest fails - Console, JSON, and Markdown reports with deterministic CI exit codes
- English and Ukrainian UI in full key parity
- 0 jscpd code-clone duplication enforced in CI
| OWASP category | Coverage | Rules |
|---|---|---|
| LLM01 Prompt Injection | covered | R011 |
| LLM02 Sensitive Information Disclosure | partial — API key literals only | R001 |
| LLM03 Supply Chain | partial — pickle / torch.load / typosquats |
R013, R014 |
| LLM04 Data and Model Poisoning | not covered (mostly a process / data-curation concern) | — |
| LLM05 Improper Output Handling | covered, package-scoped via requiresAiContext |
R005, R006, R007, R008, R009, R010 |
| LLM06 Excessive Agency | covered — tool design + destructive-verb gating | R003, R004, R016 |
| LLM07 System Prompt Leakage | covered — credentials and schema dumps inside prompts | R002, R012 |
| LLM08 Vector and Embedding Weaknesses | not covered | — |
| LLM09 Misinformation | not covered (runtime / behavioural concern, not lintable) | — |
| LLM10 Unbounded Consumption | covered — messages.create without max_tokens |
R015 |
Rules in the LLM05 row are package-scoped by default: each one ships with "requiresAiContext": true in its params, so the rule fires only in npm packages whose code imports an LLM SDK. If a project hand-rolls its own HTTP client to an LLM endpoint (no SDK import in source), audithex will not consider those packages AI-context. The roadmap entry to detect raw provider HTTP calls is tracked separately.
When a rule fires on a usage you already know is safe, attach one of three pragmas. The pragma keyword must appear inside a comment — language does not matter (//, #, /* */ all work).
// All rules on this line — leaves stylelint and ESLint alone:
return fetch(`/api/storefront/wishlist/${encodeURIComponent(sku)}`); // audithex-ignore-line
// Suppress only the next non-blank line:
// audithex-ignore-next-line
return fetch(`/api/${id}`);
// Suppress a specific rule (or several) — other rules still report on the same line:
element.innerHTML = STATIC_CSS; // audithex-ignore: R010Use pragmas sparingly. The combination of requiresAiContext (package gating) and inline pragmas is meant to keep audithex quiet without ignoring the genuine LLM05 surface — over-silencing is a regression, not a win.
Audithex runs on macOS, Linux, and Windows (WSL2 recommended).
| Tool | Minimum | Notes |
|---|---|---|
| Node.js | 22 LTS | engines field enforces it. Use nvm to manage versions. |
| yarn | 4.13.0 | Activated through corepack — do not install globally. |
| git | any recent | Required for clone and for the rules-pack update channel. |
No database, no Docker, no native build step. All on-disk state lives in plain files under ~/.audithex/.
# 1. Activate Node 22 (skip if already on it)
nvm install 22
nvm use 22
# 2. Activate the pinned yarn through corepack
corepack enable
corepack prepare yarn@4.13.0 --activate
# 3. Clone and install
git clone git@github.com:rtsehynka/audithex.git
cd audithex
yarn install # ≈10s warm, ≈30s on a fresh machine
# 4. Build all packages (TypeScript → dist/)
yarn build
# 5. Run all quality gates
yarn verify # lint + typecheck + test + jscpd + docs + locales + stubsThe CLI executable lives at apps/cli/bin/audithex.js after yarn build. Invoke it directly during development. A yarn link step to expose audithex as a global binary is intentionally omitted to avoid polluting the global node_modules.
Copy .env.example to .env at the root of any project you scan. Everything in .env is optional — a basic scan runs end-to-end without a single key set. The CLI validates .env with a zod schema at startup and fails fast on malformed values.
cp .env.example .envVariables Audithex understands:
| Variable | What it unlocks | Default |
|---|---|---|
ANTHROPIC_API_KEY |
LLM-judge evals and AI-fallback extractor (when wired in your project) | unset |
OPENAI_API_KEY |
Same as above, alternate provider | unset |
AUDITHEX_AGENT_ENDPOINT |
URL of a running agent the dynamic tester can hit | unset |
AUDITHEX_AGENT_AUTH |
Authorization header for the above endpoint |
unset |
AUDITHEX_LLM_COST_CAP_USD |
Hard cap on per-scan LLM spend in USD | 1.00 |
AUDITHEX_AUTO_UPDATE_CHECK |
Daily HEAD probe to the rules-pack channel | true |
AUDITHEX_LOCALE |
UI language (en or uk) |
derived from LANG/LC_ALL, falls back to en |
AUDITHEX_HOME |
Root for cached rules-pack, selftest history, etc. | ~/.audithex |
AUDITHEX_LOCALES_ROOT |
Absolute path to the locales directory (packaged builds only) | walked up from the package's install location |
AUDITHEX_RULES_PACK_URL |
Git URL the update command clones / pulls from |
https://github.com/audithex/rules-pack.git |
MONGODB_URI |
Connection string for the optional persistence layer. Required for audithex history; transparent for audithex scan. Must start with mongodb:// or mongodb+srv://. |
unset (persistence disabled) |
AUDITHEX_UI_SESSION_SECRET |
HMAC key for the web UI's signed session cookie. At least 32 characters. Required for audithex ui. Generate with openssl rand -base64 48. |
unset (web UI refuses to boot) |
AUDITHEX_UI_PORT |
Default port for the local web UI. Can be overridden per-invocation with audithex ui --port <port>. |
7777 |
Audithex never sends data to a third party. The two LLM keys above are used only when you explicitly run an LLM-using action; the request then goes straight from your machine to the provider you chose.
The scanner is read-only. The only directory it ever writes to is .audithex/ inside the project it scans, and only when you invoke init.
# Scan the audithex repo itself
node apps/cli/bin/audithex.js scan .
# Scan a project elsewhere on disk
node apps/cli/bin/audithex.js scan ~/work/my-agent
# Machine-readable output for CI or scripting
node apps/cli/bin/audithex.js scan . --report json > audit.json
node apps/cli/bin/audithex.js scan . --report md > audit.md
# Filter findings by severity (default prints all)
node apps/cli/bin/audithex.js scan . --severity critical
# Switch language for one invocation
AUDITHEX_LOCALE=uk node apps/cli/bin/audithex.js scan .The exit code is deterministic so CI can fail on critical findings:
| Code | Meaning |
|---|---|
0 |
No findings. |
1 |
Only low / medium findings. |
2 |
At least one high or critical finding. |
Audithex Scan Report
Project root: /Users/you/work/my-agent
Scanned: 247 files in 142 ms
Rules version: 0.1.0 (bundled)
CRITICAL (2)
R001 Hardcoded OpenAI key literal in source src/agent.ts:4:21
R005 eval() called on LLM output src/agent.ts:8:10
HIGH (3)
R003 Tool 'transfer_funds' has no description tools/anthropic-tools.json:4:5
R006 fs.writeFileSync with interpolated path src/tools/database.ts:11:3
R008 fetch() with interpolated URL (SSRF surface) src/tools/http.ts:3:10
MEDIUM (0) LOW (0)
The same scan with --report json produces a stable JSON shape suitable for diffing across scans (location, messageKey, messageParams, fixKey, severity, owasp, cwe, rulesVersion, audithexVersion, elapsedMs).
Audithex tracks an external rules-pack repository as a plain git checkout under ~/.audithex/rules-pack/current/. git pull is the integrity gate — commit SHAs and (optionally) signed tags raise it.
# Interactive (prompts before applying)
node apps/cli/bin/audithex.js update
# CI-safe (skips the confirmation prompt)
node apps/cli/bin/audithex.js update --yes
# Use a custom rules-pack channel (any git URL — including file://)
AUDITHEX_RULES_PACK_URL=file:///path/to/my-rules.git \
node apps/cli/bin/audithex.js update --yesPossible outcomes:
| Outcome | What happened on disk |
|---|---|
up-to-date |
~/.audithex/rules-pack/current is at the same commit as the remote HEAD. |
installed |
First-ever git clone --depth 1 or successful git pull --ff-only. |
rolled-back |
New checkout failed the post-update selftest. git reset --hard <previous-HEAD> reverted the tree. First-ever clones that fail selftest are wiped. |
fetch-failed |
Network error, bad URL, or non-fast-forward. The working tree is untouched. |
Setting AUDITHEX_AUTO_UPDATE_CHECK=false disables the daily silent HEAD probe scan makes; offline / air-gapped environments should set this.
A single docker-compose.yml at the repo root provisions everything audithex needs locally — currently just one MongoDB service. The shape is the same in CI, on a workstation, and on a server.
# Start MongoDB on localhost:27017 (named volume keeps data across restarts)
yarn infra:up
# Equivalent: docker compose up -d
yarn infra:status # docker compose ps
yarn infra:logs # docker compose logs -f
yarn infra:down # stop the container, keep the volume
yarn infra:nuke # stop + drop the volume (wipes scan history)After yarn infra:up, copy .env.example to .env (the MONGODB_URI=mongodb://localhost:27017/audithex line already matches the compose file) and every audithex scan will persist. The compose file accepts AUDITHEX_MONGO_PORT if 27017 is taken on your host.
Set MONGODB_URI in .env (or export it for one invocation) and every scan quietly saves the full result to the scan_runs collection. Nothing else about the scan changes — the report still prints to stdout, the exit code still mirrors severity, and the value of MONGODB_URI is the only opt-in.
# Local Mongo (use yarn infra:up or the line below)
yarn infra:up
export MONGODB_URI=mongodb://localhost:27017/audithex
# Run a scan — it now persists. The new ScanRun id is printed at the end.
node apps/cli/bin/audithex.js scan .
# List every persisted scan (newest first)
node apps/cli/bin/audithex.js history
# Same data as JSON for piping into jq / scripts
node apps/cli/bin/audithex.js history --json
# Open one scan in full
node apps/cli/bin/audithex.js history --show <scan-run-id>
# Pagination + rootPath filter
node apps/cli/bin/audithex.js history --limit 50 --skip 100 --root-path /Users/you/work/my-agenthistory exits 2 if MONGODB_URI is missing or the connection fails, and 0 otherwise. The schema (ScanRun, User, RulesPackUpdate) is defined in packages/core-persistence/src/models/ — Mongoose-native, so the same documents power the local web UI when it ships.
If a scan runs while Mongo is unreachable, it logs Could not persist scan to MongoDB: ... to stderr and proceeds with the normal exit code. Persistence never blocks the scan.
Audithex is single-user. There is no default account, no seeded password, no "admin/admin". The local user is created by you, once, via the CLI — the credentials are stored in MongoDB as a bcrypt hash and used to log in to the web UI on /login.
# Requires MONGODB_URI in .env (or exported) — points at the Mongo
# where the user record will live.
yarn build
# Interactive — prompts for email and a min-8-character password.
yarn user:create
# equivalent: node apps/cli/bin/audithex.js user create
# Non-interactive — pass both up front. Useful for first-run scripts.
node apps/cli/bin/audithex.js user create --email you@example.com --password 's3cret-pw-1'
# Rotate the password for an existing user (prompts for the new one).
yarn user:rotate
# equivalent: node apps/cli/bin/audithex.js user create --forceExit codes: 0 on success, 2 on misuse (no MONGODB_URI, invalid email, password under 8 chars, user already exists without --force, or Mongo error).
The credentials never leave your machine. The web UI's session is an HMAC cookie signed with AUDITHEX_UI_SESSION_SECRET; the cookie carries the user id + email, not the password.
Once the user exists you can also edit email + password from the UI itself at /settings/account.
The web UI is a single-user dashboard on http://localhost:7777. Authentication is bcrypt over a Mongo-stored user, sessions are HMAC-signed cookies (no third-party service). It needs both MONGODB_URI and AUDITHEX_UI_SESSION_SECRET set.
# 1. Generate a session secret (32+ characters)
openssl rand -base64 48 | tr -d '\n'
# 2. Add it (and MONGODB_URI) to .env, then start MongoDB
yarn infra:up
# 3. Create the local user (interactive password prompt — see the
# "Create the local user" section above for the full command list)
yarn build # build all packages once
yarn user:create # asks for email + password
# 4. Build the web app and boot the UI
yarn workspace @audithex/web run build
node apps/cli/bin/audithex.js ui # opens localhost:7777
node apps/cli/bin/audithex.js ui --dev --no-open # dev server, do not open browser
node apps/cli/bin/audithex.js ui --port 8080 # change the portThe UI redirects unauthenticated visitors to /login; signing in lands them on /. Sign out clears the cookie and returns to /login.
Every dashboard page renders inside an <AppShell> with a persistent left sidebar (Scans / Projects / Rules / Coverage / Settings) and a session footer (email + sign-out). On small viewports the sidebar collapses into a top bar with horizontally-scrolling pills.
The dashboard surfaces these routes:
/— Mongo-backed scan history table. Columns are id (clickable, ObjectId),scannedAt(UTC), top severity badge, severity counts (C/H/M/L), rules-pack version, elapsed time, and project root. Pagination via?skip=…&limit=…(default 25, max 100). Empty state explains how to seed scans by runningaudithex scanwithMONGODB_URIset./scans/[id]— full detail of one scan run: metadata grid (project root, rules pack, audithex version, elapsed time, discovery summary, fingerprint), then findings grouped by severity (critical→high→medium→low). A "Diff vs…" picker in the header jumps straight to a side-by-side compare; a "Download PDF" link streams a real PDF of the report. Each finding row carries an Explain how to fix button that calls the configured LLM (or the canned dry-run response) and caches the result in Mongo. Unknown ids 404./scans/[id]/compare/[otherId]— diff between two scans, keyed byruleId + file + line. The olderscannedAtis automatically treated as the baseline. Shows totals (added / removed / unchanged) and grouped rows with severity badges./scans/[id]/pdf— real PDF stream (server-rendered via@react-pdf/renderer): A4 page, metadata grid, findings grouped by severity,AI FIX CACHEDmarkers next to findings that have a stored explanation. ASCII-sanitised before render so future Unicode field values do not crash the type-shaper./projects— list of every project record in Mongo with name, root path, count of disabled rule ids, count of severity overrides, and last-updated timestamp. The header carries a + New project action that lands on/projects/new./projects/new— tabbed create form (Basics / Scan scope / Rules / Database). Basics carries the unique name, absolute root path, and optional description. Scan scope toggles the ten OWASP LLM Top 10 groups (all on by default) plus comma-separatedlanguagesandextraExtensions. Rules lists every rule in the active pack with an Enabled checkbox and per-project severity override. Database is optional — driver dropdown plus connection fields. Submitting redirects to the new project's detail page./projects/[id]— same tabbed edit form pre-filled from the record, a "Run scan" card that streams per-rule progress over Server-Sent Events and links to the persisted scan when finished, plus a per-project scan history strip showing the latest 25 runs attached to it. When any AI-required group is enabled but no AI provider is configured, the form surfaces a yellow banner pointing at/settings/ai. Deleting from the header asks no extra confirmation (button is destructive — guard against fat-fingers via the back link); deletes redirect to/projects./api/scans/run?projectId=<id>— SSE endpoint backing the "Run scan" card. Emitsstart→discovery (begin|end)→rules (loaded)→ per-ruleruleevents →persist (begin)→donewith the new scan id. Session cookie required (no anonymous scans)./rules— read-only browser of every rule in the active rules pack: id (clickable), human-readable title (fromfindings:<id>.title), default severity badge, OWASP categories, CWE, engine kind. Header shows the active pack version and source ("bundled" or the installed git channel)./rules/[id]— detail page for one rule: id + title + severity, metadata grid (OWASP / CWE / engine / languages), the i18n message template, the i18n fix recommendation, the engine parameter object, and the rule's free-formmetablock (references, authors). Rule rows in the project form's rule picker open this page in a new tab so the editing form keeps its unsaved state./coverage— read-only OWASP LLM Top 10 (2025) matrix. Ten rows (LLM01–LLM10) each carry a status badge —covered(≥1 static rule fires),planned(static rule mapped but not yet shipped),dynamic only(waiting on the live-LLM attack engine), orout of scope(training-data concerns the static scanner cannot address, e.g. LLM04) — plus the list of mapped rule ids as clickable pills./settings— read-only info page: Audithex CLI version, session TTL, cookie name, MongoDB connection status + masked URI + database name +scan_runscount, the latest five rules-pack update outcomes. Surfaces a clear hint that on-disk overrides live in.audithex/config.jsonand the CLI owns the truth. A "→ Change email or password" link jumps to/settings/account./settings/account— change-email and change-password forms. Both require the current password (verifyPasswordagainst the stored bcrypt hash) before any update lands; the change-email path re-issues the session cookie so the user stays signed in under the new address. Password rotation does NOT re-issue the cookie — any active session keeps working until the cookie expires.
A project record can carry an optional database connection. When set, the scan pipeline (CLI audithex scan --project <name> and the web "Run scan" card) connects to the configured database after the filesystem scan and walks the listed tables with the same secret-pattern rules used against source files. Findings get a synthetic db://<database>/<schema>.<table>?row=<n>&column=<col> location so the existing report / persistence / PDF / diff pipelines treat them like any other finding.
Three drivers ship today: Postgres (tables via information_schema, pg 8.x), MySQL (tables via information_schema, mysql2 3.x lazy-loaded), and MongoDB (collections walked recursively, mongodb 6.x). The Database tab on /projects/new and /projects/[id] accepts:
| Field | Required | Notes |
|---|---|---|
| Driver | — | Leave blank to skip the DB scan entirely. Valid values: postgres, mysql, mongodb. |
| Connection URI | when a driver is set | Standard postgres://, mysql://, or mongodb:// URI. Override with Database name if the URI's path is blank. |
| Database name | — | Optional override; falls back to the path component of the URI for the synthetic db://... finding location. |
| Tables / Collections | — | Comma- or space-separated list. schema.table for Postgres/MySQL; collection names for Mongo. Scanned in order. |
| Scan all tables / collections | — | Opt-in only. When the list is empty and this is unchecked, the scanner refuses to run rather than silently walking every table — which can be hours of overhead on real schemas. |
The scanner samples up to 500 rows per table (text / varchar / json / jsonb columns) and runs every secret-pattern bundle the rules pack ships. CLI output shows a Database scan: N table(s), M row(s), K finding(s) line; the web UI streams per-table progress events over SSE alongside the rule events.
A project is a named, persisted bundle of "I want these rules disabled" and "I want these severities overridden" that the scanner picks up when invoked with --project <name>. Projects live in Mongo (projects collection) and are managed identically from the CLI and the web UI:
# CLI — create / list / show / delete
node apps/cli/bin/audithex.js project create --name banking-bot --root-path ./fixtures/fixture-banking-bot --disable R013,R019
node apps/cli/bin/audithex.js project list
node apps/cli/bin/audithex.js project show banking-bot
node apps/cli/bin/audithex.js project delete banking-bot --force
# CLI — scan against a project (uses its rootPath, overrides, disabled rules)
node apps/cli/bin/audithex.js scan --project banking-botThe persisted ScanRun records its projectId, so the history table renders a per-row project link and /projects/[id] shows the run under its history strip. Severity overrides are managed via the web form (R009=low, one per line); the CLI surfaces --disable R013,R019 for disabled-rule sets and reads severity overrides from the project record at scan time.
Every finding row carries a per-finding Explain how to fix button. Clicking it calls the Anthropic Messages API directly from the server (no SDK dep, plain fetch) with a focused prompt: rule id, severity, file/line, and the message key. The response is cached in MongoDB (ai_fixes collection, keyed by scanId + findingKey), so re-opening the page renders the cached fix instantly without re-paying.
# .env — required to enable the live LLM call
ANTHROPIC_API_KEY=sk-ant-api03-...
AUDITHEX_LLM_MODEL=claude-sonnet-4-6 # default
AUDITHEX_LLM_COST_CAP_USD=1.00 # per-fix cost ceiling
# Testing & screenshot pipelines set this instead of an API key — returns
# a deterministic canned answer with $0 cost.
AUDITHEX_LLM_DRY_RUN=trueThe button surfaces the cost (in USD), the active model, and whether the answer was served from the cache or freshly computed. When neither ANTHROPIC_API_KEY nor AUDITHEX_LLM_DRY_RUN is set, the button renders disabled with the hint to configure one of them. Cost projection is checked against AUDITHEX_LLM_COST_CAP_USD before any network call; over-cap requests are rejected on the server.
The UI runs entirely on localhost — no traffic leaves your machine. Cypress covers the login flow end-to-end:
yarn workspace @audithex/web run cypress:e2e # orchestrator: in-memory Mongo + seeded user + `next start` + cypress run
yarn workspace @audithex/web run cypress:e2e:dev # same, but `next dev` for hot reload
yarn workspace @audithex/web run cypress:open # interactive cypress runner against an already-running servernode apps/cli/bin/audithex.js user create --force # prompts for a new password for the existing userselftest runs the full pipeline against the bundled fixtures/fixture-banking-bot/ (intentionally-vulnerable banking chatbot) and asserts the result against expected-findings.json (10 ground-truth findings, one per rule R001 – R010).
# Run every bundled fixture (currently: fixture-banking-bot)
node apps/cli/bin/audithex.js selftest
# Run a specific fixture by directory name
node apps/cli/bin/audithex.js selftest --fixture fixture-banking-bot
# Point at a custom fixtures directory
node apps/cli/bin/audithex.js selftest --fixture-root /path/to/custom-fixturesA passing run prints:
fixture-banking-bot: PASS (tp=10 fp=0 fn=0 precision=1.00 recall=1.00)
selftest: PASS across 1 fixture(s) — thresholds precision>=0.95, recall>=0.90
The exit code is 0 when every fixture clears precision ≥ 0.95 and recall ≥ 0.9, and 2 otherwise. update runs the same selftest on every freshly-installed rules pack and rolls back when it fails.
audithex init writes .audithex/config.json next to your code:
node apps/cli/bin/audithex.js initDefault contents:
{
"schemaVersion": "0.1",
"scan": {
"includeGlobs": ["**/*.{ts,tsx,js,jsx,mjs,cjs,md,txt}"],
"excludeGlobs": ["node_modules/**", "dist/**", "build/**", ".next/**"]
},
"rules": {
"overrides": {}
},
"dynamic": {
"enabled": false
}
}Override one rule's severity or disable it entirely:
{
"rules": {
"overrides": {
"R009": { "severity": "low" },
"R010": { "disabled": true }
}
}
}.env values take precedence over .audithex/config.json.
audithex/
├── apps/
│ ├── cli/ Node 22 CLI: commander, @clack/prompts, dotenv, zod, i18next
│ └── web/ Next.js 16 + React 19 + Tailwind 3 — local single-user dashboard
│ (server-action auth, signed-cookie sessions, Cypress e2e suite)
├── packages/
│ ├── core-languages Central language registry (TS, JS, Python, PHP, Go, Java, Ruby, plain-text)
│ ├── core-discovery gitignore-aware walker + 6 multi-language artifact extractors
│ │ (TS Compiler API on .ts/.tsx/.js/.jsx/.mjs/.cjs, regex elsewhere)
│ ├── core-rules JSON rules-pack engine + loader + 3 engines
│ ├── core-report Console / JSON / Markdown report renderers
│ ├── core-update Git-based rules-pack update channel + selftest rollback
│ ├── core-persistence MongoDB + Mongoose schemas (ScanRun, User, RulesPackUpdate),
│ │ bcryptjs auth helpers, in-memory test harness
│ ├── core-eval-runner Fixture evaluator + bundled-fixtures loader (precision / recall thresholds)
│ ├── core-payloads Schema and loader for the attack payload library
│ ├── core-i18n i18next loader auto-resolving locales/ root, namespace-aware t()
│ └── core-types Shared TypeScript types and the exit-code mapper
├── fixtures/
│ └── fixture-banking-bot/ Intentionally-vulnerable code + expected-findings.json (selftest ground truth)
├── locales/{en,uk}/ UI strings, kept in parity by scripts/check-locale-parity.mjs
├── scripts/ CI enforcement: check-docs, check-locale-parity, check-no-stubs
└── docs/overview.md Authoritative architecture overview
Every change must clear all of these. yarn verify runs them in one go; CI runs the same set on every PR.
yarn verify # lint + typecheck + test + jscpd + docs + locales + stubs (in that order)
yarn lint # Biome
yarn typecheck # tsc per workspace
yarn test # Vitest across every workspace
yarn dupes # jscpd (TypeScript-only, 0 clones allowed)
yarn docs:check # no TODO: placeholders inside docs/
yarn locales:check # locales/en and locales/uk in full key parity
yarn stubs:check # no "throw new Error('not implemented')" or "TODO: implement"Test count by workspace:
| Workspace | Tests |
|---|---|
@audithex/core-discovery |
35 (+ 1 perf benchmark gated by AUDITHEX_RUN_PERF_BENCH=true) |
@audithex/core-rules |
15 |
@audithex/core-update |
13 |
@audithex/core-persistence |
11 (in-memory MongoDB via mongodb-memory-server) |
@audithex/cli |
15 (incl. banking-bot selftest, exit-code coverage, end-to-end Mongo-backed history) |
@audithex/web (Cypress) |
11 end-to-end specs — login (3), history list + detail + 404 (3), Diff vs… picker + grouped diff rows (2), settings (1), AI fix dry-run with cache round-trip + real PDF download with %PDF magic (2) |
@audithex/core-i18n |
7 |
@audithex/core-report |
3 |
@audithex/core-eval-runner |
3 |
A gated perf test under packages/core-discovery/src/perf.bench.test.ts generates 5 000 synthetic .ts files (1 in 50 enriched with SDK imports + model literals + system prompts + tool definitions) and asserts that discover() finishes in under 30 s.
AUDITHEX_RUN_PERF_BENCH=true \
npx vitest run --root packages/core-discovery --dir packages/core-discovery/srcReference run on an M2 MacBook Pro: 547 ms for 5 000 files, 300 artifacts.
# Run the CLI in watch mode (recompiles on every save)
yarn workspace @audithex/cli dev
# Run a single workspace's tests
yarn workspace @audithex/core-rules test
# Check locale parity after editing translations
yarn locales:check
# Reset the rules-pack cache and fall back to the bundled pack
rm -rf ~/.audithex/rules-pack
node apps/cli/bin/audithex.js scan .
# Use a local file:// rules-pack for development
AUDITHEX_RULES_PACK_URL=file:///path/to/my-rules.git \
node apps/cli/bin/audithex.js update --yesUnsupported engine: wanted: {"node":">=22.0.0"} (current: "18.x")
Wrong Node version. Run nvm use 22 in the shell that invokes Audithex.
Cannot find matching keyid from corepack
The OS-bundled corepack on older Node ships with stale signing keys. Switch to Node 22; that version's corepack ships with current keys.
The nearest package directory doesn't seem to be part of the project declared in ...
yarn 4 sees a parent package.json and assumes audithex must be a workspace of it. The repo's yarn.lock is the official escape hatch; if it ever gets deleted, run touch yarn.lock at the repo root and try again.
Cannot find module '@audithex/core-languages' during typecheck
A workspace package has not been built yet. Run yarn build once.
Module ... was compiled against a different Node.js version
A native module was built against an older Node. Run yarn rebuild while on Node 22.
Scan reports no findings on a file you know is vulnerable
Check that the file extension is registered. node -e "import('@audithex/core-languages').then(m => console.log(m.listExtensions()))" lists what the scanner accepts. To add an extension, edit the language definition in packages/core-languages/src/languages/ — the registry is the single source of truth.
Failed to fetch rules pack: ... HTTP 404 Not Found from audithex update
The default channel URL (https://github.com/audithex/rules-pack.git) points at a repository that may not yet exist. Set AUDITHEX_RULES_PACK_URL to any git URL — a public mirror, a private fork, or a local file:// path during development.
Invalid environment configuration
The .env failed zod validation. The error message lists the offending key and what was expected.
Copyright (C) 2026 Roman Tsehynka and Audithex contributors.
Audithex is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Audithex is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
The full license text is in LICENSE.
- You can use Audithex commercially, modify it, distribute it, and run it privately — for free.
- You must keep the same AGPL-3.0 license on any fork or derived work, include the original copyright notice, and state significant changes you made.
- Network use is distribution. If you run a modified Audithex as a network-accessible service (e.g. a hosted scanner), you must offer the modified source code to the users of that service. This is the key difference from plain GPL-3.0 and the reason Audithex picked AGPL.
Per sections 15, 16, and 17 of the GNU Affero General Public License
(reproduced verbatim in LICENSE):
- Audithex is provided "AS IS" without warranty of any kind, express or implied — including but not limited to the implied warranties of merchantability and fitness for a particular purpose.
- The entire risk of running Audithex against your code, your database, or your production data is yours. Should the tool prove defective, you assume the cost of all necessary servicing, repair, or correction.
- In no event will the copyright holders or any contributor be liable for any damages — general, special, incidental, or consequential — arising out of the use or inability to use the program, including lost data, data rendered inaccurate, losses sustained by you or by third parties, or any failure of the program to operate with other software, even if a holder has been advised of the possibility of such damages.
- If a court refuses to give the disclaimer above local legal effect, section 17 instructs that court to apply the local law that most closely approximates an absolute waiver of all civil liability in connection with the program.
This applies to every byte the project ships — the CLI, the web UI, the
rules pack, the AI-fix recommendations served by claude-sonnet, the
Postgres scanner in @audithex/core-db-scan, and every script under
apps/web/scripts/. Run it against your own systems, on your own
authority. The authors and contributors carry no responsibility for the
outcome.
If AGPL-3.0 does not fit your use case (for example, you want to bundle Audithex into a closed-source product), open an issue to discuss a separate commercial license.