Merge pull request #25 from mane/fix/config-env-export-consistency
fix: keep API key setup and export in sync
This commit is contained in:
3 files changed
+57
-21
No files matched your search
+4
-15
@@ -10,10 +10,11 @@ echo "║ T3MP3ST API KEY CONFIGURATION ║"
|
|||||||
echo "╚═══════════════════════════════════════════════════════════╝"
|
echo "╚═══════════════════════════════════════════════════════════╝"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
ENV_FILE="$(dirname "$0")/../.env"
|
ENV_FILE="$HOME/.t3mp3st/.env"
|
||||||
|
mkdir -p "$(dirname "$ENV_FILE")"
|
||||||
|
|
||||||
if [ -f "$ENV_FILE" ]; then
|
if [ -f "$ENV_FILE" ]; then
|
||||||
echo "[*] Existing .env file found; it will be replaced after you enter a new key."
|
echo "[*] Existing T3MP3ST env file found; it will be replaced after you enter a new key."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
@@ -64,24 +65,12 @@ esac
|
|||||||
|
|
||||||
chmod 600 "$ENV_FILE"
|
chmod 600 "$ENV_FILE"
|
||||||
|
|
||||||
# Add .env to gitignore if not already there
|
|
||||||
GITIGNORE="$(dirname "$0")/../.gitignore"
|
|
||||||
if [ -f "$GITIGNORE" ]; then
|
|
||||||
if ! grep -q "^\.env$" "$GITIGNORE"; then
|
|
||||||
echo ".env" >> "$GITIGNORE"
|
|
||||||
echo "[+] Added .env to .gitignore"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo ".env" > "$GITIGNORE"
|
|
||||||
echo "[+] Created .gitignore with .env"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "╔═══════════════════════════════════════════════════════════╗"
|
echo "╔═══════════════════════════════════════════════════════════╗"
|
||||||
echo "║ CONFIGURATION COMPLETE! ║"
|
echo "║ CONFIGURATION COMPLETE! ║"
|
||||||
echo "╚═══════════════════════════════════════════════════════════╝"
|
echo "╚═══════════════════════════════════════════════════════════╝"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Your API key has been saved to .env (mode 600)"
|
echo "Your API key has been saved to $ENV_FILE (mode 600)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Start the server with:"
|
echo "Start the server with:"
|
||||||
echo " npm run server"
|
echo " npm run server"
|
||||||
|
|||||||
@@ -5,14 +5,26 @@ import { join } from 'path';
|
|||||||
const configSource = readFileSync(join(process.cwd(), 'src/config/index.ts'), 'utf8');
|
const configSource = readFileSync(join(process.cwd(), 'src/config/index.ts'), 'utf8');
|
||||||
const setupScript = readFileSync(join(process.cwd(), 'scripts/setup-api.sh'), 'utf8');
|
const setupScript = readFileSync(join(process.cwd(), 'scripts/setup-api.sh'), 'utf8');
|
||||||
|
|
||||||
function configLoadEnvBlock(): string {
|
function sourceBlock(startMarker: string, endMarker: string): string {
|
||||||
const start = configSource.indexOf('private loadEnvVariables(): void');
|
const start = configSource.indexOf(startMarker);
|
||||||
expect(start).toBeGreaterThanOrEqual(0);
|
expect(start, `missing start marker ${startMarker}`).toBeGreaterThanOrEqual(0);
|
||||||
const end = configSource.indexOf('\n /**\n * Get all settings', start);
|
const end = configSource.indexOf(endMarker, start);
|
||||||
expect(end).toBeGreaterThan(start);
|
expect(end, `missing end marker ${endMarker}`).toBeGreaterThan(start);
|
||||||
return configSource.slice(start, end);
|
return configSource.slice(start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function configLoadEnvBlock(): string {
|
||||||
|
return sourceBlock('private loadEnvVariables(): void', '\n /**\n * Get all settings');
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportConfigBlock(): string {
|
||||||
|
return sourceBlock('exportConfig(filePath: string): void', '\n /**\n * Create a .env template file');
|
||||||
|
}
|
||||||
|
|
||||||
|
function envTemplateBlock(): string {
|
||||||
|
return sourceBlock('createEnvTemplate(filePath:', '\n writeFileSync(filePath, template);');
|
||||||
|
}
|
||||||
|
|
||||||
describe('API key environment handling hardening', () => {
|
describe('API key environment handling hardening', () => {
|
||||||
it('ConfigManager does not implicitly read .env from the caller working directory', () => {
|
it('ConfigManager does not implicitly read .env from the caller working directory', () => {
|
||||||
const block = configLoadEnvBlock();
|
const block = configLoadEnvBlock();
|
||||||
@@ -21,6 +33,30 @@ describe('API key environment handling hardening', () => {
|
|||||||
expect(block).toContain("join(homedir(), '.t3mp3st', '.env')");
|
expect(block).toContain("join(homedir(), '.t3mp3st', '.env')");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('setup-api.sh writes the same T3MP3ST-owned env file that ConfigManager reads', () => {
|
||||||
|
const block = configLoadEnvBlock();
|
||||||
|
|
||||||
|
expect(setupScript).not.toMatch(/dirname "\$0"\).*\.\.\/\.env/);
|
||||||
|
expect(setupScript).toMatch(/\.t3mp3st/);
|
||||||
|
expect(setupScript).toMatch(/mkdir\s+-p\s+"\$\(dirname "\$ENV_FILE"\)"/);
|
||||||
|
expect(block).toContain("join(homedir(), '.t3mp3st', '.env')");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('env template points users at the T3MP3ST-owned env file, not a caller-cwd .env', () => {
|
||||||
|
const block = envTemplateBlock();
|
||||||
|
|
||||||
|
expect(block).toContain('~/.t3mp3st/.env');
|
||||||
|
expect(block).not.toContain('Copy this file to .env');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exportConfig redacts every supported provider key slot', () => {
|
||||||
|
const block = exportConfigBlock();
|
||||||
|
|
||||||
|
for (const provider of ['openrouter', 'venice', 'anthropic', 'openai', 'xai']) {
|
||||||
|
expect(block).toContain(`${provider}: settings.apiKeys.${provider} ? '***REDACTED***' : undefined`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('ConfigManager uses env keys in-memory and does not persist imported env keys', () => {
|
it('ConfigManager uses env keys in-memory and does not persist imported env keys', () => {
|
||||||
const block = configLoadEnvBlock();
|
const block = configLoadEnvBlock();
|
||||||
|
|
||||||
|
|||||||
+12
-1
@@ -793,8 +793,10 @@ class ConfigManager {
|
|||||||
...settings,
|
...settings,
|
||||||
apiKeys: {
|
apiKeys: {
|
||||||
openrouter: settings.apiKeys.openrouter ? '***REDACTED***' : undefined,
|
openrouter: settings.apiKeys.openrouter ? '***REDACTED***' : undefined,
|
||||||
|
venice: settings.apiKeys.venice ? '***REDACTED***' : undefined,
|
||||||
anthropic: settings.apiKeys.anthropic ? '***REDACTED***' : undefined,
|
anthropic: settings.apiKeys.anthropic ? '***REDACTED***' : undefined,
|
||||||
openai: settings.apiKeys.openai ? '***REDACTED***' : undefined,
|
openai: settings.apiKeys.openai ? '***REDACTED***' : undefined,
|
||||||
|
xai: settings.apiKeys.xai ? '***REDACTED***' : undefined,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
writeFileSync(filePath, JSON.stringify(safeSettings, null, 2));
|
writeFileSync(filePath, JSON.stringify(safeSettings, null, 2));
|
||||||
@@ -805,12 +807,17 @@ class ConfigManager {
|
|||||||
*/
|
*/
|
||||||
createEnvTemplate(filePath: string = join(process.cwd(), '.env.template')): void {
|
createEnvTemplate(filePath: string = join(process.cwd(), '.env.template')): void {
|
||||||
const template = `# T3MP3ST Environment Configuration
|
const template = `# T3MP3ST Environment Configuration
|
||||||
# Copy this file to .env and fill in your API keys
|
# Save this file as ~/.t3mp3st/.env and fill in your API keys
|
||||||
|
# The setup script writes the same T3MP3ST-owned file.
|
||||||
|
|
||||||
# OpenRouter API Key (recommended - access to multiple models)
|
# OpenRouter API Key (recommended - access to multiple models)
|
||||||
# Get your key at: https://openrouter.ai/keys
|
# Get your key at: https://openrouter.ai/keys
|
||||||
OPENROUTER_API_KEY=
|
OPENROUTER_API_KEY=
|
||||||
|
|
||||||
|
# Venice API Key
|
||||||
|
# Get your key at: https://venice.ai
|
||||||
|
VENICE_API_KEY=
|
||||||
|
|
||||||
# Anthropic API Key (direct Claude access)
|
# Anthropic API Key (direct Claude access)
|
||||||
# Get your key at: https://console.anthropic.com/
|
# Get your key at: https://console.anthropic.com/
|
||||||
ANTHROPIC_API_KEY=
|
ANTHROPIC_API_KEY=
|
||||||
@@ -818,6 +825,10 @@ ANTHROPIC_API_KEY=
|
|||||||
# OpenAI API Key
|
# OpenAI API Key
|
||||||
# Get your key at: https://platform.openai.com/api-keys
|
# Get your key at: https://platform.openai.com/api-keys
|
||||||
OPENAI_API_KEY=
|
OPENAI_API_KEY=
|
||||||
|
|
||||||
|
# xAI API Key
|
||||||
|
# Get your key at: https://console.x.ai/
|
||||||
|
XAI_API_KEY=
|
||||||
`;
|
`;
|
||||||
writeFileSync(filePath, template);
|
writeFileSync(filePath, template);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user