fix(export): use '#' as sed delimiter so SECRET_REGEX '|' alternation parses (#3384)
Some checks failed
CLI Release / Build and release CLI (push) Has been cancelled
Lint / ShellCheck (push) Has been cancelled
Lint / Biome Lint (push) Has been cancelled
Lint / macOS Compatibility (push) Has been cancelled

Reproduces with: any staged file matching the secret regex (e.g. a test
fixture containing an OpenRouter / Anthropic / OpenAI key shape).

The redact step ran:
  sed -i -E "s|${SECRET_REGEX}|${REDACT_PLACEHOLDER}|g" "$f"

SECRET_REGEX is itself a `|`-separated alternation of provider patterns
(`(sk-or-v1-...)|(sk-ant-api...)|(...)`). After bash expansion, sed sees
multiple fields where it expected three, and bails with:

  sed: -e expression #1, char 67: unknown option to `s'

The export then fails on the VM ("sprite exec failed (exit 1)") even
though the user already approved the redaction in the host prompt.

Fix: switch the sed delimiter to '#'. None of the regex tokens
(provider prefixes, char classes, quantifiers, PEM marker) contain '#',
and neither does the placeholder, so the substitution is unambiguous.
'|' inside the pattern is now correctly interpreted by sed -E as
alternation, which is what we wanted all along.

Adds a regression test asserting the script uses 's#...#...#g' and
not 's|...|...|g'.

Bumps CLI 1.0.35 -> 1.0.36.
This commit is contained in:
Ahmed Abushagur 2026-05-02 00:42:15 -07:00 committed by GitHub
parent 3152a1911f
commit 8a4334cbe7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 18 additions and 2 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@openrouter/spawn",
"version": "1.0.35",
"version": "1.0.36",
"type": "module",
"bin": {
"spawn": "cli.js"

View file

@ -217,6 +217,18 @@ describe("buildExportScript", () => {
expect(s).not.toContain("Possible secrets detected in staged files; aborting");
});
it("uses '#' as the sed delimiter — '|' would clash with SECRET_REGEX alternation", () => {
// Regression: the sed substitution previously used '|' as its delimiter
// ("s|${SECRET_REGEX}|${REDACT}|g"). Because SECRET_REGEX itself contains
// '|' (it's a |-separated alternation of provider patterns), bash
// expansion produced a string sed parsed with the wrong number of fields,
// failing with "unknown option to `s'". '#' is absent from both the regex
// and the placeholder, so the substitution is unambiguous.
const s = buildExportScript(opts);
expect(s).toContain('sed -i -E "s#${SECRET_REGEX}#${REDACT_PLACEHOLDER}#g"');
expect(s).not.toContain('sed -i -E "s|${SECRET_REGEX}|${REDACT_PLACEHOLDER}|g"');
});
it("pauses before commit with needs_confirmation when ALLOW_REDACT=0 (first pass)", () => {
const s = buildExportScript({
...opts,

View file

@ -345,7 +345,11 @@ export function buildExportScript(opts: {
' printf "%s\\n" "$SECRET_HITS" >&2',
" while IFS= read -r f; do",
' [ -z "$f" ] && continue',
' sed -i -E "s|${SECRET_REGEX}|${REDACT_PLACEHOLDER}|g" "$f"',
" # Delimiter is '#' — SECRET_REGEX contains '|' (alternation), so '|'",
" # as the sed delimiter would close the pattern at the first alternative",
" # (\"unknown option to s\"). '#' appears in neither the regex nor the",
" # placeholder, so the substitution is unambiguous.",
' sed -i -E "s#${SECRET_REGEX}#${REDACT_PLACEHOLDER}#g" "$f"',
' done <<< "$SECRET_HITS"',
" # Re-stage so the redacted blobs replace the originals in the index.",
" git add -A",