Skip to content

fix: faithfully round-trip multi-line secret values (CAP-55)#231

Merged
cvince merged 1 commit into
mainfrom
fix/cap-55-multiline-secrets
Jun 9, 2026
Merged

fix: faithfully round-trip multi-line secret values (CAP-55)#231
cvince merged 1 commit into
mainfrom
fix/cap-55-multiline-secrets

Conversation

@cvince

@cvince cvince commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Problem

Multi-line secret values — PEM/RSA private keys, certs, service-account JSON — were truncated to their first line across several paths. Reported by Daniel Zoghalchali (Latchkey) while migrating an RSA private key; common in the regulated / on-prem ICP.

Linear: CAP-55

Root causes & fixes

Path Bug Fix
Deploy blob (capy deploycapy run) Serialized vars as KEY=value\n; decrypt split on newlines → truncated and minted phantom env vars from continuation lines containing = JSON serialization; decryptSecretsBlob auto-detects JSON vs legacy line format (leading {) for backward compat
capy edit TUI paste Pasted newlines filtered out → multi-line paste collapsed to one line Enabled bracketed paste; append pasted content verbatim; render newlines as a length-preserving marker so the single-row table stays intact
capy decrypt / dev decrypt Wrote .env.{branch}.decrypted as bare KEY=value → truncated on re-read Reuse exportCommand.dotenvEscape
Local encrypted .env snippet Cosmetic value preview could embed a newline into the .env line Collapse snippet to a single line

.env import already round-trips quoted multi-line values (dotenv); raw unquoted multi-line in .env truncates by dotenv's own design — the ergonomic entry is capy edit paste.

Backward compatibility

decryptSecretsBlob still decodes the legacy KEY=value\n format, so a newer capy run decrypts a SECRETS_BLOB minted by an older capy deploy. Covered by a test.

Tests

Adds a real 2048-bit RSA PEM fixture and 20 regression tests: deploy round-trip + no-phantom-keys + legacy decode, local encrypted-.env round-trip (incl. leading-newline / = values), quoted-.env import, decrypt re-read, and paste/render normalization. Full suite green (414 pass), typecheck clean.

Not covered by automated tests

The bracketed-paste terminal plumbing (ESC[?2004h, marker accumulation) needs a real terminal — verified the content-transformation helpers in unit tests; the TTY wiring is a manual check.

Multi-line secret values (PEM private keys, certs, service-account JSON)
were truncated to their first line across several paths. Reported by
Daniel Zoghalchali (Latchkey) while migrating an RSA private key.

Root causes and fixes:

- Deploy blob: encryptEnvBlob serialized vars as `KEY=value\n` lines and
  decryptSecretsBlob re-parsed them by splitting on newlines. A multi-line
  PEM both truncated at the first line and minted phantom env vars from
  continuation lines containing `=`. Switched to JSON; decryptSecretsBlob
  auto-detects JSON vs the legacy line format (leading `{`) so a newer
  `capy run` still decrypts blobs minted by an older `capy deploy`.

- `capy edit` TUI: pasted newlines were filtered out, so a multi-line
  paste collapsed to one line. Enabled bracketed paste and append pasted
  content verbatim; multi-line buffers/values render on one row via a ↵
  marker (length-preserving) so the single-row table stays intact.

- `capy decrypt` / dev decrypt wrote `.env.{branch}.decrypted` as bare
  `KEY=value`, truncating a decrypted PEM on re-read. Now reuse
  exportCommand.dotenvEscape so the output is faithfully re-readable.

- fileManager value snippet collapsed to a single line so the encrypted
  .env line is always one physical line for any multi-line value.

Adds regression coverage with a real multi-line RSA PEM fixture: deploy
round-trip + no-phantom-keys + legacy backward-compat, local encrypted
.env round-trip, quoted-.env import, decrypt re-read, and paste
normalization. Full suite green.
@cvince cvince merged commit 5cb314c into main Jun 9, 2026
1 check 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.

1 participant