penthera 1.7.0 → 1.8.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 +315 -0
- package/README.md +139 -76
- package/SECURITY.md +8 -6
- package/docs/examples/pr-dependency-review.yml +1 -1
- package/lib/adaptive-probes.js +3 -3
- package/lib/advisories/evidence.js +248 -0
- package/lib/advisories/index.js +228 -0
- package/lib/advisories/products.js +83 -0
- package/lib/advisories/versions.js +65 -0
- package/lib/agents/hidden-content.js +122 -11
- package/lib/agents/index.js +26 -6
- package/lib/agents/lock.js +33 -5
- package/lib/agents/rules.js +33 -4
- package/lib/compliance/index.js +4 -4
- package/lib/crawler.js +12 -15
- package/lib/deps/capabilities.js +21 -5
- package/lib/deps/findings.js +309 -0
- package/lib/deps/index.js +13 -272
- package/lib/deps/iocs.js +74 -0
- package/lib/deps/lockfile.js +3 -8
- package/lib/deps/online.js +8 -3
- package/lib/deps/popular-packages.json.gz +0 -0
- package/lib/deps/popular-pypi.json.gz +0 -0
- package/lib/deps/python/capabilities.js +16 -3
- package/lib/deps/python/index.js +5 -1
- package/lib/deps/python/lockfiles.js +11 -5
- package/lib/deps/python/source.js +75 -26
- package/lib/deps/python/toml.js +15 -3
- package/lib/deps/source.js +28 -9
- package/lib/deps/typosquat.js +30 -5
- package/lib/fix/apply.js +253 -34
- package/lib/fix/index.js +7 -4
- package/lib/fix/model.js +25 -5
- package/lib/fix/util.js +6 -2
- package/lib/fuzzer.js +3 -3
- package/lib/git.js +65 -0
- package/lib/hook/index.js +157 -73
- package/lib/hook/policy.js +10 -4
- package/lib/install/index.js +4 -7
- package/lib/machine.js +0 -13
- package/lib/memory/store.js +11 -4
- package/lib/nuclei-loader.js +0 -25
- package/lib/owasp-wstg.js +4 -0
- package/lib/plugins.js +1 -1
- package/lib/provenance.js +1 -0
- package/lib/recon.js +12 -31
- package/lib/references.json +224 -0
- package/lib/retirejs.js +4 -4
- package/lib/rules/advisories/advisories.json +186 -0
- package/lib/rules/advisories/eol.json +413 -0
- package/lib/rules/advisories/pack.json +20 -0
- package/lib/rules/advisories/pack.json.sig +6 -0
- package/lib/rules/allowed_signers +13 -0
- package/lib/rules/core/agent-exec.json +29 -0
- package/lib/rules/core/agent-instructions.json +17 -0
- package/lib/rules/core/pack.json +30 -0
- package/lib/rules/core/pack.json.sig +6 -0
- package/lib/rules/core/secrets.json +161 -0
- package/lib/rules/core/templates.json +52 -0
- package/lib/rules/index.js +107 -0
- package/lib/rules/ioc-2025-2026/files.json +43 -0
- package/lib/rules/ioc-2025-2026/hosts.json +35 -0
- package/lib/rules/ioc-2025-2026/pack.json +25 -0
- package/lib/rules/ioc-2025-2026/pack.json.sig +6 -0
- package/lib/rules/ioc-2025-2026/packages.json +1048 -0
- package/lib/rules/pack.js +452 -0
- package/lib/rules/regex-guard.js +81 -9
- package/lib/rules/sshsig.js +205 -0
- package/lib/templates.js +24 -20
- package/lib/tmp.js +21 -0
- package/lib/whitebox/frameworks.js +1 -1
- package/lib/whitebox/secrets.js +117 -41
- package/package.json +12 -4
- package/scripts/rules/build-pack.mjs +80 -0
- package/scripts/rules/check-bundled.mjs +30 -0
- package/scripts/rules/sign-pack.sh +32 -0
- package/skills/penthera/SKILL.md +16 -15
- package/src/agent/git.js +147 -14
- package/src/agent/loop.js +165 -46
- package/src/cli/agent.js +11 -5
- package/src/cli/deps.js +0 -2
- package/src/cli/detect.js +14 -7
- package/src/cli/fix.js +26 -8
- package/src/cli/hook.js +2 -1
- package/src/cli/onboarding.js +14 -6
- package/src/cli/report-outputs.js +1 -0
- package/src/cli/rules.js +144 -0
- package/src/cli/ui.js +43 -10
- package/src/cli.js +24 -1
- package/src/report/evidence.js +105 -21
- package/src/report/footer.js +1 -1
- package/src/report/md-escape.js +8 -0
- package/src/scan-repo.js +6 -0
- package/src/scan-url.js +11 -3
- package/src/ui/api/data.js +39 -6
- package/src/ui/api/routes.js +44 -11
- package/src/ui/api/runs.js +32 -7
- package/src/ui/security.js +20 -23
- package/src/ui/server.js +52 -27
- package/src/ui/static/app.css +583 -213
- package/src/ui/static/favicon.svg +1 -1
- package/src/ui/static/fonts/DepartureMono-LICENSE.txt +93 -0
- package/src/ui/static/fonts/DepartureMono-Regular.woff2 +0 -0
- package/src/ui/static/index.html +5 -4
- package/src/ui/static/js/api.js +114 -6
- package/src/ui/static/js/app.js +81 -42
- package/src/ui/static/js/center.js +246 -133
- package/src/ui/static/js/charts.js +99 -18
- package/src/ui/static/js/copy.js +68 -9
- package/src/ui/static/js/dialog.js +152 -0
- package/src/ui/static/js/left.js +23 -60
- package/src/ui/static/js/menubar.js +170 -0
- package/src/ui/static/js/onboarding.js +104 -0
- package/src/ui/static/js/panels.js +186 -0
- package/src/ui/static/js/right.js +69 -188
- package/src/ui/static/js/state.js +36 -5
- package/src/ui/static/logo.svg +10 -0
- package/src/ui/static/mark.svg +3 -0
- package/src/utils/http.js +162 -13
- package/docs/METHODOLOGY.md +0 -375
- package/docs/commands/README.md +0 -24
- package/docs/commands/action.md +0 -21
- package/docs/commands/agents.md +0 -38
- package/docs/commands/deps.md +0 -115
- package/docs/commands/fix.md +0 -53
- package/docs/commands/hook.md +0 -69
- package/docs/commands/init.md +0 -114
- package/docs/commands/install.md +0 -53
- package/docs/commands/scan.md +0 -100
- package/docs/commands/skill.md +0 -58
- package/docs/compare/README.md +0 -25
- package/docs/compare/github.md +0 -60
- package/docs/compare/semgrep.md +0 -54
- package/docs/compare/snyk.md +0 -62
- package/docs/compare/socket.md +0 -78
- package/docs/compare/zap.md +0 -59
- package/docs/compliance-cra.md +0 -141
- package/docs/compliance-nis2.md +0 -168
- package/docs/dashboard.md +0 -187
- package/docs/evidence.md +0 -162
- package/docs/funding/due-diligence-checklist.md +0 -79
- package/docs/funding/nlnet-ngi-zero-draft.md +0 -273
- package/docs/funding/partner-one-pager.md +0 -84
- package/docs/funding/sponsor-tiers.md +0 -81
- package/docs/index.md +0 -81
- package/docs/internal-audit.md +0 -83
- package/docs/local-loop.md +0 -276
- package/docs/marketing.md +0 -151
- package/docs/owasp-wstg-coverage.md +0 -79
- package/docs/plan.md +0 -102
- package/docs/privacy.md +0 -38
- package/docs/references.bib +0 -559
- package/docs/references.md +0 -119
- package/docs/release-plan.md +0 -228
- package/docs/research/socket-positioning-notes.md +0 -183
- package/docs/use-cases/README.md +0 -14
- package/docs/use-cases/agencies.md +0 -71
- package/docs/use-cases/ai-coding-teams.md +0 -81
- package/docs/use-cases/eu-sme-compliance.md +0 -68
- package/docs/use-cases/open-source-maintainers.md +0 -75
- package/docs/use-cases/solo-founder.md +0 -65
- package/lib/deps/popular-packages.json +0 -1
- package/lib/deps/popular-pypi.json +0 -1
package/docs/local-loop.md
DELETED
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
# The local loop
|
|
2
|
-
|
|
3
|
-
Penthera v1.4 adds a loop that runs entirely on your machine: scan, fix on a
|
|
4
|
-
local branch, verify, learn, repeat. Nothing in it talks to a Penthera server,
|
|
5
|
-
because there isn't one. This page covers the loop itself (`penthera agent
|
|
6
|
-
--loop`) and the part that makes it improve over time: the local learning store.
|
|
7
|
-
|
|
8
|
-
## The agent loop
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
penthera agent --loop --repo . --url http://localhost:3000 -o reports/loop.json
|
|
12
|
-
penthera agent --loop --repo . --run-tests # also run the project's test script
|
|
13
|
-
penthera agent --loop --repo . --auto low # auto-apply only recipes memory has proven
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
One iteration:
|
|
17
|
-
|
|
18
|
-
1. **Scan** the repo (white-box scan plus the fix recipes' static probes) and,
|
|
19
|
-
with `--url`, the running app.
|
|
20
|
-
2. **Triage**: drop findings suppressed in memory or `penthera.suppress.json`,
|
|
21
|
-
and rank the rest with memory (severity always first).
|
|
22
|
-
3. **Plan**: a deterministic recipe first. The local model is asked only with
|
|
23
|
-
`--fix-model <name>`, and only for findings no recipe matches.
|
|
24
|
-
4. **Patch** on a local branch `penthera/fix-<yyyymmdd-hhmm>`, created from the
|
|
25
|
-
current HEAD when the run starts.
|
|
26
|
-
5. **Verify**: re-scan. A fix passes when the findings it targeted are gone from
|
|
27
|
-
the repo scan, no new finding appeared, and, with `--run-tests`, the
|
|
28
|
-
project's `test` script exits 0 (`node --run test`).
|
|
29
|
-
6. **Learn**: `scan`, `fix_applied` and `fix_verified` or `fix_failed` go to
|
|
30
|
-
memory.
|
|
31
|
-
7. **Commit** each verified fix on its own, with a message that lists the
|
|
32
|
-
findings, the recipe, and how it was verified. A fix that fails is reverted
|
|
33
|
-
(`git checkout` of the files it changed; files it created are removed).
|
|
34
|
-
|
|
35
|
-
Plans are applied one at a time and re-planned against the tree after each
|
|
36
|
-
fix, since two recipes can touch the same file. The loop repeats until nothing
|
|
37
|
-
fixable is left, `--max-iterations` (default 5) is reached, or you stop it.
|
|
38
|
-
A fix you decline (`n`) or that fails verification is not proposed again in
|
|
39
|
-
the same run; `s` skips it until the next iteration.
|
|
40
|
-
|
|
41
|
-
A live URL serves the code it was started with, so a fix to response headers
|
|
42
|
-
shows up in the URL scan only after the app restarts. The loop counts those as
|
|
43
|
-
`liveRemaining` in the fix's `verify` block, not as a failure; the repo scan
|
|
44
|
-
decides whether the fix landed.
|
|
45
|
-
|
|
46
|
-
### What it guarantees
|
|
47
|
-
|
|
48
|
-
Each of these has a test in `tests/agent.test.js`.
|
|
49
|
-
|
|
50
|
-
- **No remote operations.** All git goes through `src/agent/git.js`, an
|
|
51
|
-
allow-list of local subcommands (`rev-parse`, `symbolic-ref`, `status`,
|
|
52
|
-
`diff`, `log`, `show-ref`, `checkout`, `add`, `commit`, `config --get`).
|
|
53
|
-
`push`, `fetch`, `pull`, `remote`, `clone`, `ls-remote`, `submodule` and
|
|
54
|
-
everything else are refused before a process starts, as are URL arguments
|
|
55
|
-
and `-c` overrides. Every call also runs with `protocol.allow=never` and
|
|
56
|
-
`GIT_NO_LAZY_FETCH=1`, so no transport can work. The loop never runs `gh`
|
|
57
|
-
and never opens a pull request.
|
|
58
|
-
- **The repo's hooks never run.** Git runs with `core.hooksPath=/dev/null`,
|
|
59
|
-
`core.fsmonitor=false` and `commit --no-verify`: a scanned repo can ship hooks
|
|
60
|
-
that run anything.
|
|
61
|
-
- **Network.** The whole run is one W1 egress context with `--offline` always
|
|
62
|
-
on: the URL target is the only host. Loopback is added only when
|
|
63
|
-
`--fix-model` is set, and the model client then accepts only
|
|
64
|
-
`http://127.0.0.1:11434`.
|
|
65
|
-
- **Clean start.** A dirty tree (untracked files included), a detached HEAD, a
|
|
66
|
-
repo without commits, or a directory that is not a git repo is refused.
|
|
67
|
-
`--allow-dirty` lets the loop start anyway, but it never patches a file that
|
|
68
|
-
had uncommitted changes.
|
|
69
|
-
- **Approval.** Every diff is shown, secrets masked, with
|
|
70
|
-
`[y]es / [n]o / [s]kip / [q]uit`. When input runs out (piped stdin), the
|
|
71
|
-
answer is `n`. `--auto low` applies a diff without asking only when it comes
|
|
72
|
-
from a recipe (not the model), its confidence is not `needs-human-review`,
|
|
73
|
-
memory's chain verifies, and memory shows that recipe with at least 3
|
|
74
|
-
verified fixes and no failures for this framework.
|
|
75
|
-
- **Interruption.** The first Ctrl-C lets the fix in flight finish: it is
|
|
76
|
-
verified and committed, or reverted. The loop then stops on the fix branch,
|
|
77
|
-
which holds only verified commits. A second Ctrl-C reverts the fix in flight
|
|
78
|
-
at once and exits.
|
|
79
|
-
|
|
80
|
-
`--run-tests` runs the project's own code, so it can do anything that code
|
|
81
|
-
does, including network calls. It is off by default for that reason.
|
|
82
|
-
|
|
83
|
-
### The loopRun object
|
|
84
|
-
|
|
85
|
-
`-o <file>` writes `{ "loopRun": { ... } }`. W6's report timeline renders it.
|
|
86
|
-
Every step carries `startedAt` and `durationMs`.
|
|
87
|
-
|
|
88
|
-
```js
|
|
89
|
-
{
|
|
90
|
-
schema: "penthera.loopRun/1",
|
|
91
|
-
id, startedAt, finishedAt, durationMs,
|
|
92
|
-
target: { repo, url },
|
|
93
|
-
branch: { base, baseSha, fix }, // base is null on a detached HEAD
|
|
94
|
-
options: { maxIterations, auto: "off" | "low", runTests, fixModel, offline: true, allowDirty },
|
|
95
|
-
stopReason: "no-fixable-findings" | "declined-or-failed-left" | "max-iterations"
|
|
96
|
-
| "user-quit" | "interrupted" | "error",
|
|
97
|
-
error, // message when stopReason is "error"
|
|
98
|
-
findings: { initial, final }, // non-info, unsuppressed
|
|
99
|
-
totals: { iterations, planned, applied, verified, failed, declined, skipped, commits: [sha] },
|
|
100
|
-
memory: { projectId, chain: "ok" | "broken", eventsAppended },
|
|
101
|
-
iterations: [{
|
|
102
|
-
n, startedAt, durationMs,
|
|
103
|
-
findings: { total, suppressed, fixable },
|
|
104
|
-
steps: [{ step: "scan" | "triage" | "plan", startedAt, durationMs }],
|
|
105
|
-
fixes: [{
|
|
106
|
-
planId, recipe, title, framework, source: "recipe" | "local-model", confidence,
|
|
107
|
-
files, findings: [{ fid, title, severity, category }],
|
|
108
|
-
memory: { verified, failed } | null, // recipe history when the plan was ranked
|
|
109
|
-
decision: "approved" | "auto" | "declined" | "skipped" | "quit" | "obsolete",
|
|
110
|
-
status: "verified" | "failed" | "not-applied",
|
|
111
|
-
reason, commit,
|
|
112
|
-
verify: { targetedRemaining, newFindings, liveRemaining, tests: { ok, exitCode, durationMs } | null } | null,
|
|
113
|
-
testOutput, // tail of the test output, only when tests failed
|
|
114
|
-
steps: [{ step: "approve" | "patch" | "learn" | "verify" | "commit" | "revert", startedAt, durationMs }],
|
|
115
|
-
}],
|
|
116
|
-
}],
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
`obsolete` means an earlier fix in the same iteration already covered the plan.
|
|
121
|
-
The same object, without `testOutput` and with finding ids in place of finding
|
|
122
|
-
objects, is appended to memory as a `loop_run` event.
|
|
123
|
-
|
|
124
|
-
Exit codes: `0` nothing fixable left, `1` fixable findings remain (declined,
|
|
125
|
-
failed, or out of iterations), `2` the loop could not start (dirty tree,
|
|
126
|
-
detached HEAD, bad options), `130` interrupted.
|
|
127
|
-
|
|
128
|
-
## The learning store
|
|
129
|
-
|
|
130
|
-
Scans run with `--memory` record what happened, and the next scan uses that
|
|
131
|
-
record to rank and suppress findings.
|
|
132
|
-
|
|
133
|
-
```bash
|
|
134
|
-
penthera --repo . --memory # scan, apply memory, record this scan
|
|
135
|
-
penthera memory show # what memory knows about this project
|
|
136
|
-
penthera memory verify # check the hash chain
|
|
137
|
-
penthera memory suppress <id> --reason "set by the CDN" [--expires 2027-01-01]
|
|
138
|
-
penthera memory forget # delete this project's memory
|
|
139
|
-
penthera memory forget --all # delete all memory
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
`<id>` is the 16-character `fid` shown next to each finding in a `--memory`
|
|
143
|
-
report.
|
|
144
|
-
|
|
145
|
-
### Where it lives
|
|
146
|
-
|
|
147
|
-
```
|
|
148
|
-
~/.penthera/memory/<project-id>/events.jsonl the event log
|
|
149
|
-
~/.penthera/memory/<project-id>/head.json count and hash of the last event
|
|
150
|
-
~/.penthera/memory/<project-id>/meta.json the project's path or origin, for `memory show`
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
`<project-id>` is a SHA-256 of the repo's absolute path plus its git remote URL,
|
|
154
|
-
or of the target origin for URL-only scans. Set `PENTHERA_HOME` to move the
|
|
155
|
-
root (the tests do). Penthera reads the remote from `.git/config` itself rather
|
|
156
|
-
than running `git`, because running git inside an untrusted repo can execute
|
|
157
|
-
that repo's hooks or `core.fsmonitor` command.
|
|
158
|
-
|
|
159
|
-
### What it records
|
|
160
|
-
|
|
161
|
-
One JSON event per line, append-only:
|
|
162
|
-
|
|
163
|
-
```json
|
|
164
|
-
{ "v": 1, "ts": "...", "type": "scan", "data": { ... }, "prev": "<hash of previous line>", "hash": "..." }
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
`hash = sha256(prev + canonical JSON of { v, ts, type, data })`, with object
|
|
168
|
-
keys sorted. The first line's `prev` is derived from the project id.
|
|
169
|
-
|
|
170
|
-
| Type | Data |
|
|
171
|
-
| --- | --- |
|
|
172
|
-
| `scan` | stack, modes, counts by severity, ids of the actionable findings |
|
|
173
|
-
| `probe_yield` | probe id, stack, findings count |
|
|
174
|
-
| `fix_applied` | finding id, recipe id, framework, category, diff SHA-256 |
|
|
175
|
-
| `fix_verified` / `fix_failed` | finding id (recipe and framework are joined from `fix_applied`) |
|
|
176
|
-
| `fp_suppressed` | finding id, reason, actor, optional expiry |
|
|
177
|
-
| `loop_run` | one `penthera agent --loop` run: the [loopRun](#the-looprun-object) without test output, finding ids only |
|
|
178
|
-
|
|
179
|
-
From these, `penthera memory show` derives four views: recipe success rate per
|
|
180
|
-
framework, active suppressions, probe ranking by yield per stack, and median
|
|
181
|
-
time to fix (first seen in a scan to `fix_verified`).
|
|
182
|
-
|
|
183
|
-
### How a scan uses it
|
|
184
|
-
|
|
185
|
-
Memory can do two things to a report, and nothing else:
|
|
186
|
-
|
|
187
|
-
1. **Suppress** a finding the user (or the loop, on the user's behalf) marked as
|
|
188
|
-
a false positive, with a reason. Suppressed findings move to a separate
|
|
189
|
-
"Suppressed" section in the terminal and Markdown reports, each with who
|
|
190
|
-
suppressed it and why. They do not count toward the exit code.
|
|
191
|
-
2. **Re-order** findings within a severity: categories with a fix recipe that
|
|
192
|
-
has worked on this framework come first, then findings that have shown up in
|
|
193
|
-
more past scans. Severity is never changed and no finding is dropped.
|
|
194
|
-
|
|
195
|
-
## Threat model
|
|
196
|
-
|
|
197
|
-
The learning store is an attack surface. An agent loop that learns from its own
|
|
198
|
-
past is open to memory poisoning (OWASP Agentic Security Initiative, ASI06): if
|
|
199
|
-
an attacker can write to what the loop remembers, they can steer what it does
|
|
200
|
-
next. In Penthera's case the obvious targets are hiding a real finding and
|
|
201
|
-
getting the loop to run something. The store is designed so neither works
|
|
202
|
-
quietly.
|
|
203
|
-
|
|
204
|
-
### Why it never executes
|
|
205
|
-
|
|
206
|
-
Memory is data. No value read from it is ever executed, evaluated, imported,
|
|
207
|
-
used as a file path, or passed to a shell. A recipe id in memory is only a
|
|
208
|
-
string that gets counted; the recipe code lives in Penthera's own source
|
|
209
|
-
(`lib/fix/`) and is chosen by Penthera, not by the log. Every field a view reads
|
|
210
|
-
is type- and length-checked, and an event that fails a check is ignored rather
|
|
211
|
-
than interpreted. Strings shown in the terminal have control characters stripped,
|
|
212
|
-
so a stored reason cannot inject ANSI escape sequences.
|
|
213
|
-
|
|
214
|
-
The worst a fully poisoned memory can do is hide findings, and hidden findings
|
|
215
|
-
are listed, with their reason, in every report.
|
|
216
|
-
|
|
217
|
-
### Why it is hash-chained
|
|
218
|
-
|
|
219
|
-
The chain makes tampering visible. Editing a line, reordering lines, deleting a
|
|
220
|
-
line from the start or middle, or splicing in another project's log all break
|
|
221
|
-
the chain, and `penthera memory verify` names the first broken line. `head.json`
|
|
222
|
-
records the count and hash of the last event, so cutting lines off the end is
|
|
223
|
-
caught too.
|
|
224
|
-
|
|
225
|
-
When the chain is broken, scans **fail closed**: memory suppressions and
|
|
226
|
-
ranking are not applied, nothing new is appended, and the scan prints a warning
|
|
227
|
-
telling you to run `penthera memory verify`. You can then inspect the log or
|
|
228
|
-
start over with `penthera memory forget`.
|
|
229
|
-
|
|
230
|
-
What the chain does not do: it is tamper-evident, not tamper-proof. Anyone who
|
|
231
|
-
can write to your home directory can rewrite the whole log and recompute every
|
|
232
|
-
hash. Keying the chain would not help, since the key would sit in the same home
|
|
233
|
-
directory. The chain is there to catch a partial edit, an accidental truncation,
|
|
234
|
-
or a tool that writes where it should not; it is not a defence against malware
|
|
235
|
-
already running as you.
|
|
236
|
-
|
|
237
|
-
### Why it lives in your home directory
|
|
238
|
-
|
|
239
|
-
Memory is never read from the scanned repo. If it were, a repo could ship
|
|
240
|
-
pre-poisoned memory, for example a suppression for the secret it is about to
|
|
241
|
-
leak. Keeping memory under `~/.penthera` means only runs on your machine, as
|
|
242
|
-
you, write to it.
|
|
243
|
-
|
|
244
|
-
### The one thing a repo can say: `penthera.suppress.json`
|
|
245
|
-
|
|
246
|
-
Teams do need to share decisions like "this key is a revoked test fixture". For
|
|
247
|
-
that a repo may contain `penthera.suppress.json`:
|
|
248
|
-
|
|
249
|
-
```json
|
|
250
|
-
{
|
|
251
|
-
"version": 1,
|
|
252
|
-
"suppress": [
|
|
253
|
-
{ "id": "16e06eb3fd94664b", "reason": "test fixture, revoked", "expires": "2027-01-01" }
|
|
254
|
-
]
|
|
255
|
-
}
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
It is held to a stricter standard than memory:
|
|
259
|
-
|
|
260
|
-
- Every entry needs a reason. Entries without one, with a malformed id or date,
|
|
261
|
-
or past their expiry are dropped with a warning.
|
|
262
|
-
- Every finding it hides is listed in the report as **suppressed by repo file**
|
|
263
|
-
with the reason, so a reviewer sees both the file change in the PR and its
|
|
264
|
-
effect in the report.
|
|
265
|
-
- It only applies with `--memory`, and a local suppression for the same finding
|
|
266
|
-
takes precedence.
|
|
267
|
-
|
|
268
|
-
Treat a change to `penthera.suppress.json` in a pull request like a change to
|
|
269
|
-
CI config: it decides what the scanner reports.
|
|
270
|
-
|
|
271
|
-
### Concurrency
|
|
272
|
-
|
|
273
|
-
Two scans of the same project can run at once. Each append takes an exclusive
|
|
274
|
-
lock file (`events.lock`, created with `O_EXCL`), reads the tip, and writes one
|
|
275
|
-
line with a single `O_APPEND` write. Lines never interleave and the chain never
|
|
276
|
-
forks. A lock older than 30 seconds is treated as stale and removed.
|
package/docs/marketing.md
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
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.
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# OWASP WSTG coverage map
|
|
2
|
-
|
|
3
|
-
Penthera maps its probes to the [OWASP Web Security Testing Guide (WSTG) v4.2](https://owasp.org/www-project-web-security-testing-guide/). This document is the canonical reference; scan reports include a summary table generated from `lib/owasp-wstg.js`.
|
|
4
|
-
|
|
5
|
-
## Coverage by profile
|
|
6
|
-
|
|
7
|
-
| Profile | Penthera probes | WSTG areas | Destructive payloads |
|
|
8
|
-
|---------|-----------------|------------|----------------------|
|
|
9
|
-
| **quick** | Headers, OpenAPI, auth smoke | INFO, CONF, ATHN | No |
|
|
10
|
-
| **standard** | Full non-destructive black-box + repo | INFO, CONF, CRYP, ATHN, ATHZ, SESS, CLNT | No |
|
|
11
|
-
| **deep** | standard + recon + injections + fuzz | All above + INPV, BUSL | Yes |
|
|
12
|
-
|
|
13
|
-
## Probe → WSTG mapping
|
|
14
|
-
|
|
15
|
-
| Penthera probe | WSTG IDs | Profiles | Mode |
|
|
16
|
-
|----------------|----------|----------|------|
|
|
17
|
-
| Reachability & fingerprint | WSTG-INFO-02, WSTG-INFO-04 | quick, standard, deep | URL |
|
|
18
|
-
| Endpoint discovery | WSTG-INFO-06, WSTG-INFO-07 | standard, deep | URL |
|
|
19
|
-
| TLS / certificate audit | WSTG-CRYP-01, WSTG-CRYP-02 | standard, deep | URL |
|
|
20
|
-
| Security headers | WSTG-CONF-12, WSTG-CONF-13, WSTG-CONF-14 | quick, standard, deep | URL |
|
|
21
|
-
| OpenAPI / Swagger exposure | WSTG-INFO-09, WSTG-CONF-02 | quick, standard, deep | URL |
|
|
22
|
-
| Sensitive file templates | WSTG-CONF-02, WSTG-CONF-04 | standard, deep | URL |
|
|
23
|
-
| CORS validation | WSTG-CLNT-07 | standard, deep | URL |
|
|
24
|
-
| Cookie security | WSTG-SESS-02 | standard, deep | URL |
|
|
25
|
-
| Auth endpoint hardening | WSTG-ATHN-03, WSTG-ATHN-10 | quick, standard, deep | URL |
|
|
26
|
-
| Client-side-only auth | WSTG-ATHZ-02, WSTG-CLNT-01 | standard, deep | URL |
|
|
27
|
-
| JWT probes | WSTG-ATHN-04, WSTG-SESS-10 | standard, deep | URL |
|
|
28
|
-
| IDOR / BOLA | WSTG-ATHZ-04, WSTG-ATHZ-05 | standard, deep | URL |
|
|
29
|
-
| OAuth open redirect | WSTG-ATHN-11, WSTG-CLNT-04 | standard, deep | URL |
|
|
30
|
-
| Retire.js (JS CVEs) | WSTG-CONF-04, WSTG-CLNT-02 | standard, deep | URL |
|
|
31
|
-
| Parameter discovery | WSTG-INFO-06 | standard, deep | URL |
|
|
32
|
-
| OSINT recon | WSTG-INFO-01, WSTG-INFO-07 | deep | URL |
|
|
33
|
-
| SQL injection | WSTG-INPV-05 | deep | URL |
|
|
34
|
-
| SSTI | WSTG-INPV-18 | deep | URL |
|
|
35
|
-
| SSRF | WSTG-INPV-19 | deep | URL |
|
|
36
|
-
| Reflected XSS | WSTG-INPV-01, WSTG-CLNT-01 | deep | URL |
|
|
37
|
-
| Command injection | WSTG-INPV-12 | deep | URL |
|
|
38
|
-
| Open redirect (injection suite) | WSTG-CLNT-04 | deep | URL |
|
|
39
|
-
| API fuzzing | WSTG-INPV-11, WSTG-BUSL-05 | deep | URL |
|
|
40
|
-
| Secret scanning | WSTG-CONF-02, WSTG-CONF-05 | quick, standard, deep | Repo |
|
|
41
|
-
| Route / trust-boundary analysis | WSTG-ATHZ-02, WSTG-CONF-02 | quick, standard, deep | Repo |
|
|
42
|
-
| Risky code patterns | WSTG-INPV-01, WSTG-INPV-12 | standard, deep | Repo |
|
|
43
|
-
|
|
44
|
-
## WSTG categories not covered (yet)
|
|
45
|
-
|
|
46
|
-
Penthera does **not** currently automate these WSTG areas:
|
|
47
|
-
|
|
48
|
-
| Category | Examples | Planned |
|
|
49
|
-
|----------|----------|---------|
|
|
50
|
-
| WSTG-IDNT | Registration, username policy | v0.4 |
|
|
51
|
-
| WSTG-ERRH | Verbose stack traces in all contexts | Partial (template scans) |
|
|
52
|
-
| WSTG-BUSL | Full business-logic workflows | Partial (fuzz only) |
|
|
53
|
-
| WSTG-APIT | GraphQL-specific tests | v0.4 |
|
|
54
|
-
| Manual / social | Phishing, physical access | Out of scope |
|
|
55
|
-
|
|
56
|
-
## Finding category → WSTG
|
|
57
|
-
|
|
58
|
-
When a finding is reported, Penthera tags it with relevant WSTG IDs in JSON (`finding.wstg[]`), SARIF (`properties.tags`), and Markdown reports.
|
|
59
|
-
|
|
60
|
-
| Finding category | WSTG IDs |
|
|
61
|
-
|------------------|----------|
|
|
62
|
-
| `tls` | WSTG-CRYP-01, WSTG-CRYP-02 |
|
|
63
|
-
| `exposure` | WSTG-CONF-02, WSTG-CONF-04 |
|
|
64
|
-
| `cors` | WSTG-CLNT-07 |
|
|
65
|
-
| `cookie` | WSTG-SESS-02 |
|
|
66
|
-
| `auth` | WSTG-ATHN-03, WSTG-ATHN-04 |
|
|
67
|
-
| `secrets` | WSTG-CONF-02, WSTG-CONF-05 |
|
|
68
|
-
| `sqli` | WSTG-INPV-05 |
|
|
69
|
-
| `xss` | WSTG-INPV-01, WSTG-CLNT-01 |
|
|
70
|
-
| `ssrf` | WSTG-INPV-19 |
|
|
71
|
-
| `cmdi` | WSTG-INPV-12 |
|
|
72
|
-
| `open-redirect` | WSTG-CLNT-04, WSTG-ATHN-11 |
|
|
73
|
-
| `rate-limiting` | WSTG-ATHN-10 |
|
|
74
|
-
|
|
75
|
-
## References
|
|
76
|
-
|
|
77
|
-
- [OWASP WSTG on GitHub](https://github.com/OWASP/wstg)
|
|
78
|
-
- Penthera implementation: `lib/owasp-wstg.js`
|
|
79
|
-
- Report integration: `src/report/markdown.js`, `src/reporter.js`
|
package/docs/plan.md
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
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 | One view, one command | The single-view dashboard, the `npx penthera` setup wizard, and an APA-cited methodology. Released |
|
|
33
|
-
| Next | Before you deploy | `penthera ship` and preview-deployment scanning work on a real Vercel and Netlify project |
|
|
34
|
-
| After that | After you ship | `penthera watch` runs weekly on three real production apps for a month without a false alarm anyone complained about |
|
|
35
|
-
| v2.0.0 | Stable | Public interfaces frozen (CLI flags, JSON report schema, policy file, rule-pack format), documented, and covered by compatibility tests |
|
|
36
|
-
|
|
37
|
-
## What "perfect" means, concretely
|
|
38
|
-
|
|
39
|
-
Quality is measured, not felt. Each item below has a gate that the internal
|
|
40
|
-
audit (`npm run audit:internal`) or CI checks automatically.
|
|
41
|
-
|
|
42
|
-
### 1. Accuracy
|
|
43
|
-
- Benchmark precision and recall never drop between releases (the audit fails
|
|
44
|
-
on a drop of more than 0.02).
|
|
45
|
-
- Expand the benchmark beyond the mock server: OWASP Juice Shop, crAPI, a
|
|
46
|
-
deliberately vulnerable Next.js app, and fixture sets for `agents` and `deps`.
|
|
47
|
-
- Every false positive reported by a user becomes a regression test before it
|
|
48
|
-
is fixed.
|
|
49
|
-
|
|
50
|
-
### 2. First-run experience
|
|
51
|
-
- From `npx penthera` to a useful result in under two minutes on a fresh Next.js
|
|
52
|
-
or Express app, measured on a clean machine each release.
|
|
53
|
-
- Every error message says what happened and what to do next. No stack traces
|
|
54
|
-
for expected failures.
|
|
55
|
-
- Works on macOS, Linux, and Windows (add a Windows CI job; the symlinked Cursor
|
|
56
|
-
skill and path handling are the known risks).
|
|
57
|
-
|
|
58
|
-
### 3. Documentation
|
|
59
|
-
- The README stays a front page: problem, what it does, install, trust. Feature
|
|
60
|
-
detail moves into a docs site (`docs/`), one page per command, each with a
|
|
61
|
-
copy-paste example and its real output.
|
|
62
|
-
- Every command's `--help` and its docs page are generated from or tested
|
|
63
|
-
against the same source, so they never drift.
|
|
64
|
-
|
|
65
|
-
### 4. Stability of interfaces
|
|
66
|
-
- A versioned JSON schema for reports, the policy file, and rule packs, checked
|
|
67
|
-
in CI.
|
|
68
|
-
- A deprecation policy: one minor release of warnings before anything breaks.
|
|
69
|
-
|
|
70
|
-
### 5. Security of Penthera itself
|
|
71
|
-
- Self-scan clean on every release (`--repo`, `agents`, `deps`, the dashboard).
|
|
72
|
-
- Anything that parses untrusted input (packages, configs, scan responses, rule
|
|
73
|
-
packs) has a fuzz or hostile-input test. The ReDoS fixed in `deps` is the
|
|
74
|
-
reason why.
|
|
75
|
-
- An external review before v2.0: ask an academic security group or a volunteer
|
|
76
|
-
from the OWASP community to look at the fix loop, the install firewall, and
|
|
77
|
-
the dashboard, the three parts that write files or run commands.
|
|
78
|
-
|
|
79
|
-
### 6. Real-world proof
|
|
80
|
-
- Three pilot projects (friendly teams, a student project, one of the
|
|
81
|
-
maintainer's own) run Penthera before each deploy for a month. Their feedback
|
|
82
|
-
sets the priorities for the next release.
|
|
83
|
-
- Publish what Penthera found and fixed, with permission, as short case studies.
|
|
84
|
-
|
|
85
|
-
## Working rhythm
|
|
86
|
-
|
|
87
|
-
- One release every two to three weeks, each with a single theme.
|
|
88
|
-
- The internal audit runs weekly and before every tag; a red audit blocks the
|
|
89
|
-
release.
|
|
90
|
-
- Issues labelled `good first issue` are kept stocked (three to five open at a
|
|
91
|
-
time), so contributors can help without needing the maintainer's time.
|
|
92
|
-
- Anything that needs judgement (a new probe that sends payloads, anything
|
|
93
|
-
touching third parties) gets a short design note in `docs/` before code.
|
|
94
|
-
|
|
95
|
-
## Explicitly not doing
|
|
96
|
-
|
|
97
|
-
- A hosted service, telemetry, or an update channel that fetches rules at run
|
|
98
|
-
time.
|
|
99
|
-
- Offensive tooling for targets the user does not own, and attack-payload
|
|
100
|
-
generation for AI systems.
|
|
101
|
-
- An LLM as the judge of what is malicious. Models may help write fixes,
|
|
102
|
-
locally, and always labelled as such.
|
package/docs/privacy.md
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
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).
|