Skip to content

fix(cli): reject a non-numeric or non-positive --chunk-size before encrypt backfill starts - #1194

Draft
calvinbrewer wants to merge 1 commit into
mainfrom
fix/encrypt-backfill-chunk-size
Draft

calvinbrewer wants to merge 1 commit into
mainfrom
fix/encrypt-backfill-chunk-size

Conversation

@calvinbrewer

Copy link
Copy Markdown
Contributor

Summary

stash encrypt backfill is the CLI command that encrypts an existing plaintext column in place, in chunks of rows, so a table can be migrated without downtime. Its --chunk-size flag was never validated: --chunk-size abc became NaN and reached the backfill engine, which bound it into the SQL LIMIT. Postgres refused it, but only after the encryption client had loaded, a database connection had opened, and the dual-write safety gate had recorded its acceptance, and the user saw only the generic "Backfill failed" message. --chunk-size 0 was worse: LIMIT 0 returns no rows, which the engine reads as "nothing left to do" and reports as a completed backfill.

The command now rejects anything that is not a positive whole number with a clear message that quotes the value, and exit code 1, before it loads or opens anything.

Changes

  • New shared helper languages/typescript/packages/cli/src/cli/chunk-size.ts: parseChunkSize (text or number → validated row count, or InvalidChunkSizeError) and resolveChunkSize (same, with a default for an absent flag). Text is matched as plain decimal digits, so 1e3, 0x10 and Infinity are rejected too.
  • main.ts: the argv boundary parses --chunk-size through the helper so the error can quote exactly what was typed.
  • encrypt/backfill.ts: backfillCommand validates again through the same helper at the very top, for callers that bypass argv, and threads the resolved value (not options.chunkSize) into the engine and the progress line. Exports DEFAULT_BACKFILL_CHUNK_SIZE = 1000.
  • Registry / help: --chunk-size now shows (default: 1000) and says it must be a positive whole number.
  • Skill skills/stash-cli/SKILL.md: flag table updated to match.
  • Tests: src/cli/__tests__/chunk-size.test.ts (the helper) and src/commands/encrypt/__tests__/backfill-chunk-size.test.ts (the handler exits 1 and never loads config, the encryption client, a pool, or the migrations schema).
  • Changeset: stash patch.

Verification

  • pnpm --filter stash test: 92 files passed, 7 skipped, 1529 tests passed, 0 failed. (A fresh worktree first needs the @cipherstash/auth debug binding built, or 17 files fail at import; that is environmental and unrelated.)
  • The two new files verbosely: 34 tests passed.
  • pnpm run code:check: no errors (the pre-existing 197 warnings are unchanged).
  • stash manifest --json and stash encrypt backfill --help both show the new --chunk-size description and default, matching the skill.
  • Built CLI end to end: --chunk-size abc, 0, 1.5, 1e3, "" and --chunk-size=-5 each print --chunk-size must be a positive whole number of rows (got "…"). and exit 1.
  • tsc --noEmit in the CLI package reports 27 pre-existing errors in files this PR does not touch (stale wasm-types and @cipherstash/stack/schema types in a fresh worktree); none in the changed files.

Related

Review notes

  • Start with chunk-size.ts, then the guard at the top of backfillCommand.
  • Validating in both main.ts and the handler is deliberate: argv has the raw text for a good message, the handler is the only guard for programmatic callers.
  • Not changed here: the generic argv parser treats --chunk-size -5 (space-separated) as the flag followed by an unknown -5 flag, so the value is read as absent and the default applies. --chunk-size=-5 is rejected correctly. That is a pre-existing quirk shared by every value-taking flag.

🤖 Generated with Claude Code

…crypt backfill starts

`stash encrypt backfill --chunk-size abc` coerced the flag with `Number(...)`
and handed the resulting `NaN` straight to the backfill engine, which bound it
into the page query's `LIMIT $n`. Postgres refused it, but only after the
config and encryption client had loaded, a connection had opened and the
dual-write gate had recorded its acceptance, and the refusal surfaced under
the generic "Backfill failed" message that deliberately hides database text.
`0` was worse: `LIMIT 0` returns no rows, which the engine reads as "nothing
left" and reports as a completed backfill. Negatives and fractions failed the
NaN way.

The decision of what a chunk size is now lives in one helper,
`src/cli/chunk-size.ts`, so the forward and reverse backfills cannot
disagree. It is applied twice on purpose: at the argv boundary in `main.ts`,
where the raw text is still in hand and the error can quote exactly what was
typed, and again at the top of `backfillCommand` for callers that bypass
argv. Both exit 1 with the same author-controlled message, which carries no
row data. The handler check runs before anything is loaded or opened.

`stash decrypt backfill` (#1193, still open) validates its own chunk size
inline today; it can adopt `resolveChunkSize` once it lands.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@changeset-bot

changeset-bot Bot commented Oct 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8b8b2b5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 11 packages
Name Type
stash Patch
@cipherstash/basic-example Patch
@cipherstash/e2e Patch
@cipherstash/stack-drizzle Patch
@cipherstash/stack-prisma Patch
@cipherstash/stack-supabase Patch
@cipherstash/stack Patch
@cipherstash/wizard Patch
@cipherstash/bench Patch
@cipherstash/prisma-example Patch
@cipherstash/test-kit Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

This branch has not been deployed

No deployments
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