Skip to content

fix(subscriptions): reject unscoped batch replacement#436

Merged
tiankaima merged 1 commit into
mainfrom
agent/reject-unscoped-subscription-set
Jul 15, 2026
Merged

fix(subscriptions): reject unscoped batch replacement#436
tiankaima merged 1 commit into
mainfrom
agent/reject-unscoped-subscription-set

Conversation

@tiankaima

Copy link
Copy Markdown
Member

Goal

Prevent an unscoped batch action=set request from replacing every historical section subscription. Production logs for #431 correlate the July 10 deletion with an automated CLI sweep that ran workspace calendar set 999999 using a production credential; the invalid section resolved to an empty target and the compatibility branch replaced the full relation.

Changed Surfaces

  • Reject batch action=set unless semesterId is present.
  • Preserve the existing scoped behavior: an empty selection may still clear only the named semester.
  • Add schema and public-route regression coverage proving the rejected request does not mutate either semester.
  • Update the subscription contract.

Evidence

Static/unit/build:

  • bun run app:prepare
  • bunx biome check
  • bunx svelte-check --tsconfig ./tsconfig.json
  • all three repository tsc --noEmit checks
  • bunx vitest run — 154 files / 782 tests
  • bun run build
  • bun run openapi:check

Isolated database/API:

  • 40 migrations and seed completed in a disposable local database
  • related MCP integration: 11/11
  • focused Playwright regression: 1 passed; both DATABASE_URL and local Hyperdrive override targeted the disposable database

Docs / Contracts

Updated docs/contracts/subscription.json to require semesterId for action=set. Generated OpenAPI has no drift because the conditional Zod refinement is enforced at runtime; the companion CLI guard is Life-USTC/cli#9.

REST/MCP parity was checked. MCP exposes add/remove/append only and has no replace tool. Bot has an unused internal replace wrapper, but no callers; this server guard makes any future unscoped use fail safely.

Risk Areas

This intentionally breaks clients that relied on unscoped batch replacement; they must select a semester. The separate legacy POST /api/calendar-subscriptions compatibility endpoint still supports explicit whole-set replacement and is not changed here because removing it is a broader public API break. It was not the caller in this incident.

No production data restoration is included. #431 remains open until the affected relations are restored with explicit authorization and the exposed calendar credential is rotated.

Cleanup

No scratch artifacts, Playwright output, generated drift, or unrelated rewrites remain.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
life-ustc c54e918 Commit Preview URL

Branch Preview URL
Jul 15 2026, 04:22 PM

@github-actions

Copy link
Copy Markdown

@tiankaima
tiankaima marked this pull request as ready for review July 15, 2026 16:31
@tiankaima
tiankaima merged commit 1d818bb into main Jul 15, 2026
14 checks passed
@tiankaima
tiankaima deleted the agent/reject-unscoped-subscription-set branch July 15, 2026 16:31
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.51.4 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant