fix(e2e): reject unsafe Docker pack names

This commit is contained in:
Vincent Koc
2026-06-17 03:57:57 +02:00
parent 2f222cdc1c
commit 8b06d80655
2 changed files with 77 additions and 4 deletions
+32 -4
View File
@@ -84,6 +84,28 @@ function validateOutputName(value) {
}
}
function resolvePackedOpenClawFileName(value) {
const filename = value.trim();
if (
!filename.endsWith(".tgz") ||
(!filename.startsWith("openclaw-") &&
!filename.includes(":") &&
!filename.includes("/") &&
!filename.includes("\\"))
) {
return "";
}
if (
!/^openclaw-[A-Za-z0-9._-]+\.tgz$/u.test(filename) ||
filename.includes("\0") ||
filename !== path.basename(filename) ||
filename !== path.win32.basename(filename)
) {
throw new Error(`npm pack reported unsafe OpenClaw tarball filename: ${filename}`);
}
return filename;
}
export function parseArgs(argv) {
const options = {
outputDir: "",
@@ -283,9 +305,9 @@ async function runCapture(command, args, cwd, options = {}) {
async function newestOpenClawTarball(outputDir, packOutput) {
let fromOutput = "";
for (const line of packOutput.split(/\r?\n/u)) {
const trimmed = line.trim();
if (/^openclaw-.*\.tgz$/u.test(trimmed)) {
fromOutput = trimmed;
const filename = resolvePackedOpenClawFileName(line);
if (filename) {
fromOutput = filename;
}
}
if (fromOutput) {
@@ -294,7 +316,13 @@ async function newestOpenClawTarball(outputDir, packOutput) {
const entries = await fs.readdir(outputDir);
const packed = entries
.filter((entry) => /^openclaw-.*\.tgz$/u.test(entry))
.filter((entry) => {
try {
return resolvePackedOpenClawFileName(entry) === entry;
} catch {
return false;
}
})
.toSorted()
.at(-1);
if (!packed) {
@@ -215,6 +215,51 @@ describe("package-openclaw-for-docker", () => {
]);
});
it("rejects path-like npm pack stdout before resolving Docker package tarballs", async () => {
for (const filename of [
"../openclaw-2026.6.17.tgz",
"/tmp/openclaw-2026.6.17.tgz",
String.raw`C:\temp\openclaw-2026.6.17.tgz`,
"openclaw-nested/evil.tgz",
String.raw`openclaw-nested\evil.tgz`,
"openclaw-C:evil.tgz",
]) {
await expect(
packOpenClawPackageForDocker("/repo", "/out", {
prepareChangelog: async () => {},
restoreChangelog: async () => {},
runCaptureImpl: async () => `${filename}\n`,
}),
).rejects.toThrow("npm pack reported unsafe OpenClaw tarball filename");
}
});
it("ignores unsafe output directory tarball names when npm stdout is not usable", async () => {
const outputDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-docker-pack-"));
try {
fs.writeFileSync(path.join(outputDir, "openclaw-C:evil.tgz"), "");
fs.writeFileSync(path.join(outputDir, String.raw`openclaw-nested\evil.tgz`), "");
await expect(
packOpenClawPackageForDocker("/repo", outputDir, {
prepareChangelog: async () => {},
restoreChangelog: async () => {},
runCaptureImpl: async () => "npm notice\n",
}),
).rejects.toThrow("missing packed OpenClaw tarball");
fs.writeFileSync(path.join(outputDir, "openclaw-2026.6.17.tgz"), "");
await expect(
packOpenClawPackageForDocker("/repo", outputDir, {
prepareChangelog: async () => {},
restoreChangelog: async () => {},
runCaptureImpl: async () => "npm notice\n",
}),
).resolves.toBe(path.join(outputDir, "openclaw-2026.6.17.tgz"));
} finally {
fs.rmSync(outputDir, { recursive: true, force: true });
}
});
it("restores the changelog when ignore-scripts packaging fails", async () => {
const calls: string[] = [];