penthera 1.6.0 → 1.7.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 +67 -0
- package/README.md +158 -301
- package/bin/scan.js +2 -2
- package/docs/METHODOLOGY.md +236 -182
- package/docs/commands/README.md +2 -1
- package/docs/commands/init.md +114 -0
- package/docs/commands/scan.md +1 -1
- package/docs/compare/github.md +1 -1
- package/docs/dashboard.md +95 -152
- package/docs/examples/github-actions-scan.yml +1 -1
- package/docs/internal-audit.md +1 -1
- package/docs/plan.md +3 -2
- package/docs/references.bib +43 -33
- package/docs/references.md +92 -51
- package/lib/whitebox/frameworks.js +36 -0
- package/package.json +1 -1
- package/skills/penthera/SKILL.md +19 -5
- package/src/cli/deps.js +1 -1
- package/src/cli/detect.js +125 -0
- package/src/cli/onboarding.js +428 -6
- package/src/cli/prompt.js +42 -0
- package/src/cli.js +18 -5
- package/src/ui/api/data.js +30 -0
- package/src/ui/api/demo.js +13 -0
- package/src/ui/api/routes.js +1 -0
- package/src/ui/static/app.css +286 -560
- package/src/ui/static/index.html +8 -39
- package/src/ui/static/js/app.js +105 -107
- package/src/ui/static/js/center.js +388 -0
- package/src/ui/static/js/copy.js +36 -62
- package/src/ui/static/js/dom.js +0 -38
- package/src/ui/static/js/left.js +83 -0
- package/src/ui/static/js/right.js +270 -0
- package/src/ui/static/js/state.js +35 -0
- package/src/ui/static/js/views/ai.js +0 -68
- package/src/ui/static/js/views/deps.js +0 -96
- package/src/ui/static/js/views/evidence.js +0 -115
- package/src/ui/static/js/views/findings.js +0 -165
- package/src/ui/static/js/views/history.js +0 -92
- package/src/ui/static/js/views/overview.js +0 -184
- package/src/ui/static/js/views/run.js +0 -349
- package/src/ui/static/js/views/settings.js +0 -67
package/docs/METHODOLOGY.md
CHANGED
|
@@ -1,13 +1,50 @@
|
|
|
1
1
|
# Methodology
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
## Abstract
|
|
4
|
+
|
|
5
|
+
Penthera is an open-source, offline security scanner for web applications,
|
|
6
|
+
their dependencies, and the AI tooling used to build them. This document
|
|
7
|
+
specifies its method. It states the threat model, separates what black-box
|
|
8
|
+
(HTTP) and white-box (repository) analysis can and cannot observe, and defines
|
|
9
|
+
a four-level confidence model that distinguishes deterministic evidence from
|
|
10
|
+
heuristic signals. It describes the false-positive controls built into each
|
|
11
|
+
probe (randomized canaries, context-aware payload selection, baseline
|
|
12
|
+
comparison, and repeated timing measurement) and the non-destructive defaults
|
|
13
|
+
that follow from fail-safe design. Detection rules are distributed only in
|
|
14
|
+
reviewed, versioned releases, never through a live feed, which removes an
|
|
15
|
+
update channel from the attack surface. Each design choice is tied to prior
|
|
16
|
+
research or a published standard. On a bundled benchmark of labelled local
|
|
17
|
+
targets, Penthera reaches a precision of 0.86 and a recall of 0.90. Because the
|
|
18
|
+
same authors wrote the scanner and the targets, these figures measure
|
|
19
|
+
regressions, not accuracy on arbitrary applications.
|
|
20
|
+
|
|
21
|
+
## How to cite
|
|
22
|
+
|
|
23
|
+
If you use Penthera or this methodology in research or a report, cite the
|
|
24
|
+
software. The metadata are in [CITATION.cff](../CITATION.cff), which GitHub
|
|
25
|
+
renders as "Cite this repository".
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
van der Zwaag, D. (2026). Penthera: An offline security scanner for web apps and AI-built code (Version 1.7.0) [Computer software]. https://github.com/danoszz/penthera
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```bibtex
|
|
32
|
+
@software{vanderzwaag2026penthera,
|
|
33
|
+
author = {van der Zwaag, Daan},
|
|
34
|
+
title = {Penthera: An Offline Security Scanner for Web Apps and {AI}-Built Code},
|
|
35
|
+
year = {2026},
|
|
36
|
+
version = {1.7.0},
|
|
37
|
+
license = {MIT},
|
|
38
|
+
url = {https://github.com/danoszz/penthera}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Citations in this document follow APA 7th edition. Each one resolves to an
|
|
43
|
+
entry in [references.md](references.md), generated from
|
|
44
|
+
[references.bib](references.bib); a test fails if a citation has no entry or an
|
|
45
|
+
entry is never cited. Measured accuracy is in
|
|
46
|
+
[bench/RESULTS.md](../bench/RESULTS.md), and the method behind those numbers is
|
|
47
|
+
in [bench/README.md](../bench/README.md).
|
|
11
48
|
|
|
12
49
|
## Contents
|
|
13
50
|
|
|
@@ -23,52 +60,56 @@ Measured accuracy on the bundled benchmark is in
|
|
|
23
60
|
|
|
24
61
|
## Threat model
|
|
25
62
|
|
|
26
|
-
Penthera is built for small teams
|
|
27
|
-
coding tools,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
63
|
+
Penthera is built for small teams that ship web apps and APIs, often with AI
|
|
64
|
+
coding tools, and need a first line of evidence before a human review or a paid
|
|
65
|
+
penetration test. The threat model follows the four questions of Shostack
|
|
66
|
+
(2014): what are we building, what can go wrong, what are we going to do about
|
|
67
|
+
it, and did we do a good job.
|
|
31
68
|
|
|
32
|
-
**Assets.** User data behind the API
|
|
33
|
-
provider keys
|
|
34
|
-
|
|
69
|
+
**Assets.** User data behind the API; credentials and API keys, including AI
|
|
70
|
+
provider keys; the session and identity layer; and the integrity of the code and
|
|
71
|
+
dependencies that get deployed.
|
|
35
72
|
|
|
36
73
|
**Adversaries in scope.**
|
|
37
74
|
|
|
38
75
|
1. A remote, unauthenticated attacker on the internet who can send arbitrary
|
|
39
|
-
HTTP requests to the deployed app.
|
|
40
|
-
2. An authenticated user
|
|
41
|
-
level authorization
|
|
42
|
-
|
|
76
|
+
HTTP requests to the deployed app. The black-box scan models this attacker.
|
|
77
|
+
2. An authenticated user who tries to reach another user's data. Broken object
|
|
78
|
+
level authorization is the first item in the OWASP API Security Top 10
|
|
79
|
+
(OWASP Foundation, 2023). Penthera tests for it when given credentials.
|
|
43
80
|
3. Anyone who obtains a copy of the source: a leaked repository, a public fork,
|
|
44
|
-
a compromised CI log. Meli et al. found secrets leaked in over
|
|
45
|
-
public GitHub repositories, with thousands of new, unique secrets
|
|
46
|
-
every day
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
it
|
|
57
|
-
ones it cannot judge.
|
|
81
|
+
or a compromised CI log. Meli et al. (2019) found secrets leaked in over
|
|
82
|
+
100,000 public GitHub repositories, with thousands of new, unique secrets
|
|
83
|
+
leaked every day. The white-box secret scan models this adversary.
|
|
84
|
+
4. A supply-chain attacker who publishes or compromises a dependency (Ladisa et
|
|
85
|
+
al., 2023; Ohm et al., 2020; Zimmermann et al., 2019), including packages
|
|
86
|
+
that code-generating models invent and attackers then register (Spracklen
|
|
87
|
+
et al., 2025).
|
|
88
|
+
|
|
89
|
+
**Out of scope.** Physical access, insiders with production access, denial of
|
|
90
|
+
service, social engineering, and anything that needs exploitation beyond a
|
|
91
|
+
single benign probe. Penthera does not try to replace a human tester. It tries
|
|
92
|
+
to find the issues a scanner can find reliably, and to say clearly which ones
|
|
93
|
+
it cannot judge.
|
|
58
94
|
|
|
59
95
|
**Penthera itself is in the threat model.** A security tool runs with the
|
|
60
96
|
user's credentials and network position, so a compromised scanner is a
|
|
61
|
-
compromised machine. Thompson
|
|
62
|
-
build yourself
|
|
63
|
-
|
|
97
|
+
compromised machine. Thompson (1984) showed that you cannot fully trust code you
|
|
98
|
+
did not build yourself; the argument applies to scanners and their rule feeds.
|
|
99
|
+
This is why Penthera fetches no rules at run time
|
|
64
100
|
([below](#how-new-attack-techniques-are-adopted)).
|
|
65
101
|
|
|
66
|
-
STRIDE, the threat
|
|
67
|
-
Microsoft
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
102
|
+
STRIDE, the threat taxonomy that Shostack's book builds on, originates at
|
|
103
|
+
Microsoft (Kohnfelder & Garg, 1999). Penthera's probes map onto it as follows:
|
|
104
|
+
|
|
105
|
+
| STRIDE category | Penthera coverage |
|
|
106
|
+
|---|---|
|
|
107
|
+
| Spoofing | Authentication, JWT, OAuth, email SPF and DMARC |
|
|
108
|
+
| Tampering | Injection, CORS |
|
|
109
|
+
| Repudiation | Not tested |
|
|
110
|
+
| Information disclosure | Exposed files, secrets, verbose errors |
|
|
111
|
+
| Denial of service | Not tested; only rate limiting is checked |
|
|
112
|
+
| Elevation of privilege | IDOR/BOLA, client-side-only authorization |
|
|
72
113
|
|
|
73
114
|
## Black-box and white-box: what each can find
|
|
74
115
|
|
|
@@ -77,40 +118,39 @@ Penthera runs two kinds of analysis and reports them together.
|
|
|
77
118
|
### Black-box (URL scan)
|
|
78
119
|
|
|
79
120
|
The scanner sees only HTTP responses. Its tests are those of the OWASP Web
|
|
80
|
-
Security Testing Guide
|
|
81
|
-
[owasp-wstg-coverage.md](owasp-wstg-coverage.md).
|
|
121
|
+
Security Testing Guide (OWASP Foundation, n.d.-b); each probe's WSTG IDs are
|
|
122
|
+
listed in [owasp-wstg-coverage.md](owasp-wstg-coverage.md).
|
|
82
123
|
|
|
83
|
-
|
|
124
|
+
Black-box testing finds three kinds of issue well: properties visible in every
|
|
84
125
|
response (missing security headers, TLS configuration, cookie flags, CORS
|
|
85
126
|
policy), files and interfaces that should not be public, and injection flaws
|
|
86
|
-
whose effect shows
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
doesn't [doupe2010johnny].
|
|
127
|
+
whose effect shows in the response or its timing. Protocol checks follow the
|
|
128
|
+
relevant specifications: HSTS (Hodges et al., 2012), JWT (Jones et al., 2015;
|
|
129
|
+
Sheffer et al., 2020), and OAuth redirect and PKCE handling (Fett et al., 2016;
|
|
130
|
+
Lodderstedt et al., 2025; Sakimura et al., 2015).
|
|
131
|
+
|
|
132
|
+
Two classic evaluations of black-box scanners show what this approach misses.
|
|
133
|
+
Bau et al. (2010) tested eight commercial scanners against a custom vulnerable
|
|
134
|
+
application and older versions of real ones. As a group the tools did
|
|
135
|
+
reasonably well, but many of them missed second-order flaws such as stored XSS
|
|
136
|
+
and stored SQL injection. Doupé et al. (2010) tested eleven commercial and
|
|
137
|
+
open-source scanners against WackoPicko, a realistic purpose-built app with
|
|
138
|
+
sixteen planted flaws. Every scanner missed eight of the sixteen, among them
|
|
139
|
+
stored SQL injection, multi-step stored XSS, forceful browsing, and a business
|
|
140
|
+
logic flaw. They concluded that crawling matters as much as detection: a
|
|
141
|
+
scanner cannot test a page it never reaches, and losing track of state (logins,
|
|
142
|
+
multi-step forms) is a main reason it does not reach it.
|
|
103
143
|
|
|
104
144
|
Penthera's design responds to each point:
|
|
105
145
|
|
|
106
146
|
- **Crawl coverage.** Endpoint discovery uses a wordlist plus the app's own
|
|
107
147
|
OpenAPI document when one is exposed, and records exactly which probes ran.
|
|
108
|
-
The WSTG coverage table in every report lists
|
|
109
|
-
missing check is never presented as a pass.
|
|
148
|
+
The WSTG coverage table in every report lists each test as run or not run, so
|
|
149
|
+
a missing check is never presented as a pass.
|
|
110
150
|
- **State and logic.** Penthera does not claim to find business logic flaws,
|
|
111
|
-
which no scanner in the Doupé et al.
|
|
112
|
-
|
|
113
|
-
|
|
151
|
+
which no scanner in the study by Doupé et al. (2010) found either. IDOR/BOLA
|
|
152
|
+
results are labelled `needs-human-review` because a scanner cannot know
|
|
153
|
+
whether user A is allowed to read object B.
|
|
114
154
|
- **Stored and second-order flaws.** Not tested. They need a write followed by
|
|
115
155
|
a read elsewhere, which conflicts with the non-destructive default.
|
|
116
156
|
|
|
@@ -119,33 +159,32 @@ Penthera's design responds to each point:
|
|
|
119
159
|
The scanner reads source files. It finds hardcoded secrets, route handlers and
|
|
120
160
|
their authentication boundaries, and risky code patterns.
|
|
121
161
|
|
|
122
|
-
Secret detection uses provider-specific prefixes (for example `AKIA` for AWS
|
|
123
|
-
`sk-ant-` for Anthropic) plus a small number of assignment patterns such as
|
|
124
|
-
`api_key = "..."` with a placeholder filter. Meli et al. took the same
|
|
125
|
-
conservative route
|
|
126
|
-
distinctive formats
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
162
|
+
Secret detection uses provider-specific prefixes (for example `AKIA` for AWS
|
|
163
|
+
and `sk-ant-` for Anthropic) plus a small number of assignment patterns such as
|
|
164
|
+
`api_key = "..."` with a placeholder filter. Meli et al. (2019) took the same
|
|
165
|
+
conservative route and restricted detection to private keys and API keys with
|
|
166
|
+
distinctive formats. The cost is recall: a secret with no recognisable format
|
|
167
|
+
is invisible to this approach. SecretBench, a labelled dataset of real secrets
|
|
168
|
+
across many formats, is the natural yardstick for that gap (Basak et al.,
|
|
169
|
+
2023). The bundled benchmark includes an unprefixed signing key that Penthera
|
|
170
|
+
misses for this reason.
|
|
131
171
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
172
|
+
White-box scanning cannot see runtime configuration (headers added by a CDN, a
|
|
173
|
+
WAF in front of the app), secrets injected at deploy time, or anything in
|
|
174
|
+
dependencies that is only known from a vulnerability database.
|
|
135
175
|
|
|
136
176
|
### Combining them
|
|
137
177
|
|
|
138
178
|
Running both against the same app catches cases neither sees alone: a route
|
|
139
179
|
the repository marks as public that the black-box scan confirms is reachable,
|
|
140
|
-
or a header the code sets that the edge strips. The report keeps the
|
|
180
|
+
or a header the code sets that the edge strips. The report keeps the origin of
|
|
141
181
|
each finding (`source`, `method`) so a reader can tell which side produced it.
|
|
142
182
|
|
|
143
183
|
## Confidence model
|
|
144
184
|
|
|
145
185
|
Severity says how bad a finding would be if real. Confidence says how likely it
|
|
146
|
-
is to be real. Penthera reports both
|
|
147
|
-
fact. The labels are defined in
|
|
148
|
-
[lib/provenance.js](../lib/provenance.js):
|
|
186
|
+
is to be real. Penthera reports both and never lets a heuristic look like a
|
|
187
|
+
fact. The labels are defined in [lib/provenance.js](../lib/provenance.js):
|
|
149
188
|
|
|
150
189
|
| Label | Meaning | Typical sources |
|
|
151
190
|
|---|---|---|
|
|
@@ -154,120 +193,121 @@ fact. The labels are defined in
|
|
|
154
193
|
| `potential` | A heuristic or one-shot signal that needs corroboration. | Fuzzer, adaptive probes |
|
|
155
194
|
| `needs-human-review` | Exploitability cannot be judged from outside. | IDOR/BOLA, client-side-only auth |
|
|
156
195
|
|
|
157
|
-
Every finding also carries `source`, `method
|
|
158
|
-
|
|
196
|
+
Every finding also carries `source`, `method`, and `collected_at`. These travel
|
|
197
|
+
into SARIF, so the label survives into GitHub code scanning.
|
|
159
198
|
|
|
160
|
-
The benchmark reports precision per confidence label, which
|
|
161
|
-
|
|
162
|
-
`needs-human-review` findings were true positives
|
|
163
|
-
was a false positive
|
|
164
|
-
the evidence is real but the judgement is wrong
|
|
165
|
-
public by design, AWS's published example key
|
|
199
|
+
The benchmark reports precision per confidence label, which tests whether the
|
|
200
|
+
labels mean what they say. On the current run, all `likely` and
|
|
201
|
+
`needs-human-review` findings were true positives and the one `potential`
|
|
202
|
+
finding was a false positive. The two `confirmed` false positives are cases
|
|
203
|
+
where the evidence is real but the judgement is wrong: a health endpoint that
|
|
204
|
+
is public by design, and AWS's published example key. See
|
|
166
205
|
[bench/RESULTS.md](../bench/RESULTS.md).
|
|
167
206
|
|
|
168
207
|
## False-positive controls
|
|
169
208
|
|
|
170
|
-
|
|
171
|
-
misses.
|
|
209
|
+
A black-box scanner loses trust through false positives faster than through
|
|
210
|
+
misses. The probes apply these controls:
|
|
172
211
|
|
|
173
212
|
- **Randomized canaries.** The SSTI probe multiplies two random four-digit
|
|
174
|
-
numbers and looks for the seven- or eight-digit product, and only when
|
|
175
|
-
raw payload
|
|
213
|
+
numbers and looks for the seven- or eight-digit product, and only when
|
|
214
|
+
neither the raw payload nor the literal expression appears in the response
|
|
176
215
|
([lib/injections.js](../lib/injections.js), `buildSstiProbes`). The common
|
|
177
|
-
fixed `7*7 = 49`
|
|
178
|
-
probes use a
|
|
216
|
+
fixed canary `7*7 = 49` fires on any page that happens to contain "49". XSS
|
|
217
|
+
probes use a random reflection marker per probe (`makeCanary`).
|
|
179
218
|
- **Context before payload.** The XSS probe first checks where its marker is
|
|
180
|
-
reflected (HTML body, attribute, script)
|
|
181
|
-
execute in that context, then checks they came back unencoded.
|
|
182
|
-
|
|
183
|
-
- **Baseline comparison.** Error-based SQL injection is only
|
|
219
|
+
reflected (HTML body, attribute, or script), sends only payloads that would
|
|
220
|
+
execute in that context, and then checks that they came back unencoded.
|
|
221
|
+
Context-aware tools such as Dalfox take the same approach.
|
|
222
|
+
- **Baseline comparison.** Error-based SQL injection is reported only when the
|
|
184
223
|
database error appears in the injected response and not in the baseline.
|
|
185
224
|
- **Timing re-sampling.** A single slow response proves nothing: garbage
|
|
186
|
-
collection, cold starts and network jitter all look like an injected sleep.
|
|
187
|
-
Time-based SQL and command injection candidates are re-sent and only
|
|
188
|
-
|
|
225
|
+
collection, cold starts, and network jitter all look like an injected sleep.
|
|
226
|
+
Time-based SQL and command injection candidates are re-sent and reported only
|
|
227
|
+
when the median delay holds (`confirmTimingDelay`). Crosby et al. (2009)
|
|
189
228
|
showed that network jitter dominates single remote timing measurements and
|
|
190
|
-
that filtering over many samples is what makes them usable
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
payload merely appears in a same-site redirect's query string.
|
|
229
|
+
that filtering over many samples is what makes them usable, building on the
|
|
230
|
+
original timing attacks of Kocher (1996). Penthera's injected delays are
|
|
231
|
+
seconds, not microseconds, so a median of three samples is enough.
|
|
232
|
+
- **Host-checked redirects.** An open redirect is reported only when the
|
|
233
|
+
`Location` header resolves to the attacker host, not when the payload merely
|
|
234
|
+
appears in the query string of a same-site redirect.
|
|
197
235
|
- **Context-aware suppression.** On localhost and private hosts, findings that
|
|
198
|
-
do not apply (no HTTPS, missing `Secure` on cookies) are suppressed.
|
|
236
|
+
do not apply there (no HTTPS, missing `Secure` on cookies) are suppressed.
|
|
199
237
|
|
|
200
|
-
The benchmark's decoy target exercises each
|
|
201
|
-
fool a naive scanner. Writing it
|
|
238
|
+
The benchmark's decoy target exercises each control with an endpoint built to
|
|
239
|
+
fool a naive scanner. Writing it exposed one real flaw in the decoy itself: a
|
|
202
240
|
"safe" redirect that accepted `/\evil.com`, which browsers treat as
|
|
203
241
|
`//evil.com`. Penthera reported it correctly.
|
|
204
242
|
|
|
205
243
|
## Non-destructive by default
|
|
206
244
|
|
|
207
245
|
The `quick` and `standard` profiles send only requests a normal browser or API
|
|
208
|
-
client would send:
|
|
246
|
+
client would send: `GET`, `OPTIONS`, and invalid login attempts. They do not
|
|
209
247
|
send attack payloads, create or modify data, or exploit anything they find.
|
|
210
248
|
|
|
211
249
|
Payload-based testing (`--deep`, `--fuzz`, `--adaptive`, or `--profile deep`)
|
|
212
|
-
is opt-in and gated behind a confirmation prompt. Even then each probe sends a
|
|
213
|
-
benign marker (a random string, arithmetic, a short sleep, `id`)
|
|
214
|
-
working exploit, and stops at the first confirmation per
|
|
215
|
-
adaptive engine may send unexpected HTTP methods, including
|
|
216
|
-
routes with placeholder IDs
|
|
217
|
-
|
|
218
|
-
This follows
|
|
219
|
-
|
|
220
|
-
anything riskier needs an explicit decision. NIST SP 800-115
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
[scarfone2008sp800115].
|
|
250
|
+
is opt-in and gated behind a confirmation prompt. Even then, each probe sends a
|
|
251
|
+
benign marker (a random string, an arithmetic expression, a short sleep, `id`)
|
|
252
|
+
rather than a working exploit, and stops at the first confirmation per
|
|
253
|
+
endpoint. The adaptive engine may send unexpected HTTP methods, including
|
|
254
|
+
`DELETE`, to routes with placeholder IDs, so it is never part of a default run.
|
|
255
|
+
|
|
256
|
+
This follows the principles of fail-safe defaults and least privilege set out by
|
|
257
|
+
Saltzer and Schroeder (1975): the default does the least that is still useful,
|
|
258
|
+
and anything riskier needs an explicit decision. NIST SP 800-115 draws the same
|
|
259
|
+
line between non-intrusive assessment and penetration testing, and recommends
|
|
260
|
+
written rules of engagement before the latter (Scarfone et al., 2008).
|
|
224
261
|
|
|
225
262
|
## Offline and no telemetry
|
|
226
263
|
|
|
227
|
-
Penthera does not phone home. It sends no usage data, analytics or crash
|
|
264
|
+
Penthera does not phone home. It sends no usage data, analytics, or crash
|
|
228
265
|
reports, and it has no accounts. Reports are written to local files.
|
|
229
266
|
|
|
230
267
|
Network traffic goes to the target you name, with two documented exceptions:
|
|
231
268
|
|
|
232
|
-
1. `--recon` (and the `deep` profile) queries public OSINT sources (crt.sh,
|
|
233
|
-
|
|
234
|
-
|
|
269
|
+
1. `--recon` (and the `deep` profile) queries public OSINT sources (crt.sh, the
|
|
270
|
+
Wayback Machine, AlienVault OTX) with the target domain. It is skipped for
|
|
271
|
+
localhost.
|
|
235
272
|
2. The Retire.js check downloads the public Retire.js vulnerability database
|
|
236
|
-
from GitHub
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
273
|
+
from GitHub and compares JavaScript library versions locally. Outdated
|
|
274
|
+
client-side libraries are common on the web and often carry known
|
|
275
|
+
vulnerabilities (Lauinger et al., 2017). The database is data, not code: it
|
|
276
|
+
is parsed as JSON and never executed. It is the one place a detection input
|
|
277
|
+
arrives at run time. Under `--offline`, and whenever the download fails,
|
|
278
|
+
Penthera uses the snapshot vendored in the release instead.
|
|
279
|
+
|
|
280
|
+
The benchmark enforces offline operation mechanically. It wraps `fetch` so any
|
|
281
|
+
request to a non-loopback host is refused and counted, and it fails if the
|
|
282
|
+
count is not zero.
|
|
283
|
+
|
|
284
|
+
These choices follow the principles of economy of mechanism and complete
|
|
285
|
+
mediation (Saltzer & Schroeder, 1975): fewer network paths mean fewer things to
|
|
286
|
+
audit, and a tool that talks only to its target is easy to reason about in a
|
|
287
|
+
regulated environment.
|
|
249
288
|
|
|
250
289
|
## How new attack techniques are adopted
|
|
251
290
|
|
|
252
|
-
Attack techniques change quickly.
|
|
253
|
-
injection against LLM-integrated
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
catalogued by OWASP
|
|
257
|
-
|
|
258
|
-
|
|
291
|
+
Attack techniques change quickly. Recent examples include indirect prompt
|
|
292
|
+
injection against LLM-integrated applications (Greshake et al., 2023; Liu et
|
|
293
|
+
al., 2023; Perez & Ribeiro, 2022), package hallucination and slopsquatting
|
|
294
|
+
(Spracklen et al., 2025), and attacks on agent tooling and MCP servers,
|
|
295
|
+
catalogued by OWASP (OWASP Foundation, 2024, 2025a, 2025b) and in MITRE ATLAS
|
|
296
|
+
(The MITRE Corporation, n.d.-b). A scanner that does not keep up gives a false
|
|
297
|
+
sense of security.
|
|
259
298
|
|
|
260
|
-
Penthera keeps up through reviewed releases, not
|
|
299
|
+
Penthera keeps up through reviewed releases, not live updates:
|
|
261
300
|
|
|
262
|
-
- **Rules ship as code in a release.** New probes, patterns and templates are
|
|
301
|
+
- **Rules ship as code in a release.** New probes, patterns, and templates are
|
|
263
302
|
added by pull request, reviewed, tested against the benchmark, and published
|
|
264
303
|
in a versioned release with a CHANGELOG entry. There is no rule server, no
|
|
265
304
|
background update, and no command that fetches rules from the internet.
|
|
266
305
|
- **Every new rule carries a source and a date.** A rule added after v1.2.0
|
|
267
306
|
records where the technique comes from (a paper, an advisory, an OWASP or
|
|
268
|
-
ATLAS entry, an incident write-up) and the date it was added.
|
|
269
|
-
already linked to their sources in
|
|
270
|
-
the built-in rules that
|
|
307
|
+
ATLAS entry, an incident write-up) and the date it was added. Finding
|
|
308
|
+
categories are already linked to their sources in
|
|
309
|
+
[lib/references.json](../lib/references.json); the built-in rules that
|
|
310
|
+
predate this policy are being backfilled.
|
|
271
311
|
- **Every new rule comes with a benchmark case.** A detection rule is added
|
|
272
312
|
together with a labelled true positive and, where a false positive is
|
|
273
313
|
plausible, a decoy in [bench/](../bench/). The CI floor in the benchmark
|
|
@@ -276,25 +316,39 @@ Penthera keeps up through reviewed releases, not through live updates:
|
|
|
276
316
|
load YAML templates from directories you choose. Penthera does not download
|
|
277
317
|
them.
|
|
278
318
|
|
|
279
|
-
Why not
|
|
280
|
-
into a tool that runs with your network access and often your
|
|
281
|
-
Supply-chain research shows that update channels and package
|
|
282
|
-
exactly where attackers insert themselves
|
|
283
|
-
|
|
284
|
-
metadata, key rotation and explicit trust delegation
|
|
285
|
-
|
|
286
|
-
reuses the trust you already place in the release, and gives
|
|
287
|
-
trail: the rules you ran are the rules in the version you
|
|
319
|
+
Why not update automatically? A rule feed is a code path from someone else's
|
|
320
|
+
server into a tool that runs with your network access and often your
|
|
321
|
+
credentials. Supply-chain research shows that update channels and package
|
|
322
|
+
registries are exactly where attackers insert themselves (Ladisa et al., 2023;
|
|
323
|
+
Ohm et al., 2020; Zimmermann et al., 2019), and that securing an update channel
|
|
324
|
+
needs signed metadata, key rotation, and explicit trust delegation (Samuel et
|
|
325
|
+
al., 2010; Torres-Arias et al., 2019). Shipping rules inside the signed,
|
|
326
|
+
versioned package reuses the trust you already place in the release, and gives
|
|
327
|
+
a clear audit trail: the rules you ran are the rules in the version you
|
|
328
|
+
installed.
|
|
288
329
|
|
|
289
330
|
## Citations in reports
|
|
290
331
|
|
|
291
332
|
Each finding category maps to one to three references in
|
|
292
|
-
[lib/references.json](../lib/references.json)
|
|
333
|
+
[lib/references.json](../lib/references.json), chosen to explain why the
|
|
334
|
+
finding matters. Besides the sources discussed above, these include the
|
|
335
|
+
empirical work behind specific header and policy checks: Content Security
|
|
336
|
+
Policy (Weichselbaum et al., 2016), cookie handling and cross-site request
|
|
337
|
+
forgery (Barth et al., 2008), and CORS misconfiguration
|
|
338
|
+
(Chen et al., 2018). Email authentication findings cite the SPF and DMARC
|
|
339
|
+
specifications (Kitterman, 2014; Kucherawy & Zwicky, 2015). Injection,
|
|
340
|
+
exposure, and configuration findings cite the OWASP Top 10 (OWASP Foundation,
|
|
341
|
+
2021, 2025c), code-pattern findings cite the Common Weakness Enumeration (The
|
|
342
|
+
MITRE Corporation, n.d.-a), and license findings use the identifiers of the
|
|
343
|
+
SPDX License List (SPDX Workgroup, The Linux Foundation, n.d.).
|
|
344
|
+
|
|
345
|
+
The references appear in two places:
|
|
293
346
|
|
|
294
347
|
- in the Markdown report, as a "Why this matters / Sources" line under each
|
|
295
348
|
finding and a "Sources" line under each action-plan item;
|
|
296
|
-
- in SARIF
|
|
297
|
-
`help` text (all sources), and as `properties.references` (the BibTeX
|
|
349
|
+
- in SARIF (Fanning & Golding, 2023), as the rule's `helpUri` (first source)
|
|
350
|
+
and `help` text (all sources), and as `properties.references` (the BibTeX
|
|
351
|
+
keys).
|
|
298
352
|
|
|
299
353
|
[references.md](references.md) and the `sources` block of `references.json`
|
|
300
354
|
are generated from [references.bib](references.bib) by
|
|
@@ -303,19 +357,19 @@ are generated from [references.bib](references.bib) by
|
|
|
303
357
|
## Known limitations
|
|
304
358
|
|
|
305
359
|
- No authenticated crawling beyond the bearer token or cookie you pass, so
|
|
306
|
-
pages behind multi-step flows are not reached
|
|
307
|
-
- No stored or second-order injection testing
|
|
308
|
-
- No DOM-based XSS analysis
|
|
309
|
-
|
|
310
|
-
- No business logic testing. IDOR/BOLA findings need a human to confirm.
|
|
311
|
-
- Secret detection is pattern-based and skips Markdown and other
|
|
312
|
-
|
|
360
|
+
pages behind multi-step flows are not reached (Doupé et al., 2010).
|
|
361
|
+
- No stored or second-order injection testing (Bau et al., 2010).
|
|
362
|
+
- No DOM-based XSS analysis, because Penthera does not run JavaScript (Lekies et
|
|
363
|
+
al., 2013; Stock et al., 2014).
|
|
364
|
+
- No business logic testing. IDOR/BOLA findings need a human to confirm them.
|
|
365
|
+
- Secret detection is pattern-based and skips Markdown and other documentation
|
|
366
|
+
files, so unprefixed secrets and secrets in notes are missed.
|
|
313
367
|
- The bundled benchmark was written by the same people as the scanner. Its
|
|
314
368
|
numbers measure regressions and the false-positive controls, not accuracy on
|
|
315
369
|
arbitrary applications. Independent benchmarks such as the OWASP Benchmark
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
370
|
+
(OWASP Foundation, n.d.-a) and the benchmarking approach of Antunes and Vieira
|
|
371
|
+
(2010, 2015) are the model for a fairer comparison. Scoring against labelled
|
|
372
|
+
third-party targets (Juice Shop, crAPI) is the next step.
|
|
373
|
+
- Compliance output for the NIS2 Directive (European Parliament and Council of
|
|
374
|
+
the European Union, 2022) is evidence toward a duty of care, not a
|
|
375
|
+
certification.
|
package/docs/commands/README.md
CHANGED
|
@@ -6,6 +6,7 @@ has the full flag list.
|
|
|
6
6
|
|
|
7
7
|
| Command | Page | What it does |
|
|
8
8
|
|---|---|---|
|
|
9
|
+
| `penthera`, `penthera init` | [init.md](init.md) | Set up in one command: detect, check, then hook, workflow and dashboard |
|
|
9
10
|
| `penthera <url>`, `--repo .` | [scan.md](scan.md) | Scan a running app and its source: modes, profiles, outputs, exit codes, CI |
|
|
10
11
|
| `penthera agents` | [agents.md](agents.md) | Audit skills, MCP servers, hooks, plugins and rule files |
|
|
11
12
|
| `penthera deps` | [deps.md](deps.md) | What each package can do, the upgrade diff, typosquats, license policy |
|
|
@@ -14,7 +15,7 @@ has the full flag list.
|
|
|
14
15
|
| `penthera hook` | [hook.md](hook.md) | Pre-commit hook for secrets, agent config and dependency changes |
|
|
15
16
|
| GitHub Action | [action.md](action.md) | The capability diff as a pull-request comment, plus SARIF |
|
|
16
17
|
| Agent Skill | [skill.md](skill.md) | Let Claude Code, Cursor and other agents drive Penthera |
|
|
17
|
-
| `penthera ui` | [dashboard.md](../dashboard.md) | The local dashboard:
|
|
18
|
+
| `penthera ui` | [dashboard.md](../dashboard.md) | The local dashboard: one view with projects, findings, side cards, and the fix loop from the page |
|
|
18
19
|
|
|
19
20
|
Compliance flags (`--framework nis2`, `--readiness`, `--cra`, `--aibom`, ...)
|
|
20
21
|
are covered in [compliance-nis2.md](../compliance-nis2.md) and
|