WebScan v2.5.1
WebScan v2.5.1 — Security audit + coverage boost
🔒 Security — full audit pass
A complete static-security audit of the codebase surfaced 4 HIGH and 6 MEDIUM
issues. All HIGH-severity findings are fixed in this release; MEDIUM/LOW are
either fixed or explicitly documented.
Fixed (HIGH)
-
H-1: auth-header / cookie leak on cross-origin redirect (CWE-200 / CWE-522).
The scan engine now installs anaiohttp.TraceConfigthat strips
Authorization,Cookie,X-API-Key,X-Auth-Tokenand
Proxy-Authorizationfrom any redirect hop whose host differs from the
original request. Without this,--basic-auth admin:secretagainst a target
that returned302 Location: http://attacker/capturewould replay the
credentials on the attacker host. Seeengine._build_redirect_safe_trace. -
H-2: SSRF in
file_uploadvia cross-originform.action(CWE-918).
The plugin now refuses to follow a<form action>that points to a different
origin than the target. A target page could otherwise make the scanner POST
webscan-test.txt(and any auth headers / cookies, see H-1) to an
attacker-controlled host. The sharedsame_origin()helper lives in
utils.httpand is now reusable across plugins. -
H-3:
webscan serveHTTP backend — body-size + caps + validation
(CWE-306 / CWE-400 / CWE-770). The/scanendpoint now rejects bodies larger
than 64 KiB (HTTP 413), validates thattargets/pluginsare lists, and
clampstimeout(≤60s) andconcurrency(≤32) to safe bounds so a single
client cannot exhaust the server. Auth/rate-limit middleware remains the
operator's responsibility when binding to0.0.0.0— seeserver.pydocstring. -
H-4: state-changing plugins now opt-in (CWE-400).
mass_assignment,
race_conditionandrequest_smugglingare no longer run by default — they
sendPUT, parallel GET, and smuggled POST requests that can actually mutate
state on a vulnerable target. The operator must opt in explicitly with
--plugins mass_assignmentetc.OPT_IN_PLUGINSis the single source of
truth inregistry.py.
Fixed (MEDIUM / LOW)
- M-3: third-party plugin supply-chain guard (CWE-1357). A third-party
entry-point plugin whose name collides with a built-in (e.g. a malicious
package registeringheadersto harvest auth credentials) is now rejected
with a stderr warning rather than silently overriding the built-in. - M-5: dead-code
verify_sslfield — clarified in-place as a documented
no-op. The scan engine always disables cert verification (scanners audit
self-signed hosts); the CLI flag is kept for compatibility. - L-2: version sync —
webscan.__version__andserver.pyFastAPI(version=)
now both read2.5.x, matchingpyproject.toml. - L-3: proxy credential masking in stdout (CWE-532).
--proxy http://user:pass@...is now printed ashttp://***@...in the
scan banner, so credentials no longer leak to CI/CD logs, tmux scrollback,
etc. - L-5: CI workflow —
actions/checkout@v6/actions/setup-python@v6
(which don't exist) replaced with@v4/@v5.
🧪 Tests — coverage 96% → 97%
- +40 new tests (800 → 840 passed) covering the previously-untested
branches introduced by the security fixes:tests/test_security_hardening.py(new, 15 tests) — redirect-trace
behaviour,same_origin/same_hosthelpers, plugin-discovery supply-chain
guard,OPT_IN_PLUGINSmembership,_mask_proxy_url.tests/test_ai.py—ai_availablewith explicit key / env key,
_build_clientSDK-missing / SDK-error branches, triage skip-empty,
summary unavailable,_first_text/_first_jsonedge cases.tests/test_server.py— body-size 413, validation for targets/timeout/
plugins,_confidence_from_strinvalid input,create_app/run_server
without serve extra, timeout/concurrency clamping.tests/test_cli.py—_run_aiunavailable / available / quiet paths,
webscan servesubcommand (with and without serve extra),main()serve
dispatch, proxy-credential masking in_print_setup.
🛠 Tooling
pyproject.toml[tool.ruff.lint.per-file-ignores]—tests/**/*.pynow
explicitly ignoresANN401/ANN001/ANN201/ANN204(test fakes /
shims legitimately needtyping.Anyand don't need explicit return types).
Removes ~20 spurious lint errors previously suppressed with# noqa.
📊 Numbers
- 38 plugins (unchanged)
- 840 tests (was 800)
- 97% coverage (was 96%)
- 61 source files
- ruff clean, mypy --strict clean
⬆️ Upgrade
pip install --upgrade webscan-security==2.5.1📋 Known remaining work
- M-1 —
--no-verify-sslflag is a documented no-op (scanner always skips
cert verification by design). Field kept for CLI compatibility. - M-2 —
resp.text()calls in some plugins do not cap body size; a shared
fetch_body()helper is planned for v2.5.2.