Skip to content

[limen BLD-promptscope-harden] promptscope: harden — Harden the main entry points: input validation, error handli#7

Open
4444J99 wants to merge 1 commit into
mainfrom
limen/bld-promptscope-harden-e331
Open

[limen BLD-promptscope-harden] promptscope: harden — Harden the main entry points: input validation, error handli#7
4444J99 wants to merge 1 commit into
mainfrom
limen/bld-promptscope-harden-e331

Conversation

@4444J99

@4444J99 4444J99 commented Jun 19, 2026

Copy link
Copy Markdown
Member

Autonomous limen dispatch of task BLD-promptscope-harden.

Harden the main entry points: input validation, error handling, and structured logging on the primary request/CLI paths.

Produced in an isolated worktree off origin — review before merge.

Summary by Sourcery

Harden the PromptScope API entrypoints with stricter validation, structured logging, and more robust error handling across all primary request paths.

Enhancements:

  • Introduce a structured JSON logging framework with per-request context, trace IDs, and safe error message extraction for all API handlers.
  • Add centralized JSON error helpers and uniform method-not-allowed handling to standardize API responses and include request IDs for troubleshooting.
  • Tighten request input validation, including body size limits, strict JSON-object parsing, prompt field validation, and bounded share ID checks.
  • Improve resilience of analyze, license, share, and checkout endpoints by handling downstream failures gracefully and avoiding leakage of internal error details.
  • Refactor the main fetch handler into a guarded API router that routes non-API paths to static assets, logs request outcomes, and converts uncaught errors into clean 500 responses.

… error handli

limen task BLD-promptscope-harden
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@sourcery-ai

sourcery-ai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds structured JSON logging, consistent error handling, and stricter input validation to all primary API entrypoints, and routes non-API paths directly to static assets while enforcing size limits and safer behavior around external calls and KV operations.

File-Level Changes

Change Details Files
Introduce structured JSON logging with per-request context and centralized error responses.
  • Add LogLevel type, errorMessage helper, and emitLog function to output single-line JSON logs with timestamps and request metadata while avoiding secrets.
  • Add RequestContext and makeContext to capture request id, method, and path, and provide a log function that enriches log entries.
  • Add jsonError helper to standardize error response bodies with request_id and x-request-id header, plus methodNotAllowed helper for 405 responses.
  • Wrap main fetch handler in try/catch to log unhandled errors and return a generic 500 with a request id, and to log a per-request summary event with status and latency.
src/index.ts
Tighten input validation and body handling for JSON APIs, especially around prompt input and request size limits.
  • Introduce MAX_BODY_BYTES and MAX_SHARE_ID_CHARS constants to bound request body size and share id length.
  • Add readJsonObject helper to enforce a pre-parse size cap (via content-length), validate JSON, and require a plain object body, returning standardized error responses on failure.
  • Add validatePrompt helper to require a non-empty string prompt within MAX_PROMPT_CHARS, avoiding implicit String() coercion.
  • Refactor handleAnalyze, handleLicense, and handleShare to use readJsonObject/validatePrompt instead of ad-hoc JSON parsing and string coercion.
src/index.ts
Harden behavior of key API endpoints around error handling, logging, and KV/external integrations.
  • In handleAnalyze, log rate limiting, inference failures, malformed model output, and successful analyses with plan/score/char_count, while returning generic error messages (e.g., 502 on inference failure).
  • In handleLicense, log license checks with validity and source, and use jsonError for method errors.
  • In handleShare, add KV put/get error handling with logging, enforce share id format and length, and use jsonError for all error paths including not found/expired and invalid id.
  • In handleCheckout, switch to jsonError for missing configuration and method errors, preserving existing GET redirect and POST JSON behaviors.
src/index.ts
Rework top-level routing to cleanly separate static assets from API routes and to unify API error semantics.
  • Change fetch handler to pass non-/api paths directly to env.ASSETS.fetch without additional logic.
  • Route all /api and subpaths through a RequestContext-aware dispatcher that uses handleApiIndex, handleAnalyze, handleLicense, handleShare, and handleCheckout, with a default 404 via jsonError.
  • Ensure all non-redirect API responses receive an x-request-id header from RequestContext, making it easier to correlate client errors with logs.
src/index.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The readJsonObject helper only enforces MAX_BODY_BYTES when content-length is present and trustworthy, so very large bodies without that header can still be fully read into memory; consider adding a streaming size guard (e.g., manually reading the body and aborting after the limit) to make the limit robust.
  • For methodNotAllowed, you expose the allowed methods in the JSON body but don’t set an Allow response header; adding that header would better match HTTP semantics and make it easier for some clients/tooling to adapt.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `readJsonObject` helper only enforces `MAX_BODY_BYTES` when `content-length` is present and trustworthy, so very large bodies without that header can still be fully read into memory; consider adding a streaming size guard (e.g., manually reading the body and aborting after the limit) to make the limit robust.
- For `methodNotAllowed`, you expose the allowed methods in the JSON body but don’t set an `Allow` response header; adding that header would better match HTTP semantics and make it easier for some clients/tooling to adapt.

## Individual Comments

### Comment 1
<location path="src/index.ts" line_range="166-167" />
<code_context>
+  });
+}
+
+function methodNotAllowed(ctx: RequestContext, allow: string): Response {
+  return jsonError(ctx, 'method not allowed', 405, { allow });
+}
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** The 405 helper reports allowed methods in the JSON body but does not emit an HTTP `Allow` header.

To follow HTTP semantics and support more clients, also set an `Allow` response header (e.g., by letting `jsonError` accept extra headers or by adding the header on the `Response` returned from this helper).

Suggested implementation:

```typescript
function jsonError(
  ctx: RequestContext,
  error: string,
  status: number,
  extra: Record<string, unknown> = {},
  extraHeaders: Record<string, string> = {},
): Response {
  // request_id is additive — existing clients read `error`; new clients can
  // quote request_id when reporting problems.
  return Response.json({ error, request_id: ctx.reqId, ...extra }, {
    status,
    headers: { 'x-request-id': ctx.reqId, ...extraHeaders },
  });
}

```

```typescript
function methodNotAllowed(ctx: RequestContext, allow: string): Response {
  return jsonError(
    ctx,
    'method not allowed',
    405,
    { allow },
    { Allow: allow },
  );
}

```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/index.ts
Comment on lines +166 to +167
function methodNotAllowed(ctx: RequestContext, allow: string): Response {
return jsonError(ctx, 'method not allowed', 405, { allow });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): The 405 helper reports allowed methods in the JSON body but does not emit an HTTP Allow header.

To follow HTTP semantics and support more clients, also set an Allow response header (e.g., by letting jsonError accept extra headers or by adding the header on the Response returned from this helper).

Suggested implementation:

function jsonError(
  ctx: RequestContext,
  error: string,
  status: number,
  extra: Record<string, unknown> = {},
  extraHeaders: Record<string, string> = {},
): Response {
  // request_id is additive — existing clients read `error`; new clients can
  // quote request_id when reporting problems.
  return Response.json({ error, request_id: ctx.reqId, ...extra }, {
    status,
    headers: { 'x-request-id': ctx.reqId, ...extraHeaders },
  });
}
function methodNotAllowed(ctx: RequestContext, allow: string): Response {
  return jsonError(
    ctx,
    'method not allowed',
    405,
    { allow },
    { Allow: allow },
  );
}

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces structured logging, centralized request context, standardized error responses with trace IDs, and robust JSON body validation (including size limits) to a Cloudflare Workers API. The review feedback suggests three key improvements: ensuring RFC 7231 compliance by adding an Allow header to 405 Method Not Allowed responses, hardening body parsing against DoS/OOM by strictly validating Content-Type and Content-Length headers, and safely handling potentially immutable response headers when appending the trace ID to prevent runtime TypeErrors.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/index.ts
Comment on lines +166 to +168
function methodNotAllowed(ctx: RequestContext, allow: string): Response {
return jsonError(ctx, 'method not allowed', 405, { allow });
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to the HTTP specification (RFC 7231 Section 6.5.5), a 405 Method Not Allowed response must include an Allow header containing a list of the supported methods for the target resource. Currently, methodNotAllowed returns the list of allowed methods in the JSON body but does not set the Allow HTTP header.

function methodNotAllowed(ctx: RequestContext, allow: string): Response {
  const resp = jsonError(ctx, 'method not allowed', 405, { allow });
  resp.headers.set('Allow', allow);
  return resp;
}

Comment thread src/index.ts
Comment on lines +176 to +181
async function readJsonObject(req: Request, ctx: RequestContext): Promise<BodyResult> {
const contentLength = Number(req.headers.get('content-length'));
if (Number.isFinite(contentLength) && contentLength > MAX_BODY_BYTES) {
ctx.log('warn', 'body_too_large', { content_length: contentLength, limit: MAX_BODY_BYTES });
return { ok: false, response: jsonError(ctx, 'request body too large', 413) };
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

To robustly harden the API entry points against Denial of Service (DoS) and Out-of-Memory (OOM) attacks, we should strictly validate the incoming request headers before parsing the body:

  1. Validate Content-Type: Ensure it is application/json to prevent parsing unexpected media types and mitigate potential CSRF vectors.
  2. Validate Content-Length: If Content-Length is missing, Number(null) evaluates to 0, which bypasses the size check entirely. Requiring and validating a finite Content-Length ensures that oversized payloads are rejected before they are read into memory.
async function readJsonObject(req: Request, ctx: RequestContext): Promise<BodyResult> {
  const contentType = req.headers.get('content-type');
  if (!contentType || !contentType.includes('application/json')) {
    return { ok: false, response: jsonError(ctx, 'content-type must be application/json', 415) };
  }

  const contentLengthHeader = req.headers.get('content-length');
  if (!contentLengthHeader) {
    return { ok: false, response: jsonError(ctx, 'content-length header is required', 411) };
  }

  const contentLength = Number(contentLengthHeader);
  if (!Number.isFinite(contentLength)) {
    return { ok: false, response: jsonError(ctx, 'invalid content-length', 400) };
  }

  if (contentLength > MAX_BODY_BYTES) {
    ctx.log('warn', 'body_too_large', { content_length: contentLength, limit: MAX_BODY_BYTES });
    return { ok: false, response: jsonError(ctx, 'request body too large', 413) };
  }

Comment thread src/index.ts
Comment on lines +614 to +616
// Stamp every API response with the trace id. Redirects (3xx) carry an
// immutable Headers guard, so skip them to avoid throwing.
if (resp.status < 300 || resp.status >= 400) resp.headers.set('x-request-id', ctx.reqId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Stamping the response headers directly using resp.headers.set can throw a runtime TypeError if the response headers are immutable (e.g., if a future API route returns a cached response or forwards a response directly from another fetch call). To ensure maximum resilience, wrap the header mutation in a try-catch block and fall back to reconstructing the response with the new headers if they are immutable.

      // Stamp every API response with the trace id. Redirects (3xx) carry an
      // immutable Headers guard, so skip them to avoid throwing.
      if (resp.status < 300 || resp.status >= 400) {
        try {
          resp.headers.set('x-request-id', ctx.reqId);
        } catch {
          const newHeaders = new Headers(resp.headers);
          newHeaders.set('x-request-id', ctx.reqId);
          resp = new Response(resp.body, {
            status: resp.status,
            statusText: resp.statusText,
            headers: newHeaders,
          });
        }
      }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant