Merge pull request #13 from mane/fix/api-key-env-handling

fix: avoid implicit API key imports
This commit is contained in:
pliny authored and GitHub committed 2026-07-05 13:55:56 -04:00
commit 7e21859a4c
3 files changed
+69 -33

No files matched your search

+37
View File
@@ -0,0 +1,37 @@
import { describe, expect, it } from 'vitest';
import { readFileSync } from 'fs';
import { join } from 'path';
const configSource = readFileSync(join(process.cwd(), 'src/config/index.ts'), 'utf8');
const setupScript = readFileSync(join(process.cwd(), 'scripts/setup-api.sh'), 'utf8');
function configLoadEnvBlock(): string {
const start = configSource.indexOf('private loadEnvVariables(): void');
expect(start).toBeGreaterThanOrEqual(0);
const end = configSource.indexOf('\n /**\n * Get all settings', start);
expect(end).toBeGreaterThan(start);
return configSource.slice(start, end);
}
describe('API key environment handling hardening', () => {
it('ConfigManager does not implicitly read .env from the caller working directory', () => {
const block = configLoadEnvBlock();
expect(block).not.toContain("join(process.cwd(), '.env')");
expect(block).toContain("join(homedir(), '.t3mp3st', '.env')");
});
it('ConfigManager uses env keys in-memory and does not persist imported env keys', () => {
const block = configLoadEnvBlock();
expect(block).not.toMatch(/setApiKey\(/);
expect(block).not.toMatch(/config\.set\(['"]apiKeys['"]/);
});
it('setup-api.sh never sources .env and reads API keys silently into a 0600 file', () => {
expect(setupScript).not.toMatch(/\bsource\s+["']?\$ENV_FILE/);
expect(setupScript).toMatch(/umask\s+077/);
expect(setupScript).toMatch(/read\s+-rsp\s+"Enter your OpenRouter API key:/);
expect(setupScript).toMatch(/chmod\s+600\s+"\$ENV_FILE"/);
});
});
+6 -19
View File
@@ -452,9 +452,10 @@ class ConfigManager {
private loadEnvVariables(): void {
if (this.envLoaded) return;
// Try to load from .env file in current directory or home
// Load only T3MP3ST-owned/home env files. Do NOT read process.cwd()/.env:
// operators often run T3MP3ST inside target repos, and importing that repo's
// secrets would contaminate this process with unrelated credentials.
const envPaths = [
join(process.cwd(), '.env'),
join(homedir(), '.t3mp3st', '.env'),
join(homedir(), '.env'),
];
@@ -482,23 +483,9 @@ class ConfigManager {
}
}
// Check environment variables for API keys
const envKeys = {
openrouter: process.env.OPENROUTER_API_KEY,
venice: process.env.VENICE_API_KEY,
anthropic: process.env.ANTHROPIC_API_KEY,
openai: process.env.OPENAI_API_KEY,
};
// Only set from env if not already set in config
const currentKeys = this.config.get('apiKeys');
for (const [provider, envKey] of Object.entries(envKeys)) {
if (envKey && !currentKeys[provider as keyof typeof currentKeys]) {
this.setApiKey(provider as 'openrouter' | 'venice' | 'anthropic' | 'openai', envKey);
}
}
// Environment variables (including values loaded from ~/.t3mp3st/.env above)
// are intentionally process-local. getApiKey() already gives env vars highest
// priority, so do not persist them into Conf's apiKeys store as a side effect.
this.envLoaded = true;
}