Skip to content

Latest commit

 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🛡️ websec

Web Application Security Review for Claude Code

License: MIT Claude Code Detectors No CI required

One skill maps your codebase · thirty-three hunt a single vulnerability class each · one turns the results into a ranked report


⚡ Quick start

/plugin marketplace add emre-guler/websec
/plugin install websec@websec
/websec:scan          # full review — architecture, every applicable detector, one report

Nothing runs on its own. No hooks, no git hooks, no build gates. You invoke a review when you want one.


🔄 How it works

  /websec:analysis                    ──▶   .websec/out/architecture.md
  map the codebase                          stack · entry points · auth model · trust boundaries
          │
          ▼
  33 detectors, in parallel           ──▶   .websec/out/<detector>-results.md
  find candidates → check each              one file per vulnerability class
          │
          ▼
  /websec:report                      ──▶   .websec/out/final-report.md
  rank and consolidate                      + findings.json

/websec:scan runs all three and skips detectors the architecture rules out — no GraphQL schema, no graphql run.

Detectors do their work through two agents that ship with the plugin: websec:recon finds candidate sites, websec:verify traces and classifies them. Both are restricted to reading, searching, and writing their own output file — neither can edit your code, run your project, or reach the network. Ceilings in policy.yaml bound how many run at once and how many candidates each pass covers; anything left over is reported as unverified rather than dropped.

Already know what you are looking for? Run one detector directly:

/websec:access-control
/websec:sql-injection

🔍 What a finding looks like

Every finding carries the code path that justifies it — not just a line number.

### [VULNERABLE] Order lookup accepts any order ID

- **File**: `app/controllers/orders_controller.rb` (lines 41–47)
- **Endpoint**: `GET /orders/:id`
- **Issue**: The handler loads the order by the path parameter alone. The
  `before_action :require_login` on line 6 establishes identity but never
  checks ownership, and no policy call runs before the record is rendered.
- **Impact**: Any authenticated user can read any other customer's order,
  including billing address and line items.
- **Proof**: route (`config/routes.rb:22`) → `require_login`
  (`orders_controller.rb:6`, session lookup only) → `Order.find(params[:id])`
  (`:44`) → `render json: @order` (`:47`). No `current_user` constraint on the
  query and no comparison after it.
- **Remediation**: Scope the query to the caller — `current_user.orders.find(params[:id])`
  — so a foreign ID raises `RecordNotFound` instead of returning the record.
- **Dynamic Test**: authenticate as user A, request `GET <BASE_URL>/orders/<USER_B_ORDER_ID>`,
  and check whether the response body contains user B's data.
- **Confidence**: high

🏷️ Verdicts

Label Means Requires
🔴 VULNERABLE The flaw is present and reachable Full path traced, no effective control on it
🟠 LIKELY VULNERABLE A control exists but is incomplete, conditional, or bypassable Path traced; the remaining doubt named
🟢 NOT VULNERABLE A specific control was found The control identified at file:lines and explained
🔵 NEEDS MANUAL REVIEW The path could not be traced with confidence Where tracing stopped and what a human should inspect

🔵 NEEDS MANUAL REVIEW is a real answer, not a failure. For classes decided by deployed infrastructure — the proxy chain, hop versions, parser defaults — it is the honest one, and the finding still names the exact thing to go and check.


🎯 Detectors

💉 Injection
and execution

sql-injection · nosql-injection · os-command-injection · ssti · xxe · deserialization · prototype-pollution

🔑 Authorization
and identity

access-control · authentication · oauth · jwt · api

🌐 Client
and browser

xss · dom-based · csrf · cors · clickjacking · open-redirect · websockets

📡 Requests, proxies
and caches

ssrf · host-header · request-smuggling · web-cache-deception · web-cache-poisoning

📦 Data, logic
and surface

file-upload · path-traversal · information-disclosure · business-logic · race-conditions · graphql · llm

🔐 Secrets
and cryptography

crypto · secrets

Each detector states plainly what it is not, and hands neighbouring findings to the sibling that owns them — so an authorization gap does not get filed as injection, and the same flaw is not counted twice.


⚙️ Configure

Drop a .websec/policy.yaml in your repository. Any key you set replaces the default; anything you omit keeps it.

version: 1
classes:
  disabled: [prototype-pollution]          # no JavaScript in the runtime path
severity:
  overrides: { access-control: Critical }  # our objects are customer financial records
  fail_threshold: High                     # report prints an informational Gate line
rules:
  access-control:
    extra_checks: ["Every query must be scoped by tenant_id, not only user_id."]
    ignore_paths: ["legacy/**"]
Full schema
version: 1
output_dir: .websec/out       # where artefacts are written
batch_size: 3                 # candidates checked per parallel worker
limits:                             # these multiply — see the note below
  max_candidates_per_detector: 24   # excess is reported as unverified, never dropped
  max_parallel_batches: 3           # concurrent workers per detector
  max_detectors_in_flight: 2        # concurrent detectors in /websec:scan
classes:
  disabled: []                # detectors /websec:scan should skip
severity:
  overrides: {}               # detector name → Critical | High | Medium | Low
  fail_threshold: null        # null | Critical | High | Medium | Low
rules:
  <detector>:
    extra_checks: []          # extra questions, plain sentences
    ignore_paths: []          # globs excluded from the search
    notes: ""                 # free text passed to the detector
report:
  include_not_vulnerable: false

See references/policy.md for the merge rules and examples/policy.example.yaml for an annotated version.


📂 Output

.websec/
├── policy.yaml               # yours — commit it
└── out/                      # generated — gitignore it
    ├── architecture.md
    ├── <detector>-results.md
    ├── final-report.md
    └── findings.json

findings.json carries the same findings as structured data, for whatever you want to build on top.


🧩 Adding it to your workflow

The plugin never decides when to run — teams write that into their own CLAUDE.md. A common shape:

Run analysis once. On a pull request, run the detectors that match what changed. Do not recommend merging while a Critical or High finding is open, and never treat NEEDS MANUAL REVIEW as a pass.

See examples/CLAUDE.md.snippet for a paste-ready version.


🤝 Contributing

New detectors, corrections, and boundary fixes are welcome. CONTRIBUTING.md covers what a good detector does and what the checker enforces; docs/detector-template.md is the starting point, and skills/access-control/SKILL.md is the calibrated example to match.


📌 Scope

In Source-code review of web application vulnerability classes, plus the configuration that ships in the repository
Out Sending requests to a running application, changing your code, enforcing anything

Findings include a Dynamic Test field, but confirming it is a human step — run against a system you are authorised to test.


MIT licensed · see LICENSE

About

A Claude Code plugin that reviews web applications for security flaws — architecture recon, 33 vulnerability-class detectors, and a severity-ranked report.

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages