This tutorial takes a clean machine from installation to a scoped source audit, patch review, retest, upgrade and uninstall. The product promise is: Scope, audit, harden, and retest web projects with AI coding agents and reproducible evidence.
The deterministic path shown here reads local source files and does not contact a deployment. In the published v0.5.3 release it runs 20 built-in risk rules and 2 evidence-integrity rules, with deeper JavaScript/TypeScript and Python coverage. It remains a bounded first pass, not a general SAST scan or proof that a project is secure.
- macOS or Linux;
- Node.js 22 or 24;
- Git;
- a project you may inspect and modify.
See the compatibility matrix for the tested environment boundary.
Download every v0.5.3 asset, verify the checksums, extract the archive and install from that verified payload:
mkdir web-app-security-release && cd web-app-security-release
gh release download v0.5.3 --repo parousia8888/web-app-security-skill
sha256sum -c SHA256SUMS
tar -xzf web-app-security-skill-0.5.3.tar.gz
node web-app-security-skill-0.5.3/scripts/webapp-security.mjs install
webapp-security versionOn macOS, use shasum -a 256 -c SHA256SUMS when GNU sha256sum is unavailable. The release page
also publishes an SPDX SBOM, source manifest, build-provenance attestation and signed tag. See the
v0.5.3 release.
Use this path to evaluate the current main branch or contribute:
git clone https://github.com/parousia8888/web-app-security-skill.git
cd web-app-security-skill
node scripts/webapp-security.mjs install
webapp-security versionThe default installs Claude Code, Codex and the ordinary CLI. Use --target claude, codex, cli
or both to select a subset. Installation refuses unknown existing paths; --force only replaces
recognized current or legacy payloads and creates timestamped backups.
From a current checkout, run the complete tutorial against the intentionally misconfigured fixture:
tutorial_output="$(mktemp -d)"
node scripts/run-clean-room-tutorial.mjs --out "$tutorial_output"
cat "$tutorial_output/tutorial-result.json"The runner creates an isolated home, installs the CLI, denies network access, creates a persisted
scope, audits the before fixture, explains one lead, explicitly rebinds the hardened fixture,
retests it, upgrades and uninstalls. The expected baseline is four findings: one confirmed and
three suspected. The retest must record all four as fixed within the ten-minute budget.
Change to the root of a project you own or are authorized to inspect:
cd /path/to/your-project
webapp-security start . --run-id first-reviewReview .webapp-security/runs/first-review/security-scope.yml. It records a privacy-preserving
persisted subject ID, scope digest, discovered frameworks, package managers, lockfiles,
deployment/config paths, assumptions and blocked remote modes. The private identity record lives
under .webapp-security/project.json. Neither file grants authorization to contact a deployment.
Run the source audit into that scoped directory:
webapp-security audit .webapp-security/runs/first-review \
--name report --fail-on neverThe output includes report.json, report.sha256, report.md, report.html, report.sarif,
report.junit.xml and proposed.patch. Use JSON for automation, the sidecar for local integrity
checking, Markdown/HTML for review, SARIF/JUnit for CI, and the patch file only as a proposal.
The default policy gates confirmed HIGH security and supply-chain findings. Reports summarize by
domain, evidence state and severity. Keep --fail-on for the compatible security/supply-chain
threshold, and add a repeatable domain override only when that domain belongs in the CI gate:
webapp-security audit .webapp-security/runs/first-review \
--fail-on high --fail-on-domain reliability=high| State | Meaning | Required response |
|---|---|---|
confirmed |
Reproduced with sufficient sanitized evidence | Prioritize and retest the fix |
suspected |
A source or scanner lead lacks runtime/context evidence | Reproduce or close with evidence |
unknown |
The check or evidence source was unavailable | Restore evidence access; never count as pass |
not_applicable |
Outside the recorded scope or absent | Keep the scope reason |
Explain one finding without changing the project:
webapp-security explain <finding-id> \
--report .webapp-security/runs/first-review/report.jsonDo not promote a filename match, static pattern or AI suggestion to confirmed. For example,
enabled source maps remain suspected until a built artifact or owned deployment proves public
delivery.
The default explanation is deliberately readable before it is technical. For every actionable v3 finding, check these fields in order:
technicalTermandstate: the professional name and what the audit actually proved.plainLanguageandconsequence: what the code is doing and what might happen if the missing conditions are real.evidenceBoundary: what the rule did not establish, such as input flow or runtime reachability.proposal,alternativesandsideEffects: the suggested change, another viable path and what normal behavior could change.userDecisions,securityRetest,functionalRetestandrollback: decisions the project owner must make and the evidence required before keeping the change.
Open both the report and .webapp-security/runs/first-review/proposed.patch. The patch may contain
machine-applicable diffs and manual review instructions. It is never applied by audit, may not
cover every finding, and does not prove a fix.
Before changing source:
- Verify the evidence points to the intended component.
- Check whether the change affects production traffic, authentication, data, SEO or crawlers.
- Keep the smallest reviewable change and preserve the original report as the baseline.
- Run the project's own tests after the change.
Create a private review-only repair record for one finding:
webapp-security repair-plan <finding-id> \
--report .webapp-security/runs/first-review/report.json \
--out .webapp-security/runs/first-review/repair-review
webapp-security repair-validate \
.webapp-security/runs/first-review/repair-review/repair-record.jsonThe initial record remains review_required with approval pending and no patch applied. The CLI
does not edit it into an approved or applied state and does not edit project files. Authentication,
authorization, public routes, CORS, cookies/sessions, stored data and production infrastructure
always require an explicit owner decision. A repair reaches retested only after both its named
security verification and the affected normal product journey pass.
For an AI coding agent, use the canonical first-task prompt from the repository README or
README_AI.md. Tell the agent whether it may apply changes or must return
patch-only evidence. High-risk and production changes require explicit approval.
After reviewing and applying the chosen change, create a new run and write new evidence there:
webapp-security start . --run-id first-review-retest
webapp-security retest .webapp-security/runs/first-review-retest \
--name report \
--baseline .webapp-security/runs/first-review/report.json \
--fail-on highInspect summary.byBaseline in the new JSON report. A finding is fixed only when subject and
scope match, the rule identity is compatible, current coverage completed and the condition is
affirmatively absent. Removed or unavailable checks become unretested; incompatible revisions
become not_comparable. Keep runtime or deployment verification requirements for source-only
suspected results.
For a moved or fresh clone, first review a prior scope, then explicitly bind the clone:
webapp-security rebind /path/to/moved-project \
--scope /path/to/prior/security-scope.yml \
--acknowledge-subject <exact-subject-id>Historical v1 reports cannot become comparable. migrate-report preserves their byte digest and
explicit lineage in a new v2 document, while leaving the original unchanged. Establish a new v2
audit as the first comparable baseline.
Local source work does not authorize remote testing. Before any active request, record ownership or written authorization, exact origins/accounts, time window, prohibited actions and stop conditions. Never use a third-party hosted instance as a tutorial target.
Passive crawl inspection still sends HTTP requests. Sensitive-path probes and active rate-limit
checks additionally require --acknowledge-authorization. Stop if scope expands, third-party data
appears, production health degrades or evidence would expose a secret.
| Symptom | Resolution |
|---|---|
webapp-security: command not found |
Add ~/.local/bin to PATH, or invoke the checkout's node scripts/webapp-security.mjs |
Exit code 1 |
Findings met --fail-on; evidence was still written |
Exit code 2 |
Usage, scope, authorization or evidence setup failed; do not treat it as a pass |
Exit code 3 |
Required evidence was unknown, partial or unavailable and no configured confirmed threshold breach took precedence |
refusing to overwrite existing evidence |
Choose a new --out directory or report name; retain the baseline |
| Unsupported or ambiguous stack | Keep unknown and use the agent-guided methodology |
| Remote check blocked | Supply recorded authorization and acknowledgement only for an owned target |
Use the false-positive issue form
with the version, finding ID, minimal sanitized fixture, actual/expected state and environment. Do
not include tokens, cookies, account identifiers, private source or real client IPs. Use the private
channel in SECURITY.md when the report itself is sensitive.
The false-positive policy requires a reproduced failing regression before a rule changes.
Lifecycle commands never download code. Obtain and verify the newer release first, then run its payload:
node /path/to/new-release/scripts/webapp-security.mjs upgrade
webapp-security version
webapp-security uninstallUpgrade backs up recognized installations before replacement. Uninstall removes recognized current payloads and launchers while preserving prior backups; it refuses unknown directories.