fix(security): handle artifact directory markers (#3206)

This commit is contained in:
Patrick Erichsen
2026-07-21 17:55:22 -07:00
committed by GitHub
parent a09d42484a
commit f9713e81cd
2 changed files with 59 additions and 3 deletions
@@ -412,6 +412,51 @@ describe("run-codex-scan-worker diagnostics", () => {
expect(metadata.target.files).toEqual([{ path: "SKILL.md", sha256: "abc123", size: 42 }]);
});
it("materializes zero-byte directory markers with descendant files", async () => {
const workspace = await tempDir();
await writeArtifactWorkspace(
{
job: {
_id: "job-directory-marker",
hasMaliciousSignal: false,
leaseToken: "lease-secret",
source: "pre-publication",
targetKind: "skillVersion",
waitForVtUntil: 0,
},
target: {
files: [
{
path: "scripts",
sha256: "empty",
size: 0,
url: "data:application/octet-stream,",
},
{
path: "scripts/run.sh",
sha256: "script",
size: 18,
url: "data:text/plain,echo%20ready%0A",
},
{
path: "EMPTY",
sha256: "empty",
size: 0,
url: "data:application/octet-stream,",
},
],
},
},
workspace,
);
expect(await readFile(join(workspace, "artifact", "scripts", "run.sh"), "utf8")).toBe(
"echo ready\n",
);
expect(await readFile(join(workspace, "artifact", "EMPTY"))).toHaveLength(0);
});
it("omits signed artifact URLs from download failure errors", async () => {
const unsafeLabels = unsafeFixtureLabels();
const fetchMock = vi
+14 -3
View File
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
import { mkdirSync, readFileSync } from "node:fs";
import { appendFile, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { basename, dirname, join, resolve } from "node:path";
import { basename, dirname, join, resolve, sep } from "node:path";
import { pathToFileURL } from "node:url";
import { ConvexHttpClient } from "convex/browser";
import { api } from "../../convex/_generated/api";
@@ -743,8 +743,19 @@ export async function writeArtifactWorkspace(job: ClaimedJob, workspace: string)
};
await writeFile(join(workspace, "metadata.json"), `${JSON.stringify(metadata, null, 2)}\n`);
for (const file of job.target.files ?? []) {
const out = safeOutputPath(workspace, file.path);
const files = (job.target.files ?? []).map((file) => ({
file,
out: safeOutputPath(workspace, file.path),
}));
for (const candidate of files) {
const isDirectoryMarker =
candidate.file.size === 0 &&
files.some(
(other) => other.out !== candidate.out && other.out.startsWith(`${candidate.out}${sep}`),
);
if (isDirectoryMarker) continue;
const { file, out } = candidate;
await mkdir(dirname(out), { recursive: true });
await writeFile(out, await download(file.url, { kind: "file", path: file.path }));
}