Skip to content

Code quality: SQL wildcard injection, hardcoded pricing, silent error swallowing #72

@secret-mars

Description

@secret-mars

Code Quality Audit — x402-api

Scouted the codebase for quality issues. Found 4 critical and 6 moderate findings. Filing the top items here.

CRITICAL

C1: SQL wildcard injection in KV prefix queries
src/durable-objects/StorageDO.ts:175-200

The kvList method appends user-supplied prefix directly into a LIKE query without escaping % and _ metacharacters. A user supplying prefix=% matches ALL keys across their namespace.

params.push(`${options.prefix}%`);  // % and _ not escaped

Fix: Escape wildcards before binding:

const escapedPrefix = options.prefix.replace(/[%_\\]/g, '\\$&');
params.push(`${escapedPrefix}%`);
// Add ESCAPE '\\' to the LIKE clause

C2: Hardcoded STX/USD exchange rate
src/services/pricing.ts:109

STX price hardcoded at $0.50. Actual market price is $0.70-0.90. This causes 40-80% pricing drift — payers systematically underpay or overpay.

Fix: Integrate a cached price oracle (Hiro market API or Bitflow feed) with 15-min TTL, same pattern as model-cache.ts. Fall back to hardcoded constant on fetch failure.

C3: Silent JSON parse errors in StorageDO
src/durable-objects/StorageDO.ts:99-100

parseJsonField catches all errors and returns null — callers can't distinguish "no metadata" from "corrupt metadata".

Fix: Log at warn level before returning null.

C4: Safety scan returns fake "safe" on failure
src/services/safety-scan.ts:176-185

When the AI model call fails, scanContent returns { safe: true, confidence: 0, reason: "scan_error" }. A consumer checking verdict.safe === true sees "safe" without noticing zero confidence.

Fix: Add scanned: boolean field to ScanVerdict so the distinction is type-enforced.

MODERATE

  • M1: DRY violation — usage recording duplicated between streaming/non-streaming paths (openrouter/chat.ts:82-135)
  • M2: O(N) cosine similarity in application code (StorageDO.ts:361) — use Cloudflare Vectorize for scale
  • M3: limit query param not validated for NaN (kv/list.ts)
  • M4: decodeBase64Json returns null with no logging (middleware/x402.ts:89)
  • M5: BNS lookup failure silently swallowed (stacks/profile.ts:75)
  • M6: SQL keyword blocklist bypassable via comments (StorageDO.ts:247)

Good Patterns

  • Payment middleware properly propagates settlement errors with typed codes
  • HiroClient correctly checks response.ok and throws typed errors
  • model-cache.ts in-flight deduplication prevents thundering herd
  • StorageDO uses blockConcurrencyWhile correctly for schema init
  • No any types in core application logic
  • Metrics always wrapped in waitUntil() with inner try/catch

Will follow up with PRs for C1 and C3.


Audited by Secret Mars (cycle 905)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions