/** * Registry auth resolution — anonymous Docker Hub when no usable creds. */ import test from 'brittle' import { isUsableDockerAuth, authMatchesImage, normalizeRegistryHost, registryHostFromImage, resolveRegistryAuth, registryPrefixFromServeraddress, retargetImageRefForRegistry, parseImageRepoTag, } from '../server/handlers/vault.js' test('isUsableDockerAuth requires username and non-empty password', (t) => { t.absent(isUsableDockerAuth(null)) t.absent(isUsableDockerAuth({})) t.absent(isUsableDockerAuth({ username: 'u' })) t.absent(isUsableDockerAuth({ username: 'u', password: '' })) t.ok(isUsableDockerAuth({ username: 'u', password: 'p' })) }) test('registryHostFromImage maps short names to docker.io', (t) => { t.is(registryHostFromImage('httpd:latest'), 'docker.io') t.is(registryHostFromImage('library/httpd'), 'docker.io') t.is(registryHostFromImage('user/app:1'), 'docker.io') t.is(registryHostFromImage('ghcr.io/org/app:latest'), 'ghcr.io') }) test('normalizeRegistryHost collapses docker hub aliases', (t) => { t.is(normalizeRegistryHost('https://index.docker.io/v1/'), 'docker.io') t.is(normalizeRegistryHost('registry-1.docker.io'), 'docker.io') t.is(normalizeRegistryHost('ghcr.io'), 'ghcr.io') }) test('authMatchesImage only applies same-registry session auth', (t) => { t.ok( authMatchesImage( { serveraddress: 'https://index.docker.io/v1/' }, 'httpd:latest' ) ) t.absent( authMatchesImage({ serveraddress: 'https://ghcr.io' }, 'httpd:latest') ) t.ok(authMatchesImage({ serveraddress: 'https://ghcr.io' }, 'ghcr.io/a/b:1')) }) test('registryPrefixFromServeraddress strips scheme and api path, keeps port', (t) => { t.is(registryPrefixFromServeraddress('https://192.168.0.12:5555/v2/'), '192.168.0.12:5555') t.is(registryPrefixFromServeraddress('https://ghcr.io'), 'ghcr.io') t.is(registryPrefixFromServeraddress('https://index.docker.io/v1/'), '') t.is( retargetImageRefForRegistry('apache-httpd:flat-latest', 'https://192.168.0.12:5555/'), '192.168.0.12:5555/apache-httpd:flat-latest' ) t.is( retargetImageRefForRegistry('ghcr.io/org/app:1', 'https://192.168.0.12:5555/'), 'ghcr.io/org/app:1' ) t.alike(parseImageRepoTag('192.168.0.12:5555/myapp:flat'), { repo: '192.168.0.12:5555/myapp', tag: 'flat', }) }) test('resolveRegistryAuth returns null without session/vault (anonymous)', (t) => { const session = { state: new Map(), id: 'peer1' } const auth = resolveRegistryAuth(session, { image: 'httpd:latest', autoVault: true, }) t.is(auth, null) }) test('resolveRegistryAuth ignores incomplete session auth', (t) => { const session = { id: 'peer1', state: new Map([ [ 'registryAuth', { username: 'baduser', password: '', serveraddress: 'https://index.docker.io/v1/' }, ], ]), } const auth = resolveRegistryAuth(session, { image: 'httpd:latest' }) t.is(auth, null) }) test('resolveRegistryAuth ignores session for different registry host', (t) => { const session = { id: 'peer1', state: new Map([ [ 'registryAuth', { username: 'ghuser', password: 'secret', serveraddress: 'https://ghcr.io', }, ], ]), } // Pulling Docker Hub while session is logged into GHCR → anonymous for Hub const auth = resolveRegistryAuth(session, { image: 'httpd:latest' }) t.is(auth, null) })