penthera 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CHANGELOG.md +227 -0
  2. package/README.md +148 -575
  3. package/bin/scan.js +19 -0
  4. package/docs/commands/README.md +23 -0
  5. package/docs/commands/action.md +21 -0
  6. package/docs/commands/agents.md +38 -0
  7. package/docs/commands/deps.md +115 -0
  8. package/docs/commands/fix.md +53 -0
  9. package/docs/commands/hook.md +69 -0
  10. package/docs/commands/install.md +53 -0
  11. package/docs/commands/scan.md +100 -0
  12. package/docs/commands/skill.md +58 -0
  13. package/docs/compare/README.md +25 -0
  14. package/docs/compare/github.md +60 -0
  15. package/docs/compare/semgrep.md +54 -0
  16. package/docs/compare/snyk.md +62 -0
  17. package/docs/compare/socket.md +78 -0
  18. package/docs/compare/zap.md +59 -0
  19. package/docs/compliance-cra.md +141 -0
  20. package/docs/compliance-nis2.md +3 -1
  21. package/docs/dashboard.md +244 -0
  22. package/docs/evidence.md +8 -4
  23. package/docs/examples/github-actions-deps.yml +4 -4
  24. package/docs/examples/github-actions-scan.yml +4 -4
  25. package/docs/index.md +11 -2
  26. package/docs/internal-audit.md +83 -0
  27. package/docs/marketing.md +151 -0
  28. package/docs/plan.md +101 -0
  29. package/docs/privacy.md +38 -0
  30. package/docs/references.bib +10 -0
  31. package/docs/references.md +1 -0
  32. package/docs/research/socket-positioning-notes.md +183 -0
  33. package/docs/use-cases/README.md +14 -0
  34. package/docs/use-cases/agencies.md +71 -0
  35. package/docs/use-cases/ai-coding-teams.md +81 -0
  36. package/docs/use-cases/eu-sme-compliance.md +68 -0
  37. package/docs/use-cases/open-source-maintainers.md +75 -0
  38. package/docs/use-cases/solo-founder.md +65 -0
  39. package/lib/blackbox/email-dns.js +5 -0
  40. package/lib/compliance/cra.js +525 -0
  41. package/lib/compliance/questionnaire.js +4 -2
  42. package/lib/deps/capabilities.js +49 -12
  43. package/lib/deps/index.js +314 -30
  44. package/lib/deps/license.js +329 -0
  45. package/lib/deps/lockfile.js +29 -3
  46. package/lib/deps/popular-pypi.json +1 -0
  47. package/lib/deps/python/capabilities.js +447 -0
  48. package/lib/deps/python/index.js +234 -0
  49. package/lib/deps/python/lockfiles.js +359 -0
  50. package/lib/deps/python/source.js +396 -0
  51. package/lib/deps/python/toml.js +219 -0
  52. package/lib/deps/report.js +54 -6
  53. package/lib/deps/source.js +149 -24
  54. package/lib/deps/spdx.json +1 -0
  55. package/lib/deps/typosquat.js +24 -13
  56. package/lib/fix/apply.js +83 -19
  57. package/lib/hook/index.js +345 -0
  58. package/lib/hook/policy.js +62 -0
  59. package/lib/install/index.js +59 -18
  60. package/lib/install/policy.js +27 -7
  61. package/lib/jurisdictions/nl.json +9 -0
  62. package/lib/memory/repo-suppress.js +11 -3
  63. package/lib/references.json +77 -0
  64. package/lib/remediation/index.js +9 -0
  65. package/lib/retirejs.js +69 -17
  66. package/lib/rules/regex-guard.js +368 -0
  67. package/lib/templates.js +13 -6
  68. package/lib/tls.js +7 -0
  69. package/lib/whitebox/aibom.js +384 -0
  70. package/lib/whitebox/frameworks.js +8 -5
  71. package/lib/whitebox/sbom.js +45 -10
  72. package/lib/whitebox/secrets.js +31 -8
  73. package/package.json +8 -3
  74. package/skills/penthera/SKILL.md +55 -8
  75. package/src/cli/deps.js +81 -6
  76. package/src/cli/evidence.js +14 -4
  77. package/src/cli/hook.js +131 -0
  78. package/src/cli/install.js +14 -5
  79. package/src/cli/report-outputs.js +25 -2
  80. package/src/cli/ui.js +89 -0
  81. package/src/cli.js +81 -11
  82. package/src/report/audience.js +10 -10
  83. package/src/report/evidence.js +33 -14
  84. package/src/report/footer.js +11 -0
  85. package/src/report/markdown.js +6 -3
  86. package/src/report/md-escape.js +16 -0
  87. package/src/reporter.js +1 -0
  88. package/src/scan-url.js +5 -1
  89. package/src/ui/api/data.js +309 -0
  90. package/src/ui/api/demo.js +464 -0
  91. package/src/ui/api/routes.js +134 -0
  92. package/src/ui/api/runs.js +243 -0
  93. package/src/ui/api/summarize.js +451 -0
  94. package/src/ui/security.js +153 -0
  95. package/src/ui/server.js +186 -0
  96. package/src/ui/static/app.css +723 -0
  97. package/src/ui/static/favicon.svg +1 -0
  98. package/src/ui/static/index.html +57 -0
  99. package/src/ui/static/js/api.js +29 -0
  100. package/src/ui/static/js/app.js +138 -0
  101. package/src/ui/static/js/charts.js +207 -0
  102. package/src/ui/static/js/copy.js +79 -0
  103. package/src/ui/static/js/dom.js +120 -0
  104. package/src/ui/static/js/format.js +42 -0
  105. package/src/ui/static/js/theme.js +10 -0
  106. package/src/ui/static/js/typer.js +99 -0
  107. package/src/ui/static/js/views/ai.js +68 -0
  108. package/src/ui/static/js/views/deps.js +96 -0
  109. package/src/ui/static/js/views/evidence.js +115 -0
  110. package/src/ui/static/js/views/findings.js +165 -0
  111. package/src/ui/static/js/views/history.js +92 -0
  112. package/src/ui/static/js/views/overview.js +184 -0
  113. package/src/ui/static/js/views/run.js +349 -0
  114. package/src/ui/static/js/views/settings.js +67 -0
package/docs/evidence.md CHANGED
@@ -14,7 +14,7 @@ organisation through a channel other than the bundle itself (their security
14
14
  page, an email you can authenticate, or in person).
15
15
 
16
16
  ```bash
17
- npx penthera evidence verify ./evidence --allowed-signers allowed_signers
17
+ npx penthera evidence verify ./evidence --allowed-signers allowed_signers --require-signature
18
18
  ```
19
19
 
20
20
  Exit code `0` means every file matches the manifest and the manifest was signed
@@ -55,9 +55,13 @@ symlinked files.
55
55
  manifest. It does not prove that the scan was run against the system you
56
56
  care about, or with the options you expect. Read the manifest's `command`,
57
57
  `target`, `scan` and `git` fields, and the auditor report's scope section.
58
- - Without `--allowed-signers`, `verify` still checks that the signature is
59
- valid but cannot tell you whose key made it. It reports
60
- `valid-unknown-signer` as a warning.
58
+ - A signed bundle only verifies with `--allowed-signers`. Without it,
59
+ `verify` cannot tell whose key made the signature (anyone can re-sign a
60
+ rewritten manifest with their own key), so it fails with status
61
+ `unverified`. A manifest that declares a signature with no `.sig` file
62
+ fails too.
63
+ - An unsigned bundle passes with an `UNSIGNED` warning. Pass
64
+ `--require-signature` (with `--allowed-signers`) to make it fail.
61
65
  - A scan is a snapshot. The bundle is evidence of what Penthera saw at
62
66
  `generated_at`, not a statement that the system is secure.
63
67
 
@@ -35,18 +35,18 @@ jobs:
35
35
  env:
36
36
  PENTHERA_VERSION: latest # pin, e.g. 1.3.0
37
37
  steps:
38
- - uses: actions/checkout@v7
38
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
39
39
  with:
40
40
  path: head
41
41
  persist-credentials: false
42
42
 
43
- - uses: actions/checkout@v7
43
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
44
44
  with:
45
45
  ref: ${{ github.event.pull_request.base.sha }}
46
46
  path: base
47
47
  persist-credentials: false
48
48
 
49
- - uses: actions/setup-node@v6
49
+ - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
50
50
  with:
51
51
  node-version: 22
52
52
 
@@ -89,7 +89,7 @@ jobs:
89
89
  gh pr comment "$PR" --repo "$REPO" --body-file "$body"
90
90
  fi
91
91
 
92
- - uses: actions/upload-artifact@v7
92
+ - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
93
93
  if: always()
94
94
  with:
95
95
  name: penthera-deps
@@ -24,7 +24,7 @@ jobs:
24
24
  contents: read
25
25
  security-events: write
26
26
  steps:
27
- - uses: actions/setup-node@v6
27
+ - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
28
28
  with:
29
29
  node-version: 22
30
30
 
@@ -34,7 +34,7 @@ jobs:
34
34
  run: |
35
35
  mkdir -p reports
36
36
  set +e
37
- npx --yes penthera@1.4.0 "$PENTEST_URL" \
37
+ npx --yes penthera@1.6.0 "$PENTEST_URL" \
38
38
  --profile standard \
39
39
  -o reports/scan.json \
40
40
  --sarif reports/scan.sarif
@@ -43,12 +43,12 @@ jobs:
43
43
  # Exit 1 = critical or high findings (reported via SARIF); only 2 (scan error) fails the job.
44
44
  if [ "$code" -eq 2 ]; then exit 2; fi
45
45
 
46
- - uses: github/codeql-action/upload-sarif@v4
46
+ - uses: github/codeql-action/upload-sarif@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
47
47
  if: always()
48
48
  with:
49
49
  sarif_file: reports/scan.sarif
50
50
 
51
- - uses: actions/upload-artifact@v7
51
+ - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
52
52
  if: always()
53
53
  with:
54
54
  name: penthera-reports
package/docs/index.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Penthera documentation
2
2
 
3
- The routine work of a security team, on your own machine: app scanning, dependency and install checks, an AI-tooling audit, a local fix loop, and auditor-grade reports. Offline, deterministic, free.
3
+ Open-source security for the apps and AI systems you ship: check it before you deploy, vet what you build with, keep watching after you ship. Offline, deterministic, free.
4
4
 
5
5
  ## Getting started
6
6
 
@@ -8,8 +8,11 @@ The routine work of a security team, on your own machine: app scanning, dependen
8
8
  |-------|-------------|
9
9
  | [README](../README.md) | Install, quick start, ethical use |
10
10
  | [Run it](../README.md#run-it) | Install + first-time wizard (`penthera` with no args) |
11
+ | [Use cases](./use-cases/README.md) | By situation: [before launch](./use-cases/solo-founder.md), [AI-coding teams](./use-cases/ai-coding-teams.md), [maintainers](./use-cases/open-source-maintainers.md), [EU SMEs](./use-cases/eu-sme-compliance.md), [agencies](./use-cases/agencies.md) |
12
+ | [Comparisons](./compare/README.md) | Using Penthera with [Socket](./compare/socket.md), [Snyk](./compare/snyk.md), [GitHub](./compare/github.md), [ZAP](./compare/zap.md), [Semgrep](./compare/semgrep.md) |
13
+ | [Commands](./commands/README.md) | One page per command: [scan](./commands/scan.md), [agents](./commands/agents.md), [deps](./commands/deps.md), [install](./commands/install.md), [fix](./commands/fix.md), [hook](./commands/hook.md), [action](./commands/action.md), [skill](./commands/skill.md) |
11
14
  | [CHANGELOG](../CHANGELOG.md) | Version history |
12
- | [Roadmap](../ROADMAP.md) and [release plan](./release-plan.md) | What ships next, in what order |
15
+ | [Roadmap](../ROADMAP.md), [plan to v2.0](./plan.md) and [release plan](./release-plan.md) | What ships next, in what order, and how quality is measured |
13
16
 
14
17
  ## Scanning
15
18
 
@@ -36,6 +39,10 @@ The routine work of a security team, on your own machine: app scanning, dependen
36
39
  | OWASP WSTG mapping | [owasp-wstg-coverage.md](./owasp-wstg-coverage.md) |
37
40
  | NIS2 compliance guide | [compliance-nis2.md](./compliance-nis2.md) |
38
41
  | Local loop and learning store | [local-loop.md](./local-loop.md) |
42
+ | CRA reporting and AI-BOM | [compliance-cra.md](./compliance-cra.md) |
43
+ | Reports and evidence bundles | [evidence.md](./evidence.md) |
44
+ | Privacy, network use, `--offline` | [privacy.md](./privacy.md) |
45
+ | Local dashboard (`penthera ui`) | [dashboard.md](./dashboard.md) |
39
46
  | Scan your app in GitHub Actions | [examples/github-actions-scan.yml](./examples/github-actions-scan.yml), set `PENTEST_STAGING_URL` |
40
47
  | Penthera's own CI | `.github/workflows/ci.yml` (full tests + mock scan), `codeql.yml`, `scorecard.yml` |
41
48
  | Docker | `docker build -t penthera . && docker run penthera https://localhost:3000` |
@@ -48,6 +55,8 @@ The routine work of a security team, on your own machine: app scanning, dependen
48
55
  | Cited research and standards | [references.md](./references.md) ([BibTeX](./references.bib)) |
49
56
  | Accuracy benchmark (`npm run bench`) | [bench/README.md](../bench/README.md), latest [results](../bench/RESULTS.md) |
50
57
  | Citing Penthera | [CITATION.cff](../CITATION.cff) |
58
+ | Refresh the README's numbers | `npm run docs:numbers` (`-- --tests` to count tests) |
59
+ | How Socket positions itself, and what Penthera took from it | [research/socket-positioning-notes.md](./research/socket-positioning-notes.md) |
51
60
 
52
61
  ## Agent skill
53
62
 
@@ -0,0 +1,83 @@
1
+ # Internal testing audit
2
+
3
+ Penthera is a security tool, so its own release bar has to be easy to check.
4
+ The internal audit is that bar: one command that runs every check a release
5
+ needs and writes the result to [`audit/REPORT.md`](../audit/REPORT.md).
6
+
7
+ ```bash
8
+ npm run audit:internal
9
+ ```
10
+
11
+ It exits 1 if any gate fails. A release is only tagged from a commit whose
12
+ audit passed (see the release checklist in
13
+ [CONTRIBUTING.md](../CONTRIBUTING.md#cutting-a-release)). The same audit runs
14
+ every Monday in CI ([`internal-audit.yml`](../.github/workflows/internal-audit.yml))
15
+ so drift shows up between releases.
16
+
17
+ ## The gates
18
+
19
+ | Gate | What it checks | Fails when |
20
+ |---|---|---|
21
+ | Test suite | `npm test`, with the live suites pointed at the bundled mock server (`PENTEST_BASE_URL`) | Any test fails |
22
+ | Benchmark | `npm run bench` (offline, seeded) against the **Total** row of the committed [`bench/RESULTS.md`](../bench/RESULTS.md) | Precision or recall drops by more than 0.02, or any request leaves loopback |
23
+ | Self-scan | `penthera --repo .`, `penthera agents .`, `penthera deps .`, and `penthera hook run` on a copy of the tree with every file staged | A high or critical finding is not in [`audit/baseline.json`](../audit/baseline.json), or a baseline entry has no reason |
24
+ | CLI smoke | `--help` for every subcommand, plus a fixture run of each: a mock-server scan in every report format, `agents` on a clean and a malicious fixture, `deps`, `install --dry-run`, `fix --plan`, an evidence bundle and its `verify`, one `agent --loop` iteration (every diff declined), `hook install/uninstall` and `memory show/verify` | Any command exits with an unexpected code or produces the wrong output |
25
+ | Packaging | `npm pack --dry-run` | A file outside the allow-list ships, anything that looks like tests, fixtures, reports or secrets ships, or the package exceeds its size budget |
26
+ | Offline | A full `--all --adaptive --offline` scan of the mock server | The report's `egress` field shows any third-party host |
27
+ | Supply chain | `npm audit --omit=dev`; sha512 integrity for every lockfile entry; install scripts in runtime dependencies (via `penthera deps --prod`); every `uses:` in `.github/workflows`, `action.yml` and `docs/examples` | Any vulnerability, missing integrity, runtime install script, typosquat, action not pinned by commit SHA, or workflow without top-level `permissions:` |
28
+ | Versions and docs | The version in `package.json`, the lockfile, the plugin manifests, `SKILL.md` and `CITATION.cff`; a `CHANGELOG.md` section for it; every relative link in `README.md` and `docs/` | Any mismatch or broken link |
29
+
30
+ Everything runs on this machine against this repo or `127.0.0.1`. The only
31
+ network use is `npm audit` (the npm registry) and, with `--with-docker`,
32
+ pulling the Juice Shop image.
33
+
34
+ ## Options
35
+
36
+ | Flag | Effect |
37
+ |---|---|
38
+ | `--only bench,versions-docs` | Run some gates (ids: `tests`, `bench`, `self-scan`, `smoke`, `packaging`, `offline`, `supply-chain`, `versions-docs`, `docker`). Prints results and writes nothing. |
39
+ | `--update-baseline` | Rewrite `audit/baseline.json` from this run's self-scan. New entries get a `TODO` reason, which fails the gate until a person writes one. |
40
+ | `--with-docker` | Add the optional heavier targets below. |
41
+ | `--no-write` | Leave `audit/` untouched. |
42
+
43
+ Command output from each run goes to `audit/logs/` (ignored by git, uploaded
44
+ with the report as the CI artifact).
45
+
46
+ ## Optional targets: Juice Shop and crAPI
47
+
48
+ `--with-docker` starts [OWASP Juice Shop](https://github.com/juice-shop/juice-shop)
49
+ on `127.0.0.1:3300`, scans it with the benchmark's `juice-shop` target, and
50
+ removes the container. Pin the image for reproducible numbers with
51
+ `JUICE_SHOP_IMAGE=bkimminich/juice-shop@sha256:...`.
52
+
53
+ [OWASP crAPI](https://github.com/OWASP/crAPI) needs its own Docker Compose
54
+ stack, so the audit scans it only if it is already running at `CRAPI_URL`
55
+ (default `http://127.0.0.1:8888`). Start it with the project's compose file
56
+ first.
57
+
58
+ Both are skipped by default. Neither has labelled ground truth yet, so the
59
+ report lists their finding counts and shows precision and recall as
60
+ "unlabelled" until they are labelled in `bench/ground-truth.json` (see
61
+ [bench/README.md](../bench/README.md#optional-targets)).
62
+
63
+ ## When a gate fails
64
+
65
+ - **Tests, smoke, offline, packaging, supply chain, versions:** fix the cause.
66
+ These describe what Penthera promises its users.
67
+ - **Benchmark:** if the change is intended (a new rule that trades a little
68
+ precision for recall, a relabelled target), run `npm run bench`, commit the
69
+ new `bench/RESULTS.md`, and say why in the PR.
70
+ - **Self-scan:** fix the finding. If it is a deliberate fixture, run
71
+ `--update-baseline` and replace the `TODO` with the reason, so the
72
+ acceptance is reviewed like any other change.
73
+
74
+ Thresholds (the 0.02 benchmark drop, the package size budget, the allow-list)
75
+ live at the top of [`scripts/audit/run.mjs`](../scripts/audit/run.mjs).
76
+ Change them in their own PR, never to make a release pass.
77
+
78
+ ## The report
79
+
80
+ `audit/REPORT.md` has a pass/fail table with the numbers behind each gate,
81
+ the changes since the previous report (read from a data block at the end of
82
+ the previous file), and per-gate details. Commit it with each release so the
83
+ history of the gate lives in git.
@@ -0,0 +1,151 @@
1
+ # Marketing automation
2
+
3
+ Penthera is maintained by one person with no budget, so the routine parts of
4
+ getting the word out run by themselves. The rule is simple: **automation drafts
5
+ and collects, a person posts.**
6
+
7
+ - No social-media API tokens and no auto-posting. Every post is copied and
8
+ pasted by a person.
9
+ - No analytics, tracking pixels or telemetry. The numbers come from public
10
+ pages and APIs that anyone can read.
11
+ - Every workflow is SHA-pinned and has least-privilege permissions. Workflows
12
+ that write only open issues (`issues: write`). Only the demo-media workflow
13
+ pushes, and only to a `media/*` branch for a PR.
14
+ - Nothing can embarrass the project without a person in the loop: drafts land
15
+ in issues, job summaries, a collapsed release section or a PR.
16
+
17
+ ## What runs
18
+
19
+ | Automation | When | Output | Workflow / script |
20
+ |------------|------|--------|-------------------|
21
+ | Release post drafts | Every `v*` tag | LinkedIn (~600 chars) and X/Bluesky (≤280) drafts in the job summary and under "Share this release" in the GitHub Release (the read-only publish job drafts; the release job, which has no checkout, only attaches them) | `publish.yml` → `scripts/marketing/release-post.mjs` |
22
+ | Incident radar | Daily 06:00 UTC | One issue per new malware advisory for a popular npm package (max 5 per run); every other new advisory as a comment on one standing batch issue | `incident-radar.yml` → `scripts/marketing/incident-radar.mjs` |
23
+ | Product-led footer | Every report | One line at the end of Markdown reports, audience reports, the PR comment and the evidence bundle README | `src/report/footer.js` |
24
+ | Demo media | Every published release, on demand | A PR with re-rendered GIFs from `assets/tapes/*.tape` | `demo-media.yml` |
25
+ | Monthly stats | 1st of the month, 07:00 UTC | Issue "Penthera this month: <Month YYYY>" with a table, month-over-month changes and a draft post | `monthly-stats.yml` → `scripts/marketing/monthly-stats.mjs` |
26
+ | Contributor flywheel | When an issue closes | If fewer than 3 `good first issue` issues are open: one reminder issue listing `TODO(good-first-issue)` markers | `contributor-flywheel.yml` → `scripts/marketing/contributor-flywheel.mjs` |
27
+
28
+ ### Release post drafts
29
+
30
+ Built from the CHANGELOG section for the tag: the first sentence of the
31
+ summary paragraph, then three bullets (bold-titled entries first, Added before
32
+ Fixed), then the npm and GitHub release links. The X/Bluesky post is counted
33
+ the way Bluesky counts (every character), which also fits X. Run it locally:
34
+
35
+ ```bash
36
+ node scripts/marketing/release-post.mjs --tag v1.4.0
37
+ ```
38
+
39
+ The npm release is staged, not live, until a maintainer approves it. Post after
40
+ approving.
41
+
42
+ ### Incident radar
43
+
44
+ Reads `/advisories?type=malware&ecosystem=npm` and `ecosystem=pip` from the
45
+ public GitHub Advisory Database, published in the last 48 hours. About 20
46
+ malware advisories appear every day and most are typosquats nobody installs,
47
+ so by default only advisories for packages in `lib/deps/popular-packages.json`
48
+ get their own issue (with the checklist and a draft "check if you're affected
49
+ with one command" post). The rest go into one standing issue, "Incident radar:
50
+ other new malware advisories", one comment per run. Set `mode: all` on a manual
51
+ run to open one issue per advisory instead (still at most 5).
52
+
53
+ State is the issues themselves: an advisory counts as seen when its GHSA id is
54
+ in an `incident-radar` issue or a radar batch comment from the look-back
55
+ window. There is no state file or branch.
56
+
57
+ Each issue's checklist: add the IOCs to a rule pack in `lib/rules/` (or the
58
+ deps rules until rule packs exist), verify `penthera deps` flags a fixture
59
+ lockfile with the package, then post. Do not post before detection is
60
+ verified.
61
+
62
+ ### Product-led footer
63
+
64
+ Human-readable Markdown ends with:
65
+
66
+ > Checked by Penthera, open-source security for the apps and AI systems you ship · https://github.com/danoszz/penthera
67
+
68
+ It is never added to SARIF or JSON. Users turn it off with `--no-footer` (CLI)
69
+ or `footer: "false"` (GitHub Action).
70
+
71
+ ### Demo media
72
+
73
+ Tapes live in `assets/tapes/` (see its README). The workflow installs
74
+ [VHS](https://github.com/charmbracelet/vhs) with `charmbracelet/vhs-action`,
75
+ renders every tape, uploads the GIFs as a workflow artifact and, unless it is a
76
+ dry run, opens a PR from a `media/*` branch. It needs **Settings → Actions →
77
+ General → Allow GitHub Actions to create and approve pull requests**. PRs
78
+ opened by `GITHUB_TOKEN` do not trigger CI; close and reopen the PR to run it.
79
+
80
+ ### Monthly stats
81
+
82
+ Public sources only:
83
+
84
+ - npm downloads for the calendar month (`api.npmjs.org`);
85
+ - stars and forks at run time and PRs merged that month (GitHub API);
86
+ - installs from the public skills.sh skill page;
87
+ - dependent repositories from GitHub's dependents page.
88
+
89
+ Each page is parsed strictly: if the markup changed, the number shows as
90
+ `n/a`, never a guess. The issue keeps its numbers in a hidden JSON comment,
91
+ and next month's issue reads them back for the change column.
92
+
93
+ ### Contributor flywheel
94
+
95
+ Write `TODO(good-first-issue): <what to do>` in a comment next to a small,
96
+ well-scoped change. When the open `good first issue` count drops below 3, the
97
+ reminder issue lists these markers with links. Turn one or two into issues,
98
+ then close the reminder. Closing the reminder itself never triggers a new one.
99
+
100
+ ## Dry runs
101
+
102
+ Every scheduled workflow has a `workflow_dispatch` trigger whose `dry-run`
103
+ input defaults to **on**: it prints what it would open to the job summary and
104
+ writes nothing.
105
+
106
+ | Workflow | Dry run |
107
+ |----------|---------|
108
+ | Incident radar | Actions → Incident radar → Run workflow (`dry-run` checked; optional `mode`, `since-hours`) |
109
+ | Monthly stats | Actions → Monthly stats → Run workflow (`dry-run` checked; optional `month`, e.g. `2026-09`) |
110
+ | Contributor flywheel | Actions → Contributor flywheel → Run workflow (`dry-run` checked) |
111
+ | Demo media | Actions → Demo media → Run workflow (`dry-run` checked: GIFs as an artifact, no PR) |
112
+ | Release posts | `node scripts/marketing/release-post.mjs --tag vX.Y.Z` locally |
113
+
114
+ From a terminal with the GitHub CLI:
115
+
116
+ ```bash
117
+ gh workflow run incident-radar.yml -f dry-run=true
118
+ ```
119
+
120
+ The scripts also run locally with `--dry-run` (they use your `gh` login for
121
+ reads only):
122
+
123
+ ```bash
124
+ node scripts/marketing/incident-radar.mjs --repo danoszz/penthera --dry-run
125
+ ```
126
+
127
+ ## Turning things off
128
+
129
+ - **One workflow:** Actions → the workflow → "…" → Disable workflow. Or delete
130
+ its file.
131
+ - **Release drafts:** remove the "Draft release posts" and "Keep the drafts" steps (publish job) and the two share steps (release job) from `publish.yml`.
132
+ - **Footer, for one run:** `--no-footer` or `footer: "false"` in the Action.
133
+ To drop it for everyone, make `footerLines()` in `src/report/footer.js`
134
+ return `[]`.
135
+ - **Labels** (`incident-radar`, `monthly-stats`, `contributor-flywheel`) are
136
+ created on the first real run and can be deleted afterwards.
137
+
138
+ ## The weekly 30-minute routine
139
+
140
+ 1. **Incident radar (10 min).** Open the `incident-radar` issues. For a
141
+ popular package: add the IOCs, check `penthera deps` flags a fixture, post
142
+ the draft (LinkedIn, X, Bluesky), close the issue. Skim the batch issue for
143
+ names you recognise, then close it if it is long.
144
+ 2. **A clip (5 min).** If a demo-media PR is open, watch the GIFs, merge, and
145
+ post the best one as a short clip (Instagram, LinkedIn, X).
146
+ 3. **Contributors (5 min).** If the flywheel reminder is open, turn one or two
147
+ markers into `good first issue` issues. Reply to new contributors.
148
+ 4. **Release (5 min, when there is one).** Approve the staged npm package, then
149
+ copy the "Share this release" drafts from the GitHub Release, edit, post.
150
+ 5. **Monthly (5 min, first week).** Fill in "what shipped" in the stats issue,
151
+ post it, close it.
package/docs/plan.md ADDED
@@ -0,0 +1,101 @@
1
+ # Plan to v2.0
2
+
3
+ How Penthera gets from "many features, shipped fast" to a tool a team can rely
4
+ on without thinking about it. Written 2026-10-19. The [roadmap](../ROADMAP.md)
5
+ says *what* ships; this says *how well*, and in what order.
6
+
7
+ Penthera is maintained part-time alongside a PhD, so the plan is built around
8
+ small, finishable pieces and automation that keeps quality from depending on
9
+ anyone's free evenings.
10
+
11
+ ## The problem we solve
12
+
13
+ Teams ship apps and AI agents faster than anyone can secure them. Penthera is
14
+ the open-source security team for teams that don't have one, in three moments:
15
+
16
+ 1. **Before you deploy.** Test the app on localhost or a preview URL, fix what
17
+ it finds on a local branch, and prove the fix.
18
+ 2. **What you build with.** Vet dependencies, installs, and AI tooling (skills,
19
+ MCP servers, hooks) before they get access to your machine and credentials.
20
+ 3. **After you ship.** Keep watching your own production for drift, and produce
21
+ the reports and evidence that customers and regulators ask for.
22
+
23
+ All of it local, deterministic, and free. Anything that doesn't serve one of
24
+ these three moments is out of scope.
25
+
26
+ ## Milestones
27
+
28
+ | Release | Theme | Done when |
29
+ |---------|-------|-----------|
30
+ | v1.5.0 | AI apps and compliance | Tagged with the pre-commit hook, license policy, CRA helper and AI-BOM; held back from npm and shipped inside 1.6.0 |
31
+ | v1.6.0 | Hardening and the rest of AI apps | Released with the 360 security review fixes, Python `deps`, the internal audit gate and the dashboard. Signed rule packs follow once the maintainer's key is set up |
32
+ | v1.7.0 | Before you deploy | `penthera ship` and preview-deployment scanning work on a real Vercel and Netlify project |
33
+ | v1.8.0 | After you ship | `penthera watch` runs weekly on three real production apps for a month without a false alarm anyone complained about |
34
+ | v2.0.0 | Stable | Public interfaces frozen (CLI flags, JSON report schema, policy file, rule-pack format), documented, and covered by compatibility tests |
35
+
36
+ ## What "perfect" means, concretely
37
+
38
+ Quality is measured, not felt. Each item below has a gate that the internal
39
+ audit (`npm run audit:internal`) or CI checks automatically.
40
+
41
+ ### 1. Accuracy
42
+ - Benchmark precision and recall never drop between releases (the audit fails
43
+ on a drop of more than 0.02).
44
+ - Expand the benchmark beyond the mock server: OWASP Juice Shop, crAPI, a
45
+ deliberately vulnerable Next.js app, and fixture sets for `agents` and `deps`.
46
+ - Every false positive reported by a user becomes a regression test before it
47
+ is fixed.
48
+
49
+ ### 2. First-run experience
50
+ - From `npx penthera` to a useful result in under two minutes on a fresh Next.js
51
+ or Express app, measured on a clean machine each release.
52
+ - Every error message says what happened and what to do next. No stack traces
53
+ for expected failures.
54
+ - Works on macOS, Linux, and Windows (add a Windows CI job; the symlinked Cursor
55
+ skill and path handling are the known risks).
56
+
57
+ ### 3. Documentation
58
+ - The README stays a front page: problem, what it does, install, trust. Feature
59
+ detail moves into a docs site (`docs/`), one page per command, each with a
60
+ copy-paste example and its real output.
61
+ - Every command's `--help` and its docs page are generated from or tested
62
+ against the same source, so they never drift.
63
+
64
+ ### 4. Stability of interfaces
65
+ - A versioned JSON schema for reports, the policy file, and rule packs, checked
66
+ in CI.
67
+ - A deprecation policy: one minor release of warnings before anything breaks.
68
+
69
+ ### 5. Security of Penthera itself
70
+ - Self-scan clean on every release (`--repo`, `agents`, `deps`, the dashboard).
71
+ - Anything that parses untrusted input (packages, configs, scan responses, rule
72
+ packs) has a fuzz or hostile-input test. The ReDoS fixed in `deps` is the
73
+ reason why.
74
+ - An external review before v2.0: ask an academic security group or a volunteer
75
+ from the OWASP community to look at the fix loop, the install firewall, and
76
+ the dashboard, the three parts that write files or run commands.
77
+
78
+ ### 6. Real-world proof
79
+ - Three pilot projects (friendly teams, a student project, one of the
80
+ maintainer's own) run Penthera before each deploy for a month. Their feedback
81
+ sets the priorities for the next release.
82
+ - Publish what Penthera found and fixed, with permission, as short case studies.
83
+
84
+ ## Working rhythm
85
+
86
+ - One release every two to three weeks, each with a single theme.
87
+ - The internal audit runs weekly and before every tag; a red audit blocks the
88
+ release.
89
+ - Issues labelled `good first issue` are kept stocked (three to five open at a
90
+ time), so contributors can help without needing the maintainer's time.
91
+ - Anything that needs judgement (a new probe that sends payloads, anything
92
+ touching third parties) gets a short design note in `docs/` before code.
93
+
94
+ ## Explicitly not doing
95
+
96
+ - A hosted service, telemetry, or an update channel that fetches rules at run
97
+ time.
98
+ - Offensive tooling for targets the user does not own, and attack-payload
99
+ generation for AI systems.
100
+ - An LLM as the judge of what is malicious. Models may help write fixes,
101
+ locally, and always labelled as such.
@@ -0,0 +1,38 @@
1
+ # Privacy and network use
2
+
3
+ Penthera has no telemetry. It does not collect, store, or transmit any usage data, analytics, or personal information to the author or any third party. It does not phone home.
4
+
5
+ - **Scan traffic goes to the target you specify.** Auth tokens passed via `--auth-bearer`, `--auth-cookie`, or `PENTHERA_*` env vars are sent only to that target.
6
+ - **Every request identifies itself** as `penthera/<version> (+https://github.com/danoszz/penthera)`, so a target's operator can tell where the traffic comes from. Override it with `--user-agent`.
7
+ - **Reports stay local.** Output is written to `reports/` (gitignored) on your machine.
8
+ - **Every scan lists the third-party hosts it contacted** besides the target, in the terminal summary and in the report's "Network egress" section.
9
+
10
+ Without `--offline`, a scan may contact these third parties:
11
+
12
+ | Host | When | What it learns |
13
+ |---|---|---|
14
+ | `raw.githubusercontent.com`, `cdn.jsdelivr.net` | Retire.js check (standard and deep profiles) | That someone downloaded the public database. Nothing about your target. |
15
+ | `crt.sh`, `web.archive.org`, `otx.alienvault.com` | `--recon` only | The target domain. |
16
+ | CDN hosts serving your page's `<script src>` | Retire.js check, when a script's version can only be read from its contents | That someone fetched that public script. |
17
+
18
+ ### Fully local: `--offline`
19
+
20
+ ```bash
21
+ penthera http://localhost:3000 --offline
22
+ ```
23
+
24
+ With `--offline`, the HTTP client refuses every host except the scan target. A redirect may only move to a host on the target's registrable domain (`example.com` to `www.example.com`). In practice:
25
+
26
+ - **Recon is skipped.** No queries go to crt.sh, the Wayback Machine or OTX.
27
+ - **The Retire.js database comes from the snapshot vendored in the package** (`lib/data/retire-jsrepository.json.gz`), refreshed at each release by `scripts/refresh-retire-db.mjs`. The source URL, fetch date and SHA-256 sit next to it in `retire-jsrepository.meta.json`, so you can check it against upstream. Online scans fall back to the snapshot too when the live download fails.
28
+ - **Third-party scripts are not fetched.** A refused request is listed in the report as an inconclusive probe, not passed over silently.
29
+ - **`--suppliers` is refused**, because it rates third-party domains.
30
+ - **DNS:** reaching a remote target needs DNS. For non-local targets, the email-authentication check also looks up the target domain's SPF/DMARC/MX records through your system resolver. A `localhost` or IP target needs no DNS at all.
31
+
32
+ Penthera never fetches rules or code at runtime: templates, wordlists and the vulnerability database ship in the package.
33
+
34
+ ### Inconclusive probes
35
+
36
+ When a probe's requests time out, fail, or are refused by `--offline`, the scan records `{ probe, url, message }` in `result.errors`. The terminal summary shows "N probes inconclusive", and the Markdown report lists each one. A probe that could not run is not a clean result.
37
+
38
+ You are responsible for ensuring you are authorized to scan any target. See [Ethical use](../README.md#ethical-use--authorization).
@@ -537,3 +537,13 @@
537
537
  urldate = {2026-10-19},
538
538
  note = {standards}
539
539
  }
540
+
541
+ @misc{spdxlicenselist,
542
+ shorthand = {SPDX License List},
543
+ author = {{SPDX Workgroup, The Linux Foundation}},
544
+ title = {{SPDX} License List},
545
+ howpublished = {Standardized short identifiers for open source licenses, used by package manifests and SBOMs},
546
+ url = {https://spdx.org/licenses/},
547
+ urldate = {2026-10-19},
548
+ note = {standards}
549
+ }
@@ -75,3 +75,4 @@ Every entry was checked against the publisher or standards body (Crossref DOI me
75
75
  - `oasis2023sarif`: Fanning, Michael C.; Golding, Laurence J. (2023). *Static Analysis Results Interchange Format (SARIF) Version 2.1.0 Plus Errata 01*. OASIS Standard incorporating Approved Errata, 28 August 2023. <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html>
76
76
  - `eu2022nis2`: European Parliament; Council of the European Union (2022). *Directive (EU) 2022/2555 of the European Parliament and of the Council of 14 December 2022 on measures for a high common level of cybersecurity across the Union, amending Regulation (EU) No 910/2014 and Directive (EU) 2018/1972, and repealing Directive (EU) 2016/1148 (NIS 2 Directive)*. Official Journal of the European Union, L 333, 27.12.2022, p. 80. <https://eur-lex.europa.eu/eli/dir/2022/2555/oj>
77
77
  - `mitrecwe`: The MITRE Corporation. *CWE: Common Weakness Enumeration*. Community-developed list of software and hardware weakness types, sponsored by CISA. <https://cwe.mitre.org/>
78
+ - `spdxlicenselist`: SPDX Workgroup, The Linux Foundation. *SPDX License List*. Standardized short identifiers for open source licenses, used by package manifests and SBOMs. <https://spdx.org/licenses/>