fix(export): redact secrets in-place instead of aborting - #3383

Merged
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets
May 2, 2026
Merged

fix(export): redact secrets in-place instead of aborting#3383
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets

Conversation

@AhmedTMM

Copy link
Copy Markdown
Collaborator

Summary

Before: matching any of the API-key regexes in a staged file killed the export — the user got a JSON error and had to SSH in and clean up by hand.

After: matched strings are replaced with `REDACTED-BY-SPAWN-EXPORT` via `sed -i -E`, the file is re-staged, and the export proceeds. The redacted file list is included in the success result and surfaced on the host CLI:

```
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:

  • project/test/brain-sync.test.ts
    ```

Reproduces the user-reported failure where a test fixture (project/test/brain-sync.test.ts) tripped the scan and the whole export bailed.

Notes

  • Regex unchanged — same Anthropic / OpenRouter / OpenAI / GitHub / AWS / Hetzner / DO / PEM coverage as before.
  • Placeholder is intentionally loud (`REDACTED-BY-SPAWN-EXPORT`) so a reader of the public repo can tell something was scrubbed.
  • Result schema gains an optional `redacted: string[]` field; the host CLI shows it as a clack warning, not an error.
  • Bumps CLI `1.0.33` → `1.0.34`.

Test plan

  • `bunx @biomejs/biome check src/` — clean
  • `bun test` — 2168 pass / same 4 pre-existing fails
  • 34 export tests pass; legacy "aborts on hit" assertion replaced with new redact assertions
  • Manual: re-run the failing export from the user's session — confirm `brain-sync.test.ts` is redacted and the spawn link is printed

🤖 Generated with Claude Code

Before: any staged file matching the secret regex caused the export
to fail with `{"ok":false,"error":"Possible secrets detected..."}`,
forcing the user to SSH in and clean things up by hand.
After: matched strings are replaced with `***REDACTED-BY-SPAWN-EXPORT***`
via sed -i -E, the file is re-staged, and the export proceeds. The list
of redacted files is included in the success result and surfaced as a
warning on the host CLI:
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:
- project/test/brain-sync.test.ts
The regex is unchanged. The redact placeholder is intentionally loud so
a casual reader of the published repo can tell that something was
scrubbed and isn't just blank.
Bumps CLI 1.0.33 -> 1.0.34.
@AhmedTMM
AhmedTMM marked this pull request as ready for review May 2, 2026 07:19
Previously the VM would silently redact any staged files matching the
secret regex and push the repo — meaning a regex miss (OpenRouterLabs#3381 tracks
broadening) would publish a real secret without the user ever seeing
the file list. That's a fail-open posture on a tool that can push to
public GitHub.
New flow:
- buildExportScript takes allowRedact: boolean.
- First pass (allowRedact=false): VM stages, runs the secret scan,
and on hits writes a needs_confirmation result (hits=[...]) and
exits 0 before any commit or push. No hits → commit + push as
before.
- Host reads the result. If needs_confirmation: print the file list,
explain that the regex has known gaps, and ask "Redact these N files
and continue pushing?" (initialValue false). Decline → exit 0, no
push. Approve → re-run the script with allowRedact=true, which now
actually does the sed + re-stage + commit + push.
Other changes:
- ResultSchema gains the needs_confirmation variant.
- cmdExport factors the runServer + downloadFile + parse cycle into
runPassAndParseResult so the two-pass orchestration is readable.
- Tests: 4 new cases cover the gate scripting (ALLOW_REDACT=0 writes
needs_confirmation and exits 0, ALLOW_REDACT=1 redacts) and the
end-to-end host flow (approve → two passes with ALLOW_REDACT 0→1;
decline → one pass, exit 0; no-secrets happy path → one pass, no
confirm). 38/38 export tests, 2176/0 fail overall.
- CLI 1.0.34 → 1.0.35.
@la14-1

Copy link
Copy Markdown
Collaborator

Pushed a gate in commit 855a89e to turn the silent redact-and-push into a two-pass flow:

First pass (ALLOW_REDACT=0): VM stages + scans. If hits found, writes {"ok":false,"needsConfirmation":true,"hits":[...]} and exits 0 before any commit or push.

Host: reads the result, prints the file list, notes the regex has known gaps (#3381), and asks:

Redact these N files and continue pushing to GitHub? (initialValue: false)

  • Declineexit 0, nothing pushed.
  • Approve → re-run with ALLOW_REDACT=1, which does the sed + re-stage + commit + push, and returns the redacted list in the success result.

No-hits path is unchanged: single pass, single push.

Diff

  • packages/cli/src/commands/export.ts (+161 / -41): ResultSchema gains a needs_confirmation variant; buildExportScript takes allowRedact: boolean; cmdExport factors the run+download+parse cycle into runPassAndParseResult so two-pass orchestration is readable.
  • packages/cli/src/__tests__/export.test.ts (+147 / -2): 4 new tests — two scripting assertions (ALLOW_REDACT=0 writes needs_confirmation and exits 0; ALLOW_REDACT=1 redacts) and three end-to-end flows (approve → two passes 0→1; decline → one pass, exit 0; no-secrets → one pass, no confirm).
  • CLI 1.0.34 → 1.0.35.

Verification

  • bunx @biomejs/biome check src/ clean
  • bun test → 2176/0 fail (38/38 export tests, up from 34)
  • bash -n on both rendered script variants → both parse

Ready for another look / merge.

@la14-1la14-1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate wired up in 855a89e. First pass scans + pauses on hits; host prompts with the file list; only on approval does the redact+push run. Tests cover all four flows (approve / decline / no-secrets / script variants). All CI green. Approving.

@la14-1
la14-1 merged commit 3152a19 into OpenRouterLabs:mainMay 2, 2026
5 checks passed
Sign up for freeto 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.

3 participants

@AhmedTMM@la14-1@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(export): redact secrets in-place instead of aborting - #3383

Merged
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets
May 2, 2026
Merged

fix(export): redact secrets in-place instead of aborting#3383
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets

Conversation

@AhmedTMM

Copy link
Copy Markdown
Collaborator

Summary

Before: matching any of the API-key regexes in a staged file killed the export — the user got a JSON error and had to SSH in and clean up by hand.

After: matched strings are replaced with `REDACTED-BY-SPAWN-EXPORT` via `sed -i -E`, the file is re-staged, and the export proceeds. The redacted file list is included in the success result and surfaced on the host CLI:

```
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:

  • project/test/brain-sync.test.ts
    ```

Reproduces the user-reported failure where a test fixture (project/test/brain-sync.test.ts) tripped the scan and the whole export bailed.

Notes

  • Regex unchanged — same Anthropic / OpenRouter / OpenAI / GitHub / AWS / Hetzner / DO / PEM coverage as before.
  • Placeholder is intentionally loud (`REDACTED-BY-SPAWN-EXPORT`) so a reader of the public repo can tell something was scrubbed.
  • Result schema gains an optional `redacted: string[]` field; the host CLI shows it as a clack warning, not an error.
  • Bumps CLI `1.0.33` → `1.0.34`.

Test plan

  • `bunx @biomejs/biome check src/` — clean
  • `bun test` — 2168 pass / same 4 pre-existing fails
  • 34 export tests pass; legacy "aborts on hit" assertion replaced with new redact assertions
  • Manual: re-run the failing export from the user's session — confirm `brain-sync.test.ts` is redacted and the spawn link is printed

🤖 Generated with Claude Code

Before: any staged file matching the secret regex caused the export
to fail with `{"ok":false,"error":"Possible secrets detected..."}`,
forcing the user to SSH in and clean things up by hand.
After: matched strings are replaced with `***REDACTED-BY-SPAWN-EXPORT***`
via sed -i -E, the file is re-staged, and the export proceeds. The list
of redacted files is included in the success result and surfaced as a
warning on the host CLI:
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:
- project/test/brain-sync.test.ts
The regex is unchanged. The redact placeholder is intentionally loud so
a casual reader of the published repo can tell that something was
scrubbed and isn't just blank.
Bumps CLI 1.0.33 -> 1.0.34.
@AhmedTMM
AhmedTMM marked this pull request as ready for review May 2, 2026 07:19
Previously the VM would silently redact any staged files matching the
secret regex and push the repo — meaning a regex miss (OpenRouterLabs#3381 tracks
broadening) would publish a real secret without the user ever seeing
the file list. That's a fail-open posture on a tool that can push to
public GitHub.
New flow:
- buildExportScript takes allowRedact: boolean.
- First pass (allowRedact=false): VM stages, runs the secret scan,
and on hits writes a needs_confirmation result (hits=[...]) and
exits 0 before any commit or push. No hits → commit + push as
before.
- Host reads the result. If needs_confirmation: print the file list,
explain that the regex has known gaps, and ask "Redact these N files
and continue pushing?" (initialValue false). Decline → exit 0, no
push. Approve → re-run the script with allowRedact=true, which now
actually does the sed + re-stage + commit + push.
Other changes:
- ResultSchema gains the needs_confirmation variant.
- cmdExport factors the runServer + downloadFile + parse cycle into
runPassAndParseResult so the two-pass orchestration is readable.
- Tests: 4 new cases cover the gate scripting (ALLOW_REDACT=0 writes
needs_confirmation and exits 0, ALLOW_REDACT=1 redacts) and the
end-to-end host flow (approve → two passes with ALLOW_REDACT 0→1;
decline → one pass, exit 0; no-secrets happy path → one pass, no
confirm). 38/38 export tests, 2176/0 fail overall.
- CLI 1.0.34 → 1.0.35.
@la14-1

Copy link
Copy Markdown
Collaborator

Pushed a gate in commit 855a89e to turn the silent redact-and-push into a two-pass flow:

First pass (ALLOW_REDACT=0): VM stages + scans. If hits found, writes {"ok":false,"needsConfirmation":true,"hits":[...]} and exits 0 before any commit or push.

Host: reads the result, prints the file list, notes the regex has known gaps (#3381), and asks:

Redact these N files and continue pushing to GitHub? (initialValue: false)

  • Declineexit 0, nothing pushed.
  • Approve → re-run with ALLOW_REDACT=1, which does the sed + re-stage + commit + push, and returns the redacted list in the success result.

No-hits path is unchanged: single pass, single push.

Diff

  • packages/cli/src/commands/export.ts (+161 / -41): ResultSchema gains a needs_confirmation variant; buildExportScript takes allowRedact: boolean; cmdExport factors the run+download+parse cycle into runPassAndParseResult so two-pass orchestration is readable.
  • packages/cli/src/__tests__/export.test.ts (+147 / -2): 4 new tests — two scripting assertions (ALLOW_REDACT=0 writes needs_confirmation and exits 0; ALLOW_REDACT=1 redacts) and three end-to-end flows (approve → two passes 0→1; decline → one pass, exit 0; no-secrets → one pass, no confirm).
  • CLI 1.0.34 → 1.0.35.

Verification

  • bunx @biomejs/biome check src/ clean
  • bun test → 2176/0 fail (38/38 export tests, up from 34)
  • bash -n on both rendered script variants → both parse

Ready for another look / merge.

@la14-1la14-1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate wired up in 855a89e. First pass scans + pauses on hits; host prompts with the file list; only on approval does the redact+push run. Tests cover all four flows (approve / decline / no-secrets / script variants). All CI green. Approving.

@la14-1
la14-1 merged commit 3152a19 into OpenRouterLabs:mainMay 2, 2026
5 checks passed
Sign up for freeto 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.

3 participants

@AhmedTMM@la14-1@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(export): redact secrets in-place instead of aborting - #3383

Merged
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets
May 2, 2026
Merged

fix(export): redact secrets in-place instead of aborting#3383
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets

Conversation

@AhmedTMM

Copy link
Copy Markdown
Collaborator

Summary

Before: matching any of the API-key regexes in a staged file killed the export — the user got a JSON error and had to SSH in and clean up by hand.

After: matched strings are replaced with `REDACTED-BY-SPAWN-EXPORT` via `sed -i -E`, the file is re-staged, and the export proceeds. The redacted file list is included in the success result and surfaced on the host CLI:

```
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:

  • project/test/brain-sync.test.ts
    ```

Reproduces the user-reported failure where a test fixture (project/test/brain-sync.test.ts) tripped the scan and the whole export bailed.

Notes

  • Regex unchanged — same Anthropic / OpenRouter / OpenAI / GitHub / AWS / Hetzner / DO / PEM coverage as before.
  • Placeholder is intentionally loud (`REDACTED-BY-SPAWN-EXPORT`) so a reader of the public repo can tell something was scrubbed.
  • Result schema gains an optional `redacted: string[]` field; the host CLI shows it as a clack warning, not an error.
  • Bumps CLI `1.0.33` → `1.0.34`.

Test plan

  • `bunx @biomejs/biome check src/` — clean
  • `bun test` — 2168 pass / same 4 pre-existing fails
  • 34 export tests pass; legacy "aborts on hit" assertion replaced with new redact assertions
  • Manual: re-run the failing export from the user's session — confirm `brain-sync.test.ts` is redacted and the spawn link is printed

🤖 Generated with Claude Code

Before: any staged file matching the secret regex caused the export
to fail with `{"ok":false,"error":"Possible secrets detected..."}`,
forcing the user to SSH in and clean things up by hand.
After: matched strings are replaced with `***REDACTED-BY-SPAWN-EXPORT***`
via sed -i -E, the file is re-staged, and the export proceeds. The list
of redacted files is included in the success result and surfaced as a
warning on the host CLI:
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:
- project/test/brain-sync.test.ts
The regex is unchanged. The redact placeholder is intentionally loud so
a casual reader of the published repo can tell that something was
scrubbed and isn't just blank.
Bumps CLI 1.0.33 -> 1.0.34.
@AhmedTMM
AhmedTMM marked this pull request as ready for review May 2, 2026 07:19
Previously the VM would silently redact any staged files matching the
secret regex and push the repo — meaning a regex miss (OpenRouterLabs#3381 tracks
broadening) would publish a real secret without the user ever seeing
the file list. That's a fail-open posture on a tool that can push to
public GitHub.
New flow:
- buildExportScript takes allowRedact: boolean.
- First pass (allowRedact=false): VM stages, runs the secret scan,
and on hits writes a needs_confirmation result (hits=[...]) and
exits 0 before any commit or push. No hits → commit + push as
before.
- Host reads the result. If needs_confirmation: print the file list,
explain that the regex has known gaps, and ask "Redact these N files
and continue pushing?" (initialValue false). Decline → exit 0, no
push. Approve → re-run the script with allowRedact=true, which now
actually does the sed + re-stage + commit + push.
Other changes:
- ResultSchema gains the needs_confirmation variant.
- cmdExport factors the runServer + downloadFile + parse cycle into
runPassAndParseResult so the two-pass orchestration is readable.
- Tests: 4 new cases cover the gate scripting (ALLOW_REDACT=0 writes
needs_confirmation and exits 0, ALLOW_REDACT=1 redacts) and the
end-to-end host flow (approve → two passes with ALLOW_REDACT 0→1;
decline → one pass, exit 0; no-secrets happy path → one pass, no
confirm). 38/38 export tests, 2176/0 fail overall.
- CLI 1.0.34 → 1.0.35.
@la14-1

Copy link
Copy Markdown
Collaborator

Pushed a gate in commit 855a89e to turn the silent redact-and-push into a two-pass flow:

First pass (ALLOW_REDACT=0): VM stages + scans. If hits found, writes {"ok":false,"needsConfirmation":true,"hits":[...]} and exits 0 before any commit or push.

Host: reads the result, prints the file list, notes the regex has known gaps (#3381), and asks:

Redact these N files and continue pushing to GitHub? (initialValue: false)

  • Declineexit 0, nothing pushed.
  • Approve → re-run with ALLOW_REDACT=1, which does the sed + re-stage + commit + push, and returns the redacted list in the success result.

No-hits path is unchanged: single pass, single push.

Diff

  • packages/cli/src/commands/export.ts (+161 / -41): ResultSchema gains a needs_confirmation variant; buildExportScript takes allowRedact: boolean; cmdExport factors the run+download+parse cycle into runPassAndParseResult so two-pass orchestration is readable.
  • packages/cli/src/__tests__/export.test.ts (+147 / -2): 4 new tests — two scripting assertions (ALLOW_REDACT=0 writes needs_confirmation and exits 0; ALLOW_REDACT=1 redacts) and three end-to-end flows (approve → two passes 0→1; decline → one pass, exit 0; no-secrets → one pass, no confirm).
  • CLI 1.0.34 → 1.0.35.

Verification

  • bunx @biomejs/biome check src/ clean
  • bun test → 2176/0 fail (38/38 export tests, up from 34)
  • bash -n on both rendered script variants → both parse

Ready for another look / merge.

@la14-1la14-1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate wired up in 855a89e. First pass scans + pauses on hits; host prompts with the file list; only on approval does the redact+push run. Tests cover all four flows (approve / decline / no-secrets / script variants). All CI green. Approving.

@la14-1
la14-1 merged commit 3152a19 into OpenRouterLabs:mainMay 2, 2026
5 checks passed
Sign up for freeto 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.

3 participants

@AhmedTMM@la14-1@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(export): redact secrets in-place instead of aborting - #3383

Merged
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets
May 2, 2026
Merged

fix(export): redact secrets in-place instead of aborting#3383
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets

Conversation

@AhmedTMM

Copy link
Copy Markdown
Collaborator

Summary

Before: matching any of the API-key regexes in a staged file killed the export — the user got a JSON error and had to SSH in and clean up by hand.

After: matched strings are replaced with `REDACTED-BY-SPAWN-EXPORT` via `sed -i -E`, the file is re-staged, and the export proceeds. The redacted file list is included in the success result and surfaced on the host CLI:

```
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:

  • project/test/brain-sync.test.ts
    ```

Reproduces the user-reported failure where a test fixture (project/test/brain-sync.test.ts) tripped the scan and the whole export bailed.

Notes

  • Regex unchanged — same Anthropic / OpenRouter / OpenAI / GitHub / AWS / Hetzner / DO / PEM coverage as before.
  • Placeholder is intentionally loud (`REDACTED-BY-SPAWN-EXPORT`) so a reader of the public repo can tell something was scrubbed.
  • Result schema gains an optional `redacted: string[]` field; the host CLI shows it as a clack warning, not an error.
  • Bumps CLI `1.0.33` → `1.0.34`.

Test plan

  • `bunx @biomejs/biome check src/` — clean
  • `bun test` — 2168 pass / same 4 pre-existing fails
  • 34 export tests pass; legacy "aborts on hit" assertion replaced with new redact assertions
  • Manual: re-run the failing export from the user's session — confirm `brain-sync.test.ts` is redacted and the spawn link is printed

🤖 Generated with Claude Code

Before: any staged file matching the secret regex caused the export
to fail with `{"ok":false,"error":"Possible secrets detected..."}`,
forcing the user to SSH in and clean things up by hand.
After: matched strings are replaced with `***REDACTED-BY-SPAWN-EXPORT***`
via sed -i -E, the file is re-staged, and the export proceeds. The list
of redacted files is included in the success result and surfaced as a
warning on the host CLI:
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:
- project/test/brain-sync.test.ts
The regex is unchanged. The redact placeholder is intentionally loud so
a casual reader of the published repo can tell that something was
scrubbed and isn't just blank.
Bumps CLI 1.0.33 -> 1.0.34.
@AhmedTMM
AhmedTMM marked this pull request as ready for review May 2, 2026 07:19
Previously the VM would silently redact any staged files matching the
secret regex and push the repo — meaning a regex miss (OpenRouterLabs#3381 tracks
broadening) would publish a real secret without the user ever seeing
the file list. That's a fail-open posture on a tool that can push to
public GitHub.
New flow:
- buildExportScript takes allowRedact: boolean.
- First pass (allowRedact=false): VM stages, runs the secret scan,
and on hits writes a needs_confirmation result (hits=[...]) and
exits 0 before any commit or push. No hits → commit + push as
before.
- Host reads the result. If needs_confirmation: print the file list,
explain that the regex has known gaps, and ask "Redact these N files
and continue pushing?" (initialValue false). Decline → exit 0, no
push. Approve → re-run the script with allowRedact=true, which now
actually does the sed + re-stage + commit + push.
Other changes:
- ResultSchema gains the needs_confirmation variant.
- cmdExport factors the runServer + downloadFile + parse cycle into
runPassAndParseResult so the two-pass orchestration is readable.
- Tests: 4 new cases cover the gate scripting (ALLOW_REDACT=0 writes
needs_confirmation and exits 0, ALLOW_REDACT=1 redacts) and the
end-to-end host flow (approve → two passes with ALLOW_REDACT 0→1;
decline → one pass, exit 0; no-secrets happy path → one pass, no
confirm). 38/38 export tests, 2176/0 fail overall.
- CLI 1.0.34 → 1.0.35.
@la14-1

Copy link
Copy Markdown
Collaborator

Pushed a gate in commit 855a89e to turn the silent redact-and-push into a two-pass flow:

First pass (ALLOW_REDACT=0): VM stages + scans. If hits found, writes {"ok":false,"needsConfirmation":true,"hits":[...]} and exits 0 before any commit or push.

Host: reads the result, prints the file list, notes the regex has known gaps (#3381), and asks:

Redact these N files and continue pushing to GitHub? (initialValue: false)

  • Declineexit 0, nothing pushed.
  • Approve → re-run with ALLOW_REDACT=1, which does the sed + re-stage + commit + push, and returns the redacted list in the success result.

No-hits path is unchanged: single pass, single push.

Diff

  • packages/cli/src/commands/export.ts (+161 / -41): ResultSchema gains a needs_confirmation variant; buildExportScript takes allowRedact: boolean; cmdExport factors the run+download+parse cycle into runPassAndParseResult so two-pass orchestration is readable.
  • packages/cli/src/__tests__/export.test.ts (+147 / -2): 4 new tests — two scripting assertions (ALLOW_REDACT=0 writes needs_confirmation and exits 0; ALLOW_REDACT=1 redacts) and three end-to-end flows (approve → two passes 0→1; decline → one pass, exit 0; no-secrets → one pass, no confirm).
  • CLI 1.0.34 → 1.0.35.

Verification

  • bunx @biomejs/biome check src/ clean
  • bun test → 2176/0 fail (38/38 export tests, up from 34)
  • bash -n on both rendered script variants → both parse

Ready for another look / merge.

@la14-1la14-1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate wired up in 855a89e. First pass scans + pauses on hits; host prompts with the file list; only on approval does the redact+push run. Tests cover all four flows (approve / decline / no-secrets / script variants). All CI green. Approving.

@la14-1
la14-1 merged commit 3152a19 into OpenRouterLabs:mainMay 2, 2026
5 checks passed
Sign up for freeto 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.

3 participants

@AhmedTMM@la14-1@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(export): redact secrets in-place instead of aborting - #3383

Merged
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets
May 2, 2026
Merged

fix(export): redact secrets in-place instead of aborting#3383
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets

Conversation

@AhmedTMM

Copy link
Copy Markdown
Collaborator

Summary

Before: matching any of the API-key regexes in a staged file killed the export — the user got a JSON error and had to SSH in and clean up by hand.

After: matched strings are replaced with `REDACTED-BY-SPAWN-EXPORT` via `sed -i -E`, the file is re-staged, and the export proceeds. The redacted file list is included in the success result and surfaced on the host CLI:

```
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:

  • project/test/brain-sync.test.ts
    ```

Reproduces the user-reported failure where a test fixture (project/test/brain-sync.test.ts) tripped the scan and the whole export bailed.

Notes

  • Regex unchanged — same Anthropic / OpenRouter / OpenAI / GitHub / AWS / Hetzner / DO / PEM coverage as before.
  • Placeholder is intentionally loud (`REDACTED-BY-SPAWN-EXPORT`) so a reader of the public repo can tell something was scrubbed.
  • Result schema gains an optional `redacted: string[]` field; the host CLI shows it as a clack warning, not an error.
  • Bumps CLI `1.0.33` → `1.0.34`.

Test plan

  • `bunx @biomejs/biome check src/` — clean
  • `bun test` — 2168 pass / same 4 pre-existing fails
  • 34 export tests pass; legacy "aborts on hit" assertion replaced with new redact assertions
  • Manual: re-run the failing export from the user's session — confirm `brain-sync.test.ts` is redacted and the spawn link is printed

🤖 Generated with Claude Code

Before: any staged file matching the secret regex caused the export
to fail with `{"ok":false,"error":"Possible secrets detected..."}`,
forcing the user to SSH in and clean things up by hand.
After: matched strings are replaced with `***REDACTED-BY-SPAWN-EXPORT***`
via sed -i -E, the file is re-staged, and the export proceeds. The list
of redacted files is included in the success result and surfaced as a
warning on the host CLI:
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:
- project/test/brain-sync.test.ts
The regex is unchanged. The redact placeholder is intentionally loud so
a casual reader of the published repo can tell that something was
scrubbed and isn't just blank.
Bumps CLI 1.0.33 -> 1.0.34.
@AhmedTMM
AhmedTMM marked this pull request as ready for review May 2, 2026 07:19
Previously the VM would silently redact any staged files matching the
secret regex and push the repo — meaning a regex miss (OpenRouterLabs#3381 tracks
broadening) would publish a real secret without the user ever seeing
the file list. That's a fail-open posture on a tool that can push to
public GitHub.
New flow:
- buildExportScript takes allowRedact: boolean.
- First pass (allowRedact=false): VM stages, runs the secret scan,
and on hits writes a needs_confirmation result (hits=[...]) and
exits 0 before any commit or push. No hits → commit + push as
before.
- Host reads the result. If needs_confirmation: print the file list,
explain that the regex has known gaps, and ask "Redact these N files
and continue pushing?" (initialValue false). Decline → exit 0, no
push. Approve → re-run the script with allowRedact=true, which now
actually does the sed + re-stage + commit + push.
Other changes:
- ResultSchema gains the needs_confirmation variant.
- cmdExport factors the runServer + downloadFile + parse cycle into
runPassAndParseResult so the two-pass orchestration is readable.
- Tests: 4 new cases cover the gate scripting (ALLOW_REDACT=0 writes
needs_confirmation and exits 0, ALLOW_REDACT=1 redacts) and the
end-to-end host flow (approve → two passes with ALLOW_REDACT 0→1;
decline → one pass, exit 0; no-secrets happy path → one pass, no
confirm). 38/38 export tests, 2176/0 fail overall.
- CLI 1.0.34 → 1.0.35.
@la14-1

Copy link
Copy Markdown
Collaborator

Pushed a gate in commit 855a89e to turn the silent redact-and-push into a two-pass flow:

First pass (ALLOW_REDACT=0): VM stages + scans. If hits found, writes {"ok":false,"needsConfirmation":true,"hits":[...]} and exits 0 before any commit or push.

Host: reads the result, prints the file list, notes the regex has known gaps (#3381), and asks:

Redact these N files and continue pushing to GitHub? (initialValue: false)

  • Declineexit 0, nothing pushed.
  • Approve → re-run with ALLOW_REDACT=1, which does the sed + re-stage + commit + push, and returns the redacted list in the success result.

No-hits path is unchanged: single pass, single push.

Diff

  • packages/cli/src/commands/export.ts (+161 / -41): ResultSchema gains a needs_confirmation variant; buildExportScript takes allowRedact: boolean; cmdExport factors the run+download+parse cycle into runPassAndParseResult so two-pass orchestration is readable.
  • packages/cli/src/__tests__/export.test.ts (+147 / -2): 4 new tests — two scripting assertions (ALLOW_REDACT=0 writes needs_confirmation and exits 0; ALLOW_REDACT=1 redacts) and three end-to-end flows (approve → two passes 0→1; decline → one pass, exit 0; no-secrets → one pass, no confirm).
  • CLI 1.0.34 → 1.0.35.

Verification

  • bunx @biomejs/biome check src/ clean
  • bun test → 2176/0 fail (38/38 export tests, up from 34)
  • bash -n on both rendered script variants → both parse

Ready for another look / merge.

@la14-1la14-1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate wired up in 855a89e. First pass scans + pauses on hits; host prompts with the file list; only on approval does the redact+push run. Tests cover all four flows (approve / decline / no-secrets / script variants). All CI green. Approving.

@la14-1
la14-1 merged commit 3152a19 into OpenRouterLabs:mainMay 2, 2026
5 checks passed
Sign up for freeto 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.

3 participants

@AhmedTMM@la14-1@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(export): redact secrets in-place instead of aborting - #3383

Merged
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets
May 2, 2026
Merged

fix(export): redact secrets in-place instead of aborting#3383
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets

Conversation

@AhmedTMM

Copy link
Copy Markdown
Collaborator

Summary

Before: matching any of the API-key regexes in a staged file killed the export — the user got a JSON error and had to SSH in and clean up by hand.

After: matched strings are replaced with `REDACTED-BY-SPAWN-EXPORT` via `sed -i -E`, the file is re-staged, and the export proceeds. The redacted file list is included in the success result and surfaced on the host CLI:

```
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:

  • project/test/brain-sync.test.ts
    ```

Reproduces the user-reported failure where a test fixture (project/test/brain-sync.test.ts) tripped the scan and the whole export bailed.

Notes

  • Regex unchanged — same Anthropic / OpenRouter / OpenAI / GitHub / AWS / Hetzner / DO / PEM coverage as before.
  • Placeholder is intentionally loud (`REDACTED-BY-SPAWN-EXPORT`) so a reader of the public repo can tell something was scrubbed.
  • Result schema gains an optional `redacted: string[]` field; the host CLI shows it as a clack warning, not an error.
  • Bumps CLI `1.0.33` → `1.0.34`.

Test plan

  • `bunx @biomejs/biome check src/` — clean
  • `bun test` — 2168 pass / same 4 pre-existing fails
  • 34 export tests pass; legacy "aborts on hit" assertion replaced with new redact assertions
  • Manual: re-run the failing export from the user's session — confirm `brain-sync.test.ts` is redacted and the spawn link is printed

🤖 Generated with Claude Code

Before: any staged file matching the secret regex caused the export
to fail with `{"ok":false,"error":"Possible secrets detected..."}`,
forcing the user to SSH in and clean things up by hand.
After: matched strings are replaced with `***REDACTED-BY-SPAWN-EXPORT***`
via sed -i -E, the file is re-staged, and the export proceeds. The list
of redacted files is included in the success result and surfaced as a
warning on the host CLI:
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:
- project/test/brain-sync.test.ts
The regex is unchanged. The redact placeholder is intentionally loud so
a casual reader of the published repo can tell that something was
scrubbed and isn't just blank.
Bumps CLI 1.0.33 -> 1.0.34.
@AhmedTMM
AhmedTMM marked this pull request as ready for review May 2, 2026 07:19
Previously the VM would silently redact any staged files matching the
secret regex and push the repo — meaning a regex miss (OpenRouterLabs#3381 tracks
broadening) would publish a real secret without the user ever seeing
the file list. That's a fail-open posture on a tool that can push to
public GitHub.
New flow:
- buildExportScript takes allowRedact: boolean.
- First pass (allowRedact=false): VM stages, runs the secret scan,
and on hits writes a needs_confirmation result (hits=[...]) and
exits 0 before any commit or push. No hits → commit + push as
before.
- Host reads the result. If needs_confirmation: print the file list,
explain that the regex has known gaps, and ask "Redact these N files
and continue pushing?" (initialValue false). Decline → exit 0, no
push. Approve → re-run the script with allowRedact=true, which now
actually does the sed + re-stage + commit + push.
Other changes:
- ResultSchema gains the needs_confirmation variant.
- cmdExport factors the runServer + downloadFile + parse cycle into
runPassAndParseResult so the two-pass orchestration is readable.
- Tests: 4 new cases cover the gate scripting (ALLOW_REDACT=0 writes
needs_confirmation and exits 0, ALLOW_REDACT=1 redacts) and the
end-to-end host flow (approve → two passes with ALLOW_REDACT 0→1;
decline → one pass, exit 0; no-secrets happy path → one pass, no
confirm). 38/38 export tests, 2176/0 fail overall.
- CLI 1.0.34 → 1.0.35.
@la14-1

Copy link
Copy Markdown
Collaborator

Pushed a gate in commit 855a89e to turn the silent redact-and-push into a two-pass flow:

First pass (ALLOW_REDACT=0): VM stages + scans. If hits found, writes {"ok":false,"needsConfirmation":true,"hits":[...]} and exits 0 before any commit or push.

Host: reads the result, prints the file list, notes the regex has known gaps (#3381), and asks:

Redact these N files and continue pushing to GitHub? (initialValue: false)

  • Declineexit 0, nothing pushed.
  • Approve → re-run with ALLOW_REDACT=1, which does the sed + re-stage + commit + push, and returns the redacted list in the success result.

No-hits path is unchanged: single pass, single push.

Diff

  • packages/cli/src/commands/export.ts (+161 / -41): ResultSchema gains a needs_confirmation variant; buildExportScript takes allowRedact: boolean; cmdExport factors the run+download+parse cycle into runPassAndParseResult so two-pass orchestration is readable.
  • packages/cli/src/__tests__/export.test.ts (+147 / -2): 4 new tests — two scripting assertions (ALLOW_REDACT=0 writes needs_confirmation and exits 0; ALLOW_REDACT=1 redacts) and three end-to-end flows (approve → two passes 0→1; decline → one pass, exit 0; no-secrets → one pass, no confirm).
  • CLI 1.0.34 → 1.0.35.

Verification

  • bunx @biomejs/biome check src/ clean
  • bun test → 2176/0 fail (38/38 export tests, up from 34)
  • bash -n on both rendered script variants → both parse

Ready for another look / merge.

@la14-1la14-1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate wired up in 855a89e. First pass scans + pauses on hits; host prompts with the file list; only on approval does the redact+push run. Tests cover all four flows (approve / decline / no-secrets / script variants). All CI green. Approving.

@la14-1
la14-1 merged commit 3152a19 into OpenRouterLabs:mainMay 2, 2026
5 checks passed
Sign up for freeto 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.

3 participants

@AhmedTMM@la14-1@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(export): redact secrets in-place instead of aborting - #3383

Merged
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets
May 2, 2026
Merged

fix(export): redact secrets in-place instead of aborting#3383
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets

Conversation

@AhmedTMM

Copy link
Copy Markdown
Collaborator

Summary

Before: matching any of the API-key regexes in a staged file killed the export — the user got a JSON error and had to SSH in and clean up by hand.

After: matched strings are replaced with `REDACTED-BY-SPAWN-EXPORT` via `sed -i -E`, the file is re-staged, and the export proceeds. The redacted file list is included in the success result and surfaced on the host CLI:

```
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:

  • project/test/brain-sync.test.ts
    ```

Reproduces the user-reported failure where a test fixture (project/test/brain-sync.test.ts) tripped the scan and the whole export bailed.

Notes

  • Regex unchanged — same Anthropic / OpenRouter / OpenAI / GitHub / AWS / Hetzner / DO / PEM coverage as before.
  • Placeholder is intentionally loud (`REDACTED-BY-SPAWN-EXPORT`) so a reader of the public repo can tell something was scrubbed.
  • Result schema gains an optional `redacted: string[]` field; the host CLI shows it as a clack warning, not an error.
  • Bumps CLI `1.0.33` → `1.0.34`.

Test plan

  • `bunx @biomejs/biome check src/` — clean
  • `bun test` — 2168 pass / same 4 pre-existing fails
  • 34 export tests pass; legacy "aborts on hit" assertion replaced with new redact assertions
  • Manual: re-run the failing export from the user's session — confirm `brain-sync.test.ts` is redacted and the spawn link is printed

🤖 Generated with Claude Code

Before: any staged file matching the secret regex caused the export
to fail with `{"ok":false,"error":"Possible secrets detected..."}`,
forcing the user to SSH in and clean things up by hand.
After: matched strings are replaced with `***REDACTED-BY-SPAWN-EXPORT***`
via sed -i -E, the file is re-staged, and the export proceeds. The list
of redacted files is included in the success result and surfaced as a
warning on the host CLI:
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:
- project/test/brain-sync.test.ts
The regex is unchanged. The redact placeholder is intentionally loud so
a casual reader of the published repo can tell that something was
scrubbed and isn't just blank.
Bumps CLI 1.0.33 -> 1.0.34.
@AhmedTMM
AhmedTMM marked this pull request as ready for review May 2, 2026 07:19
Previously the VM would silently redact any staged files matching the
secret regex and push the repo — meaning a regex miss (OpenRouterLabs#3381 tracks
broadening) would publish a real secret without the user ever seeing
the file list. That's a fail-open posture on a tool that can push to
public GitHub.
New flow:
- buildExportScript takes allowRedact: boolean.
- First pass (allowRedact=false): VM stages, runs the secret scan,
and on hits writes a needs_confirmation result (hits=[...]) and
exits 0 before any commit or push. No hits → commit + push as
before.
- Host reads the result. If needs_confirmation: print the file list,
explain that the regex has known gaps, and ask "Redact these N files
and continue pushing?" (initialValue false). Decline → exit 0, no
push. Approve → re-run the script with allowRedact=true, which now
actually does the sed + re-stage + commit + push.
Other changes:
- ResultSchema gains the needs_confirmation variant.
- cmdExport factors the runServer + downloadFile + parse cycle into
runPassAndParseResult so the two-pass orchestration is readable.
- Tests: 4 new cases cover the gate scripting (ALLOW_REDACT=0 writes
needs_confirmation and exits 0, ALLOW_REDACT=1 redacts) and the
end-to-end host flow (approve → two passes with ALLOW_REDACT 0→1;
decline → one pass, exit 0; no-secrets happy path → one pass, no
confirm). 38/38 export tests, 2176/0 fail overall.
- CLI 1.0.34 → 1.0.35.
@la14-1

Copy link
Copy Markdown
Collaborator

Pushed a gate in commit 855a89e to turn the silent redact-and-push into a two-pass flow:

First pass (ALLOW_REDACT=0): VM stages + scans. If hits found, writes {"ok":false,"needsConfirmation":true,"hits":[...]} and exits 0 before any commit or push.

Host: reads the result, prints the file list, notes the regex has known gaps (#3381), and asks:

Redact these N files and continue pushing to GitHub? (initialValue: false)

  • Declineexit 0, nothing pushed.
  • Approve → re-run with ALLOW_REDACT=1, which does the sed + re-stage + commit + push, and returns the redacted list in the success result.

No-hits path is unchanged: single pass, single push.

Diff

  • packages/cli/src/commands/export.ts (+161 / -41): ResultSchema gains a needs_confirmation variant; buildExportScript takes allowRedact: boolean; cmdExport factors the run+download+parse cycle into runPassAndParseResult so two-pass orchestration is readable.
  • packages/cli/src/__tests__/export.test.ts (+147 / -2): 4 new tests — two scripting assertions (ALLOW_REDACT=0 writes needs_confirmation and exits 0; ALLOW_REDACT=1 redacts) and three end-to-end flows (approve → two passes 0→1; decline → one pass, exit 0; no-secrets → one pass, no confirm).
  • CLI 1.0.34 → 1.0.35.

Verification

  • bunx @biomejs/biome check src/ clean
  • bun test → 2176/0 fail (38/38 export tests, up from 34)
  • bash -n on both rendered script variants → both parse

Ready for another look / merge.

@la14-1la14-1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate wired up in 855a89e. First pass scans + pauses on hits; host prompts with the file list; only on approval does the redact+push run. Tests cover all four flows (approve / decline / no-secrets / script variants). All CI green. Approving.

@la14-1
la14-1 merged commit 3152a19 into OpenRouterLabs:mainMay 2, 2026
5 checks passed
Sign up for freeto 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.

3 participants

@AhmedTMM@la14-1@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(export): redact secrets in-place instead of aborting - #3383

Merged
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets
May 2, 2026
Merged

fix(export): redact secrets in-place instead of aborting#3383
la14-1 merged 2 commits into
OpenRouterLabs:mainfrom
AhmedTMM:fix-export-redact-secrets

Conversation

@AhmedTMM

Copy link
Copy Markdown
Collaborator

Summary

Before: matching any of the API-key regexes in a staged file killed the export — the user got a JSON error and had to SSH in and clean up by hand.

After: matched strings are replaced with `REDACTED-BY-SPAWN-EXPORT` via `sed -i -E`, the file is re-staged, and the export proceeds. The redacted file list is included in the success result and surfaced on the host CLI:

```
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:

  • project/test/brain-sync.test.ts
    ```

Reproduces the user-reported failure where a test fixture (project/test/brain-sync.test.ts) tripped the scan and the whole export bailed.

Notes

  • Regex unchanged — same Anthropic / OpenRouter / OpenAI / GitHub / AWS / Hetzner / DO / PEM coverage as before.
  • Placeholder is intentionally loud (`REDACTED-BY-SPAWN-EXPORT`) so a reader of the public repo can tell something was scrubbed.
  • Result schema gains an optional `redacted: string[]` field; the host CLI shows it as a clack warning, not an error.
  • Bumps CLI `1.0.33` → `1.0.34`.

Test plan

  • `bunx @biomejs/biome check src/` — clean
  • `bun test` — 2168 pass / same 4 pre-existing fails
  • 34 export tests pass; legacy "aborts on hit" assertion replaced with new redact assertions
  • Manual: re-run the failing export from the user's session — confirm `brain-sync.test.ts` is redacted and the spawn link is printed

🤖 Generated with Claude Code

Before: any staged file matching the secret regex caused the export
to fail with `{"ok":false,"error":"Possible secrets detected..."}`,
forcing the user to SSH in and clean things up by hand.
After: matched strings are replaced with `***REDACTED-BY-SPAWN-EXPORT***`
via sed -i -E, the file is re-staged, and the export proceeds. The list
of redacted files is included in the success result and surfaced as a
warning on the host CLI:
✓ Exported to https://github.com/alice/my-vm
⚠ Redacted potential secrets in 1 file:
- project/test/brain-sync.test.ts
The regex is unchanged. The redact placeholder is intentionally loud so
a casual reader of the published repo can tell that something was
scrubbed and isn't just blank.
Bumps CLI 1.0.33 -> 1.0.34.
@AhmedTMM
AhmedTMM marked this pull request as ready for review May 2, 2026 07:19
Previously the VM would silently redact any staged files matching the
secret regex and push the repo — meaning a regex miss (OpenRouterLabs#3381 tracks
broadening) would publish a real secret without the user ever seeing
the file list. That's a fail-open posture on a tool that can push to
public GitHub.
New flow:
- buildExportScript takes allowRedact: boolean.
- First pass (allowRedact=false): VM stages, runs the secret scan,
and on hits writes a needs_confirmation result (hits=[...]) and
exits 0 before any commit or push. No hits → commit + push as
before.
- Host reads the result. If needs_confirmation: print the file list,
explain that the regex has known gaps, and ask "Redact these N files
and continue pushing?" (initialValue false). Decline → exit 0, no
push. Approve → re-run the script with allowRedact=true, which now
actually does the sed + re-stage + commit + push.
Other changes:
- ResultSchema gains the needs_confirmation variant.
- cmdExport factors the runServer + downloadFile + parse cycle into
runPassAndParseResult so the two-pass orchestration is readable.
- Tests: 4 new cases cover the gate scripting (ALLOW_REDACT=0 writes
needs_confirmation and exits 0, ALLOW_REDACT=1 redacts) and the
end-to-end host flow (approve → two passes with ALLOW_REDACT 0→1;
decline → one pass, exit 0; no-secrets happy path → one pass, no
confirm). 38/38 export tests, 2176/0 fail overall.
- CLI 1.0.34 → 1.0.35.
@la14-1

Copy link
Copy Markdown
Collaborator

Pushed a gate in commit 855a89e to turn the silent redact-and-push into a two-pass flow:

First pass (ALLOW_REDACT=0): VM stages + scans. If hits found, writes {"ok":false,"needsConfirmation":true,"hits":[...]} and exits 0 before any commit or push.

Host: reads the result, prints the file list, notes the regex has known gaps (#3381), and asks:

Redact these N files and continue pushing to GitHub? (initialValue: false)

  • Declineexit 0, nothing pushed.
  • Approve → re-run with ALLOW_REDACT=1, which does the sed + re-stage + commit + push, and returns the redacted list in the success result.

No-hits path is unchanged: single pass, single push.

Diff

  • packages/cli/src/commands/export.ts (+161 / -41): ResultSchema gains a needs_confirmation variant; buildExportScript takes allowRedact: boolean; cmdExport factors the run+download+parse cycle into runPassAndParseResult so two-pass orchestration is readable.
  • packages/cli/src/__tests__/export.test.ts (+147 / -2): 4 new tests — two scripting assertions (ALLOW_REDACT=0 writes needs_confirmation and exits 0; ALLOW_REDACT=1 redacts) and three end-to-end flows (approve → two passes 0→1; decline → one pass, exit 0; no-secrets → one pass, no confirm).
  • CLI 1.0.34 → 1.0.35.

Verification

  • bunx @biomejs/biome check src/ clean
  • bun test → 2176/0 fail (38/38 export tests, up from 34)
  • bash -n on both rendered script variants → both parse

Ready for another look / merge.

@la14-1la14-1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate wired up in 855a89e. First pass scans + pauses on hits; host prompts with the file list; only on approval does the redact+push run. Tests cover all four flows (approve / decline / no-secrets / script variants). All CI green. Approving.

@la14-1
la14-1 merged commit 3152a19 into OpenRouterLabs:mainMay 2, 2026
5 checks passed
Sign up for freeto 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.

3 participants

@AhmedTMM@la14-1@claude