Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ curl -fsSL https://claude.ai/install.sh | bash
irm https://claude.ai/install.ps1 | iex
```

**Windows Symbolic Link Prerequisite**

Windows users: Archon creates symlinks under `~/.archon/workspaces/` for project registration. Windows requires either:
- **Developer Mode enabled** (Settings → Privacy & Security → For developers → Developer Mode)
- or running Archon with **administrator privileges**

</details>

```bash
Expand Down
14 changes: 13 additions & 1 deletion packages/core/src/utils/env-leak-scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,18 @@ export function formatLeakError(

const consent = consentCopy(context);

// Collect all unique keys from findings
const allKeys = new Set<string>();
report.findings.forEach(finding => {
finding.keys.forEach(key => allKeys.add(key));
});
const keysArray = Array.from(allKeys);

// Generate grep command that excludes all detected keys
const grepCommand = keysArray
.map(key => `grep -v '^${key}='`)
.join(' | ') + ' .env > .env.tmp && mv .env.tmp .env';
Comment on lines +143 to +145
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Put .env input on first grep stage

When more than one sensitive key is detected, this formatter emits a command like grep -v '^KEY1=' | grep -v '^KEY2=' .env > .env.tmp ...; in that form, the first grep has no file/stdin source and waits on terminal input, so the recommended remediation hangs instead of editing .env. This only appears in multi-key findings (for example .env containing both OPENAI_API_KEY and GEMINI_API_KEY), which is a common case for this gate.

Useful? React with 👍 / 👎.


Comment on lines +135 to +146
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix generated remediation command for multiple keys.

Line 143-145 builds an invalid pipeline for >1 key (only the last grep gets .env). It also includes non-env placeholders (e.g. unreadable markers), which can generate broken regex patterns.

💡 Proposed fix
-  // Collect all unique keys from findings
-  const allKeys = new Set<string>();
-  report.findings.forEach(finding => {
-    finding.keys.forEach(key => allKeys.add(key));
-  });
-  const keysArray = Array.from(allKeys);
-
-  // Generate grep command that excludes all detected keys
-  const grepCommand = keysArray
-    .map(key => `grep -v '^${key}='`)
-    .join(' | ') + ' .env > .env.tmp && mv .env.tmp .env';
+  // Collect unique concrete env var keys only (skip diagnostic placeholders)
+  const keysArray = Array.from(new Set(report.findings.flatMap(f => f.keys))).filter(key =>
+    /^[A-Z_][A-Z0-9_]*$/.test(key)
+  );
+
+  const escapedKeys = keysArray.map(key => key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
+  const grepCommand =
+    escapedKeys.length > 0
+      ? `grep -v -E '^(${escapedKeys.join('|')})=' .env > .env.tmp && mv .env.tmp .env`
+      : '# No concrete key names could be derived; remove keys manually from .env';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Collect all unique keys from findings
const allKeys = new Set<string>();
report.findings.forEach(finding => {
finding.keys.forEach(key => allKeys.add(key));
});
const keysArray = Array.from(allKeys);
// Generate grep command that excludes all detected keys
const grepCommand = keysArray
.map(key => `grep -v '^${key}='`)
.join(' | ') + ' .env > .env.tmp && mv .env.tmp .env';
// Collect unique concrete env var keys only (skip diagnostic placeholders)
const keysArray = Array.from(new Set(report.findings.flatMap(f => f.keys))).filter(key =>
/^[A-Z_][A-Z0-9_]*$/.test(key)
);
const escapedKeys = keysArray.map(key => key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
const grepCommand =
escapedKeys.length > 0
? `grep -v -E '^(${escapedKeys.join('|')})=' .env > .env.tmp && mv .env.tmp .env`
: '# No concrete key names could be derived; remove keys manually from .env';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/utils/env-leak-scanner.ts` around lines 135 - 146, The
generated grep pipeline is invalid for multiple keys and may include unsafe
patterns; update the code that builds grepCommand (using
keysArray/allKeys/report.findings) to first filter keys to only valid env
variable names (e.g. /^[A-Za-z_][A-Za-z0-9_]*$/), skip if none remain, escape
regex metacharacters for each key (add a small escapeRegex helper), and then
build a safe pipeline that starts with "cat .env" and pipes through each grep -v
like: "cat .env | grep -v '^ESCAPED_KEY=' | grep -v '^OTHER_KEY=' > .env.tmp &&
mv .env.tmp .env" so each grep operates on the stream and the command is valid
for multiple keys.

return `${header}

Found:
Expand All @@ -145,7 +157,7 @@ ${fileList}

Choose one:
1. Remove the key from this repo's .env (recommended):
grep -v '^ANTHROPIC_API_KEY=' .env > .env.tmp && mv .env.tmp .env
${grepCommand}

2. Rename to a non-auto-loaded file:
mv .env .env.secrets
Expand Down
28 changes: 27 additions & 1 deletion packages/providers/src/claude/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ function normalizeClaudeUsage(usage?: {
* process.env is already clean at this point:
* - stripCwdEnv() at entry point removed CWD .env keys + CLAUDECODE markers
* - ~/.archon/.env loaded with override:true as the trusted source
*
* Double-filter here to be extra safe — ensures no CLAUDE_CODE_* markers leak
* to the subprocess, even if stripCwdEnv() was bypassed or if platform-specific
* env inheritance adds them back.
*/
function buildSubprocessEnv(): NodeJS.ProcessEnv {
const hasExplicitTokens = Boolean(
Expand All @@ -83,7 +87,29 @@ function buildSubprocessEnv(): NodeJS.ProcessEnv {
{ authMode },
authMode === 'global' ? 'using_global_auth' : 'using_explicit_tokens'
);
return { ...process.env };

// Create a clean copy of process.env
const env = { ...process.env };

// Filter out CLAUDE_CODE_* markers (keep only auth-related ones)
const CLAUDE_CODE_AUTH_VARS = new Set([
'CLAUDE_CODE_OAUTH_TOKEN',
'CLAUDE_CODE_USE_BEDROCK',
'CLAUDE_CODE_USE_VERTEX',
]);

for (const key of Object.keys(env)) {
if (key.startsWith('CLAUDE_CODE_') && !CLAUDE_CODE_AUTH_VARS.has(key)) {
delete env[key];
}
}

// Also remove CLAUDECODE marker if present
if (env.CLAUDECODE) {
delete env.CLAUDECODE;
}

return env;
}

/** Max retries for transient subprocess failures */
Expand Down