Files
clawhub/convex/lib/packageSecurity.test.ts
Patrick Erichsen cc16d7fbd9 fix: route static scan findings through clawscan (#2398)
* fix: route static scan findings through clawscan

* fix: preserve clawscan-only security verdicts after merge
2026-05-25 18:33:33 -07:00

237 lines
7.0 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
getPackageDownloadSecurityBlock,
getPackageTrustReasons,
isPackageBlockedFromPublic,
resolvePackageReleaseScanStatus,
} from "./packageSecurity";
describe("packageSecurity", () => {
it("treats pending package scans as public", () => {
expect(isPackageBlockedFromPublic("pending")).toBe(false);
});
it("allows package downloads while VT is pending", () => {
expect(
getPackageDownloadSecurityBlock({
sha256hash: "a".repeat(64),
} as never),
).toBeNull();
});
it("still resolves sha256-only releases to pending", () => {
expect(
resolvePackageReleaseScanStatus({
sha256hash: "a".repeat(64),
} as never),
).toBe("pending");
});
it("does not block VT-only malicious package releases", () => {
expect(isPackageBlockedFromPublic("malicious")).toBe(true);
expect(
getPackageDownloadSecurityBlock({
vtAnalysis: {
status: "malicious",
source: "engines",
engineStats: { malicious: 1, suspicious: 0, harmless: 12, undetected: 54 },
},
} as never),
).toBeNull();
});
it("keeps legacy VT suspicious status as telemetry when engines are clean", () => {
const release = {
sha256hash: "a".repeat(64),
vtAnalysis: {
status: "suspicious",
scanner: "legacy-ai",
source: "legacy-ai",
engineStats: { malicious: 0, suspicious: 0, harmless: 12, undetected: 54 },
},
} as never;
expect(resolvePackageReleaseScanStatus(release)).toBe("pending");
expect(getPackageDownloadSecurityBlock(release)).toBeNull();
});
it("keeps legacy VT malicious status as telemetry when engines are clean", () => {
const release = {
sha256hash: "a".repeat(64),
vtAnalysis: {
status: "malicious",
scanner: "legacy-ai",
source: "legacy-ai",
engineStats: { malicious: 0, suspicious: 0, harmless: 12, undetected: 54 },
},
} as never;
expect(resolvePackageReleaseScanStatus(release)).toBe("pending");
expect(getPackageDownloadSecurityBlock(release)).toBeNull();
});
it("keeps engine-backed VT suspicious as telemetry", () => {
expect(
resolvePackageReleaseScanStatus({
vtAnalysis: {
status: "clean",
scanner: "legacy-ai",
source: "legacy-ai",
engineStats: { malicious: 0, suspicious: 1, harmless: 12, undetected: 54 },
},
} as never),
).toBe("not-run");
});
it("keeps engine-backed VT malicious as telemetry", () => {
const release = {
vtAnalysis: {
status: "clean",
scanner: "legacy-ai",
source: "legacy-ai",
engineStats: { malicious: 1, suspicious: 0, harmless: 12, undetected: 54 },
},
} as never;
expect(resolvePackageReleaseScanStatus(release)).toBe("not-run");
expect(getPackageDownloadSecurityBlock(release)).toBeNull();
});
it("does not let suspicious static scans override clean verification", () => {
expect(
resolvePackageReleaseScanStatus({
staticScan: { status: "suspicious" },
verification: { scanStatus: "clean" },
} as never),
).toBe("clean");
});
it("does not preserve old static-only suspicious verification", () => {
expect(
resolvePackageReleaseScanStatus({
staticScan: { status: "suspicious" },
verification: { scanStatus: "suspicious" },
sha256hash: "a".repeat(64),
} as never),
).toBe("pending");
});
it("does not preserve old static-only malicious verification", () => {
expect(
resolvePackageReleaseScanStatus({
staticScan: { status: "malicious" },
verification: { scanStatus: "malicious" },
sha256hash: "a".repeat(64),
} as never),
).toBe("pending");
});
it("lets package ClawScan clear non-malicious scanner noise", () => {
expect(
resolvePackageReleaseScanStatus({
vtAnalysis: { status: "suspicious" },
llmAnalysis: { status: "completed", verdict: "benign" },
verification: { scanStatus: "suspicious" },
} as never),
).toBe("clean");
});
it("lets package ClawScan clear a static malicious hold", () => {
expect(
resolvePackageReleaseScanStatus({
staticScan: { status: "malicious" },
llmAnalysis: { status: "clean", verdict: "benign" },
} as never),
).toBe("clean");
});
it("trusts verified OpenClaw plugins while Codex reviews static holds", () => {
const release = {
staticScan: { status: "malicious" },
verification: { scanStatus: "clean", trustedOpenClawPlugin: true },
} as never;
expect(resolvePackageReleaseScanStatus(release)).toBe("clean");
expect(getPackageDownloadSecurityBlock(release)).toBeNull();
});
it("keeps static malicious package scans advisory until ClawScan decides", () => {
const release = {
staticScan: { status: "malicious" },
sha256hash: "a".repeat(64),
} as never;
expect(resolvePackageReleaseScanStatus(release)).toBe("pending");
expect(getPackageDownloadSecurityBlock(release)).toBeNull();
});
it("lets manual package moderation approve or block releases", () => {
expect(
resolvePackageReleaseScanStatus({
staticScan: { status: "malicious" },
manualModeration: { state: "approved" },
} as never),
).toBe("clean");
expect(
getPackageDownloadSecurityBlock({
verification: { scanStatus: "clean" },
manualModeration: { state: "quarantined" },
} as never),
).toEqual(
expect.objectContaining({
status: 403,
message: expect.stringContaining("quarantined"),
}),
);
});
it("explains blocked trust decisions with compact reason codes", () => {
expect(
getPackageTrustReasons(
{
manualModeration: { state: "quarantined" },
vtAnalysis: {
status: "malicious",
engineStats: { malicious: 1, suspicious: 0, harmless: 12, undetected: 54 },
},
} as never,
"malicious",
2,
),
).toEqual(["manual:quarantined", "scan:malicious", "reports:2"]);
});
it("does not expose legacy VT-only statuses as public trust reasons", () => {
expect(
getPackageTrustReasons(
{
vtAnalysis: {
status: "malicious",
scanner: "legacy-ai",
source: "legacy-ai",
engineStats: { malicious: 0, suspicious: 0, harmless: 12, undetected: 54 },
},
} as never,
"pending",
),
).toEqual(["scan:pending"]);
});
it("keeps static-only package findings out of trust reason codes", () => {
expect(
getPackageTrustReasons(
{
staticScan: { status: "malicious" },
} as never,
"malicious",
),
).toEqual(["scan:malicious"]);
});
it("keeps clean and not-run releases free of scan reason noise", () => {
expect(getPackageTrustReasons({} as never, "clean")).toEqual([]);
expect(getPackageTrustReasons({} as never, "not-run")).toEqual([]);
});
});