penthera 1.1.1 → 1.2.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 CHANGED
@@ -4,6 +4,60 @@ All notable changes to Penthera are documented here. Versioning follows [SemVer]
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.2.0] - 2026-10-19
8
+
9
+ The first step toward securing the AI toolchain itself: catch AI provider keys,
10
+ harden Penthera's own supply chain, and make CI test what it says it tests. The
11
+ [roadmap](ROADMAP.md) now sets out the AI supply-chain and local agent-loop
12
+ tracks.
13
+
14
+ ### Added
15
+
16
+ - **AI provider key detection** in repo scans: Anthropic, OpenAI project and
17
+ service-account keys, OpenRouter, Google (Gemini / Firebase), Groq, Hugging
18
+ Face, Replicate, xAI, Perplexity, LangSmith, and Pinecone. Jupyter notebooks
19
+ (`.ipynb`) are now scanned too. A provider key inside an `api_key = "..."`
20
+ assignment is reported once, under its provider, instead of twice
21
+ - `findSecretsInText()` exported from `lib/whitebox/secrets.js`, so the same
22
+ rules can run on any text (groundwork for scanning JS bundles)
23
+ - CodeQL and OpenSSF Scorecard workflows, Dependabot for npm and GitHub Actions
24
+ (with a 7-day cooldown before adopting new releases)
25
+ - CI check that `package.json`, the lockfile, the skill, and both plugin
26
+ manifests carry the same version
27
+ - Roadmap: AI supply-chain audit (`penthera agents`), a fully local
28
+ scan → fix → verify → learn loop, and LLM-endpoint probes. Principles now
29
+ state no telemetry, no auto-push, and no remote learning
30
+
31
+ ### Fixed
32
+
33
+ - **Modern OpenAI keys were missed.** The old pattern stopped at the hyphen in
34
+ `sk-proj-...` and `sk-svcacct-...`, so the key format OpenAI has issued since
35
+ 2024 never matched
36
+ - The example scan workflow lived in `.github/workflows/`, so it ran on its
37
+ weekly cron and failed every Monday. It moved to
38
+ `docs/examples/github-actions-scan.yml` and now uses `npx penthera@<version>`,
39
+ so it works when copied into your own repo
40
+ - CI ran only 4 of 24 test files; it now runs the full `npm test`
41
+ - Removed `tests/pentest-static.test.js`, which tested functions defined inside
42
+ the test file rather than anything Penthera ships
43
+ - `.claude-plugin/plugin.json` still said 1.0.0
44
+ - `SECURITY.md` listed 1.0.x as supported. It now covers 1.2.x and gives response
45
+ times, scope, and safe-harbor terms
46
+
47
+ ### Changed
48
+
49
+ - **Node.js 22 or newer is now required.** Node 18 and 20 are end-of-life, and
50
+ the test toolchain (vitest 5) needs 22. CI runs on Node 22 and 24
51
+ - GitHub Actions are pinned to commit SHAs, and every workflow declares
52
+ least-privilege `permissions`
53
+ - Releases use npm trusted publishing (OIDC, no stored token) and **staged
54
+ publishing**: CI stages each version, and it only goes live after a
55
+ maintainer approves it with 2FA. The workflow also creates the GitHub Release
56
+ - The Docker image runs as the unprivileged `node` user on `node:22-alpine`
57
+ - README reordered so install and quick start come before compliance. The full
58
+ NIS2 guide moved to [`docs/compliance-nis2.md`](docs/compliance-nis2.md)
59
+ - vitest 3 → 5 (clears 8 dev-only advisories), yaml 2.8 → 2.9
60
+
7
61
  ## [1.1.1] - 2026-08-16
8
62
 
9
63
  A correctness release. One of these was a live outage: if you are on 1.1.0,
@@ -176,7 +230,7 @@ and generated documents.
176
230
  live integration tests report as skipped (not passed) without a running target
177
231
  - Corrected the built-in template count (14, not 17) in docs and progress output
178
232
 
179
- ## [1.0.0], 2026-06-30
233
+ ## [1.0.0] - 2026-06-30
180
234
 
181
235
  First production-ready release.
182
236
 
@@ -201,11 +255,11 @@ First production-ready release.
201
255
 
202
256
  - URL trailing-slash normalization (double-slash 404 bug)
203
257
 
204
- ## [0.2.0], 2026-04
258
+ ## [0.2.0] - 2026-04
205
259
 
206
260
  - Markdown reports, scan profiles, auth tooling, expanded API discovery
207
261
 
208
- ## [0.1.x], 2026-04
262
+ ## [0.1.x] - 2026-04
209
263
 
210
264
  - Initial CLI: URL, repo, and machine scan modes
211
265
  - Built-in templates, TLS, CORS, SARIF export
package/README.md CHANGED
@@ -5,9 +5,10 @@
5
5
  <br/><br/>
6
6
 
7
7
  [![npm](https://img.shields.io/npm/v/penthera?color=0000ed)](https://www.npmjs.com/package/penthera)
8
- ![node](https://img.shields.io/badge/node-%E2%89%A518-0000ed)
8
+ ![node](https://img.shields.io/badge/node-%E2%89%A522-0000ed)
9
9
  ![license](https://img.shields.io/badge/license-MIT-0000ed)
10
10
  [![CI](https://github.com/danoszz/penthera/actions/workflows/ci.yml/badge.svg)](https://github.com/danoszz/penthera/actions/workflows/ci.yml)
11
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/danoszz/penthera/badge)](https://scorecard.dev/viewer/?uri=github.com/danoszz/penthera)
11
12
 
12
13
  <br/>
13
14
 
@@ -33,7 +34,8 @@ issues." The agent picks the scan, runs it, and gives you the fixes.
33
34
  You shipped something fast: vibecoded, AI-assisted, already live. The same models
34
35
  that helped you build it also make it easy for someone to probe it. Exposed API
35
36
  routes, missing security headers, weak auth, a hardcoded key in the repo. These
36
- are the common mistakes that get apps broken into.
37
+ are the common mistakes that get apps broken into. An AI provider key in the
38
+ frontend bundle is the newest and most expensive of them.
37
39
 
38
40
  Penthera is the check you run before you launch. Ask your agent to "scan my app
39
41
  for security issues" and it runs an authorized, non-destructive scan, then lists
@@ -114,7 +116,7 @@ Prefer the terminal? Run `penthera` with no arguments for a guided wizard.
114
116
  | Mode | Command | Coverage |
115
117
  |------|---------|----------|
116
118
  | **URL scan** | `penthera https://example.com` | TLS, fingerprinting, endpoint discovery, templates, CORS, cookies, JS CVEs, param discovery, OpenAPI/FastAPI probes, security headers, auth hardening |
117
- | **Repo scan** | `penthera --repo ./my-app` | API route discovery (Next.js, Express, Hono, Fastify), trust-boundary mapping, hardcoded-secret scanning |
119
+ | **Repo scan** | `penthera --repo ./my-app` | API route discovery (Next.js, Express, Hono, Fastify), trust-boundary mapping, hardcoded-secret scanning (including Anthropic, OpenAI, Gemini, Groq, Hugging Face and other AI provider keys) |
118
120
  | **Combined** | `penthera https://staging.app --repo .` | Black-box and white-box in one run |
119
121
  | **Interactive** | `penthera` *(no args)* | Guided wizard (URL plus optional repo scan, Markdown report) |
120
122
  | **Machine audit** | `penthera --machine` | macOS checks (keyloggers, persistence, rootkits) |
@@ -157,245 +159,6 @@ Findings map to the [OWASP Web Security Testing Guide](docs/owasp-wstg-coverage.
157
159
 
158
160
  ---
159
161
 
160
- ## Compliance: NIS2 and the cyberwetgeving
161
-
162
- The EU NIS2 directive and its national transpositions (the Cyberbeveiligingswet
163
- in the Netherlands, the NIS2-wet in Belgium) put a duty of care, the *zorgplicht*,
164
- on in-scope organisations. That duty is the set of risk-management measures in
165
- Article 21(2) of the directive. Most of them are organisational. Some are
166
- technical and testable, and that is the part Penthera helps with.
167
-
168
- Penthera does not make you compliant and does not certify anything. What it gives
169
- you is automated, repeatable technical evidence for the measures a scanner can
170
- honestly verify: the kind of artefact you can attach to a risk file, show an
171
- auditor, or run in CI so a regression shows up before it ships.
172
-
173
- | Art. 21(2) measure | What Penthera does | Coverage |
174
- |--------------------|--------------------|----------|
175
- | **(a)** Risk analysis & information-security policy | Findings, severity, and baseline history feed your risk analysis | Evidence |
176
- | **(b)** Incident handling | `--incident` drafts the 24h / 72h / 1-month reports against your authority's required content | Partial |
177
- | **(c)** Business continuity, backup, crisis management | Organisational; outside a scanner's reach | Out of scope |
178
- | **(d)** Supply-chain security | Known-CVE checks on JS dependencies, leaked credentials, a CycloneDX SBOM (`--sbom`), and passive supplier rating (`--suppliers`) | Partial |
179
- | **(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** |
180
- | **(f)** Assessing the effectiveness of measures | Baseline diff, re-scan-to-verify, and `--history` tracking of time-to-fix, ageing, and drift | Partial |
181
- | **(g)** Cyber hygiene & training | Remediation playbook + secure-defaults guidance (guidance, not a training programme) | Partial |
182
- | **(h)** Cryptography & encryption | TLS protocol/cipher/certificate audit, HSTS, cookie `Secure` (transport layer only) | Partial |
183
- | **(i)** HR security, **access control**, asset management | Access control tested: JWT, IDOR/BOLA, OAuth redirect, client-side-auth, trust-boundary mapping | Partial |
184
- | **(j)** Multi-factor authentication & secure communications | MFA probe (on the roadmap) | Planned |
185
-
186
- **Coverage key:** *Strong* = automated technical testing · *Partial* = part of the
187
- measure · *Evidence* = output feeds the measure, no direct test · *Planned* = on the
188
- roadmap · *Out of scope* = organisational or physical.
189
-
190
- In practice, Penthera today gives you solid automated evidence for the
191
- secure-development and vulnerability-handling duty (e), which is the technical
192
- heart of the zorgplicht, plus meaningful coverage of supply chain (d),
193
- cryptography in transit (h), access control (i), and effectiveness testing (f).
194
- That is not the whole duty of care, but it is a real, defensible start you can run
195
- today and re-run on every deploy.
196
-
197
- > **Not legal advice, not a certification, not a guarantee of compliance.**
198
- > Penthera automates the technical-testing part of the duty of care. The
199
- > directive's organisational duties (governance, risk-management policy,
200
- > incident-notification processes, supply-chain contracts, staff training) need
201
- > human process and, where it matters, review by a qualified advisor. For an
202
- > authoritative reading, consult your national authority (the NCSC in the
203
- > Netherlands, the CCB and its CyFun framework in Belgium) or a specialised jurist.
204
-
205
- Generate this mapping for your own scan with `--framework nis2`. It adds a
206
- per-run coverage table (which measures *this* scan actually exercised, driven by
207
- the probes that ran) to the terminal, Markdown, and JSON reports:
208
-
209
- ```bash
210
- penthera https://myapp.com --framework nis2 -o reports/scan.json
211
- ```
212
-
213
- ### Readiness report (clone → report in two commands)
214
-
215
- Go further than the mapping: combine the scan with a short offline
216
- self-assessment (the organisational measures a scanner can't test, like policies,
217
- backups, training, and MFA) into a full NIS2 readiness report. Every Article 21
218
- measure is marked `met / partial / gap / n-a`, with proportionality notes, your
219
- incident-reporting deadlines, and provenance on every piece of evidence.
220
-
221
- ```bash
222
- # 1. Create your self-assessment (offline, one-time). Fill in yes | partial | no | na.
223
- penthera --assessment-init assessment.json
224
-
225
- # 2. Scan + self-assessment → readiness report (writes reports/scan-readiness.md)
226
- penthera https://myapp.com --repo . --readiness --assessment assessment.json -o reports/scan.json
227
- ```
228
-
229
- The readiness report is **evidence and self-assessment tooling, not a
230
- certification**. It is provenance-linked (`source, method, collected_at,
231
- confidence`) and reproducible, and it lists exactly what only you can attest to.
232
- Jurisdiction specifics live in `lib/jurisdictions/nl.json` (NL today; BE and
233
- others are just added config).
234
-
235
- ### Answer a customer's security questionnaire
236
-
237
- The questionnaire blocking your deal, drafted from evidence you already have:
238
-
239
- ```bash
240
- # one question per line, pasted from the customer's form
241
- penthera https://myapp.com --readiness --assessment assessment.json --questionnaire questions.txt
242
- ```
243
-
244
- Each question is mapped to the Article 21 measure it belongs to and answered
245
- from the scan and your self-assessment, with the backing evidence cited.
246
- Anything Penthera can't map is marked `NEEDS HUMAN INPUT` instead of guessed.
247
- Review every answer before you send it.
248
-
249
- ### Policy pack
250
-
251
- Generate the proportionate written policies NIS2 expects, pre-filled from your
252
- readiness gaps and the NL incident deadlines:
253
-
254
- ```bash
255
- penthera --policy-pack ./policies --org "Your Company" --readiness --assessment assessment.json
256
- ```
257
-
258
- Writes editable Markdown starting points: information security, access control,
259
- incident response, backup/continuity, acceptable use, supplier security, and
260
- vulnerability management. **Templates, not legal advice.** Keep them short and
261
- used. A two-page policy that genuinely exists is what "proportionate" means.
262
-
263
- ### Incident-report helper
264
-
265
- NIS2 has hard reporting deadlines (24h early warning, 72h notification, 1-month
266
- final report). If something happens, get the timeline and draft reports:
267
-
268
- ```bash
269
- penthera --incident-init incident.json # fill in what happened (set aware_at)
270
- penthera --incident incident.json # writes incident-reports.md
271
- ```
272
-
273
- It computes your three deadlines from when you became aware, flags anything
274
- overdue, and drafts the early-warning, notification, and final report against
275
- the required content, with `NEEDS INPUT` where you still have to fill in facts.
276
- **These are drafts to help you meet the deadlines, not a substitute for your
277
- authority's official form.**
278
-
279
- ### Software bill of materials (SBOM)
280
-
281
- Emit a standard CycloneDX SBOM of your dependencies. It's what enterprises
282
- increasingly require of suppliers, and direct evidence for supply-chain security
283
- (measure d):
284
-
285
- ```bash
286
- penthera --repo . --sbom sbom.cdx.json
287
- ```
288
-
289
- Parses `package-lock.json` / `package.json` / `requirements.txt` offline (no
290
- network), with resolved versions and package URLs (purls).
291
-
292
- ### Remediation tracking (the audit loop)
293
-
294
- Closing findings matters more than finding them. Track them over time:
295
- time-to-fix, ageing of open items, and what changed since the last scan (evidence
296
- for measure f, effectiveness):
297
-
298
- ```bash
299
- penthera https://myapp.com --history reports/history.json -o reports/scan.json
300
- ```
301
-
302
- Each run folds the current findings into a small history keyed by a **stable
303
- fingerprint** (so a re-worded or re-counted finding isn't mistaken for a new
304
- one), then reports open vs resolved, median time-to-fix, and ageing buckets.
305
- Re-run on a cadence to watch the trend.
306
-
307
- ### Supplier rating (passive)
308
-
309
- Assess your suppliers' public security posture for supply-chain risk (measure d):
310
-
311
- ```bash
312
- printf 'supplier1.com\nsupplier2.com\n' > suppliers.txt
313
- penthera --suppliers suppliers.txt
314
- ```
315
-
316
- **Strictly passive and public-data-only.** One homepage GET (security headers),
317
- a TLS handshake (certificate), and public DNS lookups (SPF/DMARC/MX) per domain.
318
- No endpoint discovery, no auth probes, no intrusive testing. You get a
319
- red/amber/green rating per supplier. It's a screening aid for your own vendor
320
- risk assessment, **not** an authorised penetration test of the supplier.
321
-
322
- Because the mapping targets the NIS2 directive itself, it applies across the
323
- national transpositions; jurisdiction-specific profiles (NL, BE) are on the roadmap.
324
-
325
- ---
326
-
327
- ## Ethical use & authorization
328
-
329
- Penthera sends HTTP requests to the targets you give it. Some modes (`--deep`,
330
- `--fuzz`, `--all`) send attack payloads designed to find vulnerabilities. Use it
331
- responsibly.
332
-
333
- **You may use Penthera when:**
334
-
335
- - You **own** the target (your app, your server, your project).
336
- - You have **written authorization** from the system owner (email, ticket, signed scope).
337
- - You scan **localhost** or private lab environments you control.
338
- - You use it for **defensive security**: research, coursework, CI hardening, pre-release audits.
339
-
340
- **You must not use Penthera to:**
341
-
342
- - Scan systems you do not own or lack explicit permission to test.
343
- - Probe government, healthcare, financial, or third-party production systems without authorization.
344
- - Exfiltrate data, disrupt services, or bypass access controls.
345
- - Use findings to attack, extort, or harm anyone.
346
-
347
- > Before scanning any URL that is not localhost, confirm you have permission.
348
- > **When in doubt, do not scan.**
349
-
350
- Unauthorized security testing may violate computer-misuse laws (for example the US
351
- CFAA, the UK Computer Misuse Act, and EU national equivalents) and can result in
352
- criminal prosecution, civil liability, IP blocking, account termination, and
353
- academic or professional penalties.
354
-
355
- ---
356
-
357
- ## Disclaimer
358
-
359
- Penthera is provided for legitimate security research and defensive testing of
360
- systems you own or are authorized to test, and for that purpose only.
361
-
362
- - It is a research and self-assessment tool. Scan your own apps to find and fix
363
- weaknesses before attackers do.
364
- - The same capabilities that harden a system can cause harm if pointed at someone
365
- else's. Use it carefully and lawfully.
366
- - The author does not condone unauthorized or malicious use, and accepts no
367
- liability for any damage, loss, or legal consequence arising from use or misuse
368
- of this software.
369
- - The software is provided "as is", without warranty of any kind. See [LICENSE](LICENSE) (MIT).
370
- - You are solely responsible for ensuring every scan you run is authorized and
371
- legal in your jurisdiction.
372
-
373
- By using Penthera you accept these terms.
374
-
375
- ---
376
-
377
- ## Install
378
-
379
- The [one-line installer](#run-it) is the fastest path. To set it up manually:
380
-
381
- ```bash
382
- git clone https://github.com/danoszz/penthera.git
383
- cd penthera
384
- npm install
385
- npm link # exposes `penthera` and `penthera-scan` globally
386
- penthera --version
387
- ```
388
-
389
- Requirements: Node.js 18+ and npm. Optional: nuclei-templates for `--nuclei`, and
390
- Docker. To run it in a container:
391
-
392
- ```bash
393
- docker build -t penthera .
394
- docker run --rm penthera https://your-app.example
395
- ```
396
-
397
- ---
398
-
399
162
  ## Quick start
400
163
 
401
164
  ```bash
@@ -510,19 +273,100 @@ Fixes come from a [remediation playbook](skills/penthera/references/remediation.
510
273
 
511
274
  ---
512
275
 
276
+ ## Compliance: NIS2 and the cyberwetgeving
277
+
278
+ Shipping in the EU? NIS2 (the Cyberbeveiligingswet in the Netherlands, the
279
+ NIS2-wet in Belgium) puts a duty of care on in-scope organisations. Penthera
280
+ turns the technical, testable part of that duty into repeatable evidence, and
281
+ helps with the paperwork around it. Offline, no accounts.
282
+
283
+ | Command | What you get |
284
+ |---------|--------------|
285
+ | `--framework nis2` | Per-run Article 21(2) coverage table in every report |
286
+ | `--readiness --assessment a.json` | Full readiness report: every measure marked met / partial / gap |
287
+ | `--questionnaire questions.txt` | Customer security questionnaire drafted from your evidence |
288
+ | `--policy-pack ./policies` | Proportionate policy templates, prefilled from your gaps |
289
+ | `--incident incident.json` | 24h / 72h / 1-month incident report drafts and deadlines |
290
+ | `--sbom sbom.cdx.json` | CycloneDX software bill of materials |
291
+ | `--history history.json` | Time-to-fix, ageing, and drift across scans |
292
+ | `--suppliers suppliers.txt` | Passive red/amber/green rating of your suppliers |
293
+
294
+ **Full guide, the measure-by-measure coverage table, and the limits:**
295
+ [docs/compliance-nis2.md](docs/compliance-nis2.md).
296
+
297
+ > Readiness tooling, not a certification and not legal advice.
298
+
299
+ ---
300
+
513
301
  ## CI integration
514
302
 
515
303
  ```yaml
516
304
  # Scan a staging URL on a schedule and upload results to GitHub Security.
517
- # Set the PENTEST_STAGING_URL secret to enable. Full example: .github/workflows/scan.yml
518
- - run: npx penthera "$PENTEST_URL" --profile standard --sarif reports/scan.sarif
519
- - uses: github/codeql-action/upload-sarif@v3
305
+ - run: npx penthera@1.2.0 "$PENTEST_URL" --profile standard --sarif reports/scan.sarif
306
+ - uses: github/codeql-action/upload-sarif@v4
520
307
  with:
521
308
  sarif_file: reports/scan.sarif
522
309
  ```
523
310
 
524
- The repo's own CI (`.github/workflows/ci.yml`) runs the offline suites on Node 18,
525
- 20, and 22, and self-scans a mock vulnerable API on every push.
311
+ Copy the full workflow from
312
+ [`docs/examples/github-actions-scan.yml`](docs/examples/github-actions-scan.yml)
313
+ into your app's repo. Pin the Penthera version so a scheduled run never picks up
314
+ code you have not reviewed.
315
+
316
+ Penthera's own CI runs the full test suite on Node 22 and 24, self-scans a mock
317
+ vulnerable API on every push, and runs CodeQL and OpenSSF Scorecard.
318
+
319
+ ---
320
+
321
+ ## Ethical use & authorization
322
+
323
+ Penthera sends HTTP requests to the targets you give it. Some modes (`--deep`,
324
+ `--fuzz`, `--all`) send attack payloads designed to find vulnerabilities. Use it
325
+ responsibly.
326
+
327
+ **You may use Penthera when:**
328
+
329
+ - You **own** the target (your app, your server, your project).
330
+ - You have **written authorization** from the system owner (email, ticket, signed scope).
331
+ - You scan **localhost** or private lab environments you control.
332
+ - You use it for **defensive security**: research, coursework, CI hardening, pre-release audits.
333
+
334
+ **You must not use Penthera to:**
335
+
336
+ - Scan systems you do not own or lack explicit permission to test.
337
+ - Probe government, healthcare, financial, or third-party production systems without authorization.
338
+ - Exfiltrate data, disrupt services, or bypass access controls.
339
+ - Use findings to attack, extort, or harm anyone.
340
+
341
+ > Before scanning any URL that is not localhost, confirm you have permission.
342
+ > **When in doubt, do not scan.**
343
+
344
+ Unauthorized security testing may violate computer-misuse laws (for example the US
345
+ CFAA, the UK Computer Misuse Act, and EU national equivalents) and can result in
346
+ criminal prosecution, civil liability, IP blocking, account termination, and
347
+ academic or professional penalties.
348
+
349
+ ---
350
+
351
+ ## Install
352
+
353
+ The [one-line installer](#run-it) is the fastest path. To set it up manually:
354
+
355
+ ```bash
356
+ git clone https://github.com/danoszz/penthera.git
357
+ cd penthera
358
+ npm install
359
+ npm link # exposes `penthera` and `penthera-scan` globally
360
+ penthera --version
361
+ ```
362
+
363
+ Requirements: Node.js 22+ and npm. Optional: nuclei-templates for `--nuclei`, and
364
+ Docker. To run it in a container:
365
+
366
+ ```bash
367
+ docker build -t penthera .
368
+ docker run --rm penthera https://your-app.example
369
+ ```
526
370
 
527
371
  ---
528
372
 
@@ -547,9 +391,8 @@ npm run pentest:mock # offline tests against a local mock vuln API
547
391
  npm run mock-server # start the mock API on port 8765
548
392
  ```
549
393
 
550
- See the [roadmap](ROADMAP.md) for what is next and what is deliberately not planned.
551
-
552
- See the [roadmap](ROADMAP.md) for what is next and what is deliberately not
394
+ See the [roadmap](ROADMAP.md) for what is next, including auditing the skills,
395
+ MCP servers, and plugins in your AI toolchain, and what is deliberately not
553
396
  planned.
554
397
 
555
398
  See [CONTRIBUTING.md](CONTRIBUTING.md) for the project layout, how to add a probe
@@ -559,7 +402,7 @@ or template, the test matrix, and the release and publishing checklist.
559
402
 
560
403
  ## Status
561
404
 
562
- **v1.0, production-ready.** URL, repo, and machine scanning, scan profiles,
405
+ **v1.2, production-ready.** URL, repo, and machine scanning, NIS2 readiness tooling, scan profiles,
563
406
  Markdown, JSON, and SARIF reports, session-aware auth, JWT, IDOR, OAuth, and
564
407
  header probes, adaptive knowledge-graph probing, a plugins and templates API,
565
408
  Docker, CI, and the Agent Skill are all shipped. See the [CHANGELOG](CHANGELOG.md)
@@ -594,6 +437,26 @@ You are responsible for ensuring you are authorized to scan any target. See [Eth
594
437
 
595
438
  ---
596
439
 
440
+ ## Disclaimer
441
+
442
+ Penthera is provided for legitimate security research and defensive testing of
443
+ systems you own or are authorized to test, and for that purpose only.
444
+
445
+ - It is a research and self-assessment tool. Scan your own apps to find and fix
446
+ weaknesses before attackers do.
447
+ - The same capabilities that harden a system can cause harm if pointed at someone
448
+ else's. Use it carefully and lawfully.
449
+ - The author does not condone unauthorized or malicious use, and accepts no
450
+ liability for any damage, loss, or legal consequence arising from use or misuse
451
+ of this software.
452
+ - The software is provided "as is", without warranty of any kind. See [LICENSE](LICENSE) (MIT).
453
+ - You are solely responsible for ensuring every scan you run is authorized and
454
+ legal in your jurisdiction.
455
+
456
+ By using Penthera you accept these terms.
457
+
458
+ ---
459
+
597
460
  ## License & disclosure
598
461
 
599
462
  MIT. See [LICENSE](LICENSE). Report vulnerabilities in Penthera itself via
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,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.
@@ -0,0 +1,55 @@
1
+ # Example: scan your staging URL with Penthera on a schedule.
2
+ #
3
+ # Copy this file into YOUR app's repo as `.github/workflows/penthera.yml` and add
4
+ # a `PENTEST_STAGING_URL` repository secret. Only point it at a URL you own or are
5
+ # authorized to test. Findings appear under Security > Code scanning.
6
+ #
7
+ # Pin the Penthera version (and, ideally, the actions below to commit SHAs) so a
8
+ # scheduled run never picks up code you have not reviewed.
9
+
10
+ name: Penthera security scan
11
+
12
+ on:
13
+ workflow_dispatch:
14
+ schedule:
15
+ - cron: "0 6 * * 1" # weekly, Monday 06:00 UTC
16
+
17
+ permissions:
18
+ contents: read
19
+
20
+ jobs:
21
+ penthera:
22
+ runs-on: ubuntu-latest
23
+ permissions:
24
+ contents: read
25
+ security-events: write
26
+ steps:
27
+ - uses: actions/setup-node@v6
28
+ with:
29
+ node-version: 22
30
+
31
+ - name: Scan staging URL
32
+ env:
33
+ PENTEST_URL: ${{ secrets.PENTEST_STAGING_URL }}
34
+ run: |
35
+ mkdir -p reports
36
+ set +e
37
+ npx --yes penthera@1.2.0 "$PENTEST_URL" \
38
+ --profile standard \
39
+ -o reports/scan.json \
40
+ --sarif reports/scan.sarif
41
+ code=$?
42
+ set -e
43
+ # Exit 1 = critical or high findings (reported via SARIF); only 2 (scan error) fails the job.
44
+ if [ "$code" -eq 2 ]; then exit 2; fi
45
+
46
+ - uses: github/codeql-action/upload-sarif@v4
47
+ if: always()
48
+ with:
49
+ sarif_file: reports/scan.sarif
50
+
51
+ - uses: actions/upload-artifact@v7
52
+ if: always()
53
+ with:
54
+ name: penthera-reports
55
+ path: reports/
package/docs/index.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Penthera documentation
2
2
 
3
- Lightweight security scanner for developers, scan live URLs, local repos, or macOS machines from the CLI.
3
+ Security scanner for developers: scan live URLs, local repos, or macOS machines from your AI agent or the CLI.
4
4
 
5
5
  ## Getting started
6
6
 
@@ -33,8 +33,9 @@ Lightweight security scanner for developers, scan live URLs, local repos, or mac
33
33
  | Topic | Link |
34
34
  |-------|------|
35
35
  | OWASP WSTG mapping | [owasp-wstg-coverage.md](./owasp-wstg-coverage.md) |
36
- | GitHub Actions CI | `.github/workflows/ci.yml` (mock scan + SARIF) |
37
- | Staging scans | `.github/workflows/scan.yml`, set `PENTEST_STAGING_URL` secret |
36
+ | NIS2 compliance guide | [compliance-nis2.md](./compliance-nis2.md) |
37
+ | Scan your app in GitHub Actions | [examples/github-actions-scan.yml](./examples/github-actions-scan.yml), set `PENTEST_STAGING_URL` |
38
+ | Penthera's own CI | `.github/workflows/ci.yml` (full tests + mock scan), `codeql.yml`, `scorecard.yml` |
38
39
  | Docker | `docker build -t penthera . && docker run penthera https://localhost:3000` |
39
40
 
40
41
  ## Agent skill
@@ -21,7 +21,23 @@ const TEST_VALUE = /\b(?:placeholder|example|changeme|dummy|sample|redacted|inva
21
21
  const SECRET_PATTERNS = [
22
22
  { name: "AWS access key", pattern: /AKIA[0-9A-Z]{16}/, severity: "critical" },
23
23
  { name: "GitHub token", pattern: /ghp_[a-zA-Z0-9]{36,}/, severity: "critical" },
24
- { name: "OpenAI API key", pattern: /sk-[a-zA-Z0-9]{20,}/, severity: "critical" },
24
+ // AI provider keys. Vibecoded apps leak these more than any other credential,
25
+ // often via a NEXT_PUBLIC_/VITE_ variable or a committed notebook. Specific
26
+ // prefixes come first so a key is reported under its real provider.
27
+ { name: "Anthropic API key", pattern: /sk-ant-(?:api|admin)\d{2}-[A-Za-z0-9_-]{32,}/, severity: "critical" },
28
+ { name: "OpenRouter API key", pattern: /sk-or-v1-[a-f0-9]{64}/, severity: "critical" },
29
+ { name: "OpenAI API key", pattern: /sk-(?:proj|svcacct|admin)-[A-Za-z0-9_-]{20,}/, severity: "critical" },
30
+ { name: "OpenAI API key (legacy format)", pattern: /sk-[a-zA-Z0-9]{20,}/, severity: "critical" },
31
+ // AIza keys are also Firebase web keys, which are public by design, so this
32
+ // is high rather than critical: it matters if the key has Gemini/Vertex access.
33
+ { name: "Google API key (Gemini / Firebase / Maps)", pattern: /AIza[0-9A-Za-z_-]{35}/, severity: "high" },
34
+ { name: "Groq API key", pattern: /gsk_[A-Za-z0-9]{48,}/, severity: "critical" },
35
+ { name: "Hugging Face token", pattern: /hf_[A-Za-z0-9]{34,}/, severity: "critical" },
36
+ { name: "Replicate API token", pattern: /r8_[A-Za-z0-9]{37,}/, severity: "critical" },
37
+ { name: "xAI API key", pattern: /xai-[A-Za-z0-9]{70,}/, severity: "critical" },
38
+ { name: "Perplexity API key", pattern: /pplx-[A-Za-z0-9]{40,}/, severity: "critical" },
39
+ { name: "LangSmith API key", pattern: /lsv2_(?:pt|sk)_[a-f0-9]{32}_[a-f0-9]{10}/, severity: "high" },
40
+ { name: "Pinecone API key", pattern: /pcsk_[A-Za-z0-9_]{50,}/, severity: "high" },
25
41
  { name: "Stripe secret key", pattern: /sk_live_[a-zA-Z0-9]{20,}/, severity: "critical" },
26
42
  {
27
43
  name: "Generic API key assignment",
@@ -35,8 +51,8 @@ const SECRET_PATTERNS = [
35
51
  severity: "high",
36
52
  skipIf: TEST_VALUE,
37
53
  },
38
- { name: "Private key block", pattern: /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/, severity: "critical" },
39
54
  { name: "GCP/Firebase service account", pattern: /"private_key"\s*:\s*"-----BEGIN/, severity: "critical" },
55
+ { name: "Private key block", pattern: /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/, severity: "critical" },
40
56
  {
41
57
  name: "JWT secret assignment",
42
58
  pattern: /(?:jwt[_-]?secret|session[_-]?secret)\s*[:=]\s*['"][^'"\s]{8,}['"]/i,
@@ -48,6 +64,7 @@ const SECRET_PATTERNS = [
48
64
  const SCAN_EXTENSIONS = new Set([
49
65
  ".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs", ".py", ".rb", ".go", ".java",
50
66
  ".env", ".yaml", ".yml", ".json", ".toml", ".cfg", ".ini", ".sh",
67
+ ".ipynb", // notebooks are a classic place for a pasted provider key
51
68
  ]);
52
69
 
53
70
  function walkFiles(dir, files = []) {
@@ -69,9 +86,34 @@ function maskMatch(match) {
69
86
  return match.length <= 8 ? "***" : match.slice(0, 4) + "…" + match.slice(-4);
70
87
  }
71
88
 
89
+ /**
90
+ * Find secrets in a string of text (a source file, a JS bundle, a config).
91
+ * Returns one hit per rule: `{ name, severity, match }`, with the raw match so
92
+ * callers decide how to mask it. At most one rule fires per provider key: once a
93
+ * specific AI-provider rule matches a span, broader rules skip that span.
94
+ */
95
+ export function findSecretsInText(content) {
96
+ const hits = [];
97
+ const claimed = [];
98
+ for (const rule of SECRET_PATTERNS) {
99
+ const match = content.match(rule.pattern);
100
+ if (!match) continue;
101
+ // For assignment-style rules, test the placeholder/dummy filter against
102
+ // the assigned value only (not the whole `key = "value"` match), so a
103
+ // variable named e.g. `testToken` can't mask a real secret.
104
+ const value = (match[0].match(/['"]([^'"]+)['"]/) || [, match[0]])[1];
105
+ if (rule.skipIf && rule.skipIf.test(value)) continue;
106
+ const start = match.index;
107
+ const end = start + match[0].length;
108
+ if (claimed.some(([a, b]) => start < b && end > a)) continue;
109
+ claimed.push([start, end]);
110
+ hits.push({ name: rule.name, severity: rule.severity, match: match[0] });
111
+ }
112
+ return hits;
113
+ }
114
+
72
115
  export function scanSecrets(repoPath) {
73
116
  const findings = [];
74
- const seen = new Set();
75
117
 
76
118
  for (const filePath of walkFiles(repoPath)) {
77
119
  let content;
@@ -83,23 +125,11 @@ export function scanSecrets(repoPath) {
83
125
 
84
126
  const rel = relative(repoPath, filePath);
85
127
 
86
- for (const rule of SECRET_PATTERNS) {
87
- const match = content.match(rule.pattern);
88
- if (!match) continue;
89
- // For assignment-style rules, test the placeholder/dummy filter against
90
- // the assigned value only (not the whole `key = "value"` match), so a
91
- // variable named e.g. `testToken` can't mask a real secret.
92
- const value = (match[0].match(/['"]([^'"]+)['"]/) || [, match[0]])[1];
93
- if (rule.skipIf && rule.skipIf.test(value)) continue;
94
-
95
- const key = `${rel}::${rule.name}`;
96
- if (seen.has(key)) continue;
97
- seen.add(key);
98
-
128
+ for (const hit of findSecretsInText(content)) {
99
129
  findings.push({
100
- severity: rule.severity,
101
- title: `${rule.name} in source`,
102
- description: `Possible secret in \`${rel}\`: \`${maskMatch(match[0])}\``,
130
+ severity: hit.severity,
131
+ title: `${hit.name} in source`,
132
+ description: `Possible secret in \`${rel}\`: \`${maskMatch(hit.match)}\``,
103
133
  url: rel,
104
134
  category: "secrets",
105
135
  source: "secret-scan",
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "penthera",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
- "description": "Lightweight security scanner for web apps — scan URLs or repos from the CLI",
5
+ "description": "Security scanner for web apps and AI-built code: scan URLs and repos from Claude Code, Cursor or the CLI. OWASP-mapped, SARIF output, NIS2 evidence. Authorized testing only.",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/danoszz/penthera.git"
@@ -21,7 +21,6 @@
21
21
  "lib",
22
22
  "skills",
23
23
  "docs",
24
- "scripts/package-skill.sh",
25
24
  "pentest.config.example.js",
26
25
  "CHANGELOG.md",
27
26
  "LICENSE",
@@ -34,7 +33,7 @@
34
33
  "./wstg": "./lib/owasp-wstg.js"
35
34
  },
36
35
  "engines": {
37
- "node": ">=18"
36
+ "node": ">=22"
38
37
  },
39
38
  "scripts": {
40
39
  "test": "vitest run --reporter=verbose",
@@ -42,7 +41,6 @@
42
41
  "scan:full": "node bin/scan.js",
43
42
  "pentest": "vitest run --reporter=verbose",
44
43
  "pentest:watch": "vitest --reporter=verbose",
45
- "pentest:static": "vitest run tests/pentest-static.test.js --reporter=verbose",
46
44
  "pentest:live": "vitest run tests/pentest-live.test.js --reporter=verbose",
47
45
  "pentest:adaptive": "vitest run tests/pentest-adaptive.test.js --reporter=verbose",
48
46
  "pentest:recon": "vitest run tests/pentest-recon.test.js --reporter=verbose",
@@ -68,9 +66,9 @@
68
66
  ],
69
67
  "license": "MIT",
70
68
  "devDependencies": {
71
- "vitest": "^3.1.1"
69
+ "vitest": "^5.0.5"
72
70
  },
73
71
  "dependencies": {
74
- "yaml": "^2.8.3"
72
+ "yaml": "^2.9.1"
75
73
  }
76
74
  }
@@ -9,11 +9,11 @@ description: >
9
9
  against a baseline; or find hardcoded secrets before deploy. Do NOT use for targets
10
10
  without explicit authorization.
11
11
  license: MIT
12
- compatibility: Node.js 18+, network access to target, Penthera CLI (npm link or node bin/penthera.js from repo). macOS only for --machine.
12
+ compatibility: Node.js 22+, network access to target, Penthera CLI (npm link or node bin/penthera.js from repo). macOS only for --machine.
13
13
  allowed-tools: "Bash(node:*) Bash(npm:*) Bash(penthera:*) Read Write Edit"
14
14
  metadata:
15
15
  author: danoszz
16
- version: 1.1.1
16
+ version: 1.2.0
17
17
  category: security
18
18
  ---
19
19
 
@@ -44,59 +44,21 @@ Output includes: new findings count, resolved count, unchanged count. Use for we
44
44
 
45
45
  ## GitHub Actions
46
46
 
47
- **Penthera CI** (`.github/workflows/ci.yml`) runs on every push:
47
+ **Penthera's own CI** (`.github/workflows/ci.yml`) runs on every push and PR:
48
48
 
49
- 1. Unit + mock server tests
50
- 2. Skill preflight check
51
- 3. Scan against the local mock API → JSON + SARIF
52
- 4. `validate-report.mjs` on the JSON output
53
- 5. SARIF upload to GitHub Security tab
49
+ 1. The full test suite (`npm test`) on Node 22 and 24
50
+ 2. A scan of the deliberately vulnerable mock API, producing JSON + SARIF
51
+ 3. `validate-report.mjs` on the JSON output, plus the skill preflight
54
52
 
55
- For **your staging URL**, copy `.github/workflows/scan.example.yml` to `scan.yml`:
53
+ The mock-server SARIF is kept as a build artifact, not uploaded to code scanning,
54
+ because those findings are about the test fixture, not the repo.
56
55
 
57
- ```yaml
58
- name: Security Scan
59
-
60
- on:
61
- workflow_dispatch:
62
- schedule:
63
- - cron: "0 6 * * 1"
64
-
65
- jobs:
66
- penthera:
67
- runs-on: ubuntu-latest
68
- steps:
69
- - uses: actions/checkout@v4
70
-
71
- - uses: actions/setup-node@v4
72
- with:
73
- node-version: 20
74
- cache: npm
75
-
76
- - run: npm ci
77
-
78
- - name: Scan staging URL
79
- env:
80
- PENTEST_URL: ${{ secrets.PENTEST_STAGING_URL }}
81
- run: |
82
- node bin/penthera.js "$PENTEST_URL" \
83
- --profile standard \
84
- -o reports/scan.json \
85
- --sarif reports/scan.sarif
86
-
87
- - uses: github/codeql-action/upload-sarif@v3
88
- if: always()
89
- with:
90
- sarif_file: reports/scan.sarif
91
-
92
- - uses: actions/upload-artifact@v4
93
- if: always()
94
- with:
95
- name: penthera-reports
96
- path: reports/
97
- ```
98
-
99
- Store `PENTEST_STAGING_URL` as a repository secret.
56
+ **For your own staging URL**, copy
57
+ [`docs/examples/github-actions-scan.yml`](https://github.com/danoszz/penthera/blob/main/docs/examples/github-actions-scan.yml)
58
+ into your app's repo as `.github/workflows/penthera.yml` and add a
59
+ `PENTEST_STAGING_URL` repository secret. It runs `npx penthera@<version>` on a
60
+ weekly schedule and uploads SARIF to your Security tab. Pin the version so a
61
+ scheduled run never picks up code you have not reviewed.
100
62
 
101
63
  ## Agent reporting workflow
102
64
 
@@ -21,7 +21,7 @@ penthera https://myapp.com --verbose --timeout 30000
21
21
 
22
22
  **Symptoms:** `node: command not found` or engine error
23
23
 
24
- **Solution:** Install Node.js 18+ from [nodejs.org](https://nodejs.org/). Verify:
24
+ **Solution:** Install Node.js 22+ from [nodejs.org](https://nodejs.org/). Verify:
25
25
 
26
26
  ```bash
27
27
  node -v # should be v18.x or higher
@@ -20,14 +20,14 @@ ok() {
20
20
  echo "preflight: OK: $*"
21
21
  }
22
22
 
23
- # Node.js 18+
23
+ # Node.js 22+
24
24
  if ! command -v node >/dev/null 2>&1; then
25
- fail "Node.js not found. Install Node.js 18+ from https://nodejs.org/"
25
+ fail "Node.js not found. Install Node.js 22+ from https://nodejs.org/"
26
26
  fi
27
27
 
28
28
  NODE_MAJOR=$(node -e "process.stdout.write(String(process.version).replace(/^v/, '').split('.')[0])")
29
- if [ "$NODE_MAJOR" -lt 18 ]; then
30
- fail "Node.js $(node -v) is too old. Requires Node.js 18+."
29
+ if [ "$NODE_MAJOR" -lt 22 ]; then
30
+ fail "Node.js $(node -v) is too old. Requires Node.js 22+."
31
31
  fi
32
32
  ok "Node.js $(node -v)"
33
33
 
@@ -1,16 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Package the Agent Skill for Claude.ai upload or GitHub Releases.
3
- set -euo pipefail
4
-
5
- ROOT="$(cd "$(dirname "$0")/.." && pwd)"
6
- OUT="$ROOT/dist"
7
- ZIP="$OUT/penthera-skill.zip"
8
-
9
- mkdir -p "$OUT"
10
- rm -f "$ZIP"
11
-
12
- cd "$ROOT/skills"
13
- zip -r "$ZIP" penthera/ -x "*.DS_Store"
14
-
15
- echo "Created $ZIP"
16
- ls -lh "$ZIP"