105 lines
6.1 KiB
TypeScript
105 lines
6.1 KiB
TypeScript
import { describe, expect, test } from 'bun:test';
|
|
import { chmod, mkdtemp, rm, symlink, writeFile } from 'node:fs/promises';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import {
|
|
ADMIN_KEY, applicationEnvironment, BOOTSTRAP_KEYS, fetchSecrets,
|
|
localDeploymentAdmin, parseExport, readCredentials, validateProject,
|
|
type Environment, type VaultProject,
|
|
} from '../scripts/lib/infisical';
|
|
|
|
import { parseArguments } from '../scripts/secrets';
|
|
|
|
const project: VaultProject = {
|
|
workspaceId: 'e176b742-85cd-4af6-96a7-f33ab7c2c709', defaultEnvironment: 'dev',
|
|
domain: 'https://secrets.jb.codes', secretPath: '/',
|
|
};
|
|
const credentials = { [BOOTSTRAP_KEYS[0]]: 'fake-client-id', [BOOTSTRAP_KEYS[1]]: 'fake-client-secret' };
|
|
|
|
describe('Infisical boundary', () => {
|
|
test('accepts Bun-forwarded commands and preserves child --help arguments', () => {
|
|
expect(parseArguments(['run', 'docker', 'compose', 'config', '--quiet']).command).toEqual(['docker', 'compose', 'config', '--quiet']);
|
|
expect(parseArguments(['run', '--deployment', '--', 'bun', 'tool.ts', '--help'])).toMatchObject({ deployment: true, command: ['bun', 'tool.ts', '--help'] });
|
|
expect(parseArguments(['status']).action).toBe('status');
|
|
expect(() => parseArguments(['run', '--unknown'])).toThrow();
|
|
expect(() => parseArguments(['status', '--', 'printenv'])).toThrow();
|
|
});
|
|
test('reads only protected regular credentials files without evaluating dotenv', async () => {
|
|
const directory = await mkdtemp(join(tmpdir(), 'eve-vault-'));
|
|
try {
|
|
const file = join(directory, 'identity.env');
|
|
await writeFile(file, `${BOOTSTRAP_KEYS[0]}="fake-client-id"\n${BOOTSTRAP_KEYS[1]}='fake-client-secret'\nNODE_OPTIONS=untrusted\n`, { mode: 0o600 });
|
|
expect(await readCredentials(file)).toEqual(credentials);
|
|
await symlink(file, join(directory, 'link'));
|
|
await expect(readCredentials(join(directory, 'link'))).rejects.toThrow('0600');
|
|
await chmod(file, 0o644);
|
|
await expect(readCredentials(file)).rejects.toThrow('0600');
|
|
} finally { await rm(directory, { recursive: true, force: true }); }
|
|
});
|
|
|
|
test('pins the authorized vault before sending any credential', async () => {
|
|
expect(validateProject(project)).toEqual(project);
|
|
let called = false;
|
|
await expect(fetchSecrets({ ...project, domain: 'https://attacker.invalid' }, credentials, async () => { called = true; return ''; })).rejects.toThrow('approved instance');
|
|
expect(called).toBe(false);
|
|
});
|
|
|
|
test('authenticates and exports only the selected scope; never places credentials in argv', async () => {
|
|
const calls: { args: string[]; env: Environment }[] = [];
|
|
const values = await fetchSecrets(project, credentials, async (args, env) => {
|
|
calls.push({ args, env });
|
|
return args[0] === 'login' ? 'fake-access-token\n' : '[{"key":"TYPESAFE_API_KEY","value":"fake-key"}]';
|
|
}, { PATH: '/usr/bin', INFISICAL_TOKEN: 'stale-token', [ADMIN_KEY]: 'admin', ...credentials });
|
|
expect(values.TYPESAFE_API_KEY).toBe('fake-key');
|
|
expect(calls[0].env[BOOTSTRAP_KEYS[1]]).toBe('fake-client-secret');
|
|
expect(calls[0].env.INFISICAL_TOKEN).toBeUndefined();
|
|
expect(calls[1].env.INFISICAL_TOKEN).toBe('fake-access-token');
|
|
expect(calls[1].env[BOOTSTRAP_KEYS[1]]).toBeUndefined();
|
|
expect(calls[1].env[ADMIN_KEY]).toBeUndefined();
|
|
expect(calls[1].args).toContain(project.workspaceId);
|
|
expect(calls[1].args).toContain('--expand=false');
|
|
expect(calls[1].args).toContain('--include-imports=false');
|
|
expect(calls.every(call => call.args.includes(project.domain))).toBe(true);
|
|
expect(JSON.stringify(calls.map(call => call.args))).not.toContain('fake-client');
|
|
expect(JSON.stringify(calls.map(call => call.args))).not.toContain('fake-access-token');
|
|
});
|
|
|
|
test('derives admin credentials only for the local deployment and keeps other secrets out of Docker argv/env', async () => {
|
|
let calls = 0;
|
|
const runner = async (argv: string[], env: Environment) => {
|
|
calls++;
|
|
expect(argv).toEqual(['docker', 'compose', 'exec', '-T', 'backend', './generate_admin_key.sh']);
|
|
expect(env.INFISICAL_TOKEN).toBeUndefined();
|
|
expect(env.EVE_CLIENT_SECRET).toBeUndefined();
|
|
return 'fake-admin\n';
|
|
};
|
|
expect(await localDeploymentAdmin({ CONVEX_SELF_HOSTED_URL: 'http://127.0.0.1:3210', INFISICAL_TOKEN: 'token', EVE_CLIENT_SECRET: 'secret' }, runner)).toBe('fake-admin');
|
|
await expect(localDeploymentAdmin({ CONVEX_SELF_HOSTED_URL: 'https://other.example' }, runner)).rejects.toThrow('restricted');
|
|
expect(calls).toBe(1);
|
|
});
|
|
|
|
test('rejects malformed/duplicate exports without echoing values', () => {
|
|
expect(parseExport('{"TYPESAFE_API_KEY":"test"}')).toEqual({ TYPESAFE_API_KEY: 'test' });
|
|
for (const raw of ['secret-in-malformed-json', '[{"key":"KEY","value":1}]', '[{"key":"KEY","value":"a"},{"key":"KEY","value":"b"}]', 'null']) {
|
|
expect(() => parseExport(raw)).toThrow('Invalid Infisical export; values suppressed');
|
|
}
|
|
});
|
|
|
|
test('child processes receive only managed app keys, without bootstrap or ambient admin credentials', () => {
|
|
const base = { PATH: '/usr/bin', INFISICAL_TOKEN: 'token', [ADMIN_KEY]: 'ambient-admin', ...credentials, TYPESAFE_API_KEY: 'stale-key' };
|
|
const values = { EVE_CLIENT_ID: 'client', EVE_CLIENT_SECRET: 'secret', [ADMIN_KEY]: 'vault-admin', NODE_OPTIONS: 'not-injected' };
|
|
const config = { APP_ORIGIN: 'https://dev.evebot.app' };
|
|
const env = applicationEnvironment(base, values, config);
|
|
expect(env.EVE_CLIENT_SECRET).toBe('secret');
|
|
expect(env.PATH).toBe('/usr/bin');
|
|
expect(env.APP_ORIGIN).toBe(config.APP_ORIGIN);
|
|
expect(env.TYPESAFE_API_KEY).toBeUndefined();
|
|
expect(env[ADMIN_KEY]).toBeUndefined();
|
|
expect(env.NODE_OPTIONS).toBeUndefined();
|
|
expect(Object.keys(env).some(key => key.startsWith('INFISICAL_'))).toBe(false);
|
|
expect(applicationEnvironment(base, values, config, true)[ADMIN_KEY]).toBe('vault-admin');
|
|
expect(() => applicationEnvironment(base, values, { INFISICAL_TOKEN: 'do-not-forward' })).toThrow('Unexpected public');
|
|
expect(() => applicationEnvironment(base, values, { NODE_OPTIONS: 'do-not-execute' })).toThrow('Unexpected public');
|
|
});
|
|
});
|