pentest — attack surface report

Target: https://pentest.serverside.chat · Room: pentest · Owner: bwasti

Method: static review of the room repository (worker.js, index.html) plus the documented guest API contract of the QuickJS/WASM service runtime. No live exploitation was performed; findings are the reachable surface and the exploit classes each surface admits.

Bottom line. The currently deployed service is a static passthrough: it logs the request and forwards everything to 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);
  },
};

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.

CapabilityReachable viaAttack surface
requestmethod, url, path, headers, bounded text()/json()Untrusted input into any downstream sink (SQL, fs paths, HTML, logs, realtime).
Responsehandler return valueReflected content, missing security headers, content-type confusion.
env.dbroom-scoped SQLite: exec, query, prepare().all/get/runSQL injection if input is concatenated; resource exhaustion via expensive queries.
env.fs5 MiB scratch: list, readText, writeText, deletePath traversal attempts, quota exhaustion, data persistence across isolates.
env.assets.fetchimmutable repository assetsPath traversal to read unintended repo files; cache/asset enumeration.
env.logbounded structured eventsLog injection / flooding; secret leakage if sensitive bodies are logged.
env.realtime.publishbounded ephemeral room eventsEvent spoofing, flooding, and client-side trust of unauthenticated bus data.
window.roomSockethost-owned reconnecting WebSocket; roommessage eventsDOM 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:

IDVectorMechanismSev
D-01Request floodHigh-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-02Expensive SQLUnindexed scans, large JOINs, or repeated exec calls burn the per-request DB budget and grow the room database toward its size cap.high
D-03Database size exhaustionRepeated inserts fill the bounded room database; once full, legitimate writes fail.medium
D-04Filesystem quota exhaustionWriting to the 5 MiB env.fs scratch until full; persistent across isolates and deployments.medium
D-05Realtime publish floodHigh-rate env.realtime.publish events amplify fan-out to every connected client.medium
D-06Log floodingRequests with unique paths force a log line each, filling bounded log storage and drowning signal.low
D-07Large response amplificationHandlers that echo or expand input produce large outputs within the output bound, consuming bandwidth.low
D-08JSON nesting bombDeeply nested or oversized JSON passed to request.json() burns CPU/memory parsing within the per-request bound.medium
D-09ReDoSCatastrophic regex backtracking on attacker-controlled input pins the isolate until the wall-time bound trips.medium
D-10Realtime / WebSocket floodClients spam the room bus; handlers that react per message amplify fan-out to every connected client.medium
D-11__ref cache-busting floodUnique ?__ref= values defeat caching and force fresh isolate + asset resolution per request.low
D-12Connection exhaustionMany 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

7. Prioritized remediation

  1. Authorize only from authenticated server-side context — never from realtime bus data (F-01).
  2. Parameterize all SQL and escape LIKE/GLOB wildcards (F-02, F-17).
  3. Escape all output, including persisted db/fs content, and add a strict CSP (F-03, F-06, F-13).
  4. Allowlist paths before env.fs / env.assets.fetch; treat all repo files as public (F-04, F-08).
  5. Validate JSON bodies against a schema; reject prototype keys and bound size/depth (F-10, F-11).
  6. Derive identity and client address from host-authenticated context only (F-15).
  7. Add CSRF protection and strict CORS/cache headers (F-16, F-18, F-19).
  8. Log bounded structured fields only; never bodies or secrets (F-05, F-14).
  9. Add edge rate limiting and size caps to blunt DoS classes (D-01…D-12).

Generated from repository review of commit 2d40046. Static analysis only — no live exploitation performed.