Files
2026-10-06 15:31:51 +00:00

362 lines
12 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { createHash } from "node:crypto";
import {
parseImageReference,
resolveRegistryDigest,
} from "../../server/registry";
describe("OCI registry digest resolution", () => {
const validManifest = JSON.stringify({
schemaVersion: 2,
mediaType: "application/vnd.oci.image.manifest.v1+json",
config: {
mediaType: "application/vnd.oci.image.config.v1+json",
digest: `sha256:${"a".repeat(64)}`,
size: 1,
},
layers: [],
});
test("parses tags, ports, defaults, and pinned references", () => {
expect(parseImageReference("localhost:5000/team/image:v1")).toMatchObject({
registry: "localhost:5000",
repository: "team/image",
reference: "v1",
});
expect(
parseImageReference("registry.example.com/team/image").reference,
).toBe("latest");
const pinned = `sha256:${"a".repeat(64)}` as const;
expect(
parseImageReference(`registry.example.com/team/image@${pinned}`).digest,
).toBe(pinned);
expect(() => parseImageReference("image:latest")).toThrow("registry host");
expect(() =>
parseImageReference(`registry.example.com/../image@${pinned}`),
).toThrow("Invalid image reference");
});
test("accepts distribution tags and digest-pinned references", () => {
for (const tag of [
"latest",
"v1.2.3",
"Release_2026-10",
"_build",
"a".repeat(128),
]) {
expect(
parseImageReference(`localhost:5000/team/my_image:${tag}`),
).toEqual({
registry: "localhost:5000",
repository: "team/my_image",
reference: tag,
});
}
const digest = `sha256:${"a".repeat(64)}` as const;
expect(
parseImageReference(
`registry.example.com/team/my-image:Release_1@${digest}`,
),
).toEqual({
registry: "registry.example.com",
repository: "team/my-image",
reference: digest,
digest,
});
expect(
parseImageReference(`registry.example.com/team/my-image@${digest}`)
.digest,
).toBe(digest);
});
test("rejects malformed tags and uppercase repository names", () => {
for (const tag of [
"bad+tag",
".leading",
"-leading",
"bad:tag",
"bad@tag",
"bad/tag",
"é",
"a".repeat(129),
"",
]) {
expect(() =>
parseImageReference(`registry.example.com/team/image:${tag}`),
).toThrow();
}
expect(() =>
parseImageReference("registry.example.com/Team/image:Release_1"),
).toThrow("Invalid image reference");
const digest = `sha256:${"a".repeat(64)}` as const;
expect(() =>
parseImageReference(`registry.example.com/team/image:bad+tag@${digest}`),
).toThrow("Invalid image reference");
});
test("resolves an anonymous manifest using the advertised digest", async () => {
const body = validManifest;
const expected =
`sha256:${createHash("sha256").update(body).digest("hex")}` as const;
const calls: Array<{ url: string; authorization: string | null }> = [];
const fetcher = async (
input: string | URL | Request,
init?: RequestInit,
) => {
const headers = new Headers(init?.headers);
calls.push({
url: String(input),
authorization: headers.get("authorization"),
});
return new Response(body, {
headers: { "docker-content-digest": expected },
});
};
expect(
await resolveRegistryDigest("registry.example.com/team/image:v1", {
fetch: fetcher,
}),
).toBe(expected);
expect(calls).toEqual([
{
url: "https://registry.example.com/v2/team/image/manifests/v1",
authorization: null,
},
]);
});
test("rejects a valid but incorrect advertised manifest digest", async () => {
const body = validManifest;
const incorrect = `sha256:${"b".repeat(64)}`;
await expect(
resolveRegistryDigest("mismatch.example.com/team/image:v1", {
fetch: async () =>
new Response(body, {
headers: { "docker-content-digest": incorrect },
}),
}),
).rejects.toThrow("does not match Docker-Content-Digest");
});
test("caches successful digest resolutions with deterministic expiry and bounds", async () => {
let now = 0;
let calls = 0;
const body = validManifest;
const advertised = `sha256:${createHash("sha256").update(body).digest("hex")}`;
const fetcher = async () => {
calls += 1;
return new Response(body, {
headers: { "docker-content-digest": advertised },
});
};
const options = {
fetch: fetcher,
cacheTtlMs: 100,
cacheMaxEntries: 1,
clock: () => now,
};
await resolveRegistryDigest("cache.example.com/team/first:v1", options);
await resolveRegistryDigest("cache.example.com/team/first:v1", options);
expect(calls).toBe(1);
now = 100;
await resolveRegistryDigest("cache.example.com/team/first:v1", options);
expect(calls).toBe(2);
await resolveRegistryDigest("cache.example.com/team/second:v1", options);
await resolveRegistryDigest("cache.example.com/team/first:v1", options);
expect(calls).toBe(4);
});
test("does not cache failed resolutions or share entries across credentials", async () => {
let calls = 0;
const failing = async () => {
calls += 1;
return new Response("unavailable", { status: 503 });
};
const options = { fetch: failing, cacheTtlMs: 1_000 };
await expect(
resolveRegistryDigest("failure.example.com/team/image:v1", options),
).rejects.toThrow("503");
await expect(
resolveRegistryDigest("failure.example.com/team/image:v1", options),
).rejects.toThrow("503");
expect(calls).toBe(2);
const authorizedCalls: string[] = [];
const authorized = async (
_input: string | URL | Request,
init?: RequestInit,
) => {
authorizedCalls.push(new Headers(init?.headers).get("authorization")!);
return new Response(validManifest, {
headers: {
"docker-content-digest": `sha256:${createHash("sha256").update(validManifest).digest("hex")}`,
},
});
};
for (const username of ["one", "two"]) {
await resolveRegistryDigest("credentials.example.com/team/image:v1", {
fetch: authorized,
credentials: { username, password: "password" },
});
}
expect(authorizedCalls).toHaveLength(2);
});
test("resolves through a trusted internal registry origin", async () => {
const expected =
`sha256:${createHash("sha256").update(validManifest).digest("hex")}` as const;
let requested = "";
await expect(
resolveRegistryDigest("registry.example.com/team/image:v1", {
origin: "http://registry.registry.svc.cluster.local:5000/",
insecure: true,
fetch: async (input) => {
requested = String(input);
return new Response(validManifest, {
headers: {
"docker-content-digest": `sha256:${createHash("sha256").update(validManifest).digest("hex")}`,
},
});
},
}),
).resolves.toBe(expected);
expect(requested).toBe(
"http://registry.registry.svc.cluster.local:5000/v2/team/image/manifests/v1",
);
});
test("follows a standard bearer challenge and hashes a digest-less response", async () => {
const body =
'{"schemaVersion":2,"mediaType":"application/vnd.oci.image.index.v1+json","manifests":[]}';
const expected =
`sha256:${createHash("sha256").update(body).digest("hex")}` as const;
const calls: Array<{ url: string; authorization: string | null }> = [];
const fetcher = async (
input: string | URL | Request,
init?: RequestInit,
) => {
const url = String(input);
const authorization = new Headers(init?.headers).get("authorization");
calls.push({ url, authorization });
if (url.startsWith("https://auth.example/token")) {
expect(new URL(url).searchParams.get("scope")).toBe(
"repository:team/image:pull",
);
expect(authorization).toBe(
`Basic ${Buffer.from("user:pass").toString("base64")}`,
);
return Response.json({ access_token: "registry-token" });
}
if (authorization !== "Bearer registry-token") {
return new Response("unauthorized", {
status: 401,
headers: {
"www-authenticate":
'Bearer realm="https://auth.example/token",service="registry.example.com"',
},
});
}
return new Response(body, {
headers: {
"content-type": "application/vnd.oci.image.manifest.v1+json",
},
});
};
expect(
await resolveRegistryDigest("registry.example.com/team/image:v2", {
fetch: fetcher,
credentials: { username: "user", password: "pass" },
}),
).toBe(expected);
expect(calls).toHaveLength(3);
expect(calls[2]?.authorization).toBe("Bearer registry-token");
});
test("rejects unsupported challenges and malformed advertised digests", async () => {
const unauthorized = async () =>
new Response("no", {
status: 401,
headers: { "www-authenticate": 'Basic realm="registry"' },
});
await expect(
resolveRegistryDigest("registry.example.com/team/image", {
fetch: unauthorized,
}),
).rejects.toThrow("401");
const malformed = async () =>
new Response(validManifest, {
headers: { "docker-content-digest": "sha256:bad" },
});
await expect(
resolveRegistryDigest("registry.example.com/team/image", {
fetch: malformed,
}),
).rejects.toThrow("Invalid SHA-256");
});
test("rejects empty, non-JSON, and malformed manifests with or without digest headers", async () => {
const malformedBodies = [
"",
"sensitive registry response body",
'{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","layers":[]}',
];
for (const [index, body] of malformedBodies.entries()) {
for (const includeDigest of [false, true]) {
const headers = includeDigest
? {
"docker-content-digest": `sha256:${createHash("sha256").update(body).digest("hex")}`,
}
: undefined;
await expect(
resolveRegistryDigest(
`invalid-${index}-${includeDigest}.example/team/image`,
{
cacheTtlMs: 0,
fetch: async () => new Response(body, { headers }),
},
),
).rejects.toThrow("invalid manifest");
}
}
});
test("accepts supported manifest/index media types with generic content type", async () => {
const fixtures = [
validManifest,
JSON.stringify({
schemaVersion: 2,
mediaType: "application/vnd.docker.distribution.manifest.v2+json",
config: {},
layers: [],
}),
JSON.stringify({
schemaVersion: 2,
mediaType: "application/vnd.oci.image.index.v1+json",
manifests: [],
}),
JSON.stringify({
schemaVersion: 2,
mediaType: "application/vnd.docker.distribution.manifest.list.v2+json",
manifests: [],
}),
];
for (const [index, body] of fixtures.entries()) {
await expect(
resolveRegistryDigest(`valid-${index}.example/team/image`, {
cacheTtlMs: 0,
fetch: async () =>
new Response(body, {
headers: { "content-type": "application/octet-stream" },
}),
}),
).resolves.toBe(
`sha256:${createHash("sha256").update(body).digest("hex")}`,
);
}
});
});