v0.21.0 — two binaries, framework-neutral security rules, and the first CRITICAL cross-layer finding
v0.21.0 — two binaries, version-stamped caches, framework-neutral security rules, and the first CRITICAL cross-layer finding
Highlights
- Two binaries per release.
zzop(the plain CLI:analyze,cross,endpoint,analyze-envelope,validate-envelope,validate-rule-pack,contract,version) andzzop-mcp(the MCP stdio server) — thin entries over one shared library. Release assets ship both, 5 platforms each, plus the.mcpbbundles. (Reverses v0.20.0's single-binary direction; npm remains removed.) - Version SSOT. The workspace
Cargo.tomlversion is the single source; both binaries report it viaCARGO_PKG_VERSION, and CI fails the release unless the pushed tag,Cargo.toml, and the plugin manifest all agree. cross-layer/retrying-write-no-idempotencyis now CRITICAL and two-sided. A retry-configured frontend write joined to a real provider route fires only when no idempotency guard is witnessed on that provider: natively (an inline handler reading theIdempotency-Keyheader) or via an injectedidempotency-guardedattribute (Mode Battributes, exact IoKey or PathScope). Every finding carries a paste-readydata.injectionStub. Suppress per-site with the standard marker if a handler is idempotent in a way neither channel expresses.httppack goes framework-neutral.auth-gatesandroute-exposuremigrated from anapiRoutes.-only line scan to whole-tree io-scan over ASSEMBLED provides — they now fire on Express, Hono, NestJS, Spring, ASP.NET Core, FastAPI, Django, gin, axum routes alike.auth-gatesvetoes on theauth-guardedattribute (native middleware/decorator/annotation recognition — produced even whenmutating-route-no-authis disabled — or Mode B injection) and deliberately dropped its same-line keyword carve-out (a lexical token is not auth evidence).read-model-pathstays, explicitly reframed as a house-convention rule.- DSL io-scan matcher grew up. Evaluates whole-tree (it now sees router-mount/controller-prefix/file-convention/whole-corpus composed routes), and gained
symbol_pattern,attr_present/attr_absent,anchor_exclude_pattern,file_exclude_pattern, plus suppress-marker support (// marker-okand# marker-ok). - Precision fixes. Next.js
pages/apiverb detection is handler-body-scoped with the real request parameter name (helper-function mentions no longer mint verbs). Python instance-egress bindings are last-write-wins (a client variable reassigned to a dict no longer misreads.getas HTTP). Hand-rolled fixed-method wrappers over builtinfetchnow join the cross-layer graph natively (the S7 self-report quiets automatically for them).
Behavior changes to expect
- Findings previously at
warningforcross-layer/retrying-write-no-idempotencyare nowcritical(or vetoed entirely when the provider guard is witnessed). http/auth-gatesandhttp/route-exposurecan fire on trees that never produced them before (any framework with/admin//internalresp. dev/debug-style route segments). Escape hatches: theauth-guardedattribute,// auth-gate-ok/# auth-gate-okmarkers,rules: {"http/auth-gates": "off"}.- Keyword-bearing handler names (
adminHandlers.x,guardedHandlers.y) no longer clearauth-gates; route-level HOF guards (requireAdmin(handler)) currently fire as a known, suppressible false positive until natively recognized. - Next.js
pages/apiroutes may report FEWER verbs (helper-function false verbs removed) or degrade to the serve-all sentinel for HOF-wrapped default exports — disclosed viacross-layer/unknown-verb-route, never silently mis-keyed. - Every cache re-parses cold once after upgrading: all cache-bust tokens moved to package-version stamps (
<scope>/<last-change-version>). Subsequent releases that leave a scope untouched will keep its warm cache — that is the new scheme's point. - Custom packs using io-scan rules: those findings now evaluate whole-tree (composed provides visible, attributes consultable) and honor suppress markers; they no longer appear in per-file cached findings.
Corpus check (RealWorld pairs)
No regressions: Angular×Spring 19 edges, axios×ASP.NET 19, axios×FastAPI 19, Vite×Django unchanged; Vite×Express improved 39 → 48 joined edges vs v0.20.0 (builtin-fetch wrapper joins + earlier fixes). New io-scan rules fire on zero corpus trees (no admin/debug paths there) — verified against synthetic e2e fixtures instead.