pentest — attack surface report
env.assets.fetch. It exposes no dynamic endpoints, no database access, no filesystem writes, and no realtime publishing. The exploitable surface today is therefore the platform guest API contract itself (which any future handler code can reach) plus the static asset handler. The findings below enumerate that surface and the concrete exploit classes available against it.
1. Deployed surface (as reviewed)
export default {
async fetch(request, env) {
env.log.info("request", { method: request.method, path: request.path });
return env.assets.fetch(request);
},
};
- Every pathname under the room origin is routed to this single handler; there is no fixed route table.
- All requests are logged with method + path, then served from immutable repository assets.
- No
env.db,env.fs,env.realtime.publish, or request-body reads are used.
2. Exposed guest API surface
These are the capabilities the WASM service can reach. Each is a potential attack surface once handler code uses it.
| Capability | Reachable via | Attack surface |
|---|---|---|
request | method, url, path, headers, bounded text()/json() | Untrusted input into any downstream sink (SQL, fs paths, HTML, logs, realtime). |
Response | handler return value | Reflected content, missing security headers, content-type confusion. |
env.db | room-scoped SQLite: exec, query, prepare().all/get/run | SQL injection if input is concatenated; resource exhaustion via expensive queries. |
env.fs | 5 MiB scratch: list, readText, writeText, delete | Path traversal attempts, quota exhaustion, data persistence across isolates. |
env.assets.fetch | immutable repository assets | Path traversal to read unintended repo files; cache/asset enumeration. |
env.log | bounded structured events | Log injection / flooding; secret leakage if sensitive bodies are logged. |
env.realtime.publish | bounded ephemeral room events | Event spoofing, flooding, and client-side trust of unauthenticated bus data. |
window.roomSocket | host-owned reconnecting WebSocket; roommessage events | DOM XSS if message payloads are rendered without escaping. |
3. Findings
F-01 · Unauthenticated realtime bus trusted as authorization critical
Client bus events are unauthenticated. Any handler that treats a roommessage payload (or a value echoed back through env.realtime.publish) as proof of identity or permission is trivially spoofable.
Exploit: a client publishes a crafted event claiming an admin action; a handler that trusts it performs the privileged operation. Fix: authorize only from authenticated server-side context; never from bus data.
F-02 · SQL injection via concatenated input critical
env.db supports parameterized SQL, but nothing forces it. Any handler that builds SQL by string concatenation with request data is injectable.
// vulnerable
env.db.exec(`SELECT * FROM users WHERE name = '${name}'`);
// safe
env.db.prepare("SELECT * FROM users WHERE name = ?").all(name);
Exploit: name = ' OR '1'='1 bypasses filters; stacked/UNION payloads read or mutate other rows. Fix: parameterize every query; never interpolate request data.
F-03 · DOM XSS via realtime / reflected content high
HTML pages receive a host WebSocket and roommessage events. Rendering message fields with innerHTML (or reflecting request data into HTML) executes attacker script in the room origin.
Exploit: publish <img src=x onerror=...> as a message field; a client that injects it runs script. Fix: use textContent, escape output, and set a restrictive Content-Security-Policy.
F-04 · Path traversal against env.fs / env.assets.fetch high
env.fs is documented to forbid escaping the room and symlinks; env.assets.fetch serves immutable repo assets. Handlers that pass user-controlled paths into either must not assume the platform sanitizes for them.
Exploit: ../../, absolute paths, encoded traversal (%2e%2e%2f), or null bytes to reach unintended files. Fix: allowlist expected paths; reject anything containing traversal or absolute components before calling the API.
F-05 · Log injection and secret leakage medium
All requests are logged with method + path. Paths are attacker-controlled and may contain newlines or forged structured fields, polluting telemetry. Logging request bodies or headers risks leaking secrets.
Exploit: crafted path with embedded newlines forges log lines; sensitive query params end up in logs. Fix: log bounded, structured fields only; never log bodies, tokens, or credentials.
F-06 · Missing security headers / content-type confusion medium
The handler returns env.assets.fetch(request) verbatim with no added headers. There is no CSP, X-Content-Type-Options, or framing policy.
Exploit: MIME sniffing turns an uploaded/echoed asset into executable script; framing enables clickjacking. Fix: set Content-Security-Policy, X-Content-Type-Options: nosniff, and X-Frame-Options: DENY on responses.
F-07 · Unbounded request body / method handling low
The handler ignores method and body and forwards to the asset layer. Non-GET methods and large bodies are accepted and logged.
Exploit: large or malformed bodies consume parse/log budget. Fix: reject unexpected methods early and bound body reads.
4. Denial-of-service / resource exhaustion
The runtime bounds CPU, wall time, memory, output, database, network, and log usage per request, but a handler can still amplify cost within those bounds. Available DoS classes:
| ID | Vector | Mechanism | Sev |
|---|---|---|---|
| D-01 | Request flood | High-volume requests to the single handler; each spawns a fresh isolate and a log write. Cost is per-request even for static passthrough. | high |
| D-02 | Expensive SQL | Unindexed scans, large JOINs, or repeated exec calls burn the per-request DB budget and grow the room database toward its size cap. | high |
| D-03 | Database size exhaustion | Repeated inserts fill the bounded room database; once full, legitimate writes fail. | medium |
| D-04 | Filesystem quota exhaustion | Writing to the 5 MiB env.fs scratch until full; persistent across isolates and deployments. | medium |
| D-05 | Realtime publish flood | High-rate env.realtime.publish events amplify fan-out to every connected client. | medium |
| D-06 | Log flooding | Requests with unique paths force a log line each, filling bounded log storage and drowning signal. | low |
| D-07 | Large response amplification | Handlers that echo or expand input produce large outputs within the output bound, consuming bandwidth. | low |
| D-08 | JSON nesting bomb | Deeply nested or oversized JSON passed to request.json() burns CPU/memory parsing within the per-request bound. | medium |
| D-09 | ReDoS | Catastrophic regex backtracking on attacker-controlled input pins the isolate until the wall-time bound trips. | medium |
| D-10 | Realtime / WebSocket flood | Clients spam the room bus; handlers that react per message amplify fan-out to every connected client. | medium |
| D-11 | __ref cache-busting flood | Unique ?__ref= values defeat caching and force fresh isolate + asset resolution per request. | low |
| D-12 | Connection exhaustion | Many concurrent window.roomSocket clients hold host WebSocket resources. | low |
Mitigations: rate-limit per client at the edge, cap request/response sizes, index hot query paths, enforce quotas before writes, and coalesce realtime events.
5. Additional findings (second pass)
F-08 · Server source disclosure via static assets high
env.assets.fetch serves the immutable repository, and the handler forwards every path to it. Repository files such as worker.js and README.md are therefore directly fetchable at the room origin.
Exploit: GET /worker.js returns the server handler source, revealing routing, sinks, and any embedded logic to an attacker. Fix: keep no secrets or sensitive logic in served repo files; treat all repository content as public.
F-09 · Deployment selection abuse via __ref medium
The host-reserved __ref query parameter selects which commit is served. __ref=head exposes unreleased code, and any commit hash is lazily servable.
Exploit: request ?__ref=head to preview unpublished changes, or pin to a stale commit that still contains a since-fixed flaw. Fix: never rely on obscurity of unreleased commits; assume every commit is public.
F-10 · Prototype pollution via JSON body merge high
request.json() yields an attacker-controlled object. Any handler that deep-merges it into config or state can pollute Object.prototype.
Exploit: body {"__proto__":{"isAdmin":true}} sets a property on every object, bypassing naive checks. Fix: validate against a schema, use null-prototype objects, and reject __proto__/constructor/prototype keys.
F-11 · JSON parse / nesting DoS medium
Deeply nested or oversized JSON passed to request.json() consumes CPU and memory before any validation runs.
Exploit: a body of thousands of nested arrays/objects forces expensive parsing. Fix: bound body size and nesting depth before parsing.
F-12 · ReDoS in handler regex medium
Handlers that run regexes over untrusted input can hit catastrophic backtracking.
Exploit: input crafted for a vulnerable pattern (e.g. nested quantifiers) pins the isolate until the wall-time bound trips. Fix: avoid regex on untrusted input or use linear-time patterns.
F-13 · Stored XSS via persisted db/fs content high
Data written to env.db or env.fs persists across isolates and deployments. If it is later rendered without escaping, it executes in the room origin.
Exploit: store <script>…</script> via one request; a later page renders it and runs attacker script. Fix: escape on output regardless of storage; add a strict CSP.
F-14 · Error / stack disclosure low
Unhandled exceptions may surface internal paths, SQL fragments, or stack frames to the client.
Exploit: malformed input triggers an error whose message leaks implementation detail. Fix: catch and return generic errors; log detail server-side only.
F-15 · Trusting client-supplied headers medium
X-Forwarded-For, X-Real-IP, and custom auth headers are attacker-controlled.
Exploit: spoof X-Forwarded-For to bypass IP-based rate limits or forge identity. Fix: derive identity and client address from host-authenticated context only.
F-16 · CSRF on state-changing endpoints medium
If the service uses cookie/session auth, cross-site requests can trigger writes.
Exploit: a third-party page submits a form to a mutating endpoint using the victim's ambient credentials. Fix: require CSRF tokens, SameSite cookies, and origin checks.
F-17 · SQLite wildcard / second-order injection medium
Even parameterized queries can be abused when LIKE/GLOB metacharacters or stored values are reused in later queries.
Exploit: a % or _ in user input widens a LIKE match; a value stored once and concatenated later injects on reuse. Fix: escape wildcards and parameterize every reuse.
F-18 · Permissive CORS low
If a handler adds Access-Control-Allow-Origin: * alongside credentials, any origin can read responses.
Exploit: a malicious origin reads authenticated responses cross-site. Fix: use a strict origin allowlist and never combine wildcard with credentials.
F-19 · Cache poisoning / missing cache controls low
Responses carry no Cache-Control or Vary, and __ref varies content by query string.
Exploit: a shared cache serves one user's response to another, or caches a stale commit. Fix: set explicit Cache-Control and Vary.
6. Out of scope / not reachable
- No shell, Node.js, Bun, module loading, environment variables, guest sockets, or outbound network from service code.
- SQLite is host-owned and tenant-scoped; cross-database attachment and administrative SQL are forbidden.
- Deployments are immutable and content-addressed; the working tree is never canonical.
- Host-owned telemetry cannot be modified from the repository.
7. Prioritized remediation
- Authorize only from authenticated server-side context — never from realtime bus data (F-01).
- Parameterize all SQL and escape
LIKE/GLOBwildcards (F-02, F-17). - Escape all output, including persisted db/fs content, and add a strict CSP (F-03, F-06, F-13).
- Allowlist paths before
env.fs/env.assets.fetch; treat all repo files as public (F-04, F-08). - Validate JSON bodies against a schema; reject prototype keys and bound size/depth (F-10, F-11).
- Derive identity and client address from host-authenticated context only (F-15).
- Add CSRF protection and strict CORS/cache headers (F-16, F-18, F-19).
- Log bounded structured fields only; never bodies or secrets (F-05, F-14).
- Add edge rate limiting and size caps to blunt DoS classes (D-01…D-12).