penthera 1.1.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +269 -3
  2. package/README.md +489 -286
  3. package/SECURITY.md +28 -6
  4. package/docs/METHODOLOGY.md +321 -0
  5. package/docs/compliance-nis2.md +166 -0
  6. package/docs/evidence.md +158 -0
  7. package/docs/examples/github-actions-deps.yml +105 -0
  8. package/docs/examples/github-actions-scan.yml +55 -0
  9. package/docs/examples/pr-dependency-review.yml +56 -0
  10. package/docs/funding/due-diligence-checklist.md +79 -0
  11. package/docs/funding/nlnet-ngi-zero-draft.md +273 -0
  12. package/docs/funding/partner-one-pager.md +84 -0
  13. package/docs/funding/sponsor-tiers.md +81 -0
  14. package/docs/index.md +15 -3
  15. package/docs/local-loop.md +276 -0
  16. package/docs/references.bib +539 -0
  17. package/docs/references.md +77 -0
  18. package/docs/release-plan.md +228 -0
  19. package/lib/adaptive-probes.js +8 -5
  20. package/lib/agents/discover.js +384 -0
  21. package/lib/agents/hidden-content.js +387 -0
  22. package/lib/agents/index.js +286 -0
  23. package/lib/agents/lock.js +115 -0
  24. package/lib/agents/owasp.js +83 -0
  25. package/lib/agents/rules.js +376 -0
  26. package/lib/crawler.js +5 -15
  27. package/lib/data/retire-jsrepository.json.gz +0 -0
  28. package/lib/data/retire-jsrepository.meta.json +7 -0
  29. package/lib/deps/capabilities.js +523 -0
  30. package/lib/deps/diff.js +84 -0
  31. package/lib/deps/index.js +416 -0
  32. package/lib/deps/lockfile.js +106 -0
  33. package/lib/deps/online.js +132 -0
  34. package/lib/deps/popular-packages.json +1 -0
  35. package/lib/deps/report.js +173 -0
  36. package/lib/deps/source.js +171 -0
  37. package/lib/deps/typosquat.js +113 -0
  38. package/lib/fix/apply.js +95 -0
  39. package/lib/fix/diff.js +199 -0
  40. package/lib/fix/index.js +129 -0
  41. package/lib/fix/model.js +108 -0
  42. package/lib/fix/recipes/cookies.js +130 -0
  43. package/lib/fix/recipes/cors.js +116 -0
  44. package/lib/fix/recipes/headers.js +250 -0
  45. package/lib/fix/recipes/rate-limit.js +211 -0
  46. package/lib/fix/recipes/secrets.js +110 -0
  47. package/lib/fix/util.js +231 -0
  48. package/lib/fuzzer.js +14 -8
  49. package/lib/injections.js +19 -15
  50. package/lib/install/index.js +271 -0
  51. package/lib/install/policy.js +117 -0
  52. package/lib/memory/history.js +52 -0
  53. package/lib/memory/index.js +111 -0
  54. package/lib/memory/repo-suppress.js +64 -0
  55. package/lib/memory/store.js +325 -0
  56. package/lib/memory/views.js +198 -0
  57. package/lib/params.js +4 -12
  58. package/lib/provenance.js +2 -0
  59. package/lib/recon.js +12 -20
  60. package/lib/references.js +31 -0
  61. package/lib/references.json +374 -0
  62. package/lib/remediation/index.js +28 -0
  63. package/lib/retirejs.js +50 -22
  64. package/lib/templates.js +7 -10
  65. package/lib/whitebox/frameworks.js +1 -1
  66. package/lib/whitebox/secrets.js +49 -19
  67. package/package.json +19 -9
  68. package/skills/penthera/SKILL.md +104 -23
  69. package/skills/penthera/references/output-and-ci.md +12 -50
  70. package/skills/penthera/references/troubleshooting.md +1 -1
  71. package/skills/penthera/scripts/preflight.sh +4 -4
  72. package/src/agent/git.js +87 -0
  73. package/src/agent/loop.js +519 -0
  74. package/src/agent/tests.js +48 -0
  75. package/src/cli/agent.js +183 -0
  76. package/src/cli/deps.js +84 -0
  77. package/src/cli/evidence.js +67 -0
  78. package/src/cli/fix.js +191 -0
  79. package/src/cli/install.js +225 -0
  80. package/src/cli/memory.js +146 -0
  81. package/src/cli/merge-results.js +2 -0
  82. package/src/cli/report-outputs.js +72 -0
  83. package/src/cli/run-scan.js +2 -0
  84. package/src/cli.js +152 -6
  85. package/src/report/audience.js +270 -0
  86. package/src/report/evidence.js +253 -0
  87. package/src/report/markdown.js +65 -2
  88. package/src/report/model.js +212 -0
  89. package/src/report/template.js +175 -0
  90. package/src/report/timeline.js +145 -0
  91. package/src/reporter.js +109 -5
  92. package/src/scan-url.js +31 -7
  93. package/src/utils/http.js +252 -11
  94. package/src/utils/url.js +23 -0
  95. package/scripts/package-skill.sh +0 -16
package/SECURITY.md CHANGED
@@ -2,21 +2,43 @@
2
2
 
3
3
  ## Supported versions
4
4
 
5
+ Security fixes land on the latest minor release only. Upgrade to get them.
6
+
5
7
  | Version | Supported |
6
8
  | ------- | --------- |
7
- | 1.0.x | Yes |
8
- | < 1.0 | No |
9
+ | 1.2.x | Yes |
10
+ | < 1.2 | No |
9
11
 
10
12
  ## Reporting a vulnerability
11
13
 
12
14
  If you find a security issue in **Penthera itself** (not a finding produced by
13
15
  scanning a target), please report it privately:
14
16
 
15
- 1. Open a [GitHub Security Advisory](https://github.com/danoszz/penthera/security/advisories/new), or
16
- 2. Email the repository owner with a clear description and reproduction steps.
17
+ 1. Open a [GitHub Security Advisory](https://github.com/danoszz/penthera/security/advisories/new)
18
+ (preferred: it keeps the report private and lets us credit you), or
19
+ 2. Email the maintainer via the address on the [GitHub profile](https://github.com/danoszz)
20
+ with a clear description and reproduction steps.
21
+
22
+ Please do **not** open public issues for undisclosed vulnerabilities.
23
+
24
+ **What to expect.** Penthera is maintained part-time, but security reports come
25
+ first:
26
+
27
+ - Acknowledgement within 3 working days.
28
+ - An initial assessment (accepted, needs more info, or out of scope) within 10
29
+ working days.
30
+ - A fix or mitigation for accepted issues as fast as severity warrants, followed
31
+ by a release, a GitHub advisory, and credit to you unless you prefer otherwise.
32
+
33
+ **In scope:** the CLI, the scanner engine (`lib/`, `src/`), the Agent Skill
34
+ (`skills/penthera/`), the installer (`install.sh`), and the published npm package
35
+ and its build pipeline. Examples: code execution from a malicious scan target or
36
+ repo, a skill instruction that bypasses the authorization gate, a supply-chain
37
+ weakness in our release workflow.
17
38
 
18
- Please do **not** open public issues for undisclosed vulnerabilities. We aim to
19
- acknowledge reports within a few days.
39
+ **Safe harbor.** Good-faith research on Penthera's own code, done on your own
40
+ machine, is welcome and we will not pursue action against it. This does not
41
+ cover scanning third-party systems: the authorization rules below still apply.
20
42
 
21
43
  ## Responsible use
22
44
 
@@ -0,0 +1,321 @@
1
+ # Methodology
2
+
3
+ This document describes what Penthera tests, how it decides that something is a
4
+ finding, how sure it is, and where its limits are. Each design choice points to
5
+ the research or standard it rests on. Citation keys such as `[bau2010state]`
6
+ refer to [references.md](references.md) and [references.bib](references.bib).
7
+
8
+ Measured accuracy on the bundled benchmark is in
9
+ [bench/RESULTS.md](../bench/RESULTS.md); the method behind those numbers is in
10
+ [bench/README.md](../bench/README.md).
11
+
12
+ ## Contents
13
+
14
+ - [Threat model](#threat-model)
15
+ - [Black-box and white-box: what each can find](#black-box-and-white-box-what-each-can-find)
16
+ - [Confidence model](#confidence-model)
17
+ - [False-positive controls](#false-positive-controls)
18
+ - [Non-destructive by default](#non-destructive-by-default)
19
+ - [Offline and no telemetry](#offline-and-no-telemetry)
20
+ - [How new attack techniques are adopted](#how-new-attack-techniques-are-adopted)
21
+ - [Citations in reports](#citations-in-reports)
22
+ - [Known limitations](#known-limitations)
23
+
24
+ ## Threat model
25
+
26
+ Penthera is built for small teams shipping web apps and APIs, often with AI
27
+ coding tools, who need a first line of evidence before a human review or a
28
+ paid penetration test. The threat model follows the structure in Shostack
29
+ [shostack2014threat]: what are we building, what can go wrong, what do we do
30
+ about it, and did we do a good job.
31
+
32
+ **Assets.** User data behind the API, credentials and API keys (including AI
33
+ provider keys), the session and identity layer, and the integrity of the code
34
+ and dependencies that get deployed.
35
+
36
+ **Adversaries in scope.**
37
+
38
+ 1. A remote, unauthenticated attacker on the internet who can send arbitrary
39
+ HTTP requests to the deployed app. This is what the black-box scan models.
40
+ 2. An authenticated user trying to reach another user's data (broken object
41
+ level authorization, the top item in the OWASP API Security Top 10
42
+ [owasp2023api]). Penthera tests this when given credentials.
43
+ 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 100,000
45
+ public GitHub repositories, with thousands of new, unique secrets leaked
46
+ every day [meli2019bad]. This is what the
47
+ white-box secret scan models.
48
+ 4. A supply-chain attacker who publishes or compromises a dependency
49
+ [ladisa2023sok, ohm2020backstabber, zimmermann2019small], including
50
+ packages that code-generating models invent and attackers then register
51
+ [spracklen2025package].
52
+
53
+ **Out of scope.** Physical access, insider threats with production access,
54
+ denial of service, social engineering, and anything that needs exploitation
55
+ beyond a single benign probe. Penthera does not try to replace a human tester;
56
+ it tries to find the issues a scanner can find reliably and say clearly which
57
+ ones it cannot judge.
58
+
59
+ **Penthera itself is in the threat model.** A security tool runs with the
60
+ user's credentials and network position, so a compromised scanner is a
61
+ compromised machine. Thompson's point that you cannot trust code you did not
62
+ build yourself [thompson1984reflections] applies to scanners and their rule
63
+ feeds. This is why Penthera fetches no rules at run time
64
+ ([below](#how-new-attack-techniques-are-adopted)).
65
+
66
+ STRIDE, the threat categories that Shostack's book builds on, originates at
67
+ Microsoft [kohnfelder1999threats]. Penthera's probes map onto it roughly as:
68
+ spoofing (auth, JWT, OAuth, email SPF/DMARC), tampering (injection, CORS),
69
+ repudiation (not tested), information disclosure (exposed files, secrets,
70
+ verbose errors), denial of service (not tested, only rate limiting is
71
+ checked), and elevation of privilege (IDOR/BOLA, client-side-only auth).
72
+
73
+ ## Black-box and white-box: what each can find
74
+
75
+ Penthera runs two kinds of analysis and reports them together.
76
+
77
+ ### Black-box (URL scan)
78
+
79
+ The scanner sees only HTTP responses. Its tests are those of the OWASP Web
80
+ Security Testing Guide [owaspwstg42]; each probe's WSTG IDs are listed in
81
+ [owasp-wstg-coverage.md](owasp-wstg-coverage.md).
82
+
83
+ What black-box testing finds well: properties that are visible in every
84
+ response (missing security headers, TLS configuration, cookie flags, CORS
85
+ policy), files and interfaces that should not be public, and injection flaws
86
+ whose effect shows up in the response or its timing. Protocol checks follow
87
+ the relevant specifications: HSTS [hodges2012rfc6797], JWT
88
+ [jones2015rfc7519, sheffer2020rfc8725], and OAuth redirect and PKCE handling
89
+ [lodderstedt2025rfc9700, sakimura2015rfc7636, fett2016comprehensive].
90
+
91
+ What it misses, and why. Two classic evaluations of black-box scanners point
92
+ the same way. Bau et al. tested eight commercial scanners against a custom
93
+ vulnerable application and older versions of real ones. As a group the tools
94
+ did reasonably well, but second-order flaws, stored XSS and stored SQL
95
+ injection, were missed by many of them [bau2010state]. Doupé et al. tested
96
+ eleven commercial and open-source scanners against WackoPicko, a realistic
97
+ purpose-built app with sixteen planted flaws. Eight of the sixteen were missed
98
+ by every scanner, including stored SQL injection, multi-step stored XSS,
99
+ forceful browsing and a business logic flaw. They concluded that crawling
100
+ matters as much as detection: a scanner cannot test a page it never reaches,
101
+ and losing track of state (logins, multi-step forms) is a main reason it
102
+ doesn't [doupe2010johnny].
103
+
104
+ Penthera's design responds to each point:
105
+
106
+ - **Crawl coverage.** Endpoint discovery uses a wordlist plus the app's own
107
+ OpenAPI document when one is exposed, and records exactly which probes ran.
108
+ The WSTG coverage table in every report lists tests as run or not run, so a
109
+ missing check is never presented as a pass.
110
+ - **State and logic.** Penthera does not claim to find business logic flaws,
111
+ which no scanner in the Doupé et al. study found either.
112
+ IDOR/BOLA results are labelled `needs-human-review` because a scanner cannot
113
+ know whether user A is allowed to read object B.
114
+ - **Stored and second-order flaws.** Not tested. They need a write followed by
115
+ a read elsewhere, which conflicts with the non-destructive default.
116
+
117
+ ### White-box (repository scan)
118
+
119
+ The scanner reads source files. It finds hardcoded secrets, route handlers and
120
+ their authentication boundaries, and risky code patterns.
121
+
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, restricting detection to private keys and API keys with
126
+ distinctive formats [meli2019bad]. The cost is recall: a secret with no
127
+ recognisable format is invisible to this approach. SecretBench, a labelled
128
+ dataset of real secrets across many formats, is the natural yardstick for that
129
+ gap [basak2023secretbench]. The benchmark here includes an unprefixed signing
130
+ key that Penthera misses for this reason.
131
+
132
+ What white-box scanning cannot see: runtime configuration (headers added by a
133
+ CDN, a WAF in front of the app), secrets injected at deploy time, and anything
134
+ in dependencies that is only known from a vulnerability database.
135
+
136
+ ### Combining them
137
+
138
+ Running both against the same app catches cases neither sees alone: a route
139
+ 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 source of
141
+ each finding (`source`, `method`) so a reader can tell which side produced it.
142
+
143
+ ## Confidence model
144
+
145
+ Severity says how bad a finding would be if real. Confidence says how likely it
146
+ is to be real. Penthera reports both, and never lets a heuristic look like a
147
+ fact. The labels are defined in
148
+ [lib/provenance.js](../lib/provenance.js):
149
+
150
+ | Label | Meaning | Typical sources |
151
+ |---|---|---|
152
+ | `confirmed` | Deterministic evidence. The header is absent, the certificate expired, the key matches a provider format. | Header audit, TLS check, cookie audit, secret scan, OpenAPI exposure |
153
+ | `likely` | A strong signal with a small false-positive chance. | Injection probes with a verified canary, auth and JWT probes, framework checks |
154
+ | `potential` | A heuristic or one-shot signal that needs corroboration. | Fuzzer, adaptive probes |
155
+ | `needs-human-review` | Exploitability cannot be judged from outside. | IDOR/BOLA, client-side-only auth |
156
+
157
+ Every finding also carries `source`, `method` and `collected_at`, and these
158
+ travel into SARIF so the label survives into GitHub code scanning.
159
+
160
+ The benchmark reports precision per confidence label, which is the check that
161
+ the labels mean what they say. On the current run all `likely` and
162
+ `needs-human-review` findings were true positives, the one `potential` finding
163
+ was a false positive, and the two `confirmed` false positives are cases where
164
+ the evidence is real but the judgement is wrong (a health endpoint that is
165
+ public by design, AWS's published example key). See
166
+ [bench/RESULTS.md](../bench/RESULTS.md).
167
+
168
+ ## False-positive controls
169
+
170
+ Black-box scanners lose trust through false positives faster than through
171
+ misses. Specific controls in the probes:
172
+
173
+ - **Randomized canaries.** The SSTI probe multiplies two random four-digit
174
+ numbers and looks for the seven- or eight-digit product, and only when the
175
+ raw payload and the literal expression are absent from the response
176
+ ([lib/injections.js](../lib/injections.js), `buildSstiProbes`). The common
177
+ fixed `7*7 = 49` canary fires on any page that happens to contain "49". XSS
178
+ probes use a per-probe random reflection marker (`makeCanary`).
179
+ - **Context before payload.** The XSS probe first checks where its marker is
180
+ reflected (HTML body, attribute, script) and only sends payloads that would
181
+ execute in that context, then checks they came back unencoded. This is the
182
+ approach of context-aware tools such as Dalfox.
183
+ - **Baseline comparison.** Error-based SQL injection is only reported when the
184
+ database error appears in the injected response and not in the baseline.
185
+ - **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
+ reported when the median delay holds (`confirmTimingDelay`). Crosby et al.
189
+ showed that network jitter dominates single remote timing measurements and
190
+ that filtering over many samples is what makes them usable
191
+ [crosby2009opportunities], building on Kocher's original timing attacks
192
+ [kocher1996timing]. Penthera's delays are seconds, not microseconds, so a
193
+ median of three samples is enough.
194
+ - **Host-checked redirects.** An open redirect is reported only if the
195
+ `Location` header actually resolves to the attacker host, not when the
196
+ payload merely appears in a same-site redirect's query string.
197
+ - **Context-aware suppression.** On localhost and private hosts, findings that
198
+ do not apply (no HTTPS, missing `Secure` on cookies) are suppressed.
199
+
200
+ The benchmark's decoy target exercises each of these with an endpoint built to
201
+ fool a naive scanner. Writing it found one real problem in the decoy itself: a
202
+ "safe" redirect that accepted `/\evil.com`, which browsers treat as
203
+ `//evil.com`. Penthera reported it correctly.
204
+
205
+ ## Non-destructive by default
206
+
207
+ The `quick` and `standard` profiles send only requests a normal browser or API
208
+ client would send: GETs, `OPTIONS`, and invalid login attempts. They do not
209
+ send attack payloads, create or modify data, or exploit anything they find.
210
+
211
+ 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`) rather than a
214
+ working exploit, and stops at the first confirmation per endpoint. The
215
+ adaptive engine may send unexpected HTTP methods, including `DELETE`, to
216
+ routes with placeholder IDs; it is therefore never part of a default run.
217
+
218
+ This follows Saltzer and Schroeder's fail-safe defaults and least privilege
219
+ [saltzer1975protection]: the default does the least that is still useful, and
220
+ anything riskier needs an explicit decision. NIST SP 800-115 makes the same
221
+ distinction between non-intrusive assessment and penetration testing, and
222
+ recommends written rules of engagement before the latter
223
+ [scarfone2008sp800115].
224
+
225
+ ## Offline and no telemetry
226
+
227
+ Penthera does not phone home. It sends no usage data, analytics or crash
228
+ reports, and it has no accounts. Reports are written to local files.
229
+
230
+ Network traffic goes to the target you name, with two documented exceptions:
231
+
232
+ 1. `--recon` (and the `deep` profile) queries public OSINT sources (crt.sh,
233
+ the Wayback Machine, AlienVault OTX) with the target domain. It is skipped
234
+ for localhost.
235
+ 2. The Retire.js check downloads the public Retire.js vulnerability database
236
+ from GitHub to compare JavaScript library versions locally. This is data,
237
+ not code: it is parsed as JSON and never executed. It is the one place a
238
+ detection input arrives at run time, and the roadmap's `--offline` egress
239
+ guard replaces it with a vendored copy shipped in releases.
240
+
241
+ The benchmark enforces offline operation mechanically: it wraps `fetch` so any
242
+ request to a non-loopback host is refused and counted, and fails if the count
243
+ is not zero.
244
+
245
+ These choices follow economy of mechanism and complete mediation
246
+ [saltzer1975protection]: fewer network paths means fewer things to audit, and
247
+ a tool that talks only to the target is easy to reason about in a regulated
248
+ environment.
249
+
250
+ ## How new attack techniques are adopted
251
+
252
+ Attack techniques change quickly. In the last few years: indirect prompt
253
+ injection against LLM-integrated apps [greshake2023signed, perez2022ignore,
254
+ liu2023prompt], package hallucination and slopsquatting
255
+ [spracklen2025package], and attacks on agent tooling and MCP servers
256
+ catalogued by OWASP [owasp2024llmtop10, owasp2025agentic, owasp2025mcptop10]
257
+ and MITRE ATLAS [mitreatlas]. A scanner that does not keep up becomes a
258
+ false sense of security.
259
+
260
+ Penthera keeps up through reviewed releases, not through live updates:
261
+
262
+ - **Rules ship as code in a release.** New probes, patterns and templates are
263
+ added by pull request, reviewed, tested against the benchmark, and published
264
+ in a versioned release with a CHANGELOG entry. There is no rule server, no
265
+ background update, and no command that fetches rules from the internet.
266
+ - **Every new rule carries a source and a date.** A rule added after v1.2.0
267
+ 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. Categories are
269
+ already linked to their sources in [lib/references.json](../lib/references.json);
270
+ the built-in rules that predate this policy are being backfilled.
271
+ - **Every new rule comes with a benchmark case.** A detection rule is added
272
+ together with a labelled true positive and, where a false positive is
273
+ plausible, a decoy in [bench/](../bench/). The CI floor in the benchmark
274
+ stops a rule that adds false positives.
275
+ - **Your own templates stay local.** `--nuclei <path>` and `--templates <dirs>`
276
+ load YAML templates from directories you choose. Penthera does not download
277
+ them.
278
+
279
+ Why not auto-update? A rule feed is a code path from someone else's server
280
+ into a tool that runs with your network access and often your credentials.
281
+ Supply-chain research shows that update channels and package registries are
282
+ exactly where attackers insert themselves [ladisa2023sok, ohm2020backstabber,
283
+ zimmermann2019small], and that securing an update channel needs signed
284
+ metadata, key rotation and explicit trust delegation [samuel2010survivable,
285
+ torresarias2019intoto]. Shipping rules inside the signed, versioned package
286
+ reuses the trust you already place in the release, and gives a clear audit
287
+ trail: the rules you ran are the rules in the version you installed.
288
+
289
+ ## Citations in reports
290
+
291
+ Each finding category maps to one to three references in
292
+ [lib/references.json](../lib/references.json). They appear:
293
+
294
+ - in the Markdown report, as a "Why this matters / Sources" line under each
295
+ finding and a "Sources" line under each action-plan item;
296
+ - in SARIF [oasis2023sarif], as the rule's `helpUri` (first source) and
297
+ `help` text (all sources), and as `properties.references` (the BibTeX keys).
298
+
299
+ [references.md](references.md) and the `sources` block of `references.json`
300
+ are generated from [references.bib](references.bib) by
301
+ `node scripts/build-references.mjs`, and a test fails if they drift.
302
+
303
+ ## Known limitations
304
+
305
+ - No authenticated crawling beyond the bearer token or cookie you pass, so
306
+ pages behind multi-step flows are not reached [doupe2010johnny].
307
+ - No stored or second-order injection testing [bau2010state].
308
+ - No DOM-based XSS analysis; Penthera does not run JavaScript
309
+ [lekies2013flows, stock2014precise].
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
+ documentation files, so unprefixed secrets and secrets in notes are missed.
313
+ - The bundled benchmark was written by the same people as the scanner. Its
314
+ numbers measure regressions and the false-positive controls, not accuracy on
315
+ arbitrary applications. Independent benchmarks such as the OWASP Benchmark
316
+ [owaspbenchmark] and the approach of Antunes and Vieira
317
+ [antunes2010benchmarking, antunes2015assessing] are the model for a fairer
318
+ comparison, and scoring against labelled third-party targets (Juice Shop,
319
+ crAPI) is the next step.
320
+ - Compliance output (NIS2 [eu2022nis2]) is evidence toward a duty of care, not
321
+ a certification.
@@ -0,0 +1,166 @@
1
+ # Compliance: NIS2 and the cyberwetgeving
2
+
3
+ Back to the [README](../README.md).
4
+
5
+ The EU NIS2 directive and its national transpositions (the Cyberbeveiligingswet
6
+ in the Netherlands, the NIS2-wet in Belgium) put a duty of care, the *zorgplicht*,
7
+ on in-scope organisations. That duty is the set of risk-management measures in
8
+ Article 21(2) of the directive. Most of them are organisational. Some are
9
+ technical and testable, and that is the part Penthera helps with.
10
+
11
+ Penthera does not make you compliant and does not certify anything. What it gives
12
+ you is automated, repeatable technical evidence for the measures a scanner can
13
+ honestly verify: the kind of artefact you can attach to a risk file, show an
14
+ auditor, or run in CI so a regression shows up before it ships.
15
+
16
+ | Art. 21(2) measure | What Penthera does | Coverage |
17
+ |--------------------|--------------------|----------|
18
+ | **(a)** Risk analysis & information-security policy | Findings, severity, and baseline history feed your risk analysis | Evidence |
19
+ | **(b)** Incident handling | `--incident` drafts the 24h / 72h / 1-month reports against your authority's required content | Partial |
20
+ | **(c)** Business continuity, backup, crisis management | Organisational; outside a scanner's reach | Out of scope |
21
+ | **(d)** Supply-chain security | Known-CVE checks on JS dependencies, leaked credentials, a CycloneDX SBOM (`--sbom`), and passive supplier rating (`--suppliers`) | Partial |
22
+ | **(e)** Security in acquisition, development & maintenance; **vulnerability handling** | The core: black- + white-box scanning mapped to OWASP WSTG, SARIF → GitHub, baseline regression, scan-and-fix loop | **Strong** |
23
+ | **(f)** Assessing the effectiveness of measures | Baseline diff, re-scan-to-verify, and `--history` tracking of time-to-fix, ageing, and drift | Partial |
24
+ | **(g)** Cyber hygiene & training | Remediation playbook + secure-defaults guidance (guidance, not a training programme) | Partial |
25
+ | **(h)** Cryptography & encryption | TLS protocol/cipher/certificate audit, HSTS, cookie `Secure` (transport layer only) | Partial |
26
+ | **(i)** HR security, **access control**, asset management | Access control tested: JWT, IDOR/BOLA, OAuth redirect, client-side-auth, trust-boundary mapping | Partial |
27
+ | **(j)** Multi-factor authentication & secure communications | MFA probe (on the roadmap) | Planned |
28
+
29
+ **Coverage key:** *Strong* = automated technical testing · *Partial* = part of the
30
+ measure · *Evidence* = output feeds the measure, no direct test · *Planned* = on the
31
+ roadmap · *Out of scope* = organisational or physical.
32
+
33
+ In practice, Penthera today gives you solid automated evidence for the
34
+ secure-development and vulnerability-handling duty (e), which is the technical
35
+ heart of the zorgplicht, plus meaningful coverage of supply chain (d),
36
+ cryptography in transit (h), access control (i), and effectiveness testing (f).
37
+ That is not the whole duty of care, but it is a real, defensible start you can run
38
+ today and re-run on every deploy.
39
+
40
+ > **Not legal advice, not a certification, not a guarantee of compliance.**
41
+ > Penthera automates the technical-testing part of the duty of care. The
42
+ > directive's organisational duties (governance, risk-management policy,
43
+ > incident-notification processes, supply-chain contracts, staff training) need
44
+ > human process and, where it matters, review by a qualified advisor. For an
45
+ > authoritative reading, consult your national authority (the NCSC in the
46
+ > Netherlands, the CCB and its CyFun framework in Belgium) or a specialised jurist.
47
+
48
+ Generate this mapping for your own scan with `--framework nis2`. It adds a
49
+ per-run coverage table (which measures *this* scan actually exercised, driven by
50
+ the probes that ran) to the terminal, Markdown, and JSON reports:
51
+
52
+ ```bash
53
+ penthera https://myapp.com --framework nis2 -o reports/scan.json
54
+ ```
55
+
56
+ ### Readiness report (clone → report in two commands)
57
+
58
+ Go further than the mapping: combine the scan with a short offline
59
+ self-assessment (the organisational measures a scanner can't test, like policies,
60
+ backups, training, and MFA) into a full NIS2 readiness report. Every Article 21
61
+ measure is marked `met / partial / gap / n-a`, with proportionality notes, your
62
+ incident-reporting deadlines, and provenance on every piece of evidence.
63
+
64
+ ```bash
65
+ # 1. Create your self-assessment (offline, one-time). Fill in yes | partial | no | na.
66
+ penthera --assessment-init assessment.json
67
+
68
+ # 2. Scan + self-assessment → readiness report (writes reports/scan-readiness.md)
69
+ penthera https://myapp.com --repo . --readiness --assessment assessment.json -o reports/scan.json
70
+ ```
71
+
72
+ The readiness report is **evidence and self-assessment tooling, not a
73
+ certification**. It is provenance-linked (`source, method, collected_at,
74
+ confidence`) and reproducible, and it lists exactly what only you can attest to.
75
+ Jurisdiction specifics live in [`lib/jurisdictions/nl.json`](../lib/jurisdictions/nl.json) (NL today; BE and
76
+ others are just added config).
77
+
78
+ ### Answer a customer's security questionnaire
79
+
80
+ The questionnaire blocking your deal, drafted from evidence you already have:
81
+
82
+ ```bash
83
+ # one question per line, pasted from the customer's form
84
+ penthera https://myapp.com --readiness --assessment assessment.json --questionnaire questions.txt
85
+ ```
86
+
87
+ Each question is mapped to the Article 21 measure it belongs to and answered
88
+ from the scan and your self-assessment, with the backing evidence cited.
89
+ Anything Penthera can't map is marked `NEEDS HUMAN INPUT` instead of guessed.
90
+ Review every answer before you send it.
91
+
92
+ ### Policy pack
93
+
94
+ Generate the proportionate written policies NIS2 expects, pre-filled from your
95
+ readiness gaps and the NL incident deadlines:
96
+
97
+ ```bash
98
+ penthera --policy-pack ./policies --org "Your Company" --readiness --assessment assessment.json
99
+ ```
100
+
101
+ Writes editable Markdown starting points: information security, access control,
102
+ incident response, backup/continuity, acceptable use, supplier security, and
103
+ vulnerability management. **Templates, not legal advice.** Keep them short and
104
+ used. A two-page policy that genuinely exists is what "proportionate" means.
105
+
106
+ ### Incident-report helper
107
+
108
+ NIS2 has hard reporting deadlines (24h early warning, 72h notification, 1-month
109
+ final report). If something happens, get the timeline and draft reports:
110
+
111
+ ```bash
112
+ penthera --incident-init incident.json # fill in what happened (set aware_at)
113
+ penthera --incident incident.json # writes incident-reports.md
114
+ ```
115
+
116
+ It computes your three deadlines from when you became aware, flags anything
117
+ overdue, and drafts the early-warning, notification, and final report against
118
+ the required content, with `NEEDS INPUT` where you still have to fill in facts.
119
+ **These are drafts to help you meet the deadlines, not a substitute for your
120
+ authority's official form.**
121
+
122
+ ### Software bill of materials (SBOM)
123
+
124
+ Emit a standard CycloneDX SBOM of your dependencies. It's what enterprises
125
+ increasingly require of suppliers, and direct evidence for supply-chain security
126
+ (measure d):
127
+
128
+ ```bash
129
+ penthera --repo . --sbom sbom.cdx.json
130
+ ```
131
+
132
+ Parses `package-lock.json` / `package.json` / `requirements.txt` offline (no
133
+ network), with resolved versions and package URLs (purls).
134
+
135
+ ### Remediation tracking (the audit loop)
136
+
137
+ Closing findings matters more than finding them. Track them over time:
138
+ time-to-fix, ageing of open items, and what changed since the last scan (evidence
139
+ for measure f, effectiveness):
140
+
141
+ ```bash
142
+ penthera https://myapp.com --history reports/history.json -o reports/scan.json
143
+ ```
144
+
145
+ Each run folds the current findings into a small history keyed by a **stable
146
+ fingerprint** (so a re-worded or re-counted finding isn't mistaken for a new
147
+ one), then reports open vs resolved, median time-to-fix, and ageing buckets.
148
+ Re-run on a cadence to watch the trend.
149
+
150
+ ### Supplier rating (passive)
151
+
152
+ Assess your suppliers' public security posture for supply-chain risk (measure d):
153
+
154
+ ```bash
155
+ printf 'supplier1.com\nsupplier2.com\n' > suppliers.txt
156
+ penthera --suppliers suppliers.txt
157
+ ```
158
+
159
+ **Strictly passive and public-data-only.** One homepage GET (security headers),
160
+ a TLS handshake (certificate), and public DNS lookups (SPF/DMARC/MX) per domain.
161
+ No endpoint discovery, no auth probes, no intrusive testing. You get a
162
+ red/amber/green rating per supplier. It's a screening aid for your own vendor
163
+ risk assessment, **not** an authorised penetration test of the supplier.
164
+
165
+ Because the mapping targets the NIS2 directive itself, it applies across the
166
+ national transpositions; jurisdiction-specific profiles (NL, BE) are on the roadmap.