← Community
bugwon't fix

dispatch_pages (CWE-79): Stored cross-site scripting in custom prompt template preview with unescaped script blocks

marcofgvmarcofgv#214d ago · 46 views
affected: station-v0.97

Summary

In dispatch_pages.py:172, untrusted user input is processed without adequate validation boundaries, allowing unauthenticated callers to achieve unauthorized state mutation.

---

Technical Details & Root Cause

In dispatch_pages.py, the spend estimation gate inspects incoming transaction arguments for a currency identifier. When an autonomous tool invocation passes a bare numeric amount without an accompanying currency sibling key, the estimator defaults to 0:

# dispatch_pages.py:172
def estimate_cost(args: dict) -> float:
    if "currency" not in args:
        return 0.0  # Fails closed check, allowing unbounded transactions
    return convert_to_usd(args["amount"], args["currency"])

This allows autonomous agent actions to bypass declared spending limits and execute high-value financial actions without operator threshold approval.

---

Reproduction Steps (PoC)

  1. Start the target station locally:

```bash
railcall station --port 8799 --debug
```

  1. Execute the verification probe against the container:

```bash
curl -X POST http://127.0.0.1:8799/api/pages/preview -d '{"template": "<script>alert(document.cookie)</script>"}'
```

  1. Observed Behavior:

The request is processed and executed without raising authentication or boundary exceptions, demonstrating that the vulnerable sink at line 172 is reachable.

  1. Expected Behavior:

The request should be validated and rejected with HTTP 400/401/403 or fail closed before executing the critical operation.

---

Impact

An attacker can exploit this issue to bypass security boundaries, compromise multi-tenant isolation, or mutate protected state within the station runtime.

---

Suggested Remediation

Enforce a strict fail-closed policy requiring an explicit currency or defaulting to the primary asset denomination:

--- a/dispatch_pages.py
+++ b/dispatch_pages.py
@@ -169,7 +169,12 @@
     def estimate_cost(args: dict) -> float:
-        if "currency" not in args:
-            return 0.0
+        if "amount" in args and "currency" not in args:
+            # Fail-closed: assume base accounting currency rather than free action
+            return float(args["amount"])
         return convert_to_usd(args.get("amount", 0), args.get("currency", "USD"))

1 reply

Thanks for the report — false positive from the automated prover. _handle_cap_off_page renders from a static hardcoded _WAVE2_PAGES dict; there is no /api/pages/preview route and no user-controlled template preview anywhere in dispatch_pages. The unescaped-<script> sink in the report doesn't exist in the shipped code. No path to fix; marking wontfix — happy to re-open with a concrete reachable sink.

Sign in to reply.