websec-validator 0.2.0__tar.gz

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 (55) hide show
  1. websec_validator-0.2.0/LICENSE +21 -0
  2. websec_validator-0.2.0/PKG-INFO +232 -0
  3. websec_validator-0.2.0/README.md +220 -0
  4. websec_validator-0.2.0/pyproject.toml +28 -0
  5. websec_validator-0.2.0/setup.cfg +4 -0
  6. websec_validator-0.2.0/src/websec_validator/__init__.py +14 -0
  7. websec_validator-0.2.0/src/websec_validator/briefing.py +218 -0
  8. websec_validator-0.2.0/src/websec_validator/calibration.json +75 -0
  9. websec_validator-0.2.0/src/websec_validator/calibration.py +226 -0
  10. websec_validator-0.2.0/src/websec_validator/cli.py +395 -0
  11. websec_validator-0.2.0/src/websec_validator/constitution.py +81 -0
  12. websec_validator-0.2.0/src/websec_validator/corpus.json +49 -0
  13. websec_validator-0.2.0/src/websec_validator/dynamic.py +249 -0
  14. websec_validator-0.2.0/src/websec_validator/extractors/__init__.py +56 -0
  15. websec_validator-0.2.0/src/websec_validator/extractors/auth.py +77 -0
  16. websec_validator-0.2.0/src/websec_validator/extractors/authz.py +130 -0
  17. websec_validator-0.2.0/src/websec_validator/extractors/base.py +101 -0
  18. websec_validator-0.2.0/src/websec_validator/extractors/client_exposure.py +48 -0
  19. websec_validator-0.2.0/src/websec_validator/extractors/graphql.py +71 -0
  20. websec_validator-0.2.0/src/websec_validator/extractors/iac_ci.py +65 -0
  21. websec_validator-0.2.0/src/websec_validator/extractors/integrations.py +55 -0
  22. websec_validator-0.2.0/src/websec_validator/extractors/routes.py +215 -0
  23. websec_validator-0.2.0/src/websec_validator/extractors/schemas.py +75 -0
  24. websec_validator-0.2.0/src/websec_validator/extractors/stack.py +80 -0
  25. websec_validator-0.2.0/src/websec_validator/extractors/surface.py +86 -0
  26. websec_validator-0.2.0/src/websec_validator/extractors/tenant.py +33 -0
  27. websec_validator-0.2.0/src/websec_validator/findings.py +199 -0
  28. websec_validator-0.2.0/src/websec_validator/probes.py +79 -0
  29. websec_validator-0.2.0/src/websec_validator/proof.py +96 -0
  30. websec_validator-0.2.0/src/websec_validator/recon.py +28 -0
  31. websec_validator-0.2.0/src/websec_validator/report.py +114 -0
  32. websec_validator-0.2.0/src/websec_validator/scanners.py +248 -0
  33. websec_validator-0.2.0/src/websec_validator/templates/probes/bola-cross-tenant.sh +192 -0
  34. websec_validator-0.2.0/src/websec_validator/templates/probes/bola-write-verbs.py +147 -0
  35. websec_validator-0.2.0/src/websec_validator/templates/probes/compare-roles.sh +69 -0
  36. websec_validator-0.2.0/src/websec_validator/templates/probes/dlp-bypass-offline.py +149 -0
  37. websec_validator-0.2.0/src/websec_validator/templates/probes/hs256-brute-force.py +90 -0
  38. websec_validator-0.2.0/src/websec_validator/templates/probes/jwt-attacks.sh +161 -0
  39. websec_validator-0.2.0/src/websec_validator/templates/probes/mass-assignment.py +201 -0
  40. websec_validator-0.2.0/src/websec_validator/templates/probes/race-conditions.py +144 -0
  41. websec_validator-0.2.0/src/websec_validator/templates/probes/rate-limit-burst.sh +136 -0
  42. websec_validator-0.2.0/src/websec_validator/templates/probes/s3-assess.sh +120 -0
  43. websec_validator-0.2.0/src/websec_validator/templates/probes/ssrf-probes.sh +189 -0
  44. websec_validator-0.2.0/src/websec_validator/templates/probes/webhook-forgery.py +113 -0
  45. websec_validator-0.2.0/src/websec_validator/templates/reports/FINDINGS-SUMMARY.md.template +75 -0
  46. websec_validator-0.2.0/src/websec_validator/templates/reports/access-control-matrix.md.template +65 -0
  47. websec_validator-0.2.0/src/websec_validator/templates/reports/findings-triage.md.template +28 -0
  48. websec_validator-0.2.0/src/websec_validator/templates/reports/pentest-handover-brief.md.template +121 -0
  49. websec_validator-0.2.0/src/websec_validator/templates/reports/per-tool-FINDINGS.md.template +37 -0
  50. websec_validator-0.2.0/src/websec_validator.egg-info/PKG-INFO +232 -0
  51. websec_validator-0.2.0/src/websec_validator.egg-info/SOURCES.txt +53 -0
  52. websec_validator-0.2.0/src/websec_validator.egg-info/dependency_links.txt +1 -0
  53. websec_validator-0.2.0/src/websec_validator.egg-info/entry_points.txt +2 -0
  54. websec_validator-0.2.0/src/websec_validator.egg-info/top_level.txt +1 -0
  55. websec_validator-0.2.0/tests/test_recon.py +262 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ricardo Accioly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,232 @@
1
+ Metadata-Version: 2.4
2
+ Name: websec-validator
3
+ Version: 0.2.0
4
+ Summary: Local-first security recon that briefs your AI coding agent: facts + tailored probe scripts, code-in / artifacts-out. No LLM, no server, no running app.
5
+ Author: Ricardo Accioly
6
+ License: MIT
7
+ Keywords: security,pentest,sast,dast,bola,ai-agent,appsec
8
+ Requires-Python: >=3.11
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Dynamic: license-file
12
+
13
+ # websec-validator
14
+
15
+ > Local-first security recon that **briefs your AI coding agent**. It does the deterministic
16
+ > half — read the repo, map the full attack surface, run + de-duplicate the static scanners, and
17
+ > stage a probe library tailored to what it found — then hands your agent (Claude Code, Codex,
18
+ > Gemini, Cursor) a marching-orders briefing. **Code in, artifacts out. No LLM in the tool, no
19
+ > server, no running app required.**
20
+
21
+ It is *not* an autonomous scanner and *not* a SaaS. It's the missing front-half: the thing that
22
+ turns a repo into a precise, fact-grounded security brief an AI agent (with a human in the loop)
23
+ can act on — an auto-filled, repo-aware version of a senior pentester's "here's what to test and
24
+ how" handoff. Full landscape + why this niche is real: [`MARKET-ANALYSIS-AND-VERDICT.md`](MARKET-ANALYSIS-AND-VERDICT.md).
25
+
26
+ ## Quickstart — just point it at your repo
27
+
28
+ **Simplest: tell your AI agent.** In Claude Code (or any coding agent), open your project and say:
29
+
30
+ > *"Install and run the security tool at github.com/raccioly/websec-validator on this repo, then follow its briefing."*
31
+
32
+ It installs, runs, and walks the findings with you. There's nothing to host and no website — it's
33
+ local. The four ways to get there, all ending in the same `AGENT-BRIEFING.md` your agent acts on:
34
+
35
+ | Path | One-time setup | Then |
36
+ |---|---|---|
37
+ | **Tell your agent** (simplest) | — | say the line above |
38
+ | **CLI** (a terminal) | `pipx install websec-validator` | `websec run /path/to/your/app` |
39
+ | **Claude Code plugin** (slash) | `/plugin marketplace add raccioly/websec-validator` → `/plugin install websec-validator@websec-plugins` | invoke the **security-pass** skill, or just ask |
40
+ | **Docker** (no install) | `docker build -t websec-validator .` | `docker run --rm -v "$PWD:/scan" websec-validator run /scan --out /scan/websec-out` |
41
+
42
+ ➡️ **Want the reasoning behind every check?** Read **[docs/METHODOLOGY.md](docs/METHODOLOGY.md)** — what each test does and why.
43
+
44
+ ## Install
45
+
46
+ ```bash
47
+ pipx install websec-validator # from PyPI
48
+ brew install noir # OWASP Noir — the route engine (50+ frameworks); regex fallback if absent
49
+ websec --version
50
+ ```
51
+
52
+ _Until the first PyPI release publishes (or for bleeding-edge), install straight from source instead:_
53
+ `pipx install git+https://github.com/raccioly/websec-validator` (or from a clone: `pipx install .`).
54
+
55
+ Requires **Python 3.11+** (on stock macOS, `python3` is often 3.9 — use `pipx`, which picks a newer
56
+ interpreter, or install via Homebrew/pyenv). Zero Python runtime dependencies: it shells out to
57
+ scanners (Trivy, Gitleaks, Semgrep/OpenGrep, Checkov, Prowler) and Noir **when present**, reports
58
+ what's missing, and never hard-fails if a tool is absent.
59
+
60
+ ### Or run via Docker (everything bundled, zero install)
61
+
62
+ No need to install Noir or any scanner — the image bundles them all (arch-aware, amd64 + arm64):
63
+
64
+ ```bash
65
+ docker build -t websec-validator .
66
+ docker run --rm -v "$PWD:/scan" websec-validator run /scan --out /scan/websec-out
67
+ ```
68
+
69
+ The image carries Noir + Trivy + Gitleaks + Semgrep + Checkov; mount your repo at `/scan` and the
70
+ artifacts land in `/scan/websec-out`.
71
+
72
+ ## Use
73
+
74
+ ```bash
75
+ websec run ./my-app # ← the one command: recon + stage tailored probes + emit the briefing
76
+ websec ./my-app # same thing — a bare path defaults to `run`
77
+ websec run ./my-app --scan # …and also execute the available static scanners
78
+ websec doctor ./my-app # (optional) which scanners are installed?
79
+ ```
80
+
81
+ Then point your agent at the output: **"Read `websec-out/AGENT-BRIEFING.md` and follow it."**
82
+
83
+ > That's the whole user surface: **`run`** (plus the optional, advanced **`dynamic`** live-probing step below). `recon`/`proof`/`calibrate` exist for developing the tool itself and are hidden from `--help` — you never need them.
84
+
85
+ ## What it extracts (11 deterministic extractors, no LLM)
86
+
87
+ | | Dimension | Notable output |
88
+ |---|---|---|
89
+ | stack | languages, frameworks, datastores | monorepo-aware (aggregates every manifest) |
90
+ | routes | every endpoint via **OWASP Noir** | method · path · typed params · code path |
91
+ | auth | scheme + login surface | multi-scheme (primary jwt > passport), PyJWT/NextAuth/session aware |
92
+ | **authz** | access-control map | guard coverage + **write endpoints with no visible guard** + roles |
93
+ | tenant | multi-tenancy key candidates | the BOLA boundary, by frequency |
94
+ | surface | 12 user-input-gated sink classes | SSRF/SQLi/NoSQLi/traversal/SSTI/redirect/deser/XXE/proto-pollution/ReDoS/cmd/eval |
95
+ | schemas | data models + **privileged fields** | Pydantic/SQLAlchemy/Django/Prisma/Mongoose/TypeORM/Zod → `role`/`isAdmin`/`groupId` for mass-assignment targeting |
96
+ | iac_ci | IaC + CI/CD | GitHub Actions injection, unpinned actions, Dockerfile-root, tfstate |
97
+ | client_exposure | browser leakage | `NEXT_PUBLIC_*` secrets, server-secret-in-client, source maps |
98
+ | graphql | GraphQL surface | introspection / playground / missing depth-limit |
99
+ | integrations | third-party + webhooks | webhooks missing signature verification |
100
+
101
+ Plus **derived targeting** — IDOR / SSRF / open-redirect / upload / write / auth-endpoint
102
+ candidates — so probes get pointed at the *exact* endpoints, not fired blindly.
103
+
104
+ ## What you get (`websec-out/`)
105
+
106
+ | Artifact | What it is |
107
+ |---|---|
108
+ | `AGENT-BRIEFING.md` | **The product.** Marching orders: detected surface, the access-control map, targeting, findings, the method, and the staged probe list. |
109
+ | `FACTS.json` | The full structured recon. |
110
+ | `findings.json` | Static scanner results, **de-duplicated across tools** and severity-ranked (with `--scan`). |
111
+ | `findings-ledger.json` / `REPORT.md` | The traceable ledger: each finding with an evidence chain, CWE/ASVS/OWASP-API citation, remediation, and a **calibrated `P(real)`** (measured real-vuln rate + 95% CI + sample size). |
112
+ | `probes/` | The probe scripts selected + staged for *this* app (BOLA, JWT, SSRF, mass-assignment…). |
113
+
114
+ ## The flow
115
+
116
+ ```
117
+ 🔧 websec (deterministic) 🤖 your agent + 🧑 you
118
+ ───────────────────────────────── ─────────────────────────────────
119
+ 1. recon → full attack surface → confirm the tenant boundary + auth model
120
+ 2. run + de-dup static scanners → triage real-vs-noise
121
+ 3. stage tailored probes → fill placeholders, run vs a TEST instance
122
+ 4. emit AGENT-BRIEFING.md → propose fixes, re-run to confirm, report back
123
+ ```
124
+
125
+ Static recon + briefing need **only the code**. *Running* the probes needs a live test instance +
126
+ test credentials (the human supplies them) — the tool itself never touches a running app.
127
+
128
+ ## Proof harness
129
+
130
+ `websec proof` clones a vuln-app corpus (VAmPI, NodeGoat, DVGA) and scores whether recon surfaces
131
+ each app's documented attack surface — a deterministic, CI-trackable proxy (currently **10/10**).
132
+ The real kill-criterion (does the briefing lift an agent's bug-finding vs a generic prompt?) is the
133
+ manual A/B in [`corpus/PROOF-PROTOCOL.md`](corpus/PROOF-PROTOCOL.md).
134
+
135
+ ## Calibrated confidence
136
+
137
+ `websec calibrate` runs the ledger against the labeled corpus, measures how often each
138
+ *(attack-class, confidence)* bucket is a **real** documented vuln, and writes `calibration.json`
139
+ (shipped + applied at runtime). Each finding then carries `P(real)` with a **95% Wilson confidence
140
+ interval** and the sample size `n` — so "MEDIUM" stops being a vibe and becomes "real ~57% of the
141
+ time on the corpus (CI 43–70%, n=51)". A finding that matches no documented vuln counts as a false
142
+ positive (the corpus is well-documented). **Honest caveats:** the corpus is *deliberately
143
+ vulnerable*, so the rates skew **optimistic** for clean production code, and small samples mean
144
+ **wide intervals** — the CI is the headline, not the point estimate, and both tighten as the corpus
145
+ grows. With thin data a bucket falls back to the per-label aggregate, then to a clearly-flagged
146
+ uncalibrated prior. No ML, no deps — binomial proportion + Wilson interval; the structure upgrades to
147
+ isotonic regression if a large labeled set ever exists.
148
+
149
+ **It self-improves.** `websec dynamic` is an *oracle*: a write that executes unauthenticated is a
150
+ confirmed real vuln, and a recon-flagged endpoint that turns out auth-enforced is a confirmed false
151
+ positive. Every dynamic run folds those confirmed labels into a **local overlay** (`~/.cache/websec-validator/`,
152
+ gitignored, never shipped) that's merged on top of the public table — so the numbers **personalize to
153
+ your apps** the more you run it, with no extra step and nothing leaving your machine. To label by hand
154
+ instead, feed a `{attack_class, confidence, is_real}` file to `websec calibrate --ingest`.
155
+
156
+ ## Dynamic phase (v2 — read-only so far)
157
+
158
+ When you have a *running TEST instance*, `websec dynamic` mints role tokens and runs the probes the
159
+ static recon pointed at. v1 is **read-only**: authenticated **cross-tenant BOLA** on the group-scoped
160
+ GET endpoints recon discovered.
161
+
162
+ ```bash
163
+ cp dynamic-config.example.json dynamic-config.json # TEST target + role creds (gitignored)
164
+ websec run ./my-app # static recon → websec-out/FACTS.json
165
+ websec dynamic --config dynamic-config.json --facts websec-out/FACTS.json
166
+ # → "14/14 cross-tenant GET reads blocked — all isolated" (or 🚨 LEAK with the exact endpoint)
167
+ ```
168
+
169
+ Never point it at production. Write-verb BOLA, JWT/auth attacks, and a ZAP/Nuclei two-role diff are
170
+ the next dynamic probes (explicitly gated — they mutate).
171
+
172
+ ## Validated on
173
+
174
+ HugoCross (Next.js), `wu-whatsappinbox` (106-service Express/AWS monorepo), VAmPI, NodeGoat, DVGA —
175
+ independently reproducing a hand-done pentest's findings (tenant boundary, SSO-endpoint SSRF, media
176
+ upload, conversation-BOLA routes, roles).
177
+
178
+ ## Tests
179
+
180
+ ```bash
181
+ python3 -m unittest discover -s tests # stdlib only, no Noir/network — 23 tests
182
+ ```
183
+
184
+ ## Releasing (maintainer)
185
+
186
+ Published to PyPI via **Trusted Publishing** (OIDC — no API token in the repo). To cut a release:
187
+
188
+ ```bash
189
+ # 1. bump the version in pyproject.toml (e.g. 0.2.0 → 0.2.1)
190
+ # 2. tag it and push — the tag must match pyproject's version (CI verifies):
191
+ git tag v0.2.1 && git push origin v0.2.1
192
+ # → .github/workflows/publish.yml builds + publishes to PyPI
193
+ ```
194
+
195
+ One-time PyPI setup (before the first release): on pypi.org → **Account → Publishing → Add a pending
196
+ publisher** with project `websec-validator`, owner `raccioly`, repo `websec-validator`, workflow
197
+ `publish.yml`, environment `pypi`. The project is created on the first successful publish.
198
+
199
+ > Two independent channels, two update mechanisms: the **CLI** ships to **PyPI** (semver releases,
200
+ > `pip install --upgrade`); the **Claude Code plugin** ships from **git** (tracks latest commit,
201
+ > refreshed via `/plugin marketplace update`).
202
+
203
+ ## Status / roadmap
204
+
205
+ **Done:** 11-extractor recon (incl. schema/entity → mass-assignment targeting), cross-tool de-dup,
206
+ tailored probe staging, agent briefing, traceable findings ledger with **calibrated confidence
207
+ (CJE — Wilson CIs)**, proof harness, test suite, **Docker bundle** (all scanners + Noir, arch-aware),
208
+ **dynamic phase v1** (authenticated read-only cross-tenant BOLA — validated live, reproduced a
209
+ hand-pentest's 14/14).
210
+ **Next:** dynamic write-verb BOLA + JWT/auth probes + ZAP/Nuclei two-role diff (gated, they mutate),
211
+ calibration on hand-labeled real repos (more representative base rate), ASVS index lookup, optional
212
+ model-SDK adapters for no-agent fallback.
213
+
214
+ ## Using it as a Claude Code skill / plugin
215
+
216
+ This repo **is** a Claude Code plugin. Install it once —
217
+
218
+ ```
219
+ /plugin marketplace add raccioly/websec-validator
220
+ /plugin install websec-validator@websec-plugins
221
+ ```
222
+
223
+ — and the bundled **security-pass** skill ([`skills/security-pass/SKILL.md`](skills/security-pass/SKILL.md))
224
+ lets you just ask, in plain English, for a security pass: it runs `websec`, reads the briefing, and
225
+ works the findings with you. For other agents the universal interface is unchanged: run the CLI, read
226
+ `AGENT-BRIEFING.md`.
227
+
228
+ ## Credits
229
+
230
+ Methodology + probe library come from a real authenticated pentest pass
231
+ ([`base-research/REPLICATION-PLAYBOOK.md`](base-research/REPLICATION-PLAYBOOK.md), not committed).
232
+ This tool productizes that hand-written pass into something an AI agent can run on any repo.
@@ -0,0 +1,220 @@
1
+ # websec-validator
2
+
3
+ > Local-first security recon that **briefs your AI coding agent**. It does the deterministic
4
+ > half — read the repo, map the full attack surface, run + de-duplicate the static scanners, and
5
+ > stage a probe library tailored to what it found — then hands your agent (Claude Code, Codex,
6
+ > Gemini, Cursor) a marching-orders briefing. **Code in, artifacts out. No LLM in the tool, no
7
+ > server, no running app required.**
8
+
9
+ It is *not* an autonomous scanner and *not* a SaaS. It's the missing front-half: the thing that
10
+ turns a repo into a precise, fact-grounded security brief an AI agent (with a human in the loop)
11
+ can act on — an auto-filled, repo-aware version of a senior pentester's "here's what to test and
12
+ how" handoff. Full landscape + why this niche is real: [`MARKET-ANALYSIS-AND-VERDICT.md`](MARKET-ANALYSIS-AND-VERDICT.md).
13
+
14
+ ## Quickstart — just point it at your repo
15
+
16
+ **Simplest: tell your AI agent.** In Claude Code (or any coding agent), open your project and say:
17
+
18
+ > *"Install and run the security tool at github.com/raccioly/websec-validator on this repo, then follow its briefing."*
19
+
20
+ It installs, runs, and walks the findings with you. There's nothing to host and no website — it's
21
+ local. The four ways to get there, all ending in the same `AGENT-BRIEFING.md` your agent acts on:
22
+
23
+ | Path | One-time setup | Then |
24
+ |---|---|---|
25
+ | **Tell your agent** (simplest) | — | say the line above |
26
+ | **CLI** (a terminal) | `pipx install websec-validator` | `websec run /path/to/your/app` |
27
+ | **Claude Code plugin** (slash) | `/plugin marketplace add raccioly/websec-validator` → `/plugin install websec-validator@websec-plugins` | invoke the **security-pass** skill, or just ask |
28
+ | **Docker** (no install) | `docker build -t websec-validator .` | `docker run --rm -v "$PWD:/scan" websec-validator run /scan --out /scan/websec-out` |
29
+
30
+ ➡️ **Want the reasoning behind every check?** Read **[docs/METHODOLOGY.md](docs/METHODOLOGY.md)** — what each test does and why.
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ pipx install websec-validator # from PyPI
36
+ brew install noir # OWASP Noir — the route engine (50+ frameworks); regex fallback if absent
37
+ websec --version
38
+ ```
39
+
40
+ _Until the first PyPI release publishes (or for bleeding-edge), install straight from source instead:_
41
+ `pipx install git+https://github.com/raccioly/websec-validator` (or from a clone: `pipx install .`).
42
+
43
+ Requires **Python 3.11+** (on stock macOS, `python3` is often 3.9 — use `pipx`, which picks a newer
44
+ interpreter, or install via Homebrew/pyenv). Zero Python runtime dependencies: it shells out to
45
+ scanners (Trivy, Gitleaks, Semgrep/OpenGrep, Checkov, Prowler) and Noir **when present**, reports
46
+ what's missing, and never hard-fails if a tool is absent.
47
+
48
+ ### Or run via Docker (everything bundled, zero install)
49
+
50
+ No need to install Noir or any scanner — the image bundles them all (arch-aware, amd64 + arm64):
51
+
52
+ ```bash
53
+ docker build -t websec-validator .
54
+ docker run --rm -v "$PWD:/scan" websec-validator run /scan --out /scan/websec-out
55
+ ```
56
+
57
+ The image carries Noir + Trivy + Gitleaks + Semgrep + Checkov; mount your repo at `/scan` and the
58
+ artifacts land in `/scan/websec-out`.
59
+
60
+ ## Use
61
+
62
+ ```bash
63
+ websec run ./my-app # ← the one command: recon + stage tailored probes + emit the briefing
64
+ websec ./my-app # same thing — a bare path defaults to `run`
65
+ websec run ./my-app --scan # …and also execute the available static scanners
66
+ websec doctor ./my-app # (optional) which scanners are installed?
67
+ ```
68
+
69
+ Then point your agent at the output: **"Read `websec-out/AGENT-BRIEFING.md` and follow it."**
70
+
71
+ > That's the whole user surface: **`run`** (plus the optional, advanced **`dynamic`** live-probing step below). `recon`/`proof`/`calibrate` exist for developing the tool itself and are hidden from `--help` — you never need them.
72
+
73
+ ## What it extracts (11 deterministic extractors, no LLM)
74
+
75
+ | | Dimension | Notable output |
76
+ |---|---|---|
77
+ | stack | languages, frameworks, datastores | monorepo-aware (aggregates every manifest) |
78
+ | routes | every endpoint via **OWASP Noir** | method · path · typed params · code path |
79
+ | auth | scheme + login surface | multi-scheme (primary jwt > passport), PyJWT/NextAuth/session aware |
80
+ | **authz** | access-control map | guard coverage + **write endpoints with no visible guard** + roles |
81
+ | tenant | multi-tenancy key candidates | the BOLA boundary, by frequency |
82
+ | surface | 12 user-input-gated sink classes | SSRF/SQLi/NoSQLi/traversal/SSTI/redirect/deser/XXE/proto-pollution/ReDoS/cmd/eval |
83
+ | schemas | data models + **privileged fields** | Pydantic/SQLAlchemy/Django/Prisma/Mongoose/TypeORM/Zod → `role`/`isAdmin`/`groupId` for mass-assignment targeting |
84
+ | iac_ci | IaC + CI/CD | GitHub Actions injection, unpinned actions, Dockerfile-root, tfstate |
85
+ | client_exposure | browser leakage | `NEXT_PUBLIC_*` secrets, server-secret-in-client, source maps |
86
+ | graphql | GraphQL surface | introspection / playground / missing depth-limit |
87
+ | integrations | third-party + webhooks | webhooks missing signature verification |
88
+
89
+ Plus **derived targeting** — IDOR / SSRF / open-redirect / upload / write / auth-endpoint
90
+ candidates — so probes get pointed at the *exact* endpoints, not fired blindly.
91
+
92
+ ## What you get (`websec-out/`)
93
+
94
+ | Artifact | What it is |
95
+ |---|---|
96
+ | `AGENT-BRIEFING.md` | **The product.** Marching orders: detected surface, the access-control map, targeting, findings, the method, and the staged probe list. |
97
+ | `FACTS.json` | The full structured recon. |
98
+ | `findings.json` | Static scanner results, **de-duplicated across tools** and severity-ranked (with `--scan`). |
99
+ | `findings-ledger.json` / `REPORT.md` | The traceable ledger: each finding with an evidence chain, CWE/ASVS/OWASP-API citation, remediation, and a **calibrated `P(real)`** (measured real-vuln rate + 95% CI + sample size). |
100
+ | `probes/` | The probe scripts selected + staged for *this* app (BOLA, JWT, SSRF, mass-assignment…). |
101
+
102
+ ## The flow
103
+
104
+ ```
105
+ 🔧 websec (deterministic) 🤖 your agent + 🧑 you
106
+ ───────────────────────────────── ─────────────────────────────────
107
+ 1. recon → full attack surface → confirm the tenant boundary + auth model
108
+ 2. run + de-dup static scanners → triage real-vs-noise
109
+ 3. stage tailored probes → fill placeholders, run vs a TEST instance
110
+ 4. emit AGENT-BRIEFING.md → propose fixes, re-run to confirm, report back
111
+ ```
112
+
113
+ Static recon + briefing need **only the code**. *Running* the probes needs a live test instance +
114
+ test credentials (the human supplies them) — the tool itself never touches a running app.
115
+
116
+ ## Proof harness
117
+
118
+ `websec proof` clones a vuln-app corpus (VAmPI, NodeGoat, DVGA) and scores whether recon surfaces
119
+ each app's documented attack surface — a deterministic, CI-trackable proxy (currently **10/10**).
120
+ The real kill-criterion (does the briefing lift an agent's bug-finding vs a generic prompt?) is the
121
+ manual A/B in [`corpus/PROOF-PROTOCOL.md`](corpus/PROOF-PROTOCOL.md).
122
+
123
+ ## Calibrated confidence
124
+
125
+ `websec calibrate` runs the ledger against the labeled corpus, measures how often each
126
+ *(attack-class, confidence)* bucket is a **real** documented vuln, and writes `calibration.json`
127
+ (shipped + applied at runtime). Each finding then carries `P(real)` with a **95% Wilson confidence
128
+ interval** and the sample size `n` — so "MEDIUM" stops being a vibe and becomes "real ~57% of the
129
+ time on the corpus (CI 43–70%, n=51)". A finding that matches no documented vuln counts as a false
130
+ positive (the corpus is well-documented). **Honest caveats:** the corpus is *deliberately
131
+ vulnerable*, so the rates skew **optimistic** for clean production code, and small samples mean
132
+ **wide intervals** — the CI is the headline, not the point estimate, and both tighten as the corpus
133
+ grows. With thin data a bucket falls back to the per-label aggregate, then to a clearly-flagged
134
+ uncalibrated prior. No ML, no deps — binomial proportion + Wilson interval; the structure upgrades to
135
+ isotonic regression if a large labeled set ever exists.
136
+
137
+ **It self-improves.** `websec dynamic` is an *oracle*: a write that executes unauthenticated is a
138
+ confirmed real vuln, and a recon-flagged endpoint that turns out auth-enforced is a confirmed false
139
+ positive. Every dynamic run folds those confirmed labels into a **local overlay** (`~/.cache/websec-validator/`,
140
+ gitignored, never shipped) that's merged on top of the public table — so the numbers **personalize to
141
+ your apps** the more you run it, with no extra step and nothing leaving your machine. To label by hand
142
+ instead, feed a `{attack_class, confidence, is_real}` file to `websec calibrate --ingest`.
143
+
144
+ ## Dynamic phase (v2 — read-only so far)
145
+
146
+ When you have a *running TEST instance*, `websec dynamic` mints role tokens and runs the probes the
147
+ static recon pointed at. v1 is **read-only**: authenticated **cross-tenant BOLA** on the group-scoped
148
+ GET endpoints recon discovered.
149
+
150
+ ```bash
151
+ cp dynamic-config.example.json dynamic-config.json # TEST target + role creds (gitignored)
152
+ websec run ./my-app # static recon → websec-out/FACTS.json
153
+ websec dynamic --config dynamic-config.json --facts websec-out/FACTS.json
154
+ # → "14/14 cross-tenant GET reads blocked — all isolated" (or 🚨 LEAK with the exact endpoint)
155
+ ```
156
+
157
+ Never point it at production. Write-verb BOLA, JWT/auth attacks, and a ZAP/Nuclei two-role diff are
158
+ the next dynamic probes (explicitly gated — they mutate).
159
+
160
+ ## Validated on
161
+
162
+ HugoCross (Next.js), `wu-whatsappinbox` (106-service Express/AWS monorepo), VAmPI, NodeGoat, DVGA —
163
+ independently reproducing a hand-done pentest's findings (tenant boundary, SSO-endpoint SSRF, media
164
+ upload, conversation-BOLA routes, roles).
165
+
166
+ ## Tests
167
+
168
+ ```bash
169
+ python3 -m unittest discover -s tests # stdlib only, no Noir/network — 23 tests
170
+ ```
171
+
172
+ ## Releasing (maintainer)
173
+
174
+ Published to PyPI via **Trusted Publishing** (OIDC — no API token in the repo). To cut a release:
175
+
176
+ ```bash
177
+ # 1. bump the version in pyproject.toml (e.g. 0.2.0 → 0.2.1)
178
+ # 2. tag it and push — the tag must match pyproject's version (CI verifies):
179
+ git tag v0.2.1 && git push origin v0.2.1
180
+ # → .github/workflows/publish.yml builds + publishes to PyPI
181
+ ```
182
+
183
+ One-time PyPI setup (before the first release): on pypi.org → **Account → Publishing → Add a pending
184
+ publisher** with project `websec-validator`, owner `raccioly`, repo `websec-validator`, workflow
185
+ `publish.yml`, environment `pypi`. The project is created on the first successful publish.
186
+
187
+ > Two independent channels, two update mechanisms: the **CLI** ships to **PyPI** (semver releases,
188
+ > `pip install --upgrade`); the **Claude Code plugin** ships from **git** (tracks latest commit,
189
+ > refreshed via `/plugin marketplace update`).
190
+
191
+ ## Status / roadmap
192
+
193
+ **Done:** 11-extractor recon (incl. schema/entity → mass-assignment targeting), cross-tool de-dup,
194
+ tailored probe staging, agent briefing, traceable findings ledger with **calibrated confidence
195
+ (CJE — Wilson CIs)**, proof harness, test suite, **Docker bundle** (all scanners + Noir, arch-aware),
196
+ **dynamic phase v1** (authenticated read-only cross-tenant BOLA — validated live, reproduced a
197
+ hand-pentest's 14/14).
198
+ **Next:** dynamic write-verb BOLA + JWT/auth probes + ZAP/Nuclei two-role diff (gated, they mutate),
199
+ calibration on hand-labeled real repos (more representative base rate), ASVS index lookup, optional
200
+ model-SDK adapters for no-agent fallback.
201
+
202
+ ## Using it as a Claude Code skill / plugin
203
+
204
+ This repo **is** a Claude Code plugin. Install it once —
205
+
206
+ ```
207
+ /plugin marketplace add raccioly/websec-validator
208
+ /plugin install websec-validator@websec-plugins
209
+ ```
210
+
211
+ — and the bundled **security-pass** skill ([`skills/security-pass/SKILL.md`](skills/security-pass/SKILL.md))
212
+ lets you just ask, in plain English, for a security pass: it runs `websec`, reads the briefing, and
213
+ works the findings with you. For other agents the universal interface is unchanged: run the CLI, read
214
+ `AGENT-BRIEFING.md`.
215
+
216
+ ## Credits
217
+
218
+ Methodology + probe library come from a real authenticated pentest pass
219
+ ([`base-research/REPLICATION-PLAYBOOK.md`](base-research/REPLICATION-PLAYBOOK.md), not committed).
220
+ This tool productizes that hand-written pass into something an AI agent can run on any repo.
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "websec-validator"
7
+ version = "0.2.0"
8
+ description = "Local-first security recon that briefs your AI coding agent: facts + tailored probe scripts, code-in / artifacts-out. No LLM, no server, no running app."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Ricardo Accioly" }]
13
+ keywords = ["security", "pentest", "sast", "dast", "bola", "ai-agent", "appsec"]
14
+ # v1 deliberately has ZERO runtime dependencies — stdlib only.
15
+ # It SHELLS OUT to scanners (trivy/gitleaks/semgrep/...) when present; it does not import them.
16
+ dependencies = []
17
+
18
+ [project.scripts]
19
+ websec = "websec_validator.cli:main"
20
+
21
+ [tool.setuptools]
22
+ package-dir = { "" = "src" }
23
+
24
+ [tool.setuptools.packages.find]
25
+ where = ["src"]
26
+
27
+ [tool.setuptools.package-data]
28
+ websec_validator = ["templates/probes/*", "templates/reports/*", "corpus.json", "calibration.json"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,14 @@
1
+ """websec-validator — local-first security recon that briefs an AI coding agent.
2
+
3
+ The tool does the deterministic half (read the repo, run the scanners it finds,
4
+ stage the probe library tailored to what it discovered) and emits three artifacts:
5
+
6
+ 1. findings.json — de-duplicated static scanner results
7
+ 2. FACTS.json — stack, routes, auth-model candidates, attack surface
8
+ 3. AGENT-BRIEFING.md — marching orders + staged probe scripts for your AI agent
9
+
10
+ It never calls an LLM, never runs a server, and never needs a running instance of
11
+ the target app. Running the probes and applying fixes is the agent + human's job.
12
+ """
13
+
14
+ __version__ = "0.1.0"