Skip to content

Refuse a sandboxed component whose title is only whitespace - #393

Merged
davidmckayv merged 2 commits into
CopilotKit:mainfrom
kevin9327:fix/sandboxed-blank-title
Sep 6, 2026
Merged

Refuse a sandboxed component whose title is only whitespace#393
davidmckayv merged 2 commits into
CopilotKit:mainfrom
kevin9327:fix/sandboxed-blank-title

Conversation

@kevin9327

Copy link
Copy Markdown
Contributor

What this changes

POST /api/sandboxed took a title of spaces and stored it. The guard checked the field was present,
not that it said anything:

if (!body?.slug || !body.title) {  }

  slug: body.slug,
  title: body.title,

The slug is safe: SandboxedStore.save runs it against ^[a-z0-9][a-z0-9_]{0,38}[a-z0-9]$ and
refuses one made of spaces. The title has no check anywhere and is inserted verbatim, so the
component lands in the playground grid with nothing to read, and a Bot granted it is told the
component is called " ".

This is the third endpoint of that exact shape. #376 fixed POST /api/servers/custom and #377 fixed
POST /api/skills yesterday, both by trimming and refusing whitespace; this one was not in either
diff. The change here is the same two lines.

Where it runs

  • New state that outlives a request? None. The same row is written, with a trimmed title.
  • What happens on the second replica? Nothing differs. The check is per request and reads
    only the body.
  • Anything serialised? No change. save still upserts on sandboxedComponents.name.
  • Anything fanned out to a browser? No.
  • New listener, port, or schedule? None.

Boundary and audit

  • Every acting call still goes through the gateway: untouched, this is a save behind
    requireAdmin.
  • New refusals and new failures each write a row: the refusal is a 400 on a malformed body, the
    same class the two sibling endpoints answer without an audit row.
  • Nothing new is trusted from the client that the server can resolve itself: strictly less is
    trusted than before.

Changelog

  • A line in CHANGELOG.md under Unreleased.

Proof

server/tests/sandboxed-routes.test.ts, new. The two sibling fixes shipped without tests, so this
adds the ones that pin the behaviour for this route:

  • refuses a title that is only whitespace — 400, and the store is never called.
  • refuses a missing title, as it always did — the guard that already worked still works.
  • stores a padded title without its padding" Weather card " is saved as "Weather card".
  • leaves an ordinary title alone — no change to the ordinary path.

Before this change, two of the four fail:

(fail) stores a padded title without its padding
- "title": "Weather card",
+ "title": "  Weather card  ",
2 pass, 2 fail

After: 4 pass. bunx tsc --noEmit in server/ is clean, and bunx biome check on both files is
clean.

@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.

# Conflicts:
#	CHANGELOG.md
#	server/src/components/sandboxed-routes.ts
davidmckayv
davidmckayv previously approved these changes Sep 6, 2026

@davidmckayv davidmckayv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Landing as test-coverage only: #387 already ships the safer route validation (with a typeof guard #393's route change lacked, which would 500 on a non-string title). Kept #393's tests (pass against #387's route) and dropped its redundant route change + dup CHANGELOG. Net diff is the +104-line test file.

@davidmckayv davidmckayv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Deep-reviewed against live code (correctness, governance, no vendor/secret/scale issues). Composed build+tests green. CHANGELOG/format rebase on CI-validated substance.

@davidmckayv
davidmckayv merged commit 83a2995 into CopilotKit:main Sep 6, 2026
14 checks passed
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.

2 participants