security: pass secrets via SDK env option and delete temp file (#213)

Pass secrets to the SDK via the `env` query option instead of setting
process.env, so Bash subprocesses never inherit API keys. Delete
/tmp/input.json immediately after reading to remove secrets from disk.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-02-13 22:46:42 +02:00
committed by GitHub
parent 1a07869329
commit 1549ad503e
2 changed files with 10 additions and 5 deletions

View File

@@ -358,6 +358,7 @@ async function runQuery(
sessionId: string | undefined,
mcpServerPath: string,
containerInput: ContainerInput,
sdkEnv: Record<string, string | undefined>,
resumeAt?: string,
): Promise<{ newSessionId?: string; lastAssistantUuid?: string; closedDuringQuery: boolean }> {
const stream = new MessageStream();
@@ -432,6 +433,7 @@ async function runQuery(
'NotebookEdit',
'mcp__nanoclaw__*'
],
env: sdkEnv,
permissionMode: 'bypassPermissions',
allowDangerouslySkipPermissions: true,
settingSources: ['project', 'user'],
@@ -493,6 +495,8 @@ async function main(): Promise<void> {
try {
const stdinData = await readStdin();
containerInput = JSON.parse(stdinData);
// Delete the temp file the entrypoint wrote — it contains secrets
try { fs.unlinkSync('/tmp/input.json'); } catch { /* may not exist */ }
log(`Received input for group: ${containerInput.groupFolder}`);
} catch (err) {
writeOutput({
@@ -503,10 +507,11 @@ async function main(): Promise<void> {
process.exit(1);
}
// Set secrets as env vars for the SDK (needed for API auth).
// The PreToolUse hook strips these from every Bash subprocess.
// Build SDK env: merge secrets into process.env for the SDK only.
// Secrets never touch process.env itself, so Bash subprocesses can't see them.
const sdkEnv: Record<string, string | undefined> = { ...process.env };
for (const [key, value] of Object.entries(containerInput.secrets || {})) {
process.env[key] = value;
sdkEnv[key] = value;
}
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -535,7 +540,7 @@ async function main(): Promise<void> {
while (true) {
log(`Starting query (session: ${sessionId || 'new'}, resumeAt: ${resumeAt || 'latest'})...`);
const queryResult = await runQuery(prompt, sessionId, mcpServerPath, containerInput, resumeAt);
const queryResult = await runQuery(prompt, sessionId, mcpServerPath, containerInput, sdkEnv, resumeAt);
if (queryResult.newSessionId) {
sessionId = queryResult.newSessionId;
}