98 lines
3.1 KiB
TypeScript
98 lines
3.1 KiB
TypeScript
import { mkdtemp, open, rm, writeFile } from 'node:fs/promises';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
import { readDelegatedCredentialFromFd } from './delegated-credential.js';
|
|
|
|
let cleanup: string | undefined;
|
|
afterEach(async (): Promise<void> => {
|
|
if (cleanup !== undefined) await rm(cleanup, { recursive: true, force: true });
|
|
cleanup = undefined;
|
|
});
|
|
|
|
describe('protected delegated credential channel', (): void => {
|
|
it('reads authority from an inherited fd number without putting the secret in argv or env', async (): Promise<void> => {
|
|
cleanup = await mkdtemp(join(tmpdir(), 'mosaic-authority-fd-'));
|
|
const path = join(cleanup, 'authority');
|
|
await writeFile(
|
|
path,
|
|
JSON.stringify({
|
|
identity: 'provisioner',
|
|
estate: 'homelab',
|
|
host: 'git.example.invalid',
|
|
secret: 'seeded-authority-canary',
|
|
}),
|
|
{ mode: 0o600 },
|
|
);
|
|
const handle = await open(path, 'r');
|
|
try {
|
|
const resolved = await readDelegatedCredentialFromFd(
|
|
handle.fd,
|
|
'provisioner',
|
|
'homelab',
|
|
'git.example.invalid',
|
|
);
|
|
expect(resolved.identity).toBe('provisioner');
|
|
expect(Buffer.from(resolved.secret).toString('utf8')).toBe('seeded-authority-canary');
|
|
} finally {
|
|
await handle.close();
|
|
}
|
|
});
|
|
|
|
it('rejects a regular-file authority fd with group or other access', async (): Promise<void> => {
|
|
cleanup = await mkdtemp(join(tmpdir(), 'mosaic-authority-fd-'));
|
|
const path = join(cleanup, 'authority');
|
|
await writeFile(
|
|
path,
|
|
JSON.stringify({
|
|
identity: 'provisioner',
|
|
estate: 'homelab',
|
|
host: 'git.example.invalid',
|
|
secret: 'seeded-authority-canary',
|
|
}),
|
|
{ mode: 0o644 },
|
|
);
|
|
const handle = await open(path, 'r');
|
|
try {
|
|
await expect(
|
|
readDelegatedCredentialFromFd(handle.fd, 'provisioner', 'homelab', 'git.example.invalid'),
|
|
).rejects.toMatchObject({ code: 'delegated-authority-unavailable' });
|
|
} finally {
|
|
await handle.close();
|
|
}
|
|
});
|
|
|
|
it('rejects an authority identity or estate mismatch without echoing the secret', async (): Promise<void> => {
|
|
cleanup = await mkdtemp(join(tmpdir(), 'mosaic-authority-fd-'));
|
|
const path = join(cleanup, 'authority');
|
|
await writeFile(
|
|
path,
|
|
JSON.stringify({
|
|
identity: 'other',
|
|
estate: 'usc',
|
|
host: 'git.example.invalid',
|
|
secret: 'seeded-authority-canary',
|
|
}),
|
|
{ mode: 0o600 },
|
|
);
|
|
const handle = await open(path, 'r');
|
|
try {
|
|
let message = '';
|
|
try {
|
|
await readDelegatedCredentialFromFd(
|
|
handle.fd,
|
|
'provisioner',
|
|
'homelab',
|
|
'git.example.invalid',
|
|
);
|
|
} catch (error: unknown) {
|
|
message = error instanceof Error ? error.message : String(error);
|
|
}
|
|
expect(message).toContain('delegated-authority-mismatch');
|
|
expect(message).not.toContain('seeded-authority-canary');
|
|
} finally {
|
|
await handle.close();
|
|
}
|
|
});
|
|
});
|