fix: restore prepublication ClawScan authentication (#3142)

* fix: preserve prepublication judge errors

* fix: pass codex credential to prepublication scans

* fix: keep node tests out of vitest
This commit is contained in:
Patrick Erichsen
2026-07-16 23:38:09 -07:00
committed by GitHub
parent b95f9658e0
commit 0da4aa718b
5 changed files with 69 additions and 1 deletions
@@ -91,6 +91,7 @@ jobs:
- name: Run pre-publication publish worker
env:
CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
SECURITY_SCAN_WORKER_TOKEN: ${{ secrets.SECURITY_SCAN_WORKER_TOKEN }}
VIRUSTOTAL_API_KEY: ${{ secrets.VT_API_KEY }}
@@ -113,6 +113,7 @@ describe("pre-publication publish worker workflow", () => {
"${{ vars.PREPUBLICATION_TRUFFLEHOG_IMAGE || 'ghcr.io/trufflesecurity/trufflehog:3.95.6@sha256:96f8429082cb2d4ae73b1096dcdb2f5aa139881d97042b0c5e5fa226a392e056' }}",
});
expect(String(job.env?.PREPUBLICATION_TRUFFLEHOG_IMAGE)).toContain("@sha256:");
expect(job.env).not.toHaveProperty("CODEX_API_KEY");
expect(job.env).not.toHaveProperty("OPENAI_API_KEY");
expect(job.env).not.toHaveProperty("SECURITY_SCAN_WORKER_TOKEN");
expect(job.env).not.toHaveProperty("CODEX_SECURITY_SCAN_TIMEOUT_MS");
@@ -134,6 +135,7 @@ describe("pre-publication publish worker workflow", () => {
expect(steps.find((step) => step.name === "Install SkillSpector")).toBeUndefined();
expect(JSON.stringify(job)).not.toContain("CODEX_SECURITY_SCAN_SHADOW_CLAWSCAN");
expect(runStep?.env).toEqual({
CODEX_API_KEY: "${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}",
OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}",
SECURITY_SCAN_WORKER_TOKEN: "${{ secrets.SECURITY_SCAN_WORKER_TOKEN }}",
VIRUSTOTAL_API_KEY: "${{ secrets.VT_API_KEY }}",
@@ -144,6 +146,9 @@ describe("pre-publication publish worker workflow", () => {
expect(stepUsesSecret(step, "SECURITY_SCAN_WORKER_TOKEN"), stepName).toBe(
stepName === "Run pre-publication publish worker",
);
expect(stepUsesSecret(step, "CODEX_API_KEY"), stepName).toBe(
stepName === "Run pre-publication publish worker",
);
expect(stepUsesSecret(step, "OPENAI_API_KEY"), stepName).toBe(
stepName === "Run pre-publication publish worker",
);
@@ -643,6 +643,61 @@ JSON
}
});
it("preserves the redacted ClawScan judge failure reason", async () => {
const workspace = await tempDir();
await mkdir(join(workspace, "artifact"), { recursive: true });
await writeFile(join(workspace, "artifact", "SKILL.md"), "# Demo\n");
const fakeClawScan = join(workspace, "fake-clawscan");
await writeFile(
fakeClawScan,
`#!/usr/bin/env bash
set -euo pipefail
output=""
while [ "$#" -gt 0 ]; do
if [ "$1" = "--output" ]; then
output="$2"
break
fi
shift
done
cat > "$output" <<'JSON'
{"schemaVersion":"clawscan-run-v1","profile":"clawhub","scanners":{"clawscan-static":{"status":"completed"},"skillspector":{"status":"completed"},"virustotal":{"status":"completed"}},"judge":{"status":"failed","error":"Codex request was rate limited.","result":null}}
JSON
`,
);
await chmod(fakeClawScan, 0o755);
const previousCommand = process.env.PREPUBLICATION_CLAWSCAN_COMMAND;
process.env.PREPUBLICATION_CLAWSCAN_COMMAND = fakeClawScan;
try {
await expect(
runNativeClawScan(
{
job: {
_id: String(attempt.attemptId),
attempts: 1,
hasMaliciousSignal: false,
leaseToken: attempt.claimId,
source: "pre-publication",
targetKind: "skillVersion",
waitForVtUntil: 0,
},
target: {},
},
workspace,
),
).resolves.toEqual({
check: {
status: "failed",
summary: "ClawScan judge status was failed: Codex request was rate limited.",
},
});
} finally {
if (previousCommand === undefined) delete process.env.PREPUBLICATION_CLAWSCAN_COMMAND;
else process.env.PREPUBLICATION_CLAWSCAN_COMMAND = previousCommand;
}
});
it("maps TruffleHog verified-secret exit code to a blocked result", async () => {
const workspace = await tempDir();
await mkdir(join(workspace, "artifact"), { recursive: true });
@@ -405,12 +405,18 @@ function storedAnalysisFromClawScanArtifact(artifact: unknown): {
const judge = asRecord(record?.judge);
const result = asRecord(judge?.result);
const judgeStatus = readString(judge, ["status"]);
const judgeError = readString(judge, ["error"]);
const scannerFailures = collectClawScanScannerFailures(asRecord(record?.scanners));
if (scannerFailures.length > 0) {
return { error: `ClawScan scanner did not complete: ${scannerFailures.join(", ")}` };
}
if (judgeStatus !== "completed") {
return { error: `ClawScan judge status was ${judgeStatus ?? "missing"}` };
return {
error: [
`ClawScan judge status was ${judgeStatus ?? "missing"}`,
...(judgeError ? [judgeError] : []),
].join(": "),
};
}
const verdict = readString(result, ["verdict", "status"]);
if (!verdict) return { error: "ClawScan judge did not return a verdict" };
+1
View File
@@ -41,6 +41,7 @@ export default defineConfig({
"**/dist/**",
"**/coverage/**",
"**/convex/_generated/**",
".github/scripts/catalog-feed-schema-version-guard.test.cjs",
"packages/clawhub/**",
"packages/clawhub-admin/test-artifact/**",
"e2e/**",