Eden
All case studies

Spasalon theme: CSRF on theme options

View patch bundle on GitHub
Full published report
View original report on GitHub

The reviewed summary below describes the scope of the fixture testing.

Spasalon theme: CSRF on theme options (CVE-2026-125843)

Source and API bundle

cve.now entry. Product: Spasalon WordPress theme ≤ 3.0 (lab: gabriel-spasalon-app).

Eden terminates the HTTP connection to the affected product, evaluates a declarative policy and — in tier 2 — a separately compiled native Rust module under signed module authority, before any byte reaches the application. It does not patch the affected component; the vendor's fixed release remains the durable repair.

Status: Remediated at the boundary, browser-scoped (tier 1 + tier 2), live-proven 2026-11-11T17:04:28Z on the installed eden-product-release:1.103.0 lab container.

The vulnerability

The Spasalon theme (≤ 3.0) persists options on a nonce-free admin action (admin-post.php?action=spasalon_save), so a forged cross-site POST can persist attacker-chosen settings under an admin session. The fixture (_csrf("spasalon"), port 18329) reproduces the delivery shape: an options save without an Origin header records a forged-save mark; the same save with a site Origin passes without effect.

The Eden patch

Tier 1 — declarative policy (blunt, fail-closed): deny every request under /wp-admin/admin-post.php, every plugin/theme admin action included — false positives measured below.

Tier 2 — native Rust module gabriel_spasalon_v1 (surgical): refuses action=spasalon_save saves that arrive without an Origin header; same-origin saves pass unchanged. Other admin actions are untouched.

Measured comparison (fixtures10, run 2026-11-11)

ProbeBaselineTier 1Tier 2 (module)Backend effect
benign save with Origin: https://shop.example200403 (false positive)200none
attack: CSRF save without Origin200 (options_saved recorded)403403blocked at tier 2

Performance trace (Eden telemetry, live 2026-11-11)

Measured with Eden's built-in request telemetry (gateway.request.baseline spans, read back through /api/v1/analytics/telemetry?signal=traces), not client wall-clock. Latency Eden introduces per remediation arm for this case:

Armnmeanp50p95min–max
Tier 1 — declarative policy, blocked request (403)1528 µs23 µs63 µs15 µs–63 µs
Tier 2 — native module, benign passes through (200)22381 µs347 µs575 µs309 µs–734 µs
Tier 2 — native module, attack refused (403)14146 µs140 µs209 µs118 µs–209 µs

Eden-added latency for the tier-2 remediation: benign requests complete in 381 µs mean (p50 347 µs, p95 575 µs) in-product, module evaluation included. Honest scope: lab fixtures on one host, fresh connection per measured request, warm requests only; module reactivated shards 2/2 with a fresh signed statement per measurement; spans carry the full authority lineage (module SHA-256, operation, publication). Not the EDEN-3640 performance qualification contract.

Tested against: eden-product-release:1.103.0-single (image sha256:809293e1f0b525ac…, build target eden_service_single, tooling revision 81a0724a…). Raw data: module2/telemetry-ab/spasalon-telemetry.json and module2/tier1-ab/spasalon-tier1.json.

Authority lifecycle and negatives

Import 201, activation: explicit_required; publisher enroll_key, host inventory + approve, reader inspect refused 403; tampered-statement admit 400; invalid-signature admit 403; admit prepared (not serving), activate → receipt serving_now, applied shards 2/2. Revoke → the identical attack gets 503 (fail-closed); re-approve + re-activate → 403 again. All recorded in the evidence JSON.

Honest scope — partial, browser-scoped

Origin/Referer-presence enforcement stops forged browser requests (browsers set Origin on cross-site POSTs and forbid header forgery), but a non-browser client can set any Origin it likes. The rule is therefore a browser-scoped partial by construction — recorded as such, in the same honesty class as the withdrawn marker modules. A nonce at the theme would be the durable fix. Module binary SHA-256 568a4037…, source modules/gabriel_spasalon_v1.rs.

Cost record

Run window 17:04:28Z → 17:04:38Z (per-CVE ledger, marginal session cost).

Evidence appendix

  • Fixture: gabriel-2026-11-11/fixtures10.py (_csrf("spasalon"), port 18329)
  • Evidence: gabriel-2026-11-11/module2/evidence/spasalon-module.json
  • Module source: docs/case-studies/modules/gabriel_spasalon_v1.rs

The vulnerability

The Spasalon theme (≤ 3.0) persists options on a nonce-free admin action (admin-post.php?action=spasalon_save), so a forged cross-site POST can persist attacker-chosen settings under an admin session. The fixture (_csrf("spasalon"), port 18329) reproduces the delivery shape: an options save without an Origin header records a forged-save mark; the same save with a site Origin passes without effect.

The Eden patch

Tier 1 — declarative policy (blunt, fail-closed): deny every request under /wp-admin/admin-post.php, every plugin/theme admin action included — false positives measured below.

Tier 2 — native Rust module gabriel_spasalon_v1 (surgical): refuses action=spasalon_save saves that arrive without an Origin header; same-origin saves pass unchanged. Other admin actions are untouched.

Measured comparison (fixtures10, run 2026-11-11)

ProbeBaselineTier 1Tier 2 (module)Backend effect
benign save with Origin: the test URL200403 (false positive)200none
attack: CSRF save without Origin200 (options_saved recorded)403403blocked at tier 2

Performance trace (Eden telemetry, live 2026-11-11)

Measured with Eden's built-in request telemetry (gateway.request.baseline spans, read back through /api/v1/analytics/telemetry?signal=traces), not client wall-clock. Latency Eden introduces per remediation arm for this case:

Armnmeanp50p95min–max
Tier 1 — declarative policy, blocked request (403)1528 µs23 µs63 µs15 µs–63 µs
Tier 2 — native module, benign passes through (200)22381 µs347 µs575 µs309 µs–734 µs
Tier 2 — native module, attack refused (403)14146 µs140 µs209 µs118 µs–209 µs

Authority lifecycle and negatives

Import 201, activation: explicit_required; publisher enroll_key, host inventory + approve, reader inspect refused 403; tampered-statement admit 400; invalid-signature admit 403; admit prepared (not serving), activate → receipt serving_now, applied shards 2/2. Revoke → the identical attack gets 503 (fail-closed); re-approve + re-activate → 403 again. All recorded in the evidence JSON.

Honest scope — partial, browser-scoped

Origin/Referer-presence enforcement stops forged browser requests (browsers set Origin on cross-site POSTs and forbid header forgery), but a non-browser client can set any Origin it likes. The rule is therefore a browser-scoped partial by construction — recorded as such, in the same honesty class as the withdrawn marker modules. A nonce at the theme would be the durable fix. Module binary SHA-256 568a4037…, source modules/gabriel_spasalon_v1.rs.

Cost record

Run window 17:04:28Z → 17:04:38Z (per-CVE ledger, marginal session cost).

Official CVE record