Skip to content

Refuse a non-string skill slug or summary before the store - #498

Merged
davidmckayv merged 2 commits into
CopilotKit:mainfrom
Ayush7614:fix/skills-body-type-validation
Sep 12, 2026
Merged

Refuse a non-string skill slug or summary before the store#498
davidmckayv merged 2 commits into
CopilotKit:mainfrom
Ayush7614:fix/skills-body-type-validation

Conversation

@Ayush7614

Copy link
Copy Markdown
Contributor

What this changes

Fixes #494. Same bug class as the grants/call validation already on this router (400 naming the field, not a 500 reading as a broken deployment):

  • POST /api/plugins/skills checked presence with !body?.slug and then ran the slug regex, which coerces: {"slug":123\} tested the string "123" and passed. summary had no check at all (body.summary ?? ""), so {"summary":{}} reached store.installSkill where the insert threw an uncaught error — a 500 for a caller error.
  • A slug, a title and instructions must be non-empty strings now, the slug pattern is tested only after that, and a summary must be absent or a string. Anything else is a 400 before any refusal check, store write, or audit row. Well-formed skills install exactly as before.

Where it runs

Stateless request validation in the server process. Same 400 on every replica.

  • New state that outlives a request? None. Per-request validation.
  • What happens on the second replica? Same 400 on every server: pure function of the JSON body. No coordination.
  • Anything serialised? No.
  • Anything fanned out to a browser? No.
  • New listener, port, or schedule? No.

Postgres is already there and is the default answer to all of the above: nothing here needs it.

Boundary and audit

  • Every acting call still goes through the gateway: resolve, decide, audit, then act. This checks well-formedness only, same as the existing grants/call checks on this router.
  • New refusals and new failures each write a row. N/A: 400s here are caller errors answered at the edge before any store write or audit row, matching the existing behaviour.
  • Nothing new is trusted from the client that the server can resolve itself. Stricter: fewer shapes reach the store.

Changelog

  • A line in CHANGELOG.md under Unreleased: skill slug/summary validation entry.

Proof

  • bun test server/tests/plugin-grants-validation.test.ts: 20 pass (6 existing grants + 10 new skills cases incl. numeric slug, object/array/number summary, whitespace title/instructions, plus a well-formed install still 200).
  • bun test server/tests/plugin-routes.test.ts server/tests/plugin-grants-validation.test.ts server/tests/plugin-catalogue.test.ts: 78 pass, 0 fail.
  • bunx biome format + bunx biome lint --error-on-warnings on touched files: clean.
  • bunx tsc --noEmit -p server/tsconfig.json: only the 3 pre-existing copilot.ts missing-module errors, identical on clean upstream main; nothing in touched files.

@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 clean (validation, no secret leak, fail-closed, agrees with existing layers). CI green.

@davidmckayv
davidmckayv merged commit 89e9b7d into CopilotKit:main Sep 12, 2026
15 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.

POST /api/plugins/skills accepts a non-string slug/summary and answers 500 instead of 400

2 participants