feat: release 2.7.0-rc2

This commit is contained in:
2026-10-06 15:31:51 +00:00 Unverified
parent cd9fb512cd
commit c8de9ddcba
42 changed files with 6127 additions and 593 deletions
+152
View File
@@ -160,6 +160,158 @@ describe("source materialization", () => {
expect(await readlink(join(destination, "run"))).toBe("bin/run");
});
test("rejects effective symlink escapes in either manifest order before writing or reading file blobs", async () => {
for (const entries of [
[
["sub", "."],
["chain", "sub/.."],
],
[
["chain", "sub/.."],
["sub", "."],
],
]) {
const root = await mkdtemp(join(tmpdir(), "kuber-materialize-"));
roots.push(root);
const destination = join(root, "workspace");
const file = Buffer.from("content");
const fileDigest = digest(file);
const blobs = new Map<Sha256Digest, Uint8Array>();
const manifest: WorkspaceManifest = {
version: BUILD_PROTOCOL_VERSION,
files: [
{
path: "file",
type: "file",
digest: fileDigest,
size: file.length,
mode: 0o644,
},
...entries.map(([path, target]) => {
const data = Buffer.from(target!);
const id = digest(data);
blobs.set(id, data);
return {
path: path!,
type: "symlink" as const,
digest: id,
size: data.length,
mode: 0o777 as const,
};
}),
],
};
const manifestData = Buffer.from(JSON.stringify(manifest));
const manifestDigest = digest(manifestData);
const reads: Sha256Digest[] = [];
await expect(
materializeWorkspace(
{
get: async (requested) => {
reads.push(requested);
if (requested === manifestDigest) return manifestData;
if (requested === fileDigest)
throw new Error("file blob read before validation");
return blobs.get(requested)!;
},
},
manifestDigest,
destination,
),
).rejects.toThrow("Unsafe symlink target for chain");
expect(reads).not.toContain(fileDigest);
await expect(lstat(destination)).rejects.toMatchObject({
code: "ENOENT",
});
}
});
test("materializes safe chained and parent-relative links", async () => {
const root = await mkdtemp(join(tmpdir(), "kuber-materialize-"));
roots.push(root);
const cas = new FilesystemCas(join(root, "cas"));
const files = await Promise.all(
[
["nested/parent", "../file"],
["chain", "nested/parent"],
["repeat", "nested/../nested/parent"],
].map(async ([path, target]) => {
const data = Buffer.from(target!);
return {
path: path!,
type: "symlink" as const,
digest: await cas.put(data),
size: data.length,
mode: 0o777 as const,
};
}),
);
const content = Buffer.from("safe");
const manifest: WorkspaceManifest = {
version: BUILD_PROTOCOL_VERSION,
files: [
...files,
{
path: "file",
type: "file",
digest: await cas.put(content),
size: content.length,
mode: 0o644,
},
],
};
const destination = join(root, "workspace");
await materializeWorkspace(
cas,
await cas.put(Buffer.from(JSON.stringify(manifest))),
destination,
);
expect(await readFile(join(destination, "chain"), "utf8")).toBe("safe");
expect(await readFile(join(destination, "repeat"), "utf8")).toBe("safe");
});
test("rejects direct escapes and symlink cycles before destination creation", async () => {
for (const links of [
[["nested/bad", "../../outside"]],
[
["a", "b"],
["b", "a"],
],
[["self", "self"]],
]) {
const root = await mkdtemp(join(tmpdir(), "kuber-materialize-"));
roots.push(root);
const cas = new FilesystemCas(join(root, "cas"));
const files = await Promise.all(
links.map(async ([path, target]) => {
const data = Buffer.from(target!);
return {
path: path!,
type: "symlink" as const,
digest: await cas.put(data),
size: data.length,
mode: 0o777 as const,
};
}),
);
const manifest: WorkspaceManifest = {
version: BUILD_PROTOCOL_VERSION,
files,
};
const destination = join(root, "workspace");
await expect(
materializeWorkspace(
cas,
await cas.put(Buffer.from(JSON.stringify(manifest))),
destination,
),
).rejects.toThrow(/Unsafe symlink/);
await expect(lstat(destination)).rejects.toMatchObject({
code: "ENOENT",
});
}
});
test("rejects traversal, path collisions, unsafe links, and size mismatches atomically", async () => {
expect(() =>
parseWorkspaceManifest(