Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
481
test/scripts/pnpm-audit-prod.test.ts
Normal file
481
test/scripts/pnpm-audit-prod.test.ts
Normal file
@@ -0,0 +1,481 @@
|
||||
// Pnpm Audit Prod tests cover pnpm audit prod script behavior.
|
||||
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
collectProdResolvedPackagesFromLockfile,
|
||||
createBulkAdvisoryPayload,
|
||||
fetchBulkAdvisories,
|
||||
filterFindingsBySeverity,
|
||||
parseArgs,
|
||||
parseSnapshotKey,
|
||||
readBoundedBulkAdvisoryErrorText,
|
||||
runPnpmAuditProd,
|
||||
stripVersionDecorators,
|
||||
} from "../../scripts/pre-commit/pnpm-audit-prod.mjs";
|
||||
|
||||
describe("pnpm-audit-prod", () => {
|
||||
it("parses explicit audit severity flags", () => {
|
||||
expect(parseArgs(["--min-severity", "critical"])).toEqual({ minSeverity: "critical" });
|
||||
expect(parseArgs(["--audit-level=moderate"])).toEqual({ minSeverity: "moderate" });
|
||||
});
|
||||
|
||||
it("rejects missing audit severity flag values", () => {
|
||||
expect(() => parseArgs(["--min-severity"])).toThrow("--min-severity requires a value");
|
||||
expect(() => parseArgs(["--min-severity", "--audit-level", "critical"])).toThrow(
|
||||
"--min-severity requires a value",
|
||||
);
|
||||
expect(() => parseArgs(["--min-severity", "-h"])).toThrow("--min-severity requires a value");
|
||||
expect(() => parseArgs(["--audit-level="])).toThrow("--audit-level requires a value");
|
||||
});
|
||||
|
||||
it("parses scoped snapshot keys with peer suffixes", () => {
|
||||
expect(parseSnapshotKey("@scope/pkg@1.2.3(peer@4.5.6)")).toEqual({
|
||||
packageName: "@scope/pkg",
|
||||
reference: "1.2.3(peer@4.5.6)",
|
||||
version: "1.2.3",
|
||||
});
|
||||
});
|
||||
|
||||
it("strips peer and patch decorators from resolved versions", () => {
|
||||
expect(stripVersionDecorators("7.0.0-rc.9(patch_hash=abc123)(sharp@0.34.5)")).toBe(
|
||||
"7.0.0-rc.9",
|
||||
);
|
||||
expect(stripVersionDecorators("1.2.3")).toBe("1.2.3");
|
||||
});
|
||||
|
||||
it("collects the production graph from pnpm lockfile snapshots", () => {
|
||||
const lockfile = `lockfileVersion: '9.0'
|
||||
|
||||
importers:
|
||||
.:
|
||||
dependencies:
|
||||
pkg-a:
|
||||
version: 1.0.0
|
||||
devDependencies:
|
||||
dev-only:
|
||||
version: 9.9.9
|
||||
extensions/demo:
|
||||
dependencies:
|
||||
'@scope/pkg':
|
||||
version: 2.0.0(peer@4.0.0)
|
||||
workspace-lib:
|
||||
version: link:../../packages/workspace-lib
|
||||
|
||||
snapshots:
|
||||
pkg-a@1.0.0:
|
||||
dependencies:
|
||||
transitive: 3.0.0(patch_hash=abc123)
|
||||
transitive@3.0.0(patch_hash=abc123): {}
|
||||
'@scope/pkg@2.0.0(peer@4.0.0)':
|
||||
optionalDependencies:
|
||||
opt-dep: 4.0.0
|
||||
opt-dep@4.0.0: {}
|
||||
`;
|
||||
|
||||
const payload = createBulkAdvisoryPayload(collectProdResolvedPackagesFromLockfile(lockfile));
|
||||
expect(payload).toEqual({
|
||||
"@scope/pkg": ["2.0.0"],
|
||||
"opt-dep": ["4.0.0"],
|
||||
"pkg-a": ["1.0.0"],
|
||||
transitive: ["3.0.0"],
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves npm alias snapshots to the real package name", () => {
|
||||
const lockfile = `lockfileVersion: '9.0'
|
||||
|
||||
importers:
|
||||
.:
|
||||
dependencies:
|
||||
request:
|
||||
version: npm:@cypress/request@3.0.10
|
||||
|
||||
snapshots:
|
||||
'@cypress/request@3.0.10': {}
|
||||
`;
|
||||
|
||||
const payload = createBulkAdvisoryPayload(collectProdResolvedPackagesFromLockfile(lockfile));
|
||||
expect(payload).toEqual({
|
||||
"@cypress/request": ["3.0.10"],
|
||||
});
|
||||
});
|
||||
|
||||
it("reads inline importer dependency maps without repo dependencies", () => {
|
||||
const lockfile = `lockfileVersion: '9.0'
|
||||
|
||||
importers:
|
||||
.:
|
||||
dependencies:
|
||||
axios: {specifier: ^1.0.0, version: 1.0.0}
|
||||
'@scope/pkg': {'version': '2.0.0(peer@4.0.0)'}
|
||||
|
||||
snapshots:
|
||||
axios@1.0.0: {}
|
||||
'@scope/pkg@2.0.0(peer@4.0.0)': {}
|
||||
`;
|
||||
|
||||
const payload = createBulkAdvisoryPayload(collectProdResolvedPackagesFromLockfile(lockfile));
|
||||
expect(payload).toEqual({
|
||||
"@scope/pkg": ["2.0.0"],
|
||||
axios: ["1.0.0"],
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves quoted snapshot keys that contain tarball URLs", () => {
|
||||
const lockfile = `lockfileVersion: '9.0'
|
||||
|
||||
importers:
|
||||
.:
|
||||
dependencies:
|
||||
wrapper:
|
||||
version: 1.0.0
|
||||
|
||||
snapshots:
|
||||
wrapper@1.0.0:
|
||||
dependencies:
|
||||
libsignal: '@whiskeysockets/libsignal-node@https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/abc123'
|
||||
'@whiskeysockets/libsignal-node@https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/abc123':
|
||||
dependencies:
|
||||
curve25519-js: 0.0.4
|
||||
curve25519-js@0.0.4: {}
|
||||
`;
|
||||
|
||||
const payload = createBulkAdvisoryPayload(collectProdResolvedPackagesFromLockfile(lockfile));
|
||||
expect(payload).toEqual({
|
||||
"@whiskeysockets/libsignal-node": [
|
||||
"https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/abc123",
|
||||
],
|
||||
"curve25519-js": ["0.0.4"],
|
||||
wrapper: ["1.0.0"],
|
||||
});
|
||||
});
|
||||
|
||||
it("filters advisory findings by minimum severity", () => {
|
||||
const findings = filterFindingsBySeverity(
|
||||
{
|
||||
axios: [
|
||||
{
|
||||
id: "GHSA-low",
|
||||
severity: "moderate",
|
||||
title: "moderate issue",
|
||||
},
|
||||
{
|
||||
id: "GHSA-high",
|
||||
severity: "high",
|
||||
title: "high issue",
|
||||
url: "https://github.com/advisories/GHSA-high",
|
||||
},
|
||||
],
|
||||
},
|
||||
"high",
|
||||
);
|
||||
|
||||
expect(findings).toEqual([
|
||||
{
|
||||
id: "GHSA-high",
|
||||
packageName: "axios",
|
||||
severity: "high",
|
||||
title: "high issue",
|
||||
url: "https://github.com/advisories/GHSA-high",
|
||||
vulnerableVersions: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("suppresses the overbroad Mistral malware advisory for the pre-compromise locked version", () => {
|
||||
const versionsByPackage = new Map([["@mistralai/mistralai", new Set(["2.2.1"])]]);
|
||||
const findings = filterFindingsBySeverity(
|
||||
{
|
||||
"@mistralai/mistralai": [
|
||||
{
|
||||
id: "1118204",
|
||||
severity: "critical",
|
||||
title: "Malware in @mistralai/mistralai",
|
||||
vulnerable_versions: ">=0",
|
||||
url: "https://github.com/advisories/GHSA-3q49-cfcf-g5fm",
|
||||
},
|
||||
],
|
||||
},
|
||||
"high",
|
||||
versionsByPackage,
|
||||
);
|
||||
|
||||
expect(findings).toEqual([]);
|
||||
});
|
||||
|
||||
it("keeps the Mistral malware advisory blocking for compromised resolved versions", () => {
|
||||
const versionsByPackage = new Map([["@mistralai/mistralai", new Set(["2.2.4"])]]);
|
||||
const findings = filterFindingsBySeverity(
|
||||
{
|
||||
"@mistralai/mistralai": [
|
||||
{
|
||||
id: "1118204",
|
||||
severity: "critical",
|
||||
title: "Malware in @mistralai/mistralai",
|
||||
vulnerable_versions: ">=0",
|
||||
url: "https://github.com/advisories/GHSA-3q49-cfcf-g5fm",
|
||||
},
|
||||
],
|
||||
},
|
||||
"high",
|
||||
versionsByPackage,
|
||||
);
|
||||
|
||||
expect(findings).toEqual([
|
||||
{
|
||||
id: "1118204",
|
||||
packageName: "@mistralai/mistralai",
|
||||
severity: "critical",
|
||||
title: "Malware in @mistralai/mistralai",
|
||||
url: "https://github.com/advisories/GHSA-3q49-cfcf-g5fm",
|
||||
vulnerableVersions: ">=0",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("bounds bulk advisory error response bodies", async () => {
|
||||
const tail = "tail-sentinel-should-not-appear";
|
||||
const response = new Response(`${"x".repeat(5000)}${tail}`, {
|
||||
status: 500,
|
||||
});
|
||||
|
||||
const text = await readBoundedBulkAdvisoryErrorText(response);
|
||||
|
||||
expect(text).toContain("[truncated]");
|
||||
expect(text).not.toContain(tail);
|
||||
expect(text.length).toBeLessThan(4200);
|
||||
});
|
||||
|
||||
it("aborts stalled bulk advisory requests", async () => {
|
||||
let signal: AbortSignal | undefined;
|
||||
const request = fetchBulkAdvisories({
|
||||
payload: { axios: ["1.0.0"] },
|
||||
timeoutMs: 5,
|
||||
fetchImpl: ((_url, init) => {
|
||||
signal = init?.signal ?? undefined;
|
||||
return new Promise((_resolve, reject) => {
|
||||
signal?.addEventListener(
|
||||
"abort",
|
||||
() =>
|
||||
reject(
|
||||
toLintErrorObject(signal?.reason ?? new Error("aborted"), "Non-Error rejection"),
|
||||
),
|
||||
{
|
||||
once: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
}) as typeof fetch,
|
||||
});
|
||||
|
||||
await expect(request).rejects.toThrow(/Bulk advisory request exceeded timeout/u);
|
||||
expect(signal?.aborted).toBe(true);
|
||||
});
|
||||
|
||||
it("clamps oversized bulk advisory request timers before scheduling", async () => {
|
||||
let signal: AbortSignal | undefined;
|
||||
const request = fetchBulkAdvisories({
|
||||
payload: { axios: ["1.0.0"] },
|
||||
timeoutMs: Number.MAX_SAFE_INTEGER,
|
||||
fetchImpl: (async (_url, init) => {
|
||||
signal = init?.signal ?? undefined;
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const timer = setTimeout(resolve, 25);
|
||||
signal?.addEventListener(
|
||||
"abort",
|
||||
() => {
|
||||
clearTimeout(timer);
|
||||
reject(new Error("aborted"));
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
});
|
||||
return new Response("{}", { status: 200 });
|
||||
}) as typeof fetch,
|
||||
});
|
||||
|
||||
await expect(request).resolves.toEqual({});
|
||||
expect(signal?.aborted).toBe(false);
|
||||
});
|
||||
|
||||
it("cancels stalled successful bulk advisory response bodies on request timeout", async () => {
|
||||
let cancelled = false;
|
||||
const body = new ReadableStream({
|
||||
pull() {
|
||||
return new Promise(() => {});
|
||||
},
|
||||
cancel() {
|
||||
cancelled = true;
|
||||
},
|
||||
});
|
||||
const request = fetchBulkAdvisories({
|
||||
payload: { axios: ["1.0.0"] },
|
||||
timeoutMs: 5,
|
||||
fetchImpl: async () => new Response(body, { status: 200 }),
|
||||
});
|
||||
|
||||
await expect(request).rejects.toThrow(/Bulk advisory request exceeded timeout/u);
|
||||
expect(cancelled).toBe(true);
|
||||
});
|
||||
|
||||
it("cancels stalled failed bulk advisory response bodies on request timeout", async () => {
|
||||
let cancelled = false;
|
||||
const body = new ReadableStream({
|
||||
pull() {
|
||||
return new Promise(() => {});
|
||||
},
|
||||
cancel() {
|
||||
cancelled = true;
|
||||
},
|
||||
});
|
||||
const request = fetchBulkAdvisories({
|
||||
payload: { axios: ["1.0.0"] },
|
||||
timeoutMs: 5,
|
||||
fetchImpl: async () => new Response(body, { status: 500, statusText: "Internal Error" }),
|
||||
});
|
||||
|
||||
await expect(request).rejects.toThrow(/Bulk advisory request exceeded timeout/u);
|
||||
expect(cancelled).toBe(true);
|
||||
});
|
||||
|
||||
it("bounds successful bulk advisory response bodies", async () => {
|
||||
let cancelled = false;
|
||||
const body = new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(new TextEncoder().encode("{}"));
|
||||
},
|
||||
cancel() {
|
||||
cancelled = true;
|
||||
},
|
||||
});
|
||||
const request = fetchBulkAdvisories({
|
||||
payload: { axios: ["1.0.0"] },
|
||||
responseBodyMaxBytes: 4,
|
||||
fetchImpl: async () =>
|
||||
new Response(body, {
|
||||
status: 200,
|
||||
headers: { "content-length": "5" },
|
||||
}),
|
||||
});
|
||||
|
||||
await expect(request).rejects.toThrow(/Bulk advisory response body exceeded 4 bytes/u);
|
||||
expect(cancelled).toBe(true);
|
||||
});
|
||||
|
||||
it("streams non-decimal bulk advisory content-length values through the body cap", async () => {
|
||||
let readStarted = false;
|
||||
let cancelled = false;
|
||||
const body = new ReadableStream({
|
||||
pull(controller) {
|
||||
readStarted = true;
|
||||
controller.enqueue(new TextEncoder().encode("12345"));
|
||||
},
|
||||
cancel() {
|
||||
cancelled = true;
|
||||
},
|
||||
});
|
||||
const request = fetchBulkAdvisories({
|
||||
payload: { axios: ["1.0.0"] },
|
||||
responseBodyMaxBytes: 4,
|
||||
fetchImpl: async () =>
|
||||
new Response(body, {
|
||||
status: 200,
|
||||
headers: { "content-length": "5junk" },
|
||||
}),
|
||||
});
|
||||
|
||||
await expect(request).rejects.toThrow(/Bulk advisory response body exceeded 4 bytes/u);
|
||||
expect(readStarted).toBe(true);
|
||||
expect(cancelled).toBe(true);
|
||||
});
|
||||
|
||||
it("fails closed on empty successful bulk advisory response bodies", async () => {
|
||||
const request = fetchBulkAdvisories({
|
||||
payload: { axios: ["1.0.0"] },
|
||||
fetchImpl: async () => new Response("", { status: 200 }),
|
||||
});
|
||||
|
||||
await expect(request).rejects.toThrow(/Bulk advisory response body was empty/u);
|
||||
});
|
||||
|
||||
it("returns a failing exit code when bulk advisories include high severity findings", async () => {
|
||||
const tempDir = await mkdtemp(path.join(tmpdir(), "openclaw-audit-prod-"));
|
||||
await writeFile(
|
||||
path.join(tempDir, "pnpm-lock.yaml"),
|
||||
`lockfileVersion: '9.0'
|
||||
|
||||
importers:
|
||||
.:
|
||||
dependencies:
|
||||
axios:
|
||||
version: 1.0.0
|
||||
|
||||
snapshots:
|
||||
axios@1.0.0: {}
|
||||
`,
|
||||
"utf8",
|
||||
);
|
||||
|
||||
try {
|
||||
const stdoutChunks: string[] = [];
|
||||
const stderrChunks: string[] = [];
|
||||
const exitCode = await runPnpmAuditProd({
|
||||
rootDir: tempDir,
|
||||
fetchImpl: async () =>
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
axios: [
|
||||
{
|
||||
id: "GHSA-test",
|
||||
severity: "high",
|
||||
title: "test issue",
|
||||
vulnerable_versions: "<=1.0.0",
|
||||
url: "https://github.com/advisories/GHSA-test",
|
||||
},
|
||||
],
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
},
|
||||
),
|
||||
stdout: {
|
||||
write(chunk: string) {
|
||||
stdoutChunks.push(chunk);
|
||||
return true;
|
||||
},
|
||||
} as NodeJS.WriteStream,
|
||||
stderr: {
|
||||
write(chunk: string) {
|
||||
stderrChunks.push(chunk);
|
||||
return true;
|
||||
},
|
||||
} as NodeJS.WriteStream,
|
||||
});
|
||||
|
||||
expect(exitCode).toBe(1);
|
||||
expect(stdoutChunks).toStrictEqual([]);
|
||||
expect(stderrChunks.join("")).toContain("Found 1 high or higher advisories");
|
||||
} finally {
|
||||
await rm(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function toLintErrorObject(value: unknown, fallbackMessage: string): Error {
|
||||
if (value instanceof Error) {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
return new Error(value);
|
||||
}
|
||||
const error = new Error(fallbackMessage, { cause: value });
|
||||
if ((typeof value === "object" && value !== null) || typeof value === "function") {
|
||||
Object.assign(error, value);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
Reference in New Issue
Block a user