fix(release): scan publication-equivalent plugin artifacts

This commit is contained in:
Vincent Koc
2026-08-20 21:39:18 -07:00
parent dc15c4f660
commit dd9677012a
10 changed files with 556 additions and 1137 deletions
+226 -156
View File
@@ -1,21 +1,20 @@
import { execFileSync, spawnSync } from "node:child_process";
import { createHash } from "node:crypto";
import { existsSync, mkdirSync, readFileSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { basename, join } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import {
assertCanonicalNpmPackageName,
assertCompleteScannerSummary,
buildPluginNpmSecurityScanReport,
collectNpmPackedFiles,
constrainPluginNpmSecurityScanReport,
loadPluginNpmSecurityArtifacts,
listPluginNpmSecurityArtifacts,
listPublishablePluginPackages,
normalizePackedFindingPath,
parsePacklistFiles,
resolveCandidatePluginPackageDir,
resolveReviewedSourceLayout,
scanPublishablePluginPackages,
stageScannerRelevantPackedFiles,
stageScannerRelevantPluginTarballFiles,
type PublishablePluginPackage,
type ScanPackageResult,
@@ -54,14 +53,21 @@ function writePublishableManifest(
}
function writePluginArtifact(params: {
artifactRoot?: string;
extensionId: string;
files: Record<string, string | Buffer>;
packageName: string;
version?: string;
}) {
const root = tempDirs.make("openclaw-plugin-npm-security-artifact-");
const packageRoot = join(root, "source");
const artifactDir = join(root, "artifacts", params.extensionId);
const root = params.artifactRoot
? join(params.artifactRoot, "..")
: tempDirs.make("openclaw-plugin-npm-security-artifact-");
const artifactRoot = params.artifactRoot ?? join(root, "artifacts");
const packageRoot = join(root, `source-${params.extensionId}`);
const artifactDir = join(
artifactRoot,
`plugin-npm-security-package-${CANDIDATE_SHA}-${params.extensionId}`,
);
const packageVersion = params.version ?? "1.0.0";
mkdirSync(packageRoot, { recursive: true });
mkdirSync(artifactDir, { recursive: true });
@@ -95,9 +101,10 @@ function writePluginArtifact(params: {
writeFileSync(
join(artifactDir, "plugin-npm-security-artifact.json"),
`${JSON.stringify({
artifactKind: "inert-package-input",
artifactKind: "publication-equivalent-plugin-tarball",
candidateSha: CANDIDATE_SHA,
extensionId: params.extensionId,
packOwner: "scripts/plugin-npm-publish.sh",
packageDir: `extensions/${params.extensionId}`,
packageName: params.packageName,
packageVersion,
@@ -110,10 +117,16 @@ function writePluginArtifact(params: {
);
return {
artifact: {
artifactKind: "inert-package-input" as const,
artifactKind: "publication-equivalent-plugin-tarball" as const,
artifactDir,
candidateSha: CANDIDATE_SHA,
compressedBytes: readFileSync(tarballPath).byteLength,
expandedBytes: Object.values(params.files).reduce(
(total, value) => total + Buffer.byteLength(value),
0,
),
extensionId: params.extensionId,
packOwner: "scripts/plugin-npm-publish.sh" as const,
packageDir: `extensions/${params.extensionId}`,
packageName: params.packageName,
packageVersion,
@@ -121,7 +134,7 @@ function writePluginArtifact(params: {
tarballSha256,
toolingSha: TOOLING_SHA,
},
artifactRoot: join(root, "artifacts"),
artifactRoot,
expectedPackage: {
extensionId: params.extensionId,
packageDir: `extensions/${params.extensionId}`,
@@ -179,92 +192,70 @@ describe("scripts/lib/plugin-npm-security-scan.mts", () => {
expect(resolveReviewedSourceLayout([...current, current[0]!])).toBeUndefined();
});
it("collects package files without running lifecycle or candidate replacement helpers", async () => {
const packageDir = tempDirs.make("openclaw-plugin-npm-security-pack-");
const replacementMarker = join(packageDir, "replacement-helper-ran");
it("scans malicious output generated by the canonical publication pack boundary", async () => {
const candidateRoot = tempDirs.make("openclaw-plugin-security-built-pack-");
const artifactRoot = tempDirs.make("openclaw-plugin-security-built-artifacts-");
const packageDir = join(candidateRoot, "extensions", "generated");
const trustedPackScript = join(
tempDirs.make("openclaw-plugin-security-trusted-pack-"),
"pack.sh",
);
const generatedPath = join(packageDir, "dist", "generated.js");
const lifecycleMarkers = ["prepare", "prepack", "postpack"].map((name) =>
join(packageDir, `${name}-ran`),
);
writeFileSync(
join(packageDir, "package.json"),
`${JSON.stringify({
name: "@openclaw/test-inert-package",
scripts: Object.fromEntries(
["prepare", "prepack", "postpack"].map((name, index) => [
name,
`node -e "require('node:fs').writeFileSync(${JSON.stringify(lifecycleMarkers[index])}, 'ran')"`,
]),
),
version: "1.0.0",
})}\n`,
"utf8",
);
writeFileSync(join(packageDir, "index.js"), "export const value = 1;\n", "utf8");
writeFileSync(
join(packageDir, "plugin-npm-pack-files.mjs"),
`import { writeFileSync } from "node:fs";\nwriteFileSync(${JSON.stringify(replacementMarker)}, "ran");\n`,
"utf8",
);
const trustedHelper = join(tempDirs.make("openclaw-plugin-npm-security-helper-"), "helper.mjs");
writeFileSync(
trustedHelper,
'process.stdout.write(JSON.stringify(["index.js", "package.json", "plugin-npm-pack-files.mjs"]));\n',
"utf8",
);
const packedFiles = await collectNpmPackedFiles(packageDir, "@openclaw/test-inert-package", {
helperPath: trustedHelper,
});
expect(packedFiles).toContain("index.js");
expect(packedFiles).toContain("package.json");
expect(existsSync(replacementMarker)).toBe(false);
for (const marker of lifecycleMarkers) {
expect(existsSync(marker)).toBe(false);
}
});
it("packs candidate input without running lifecycle or asset build hooks", () => {
const candidateRoot = tempDirs.make("openclaw-plugin-security-inert-pack-");
const outputDir = tempDirs.make("openclaw-plugin-security-inert-output-");
const packageDir = join(candidateRoot, "extensions", "inert");
const packlistHelper = join(candidateRoot, "trusted-packlist-helper.mjs");
const markers = Object.fromEntries(
["asset", "prepare", "prepack", "postpack"].map((name) => [
name,
join(candidateRoot, `${name}-ran`),
]),
join(candidateRoot, `${name}-ran`),
);
initGitRepo(candidateRoot);
mkdirSync(packageDir, { recursive: true });
const markerCommand = (marker: string) =>
`node -e "require('node:fs').writeFileSync(${JSON.stringify(marker)}, 'ran')"`;
const generatedCode = 'const { execSync } = require("node:child_process");\nexecSync("id");\n';
const assetBuild = "node build-assets.mjs";
writeFileSync(
join(packageDir, "package.json"),
`${JSON.stringify({
name: "@openclaw/test-inert-pack",
files: ["dist", "openclaw.plugin.json", "package.json"],
name: "@openclaw/test-generated-pack",
openclaw: {
assetScripts: { build: markerCommand(markers.asset!) },
assetScripts: { build: assetBuild },
release: { publishToNpm: true },
},
scripts: {
postpack: markerCommand(markers.postpack!),
prepack: markerCommand(markers.prepack!),
prepare: markerCommand(markers.prepare!),
postpack: markerCommand(lifecycleMarkers[2]!),
prepack: markerCommand(lifecycleMarkers[1]!),
prepare: markerCommand(lifecycleMarkers[0]!),
},
version: "1.0.0",
version: "2026.8.1-beta.1",
})}\n`,
"utf8",
);
writeFileSync(join(packageDir, "index.ts"), "export const inert = true;\n", "utf8");
writeFileSync(
join(packageDir, "openclaw.plugin.json"),
`${JSON.stringify({ id: "inert" })}\n`,
`${JSON.stringify({ id: "generated" })}\n`,
"utf8",
);
writeFileSync(
packlistHelper,
'process.stdout.write(JSON.stringify(["index.ts", "openclaw.plugin.json", "package.json"]));\n',
join(packageDir, "build-assets.mjs"),
[
'import { mkdirSync, writeFileSync } from "node:fs";',
'mkdirSync("dist", { recursive: true });',
`writeFileSync("dist/generated.js", ${JSON.stringify(generatedCode)});`,
"",
].join("\n"),
"utf8",
);
writeFileSync(
trustedPackScript,
[
"#!/usr/bin/env bash",
"set -euo pipefail",
'repo_root="$2"',
'package_dir="$4"',
'cd "$repo_root/$package_dir"',
'asset_build="$(node -p "require(\'./package.json\').openclaw.assetScripts.build")"',
'bash -c "$asset_build"',
'npm pack --json --ignore-scripts --pack-destination "$OPENCLAW_PLUGIN_NPM_PACK_OUTPUT_DIR"',
"",
].join("\n"),
"utf8",
);
execFileSync("git", ["-C", candidateRoot, "add", "."]);
@@ -272,8 +263,10 @@ describe("scripts/lib/plugin-npm-security-scan.mts", () => {
const candidateSha = execFileSync("git", ["-C", candidateRoot, "rev-parse", "HEAD"], {
encoding: "utf8",
}).trim();
const outputDir = join(artifactRoot, `plugin-npm-security-package-${candidateSha}-generated`);
const toolingSha = execFileSync("git", ["rev-parse", "HEAD"], { encoding: "utf8" }).trim();
mkdirSync(outputDir, { recursive: true });
const result = spawnSync(
process.execPath,
[
@@ -286,13 +279,13 @@ describe("scripts/lib/plugin-npm-security-scan.mts", () => {
"--candidate-sha",
candidateSha,
"--extension-id",
"inert",
"generated",
"--output-dir",
outputDir,
"--package-dir",
"extensions/inert",
"extensions/generated",
"--package-name",
"@openclaw/test-inert-pack",
"@openclaw/test-generated-pack",
"--tooling-sha",
toolingSha,
],
@@ -302,78 +295,41 @@ describe("scripts/lib/plugin-npm-security-scan.mts", () => {
env: {
...process.env,
NODE_ENV: "test",
OPENCLAW_PLUGIN_SECURITY_TEST_PACKLIST_HELPER: packlistHelper,
OPENCLAW_PLUGIN_SECURITY_TEST_PUBLISH_SCRIPT: trustedPackScript,
},
},
);
expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(0);
for (const marker of Object.values(markers)) {
expect(readFileSync(generatedPath, "utf8")).toBe(generatedCode);
for (const marker of lifecycleMarkers) {
expect(existsSync(marker)).toBe(false);
}
const metadata = JSON.parse(
readFileSync(join(outputDir, "plugin-npm-security-artifact.json"), "utf8"),
) as { artifactKind?: unknown; tarballName?: unknown };
expect(metadata.artifactKind).toBe("inert-package-input");
expect(metadata.artifactKind).toBe("publication-equivalent-plugin-tarball");
expect(typeof metadata.tarballName).toBe("string");
const staged = stageScannerRelevantPluginTarballFiles(
join(outputDir, String(metadata.tarballName)),
);
try {
expect(staged.packedFiles).toContain("index.ts");
expect(staged.packedFiles.some((file) => file.startsWith("dist/"))).toBe(false);
} finally {
rmSync(staged.stageDir, { force: true, recursive: true });
}
});
it("rejects malformed, unsafe, duplicate, and excessive packlist entries", () => {
for (const malformed of [
null,
{},
1,
"",
"/absolute.js",
"dir\\file.js",
"../escape.js",
".hidden.js",
"node_modules/dependency.js",
"a".repeat(4097),
]) {
expect(() =>
parsePacklistFiles(JSON.stringify(["valid.js", malformed]), "@openclaw/test"),
).toThrow("entry 1 has an invalid path");
}
expect(() =>
parsePacklistFiles(JSON.stringify(["index.js", "index.js"]), "@openclaw/test"),
).toThrow("duplicate path");
expect(() =>
parsePacklistFiles(
JSON.stringify(Array.from({ length: 20_001 }, (_, index) => `file-${index}.js`)),
"@openclaw/test",
),
).toThrow("file-count limit");
});
it("preserves bounded packlist helper failure categories", async () => {
const root = tempDirs.make("openclaw-plugin-packlist-helper-limits-");
const packageDir = tempDirs.make("openclaw-plugin-packlist-helper-package-");
const timeoutHelper = join(root, "timeout.mjs");
const failedHelper = join(root, "failed.mjs");
writeFileSync(timeoutHelper, "setInterval(() => {}, 1_000);\n", "utf8");
writeFileSync(failedHelper, "process.exit(7);\n", "utf8");
await expect(
collectNpmPackedFiles(packageDir, "@openclaw/test-timeout", {
helperPath: timeoutHelper,
timeoutMs: 25,
}),
).rejects.toThrow("trusted packlist helper timed out");
await expect(
collectNpmPackedFiles(packageDir, "@openclaw/test-failed", {
helperPath: failedHelper,
}),
).rejects.toThrow("trusted packlist helper failed");
const expectedPackage = {
extensionId: "generated",
packageDir: "extensions/generated",
packageName: "@openclaw/test-generated-pack",
packageVersion: "2026.8.1-beta.1",
};
const loaded = loadPluginNpmSecurityArtifacts({
artifactRoot,
candidateSha,
expectedPackages: [expectedPackage],
toolingSha,
});
expect(loaded.ingestionErrors).toEqual([]);
const scanned = await scanPublishablePluginPackages(loaded.artifacts);
expect(scanned.scanErrors).toEqual([]);
expect(scanned.packageResults[0]?.unexpectedCriticalFindings).toContainEqual({
line: 2,
path: "dist/generated.js",
ruleId: "dangerous-exec",
});
});
it("bounds manifests and rejects noncanonical or duplicate package identities", async () => {
@@ -402,24 +358,27 @@ describe("scripts/lib/plugin-npm-security-scan.mts", () => {
).rejects.toThrow("manifest exceeds the byte limit");
});
it("fails closed on truncated scans, source escapes, and tarball symlinks", () => {
it("fails closed on truncated scans, candidate package escapes, and tarball symlinks", () => {
expect(() => assertCompleteScannerSummary("@openclaw/test", { truncated: true })).toThrow(
"security scan reached its file limit",
);
expect(() =>
stageScannerRelevantPackedFiles(tempDirs.make("openclaw-plugin-npm-security-path-"), [
"../escape.ts",
]),
).toThrow("npm pack returned an unsafe path");
const candidateRoot = tempDirs.make("openclaw-plugin-npm-security-candidate-");
const outsideDir = tempDirs.make("openclaw-plugin-npm-security-outside-");
mkdirSync(join(candidateRoot, "extensions"), { recursive: true });
writeFileSync(
join(outsideDir, "package.json"),
`${JSON.stringify({ name: "@openclaw/escape", version: "1.0.0" })}\n`,
"utf8",
);
symlinkSync(outsideDir, join(candidateRoot, "extensions", "escape"));
expect(() => resolveCandidatePluginPackageDir(candidateRoot, "escape")).toThrow(
"package directory is not a real directory",
);
const packageDir = tempDirs.make("openclaw-plugin-npm-security-symlink-");
const outsideDir = tempDirs.make("openclaw-plugin-npm-security-outside-");
const outsideFile = join(outsideDir, "outside.ts");
writeFileSync(outsideFile, "export const value = 1;\n", "utf8");
symlinkSync(outsideFile, join(packageDir, "escape.ts"));
expect(() => stageScannerRelevantPackedFiles(packageDir, ["escape.ts"])).toThrow(
"not a regular file",
);
const artifact = writePluginArtifact({
extensionId: "symlink",
@@ -429,7 +388,13 @@ describe("scripts/lib/plugin-npm-security-scan.mts", () => {
symlinkSync(outsideFile, join(artifact.packageRoot, "escape.ts"));
execFileSync(
"tar",
["-czf", artifact.tarballPath, "-C", join(artifact.packageRoot, ".."), "source"],
[
"-czf",
artifact.tarballPath,
"-C",
join(artifact.packageRoot, ".."),
basename(artifact.packageRoot),
],
{ env: { ...process.env, COPYFILE_DISABLE: "1" } },
);
expect(() => stageScannerRelevantPluginTarballFiles(artifact.tarballPath)).toThrow();
@@ -521,7 +486,102 @@ describe("scripts/lib/plugin-npm-security-scan.mts", () => {
expectedPackages: [],
toolingSha: TOOLING_SHA,
}),
).toThrow("does not match the trusted package plan");
).toThrow("unexpected entries");
});
it("retains valid package scans when a sibling artifact is malformed", async () => {
const artifactRoot = tempDirs.make("openclaw-plugin-npm-security-mixed-");
const valid = writePluginArtifact({
artifactRoot,
extensionId: "valid",
files: { "index.js": "export const value = 1;\n" },
packageName: "@openclaw/test-valid",
});
const malformed = writePluginArtifact({
artifactRoot,
extensionId: "malformed",
files: { "index.js": "export const value = 2;\n" },
packageName: "@openclaw/test-malformed",
});
writeFileSync(
join(malformed.artifact.artifactDir, "plugin-npm-security-artifact.json"),
"{not-json}\n",
"utf8",
);
const expectedPackages = [malformed.expectedPackage, valid.expectedPackage].toSorted(
(left, right) => (left.packageName < right.packageName ? -1 : 1),
);
const loaded = loadPluginNpmSecurityArtifacts({
artifactRoot,
candidateSha: CANDIDATE_SHA,
expectedPackages,
toolingSha: TOOLING_SHA,
});
expect(loaded.artifacts.map((artifact) => artifact.packageName)).toEqual([
"@openclaw/test-valid",
]);
expect(loaded.ingestionErrors).toEqual([
"@openclaw/test-malformed: Plugin security artifact metadata is not valid JSON.",
]);
const scanned = await scanPublishablePluginPackages(loaded.artifacts);
expect(scanned.scanErrors).toEqual([]);
expect(scanned.packageResults.map((result) => result.packageName)).toEqual([
"@openclaw/test-valid",
]);
});
it("bounds aggregate compressed and expanded artifact bytes deterministically", () => {
const artifactRoot = tempDirs.make("openclaw-plugin-npm-security-aggregate-");
const alpha = writePluginArtifact({
artifactRoot,
extensionId: "alpha",
files: { "alpha.js": Buffer.alloc(256, 1) },
packageName: "@openclaw/test-alpha",
});
const beta = writePluginArtifact({
artifactRoot,
extensionId: "beta",
files: { "beta.js": Buffer.alloc(256, 2) },
packageName: "@openclaw/test-beta",
});
const expectedPackages = [alpha.expectedPackage, beta.expectedPackage];
const baseline = loadPluginNpmSecurityArtifacts({
artifactRoot,
candidateSha: CANDIDATE_SHA,
expectedPackages,
toolingSha: TOOLING_SHA,
});
expect(baseline.ingestionErrors).toEqual([]);
const compressed = loadPluginNpmSecurityArtifacts({
artifactRoot,
candidateSha: CANDIDATE_SHA,
expectedPackages,
limits: { maxCompressedBytes: baseline.artifacts[0]!.compressedBytes },
toolingSha: TOOLING_SHA,
});
expect(compressed.artifacts.map((artifact) => artifact.packageName)).toEqual([
"@openclaw/test-alpha",
]);
expect(compressed.ingestionErrors).toEqual([
"@openclaw/test-beta: aggregate compressed-byte limit exceeded.",
]);
const expanded = loadPluginNpmSecurityArtifacts({
artifactRoot,
candidateSha: CANDIDATE_SHA,
expectedPackages,
limits: { maxExpandedBytes: baseline.artifacts[0]!.expandedBytes },
toolingSha: TOOLING_SHA,
});
expect(expanded.artifacts.map((artifact) => artifact.packageName)).toEqual([
"@openclaw/test-alpha",
]);
expect(expanded.ingestionErrors).toEqual([
"@openclaw/test-beta: aggregate expanded-byte limit exceeded.",
]);
});
it("caps total findings and emits byte-identical bounded reports", () => {
@@ -555,19 +615,26 @@ describe("scripts/lib/plugin-npm-security-scan.mts", () => {
]);
});
it("writes sanitized exact-identity reports when the bounded scanner times out or OOMs", () => {
it("writes sanitized exact-identity reports for timeout, heap, and RSS failures", () => {
const root = tempDirs.make("openclaw-plugin-npm-security-runner-");
const timeoutChild = join(root, "timeout.mjs");
const oomChild = join(root, "oom.mjs");
const rssChild = join(root, "rss.mjs");
writeFileSync(timeoutChild, "await new Promise(() => {});\n", "utf8");
writeFileSync(
oomChild,
"const values = [];\nwhile (true) values.push(new Array(100000).fill(Math.random()));\n",
"utf8",
);
for (const [label, child, timeoutMs] of [
["timeout", timeoutChild, "25"],
["oom", oomChild, "10000"],
writeFileSync(
rssChild,
"globalThis.value = Buffer.alloc(64 * 1024 * 1024, 1);\nsetInterval(() => {}, 1_000);\n",
"utf8",
);
for (const [label, child, timeoutMs, heapMb, rssMb, expectedError] of [
["timeout", timeoutChild, "25", "128", "1024", "timed out"],
["oom", oomChild, "10000", "16", "1024", "exceeded its process limit"],
["rss", rssChild, "10000", "128", "16", "exceeded its RSS limit"],
] as const) {
const reportPath = join(root, `${label}.json`);
const result = spawnSync(
@@ -592,7 +659,8 @@ describe("scripts/lib/plugin-npm-security-scan.mts", () => {
...process.env,
NODE_ENV: "test",
OPENCLAW_PLUGIN_SECURITY_RUNNER_CHILD: child,
OPENCLAW_PLUGIN_SECURITY_RUNNER_HEAP_MB: "16",
OPENCLAW_PLUGIN_SECURITY_RUNNER_HEAP_MB: heapMb,
OPENCLAW_PLUGIN_SECURITY_RUNNER_RSS_MB: rssMb,
OPENCLAW_PLUGIN_SECURITY_RUNNER_TIMEOUT_MS: timeoutMs,
},
timeout: 15_000,
@@ -600,6 +668,7 @@ describe("scripts/lib/plugin-npm-security-scan.mts", () => {
);
const report = JSON.parse(readFileSync(reportPath, "utf8")) as {
candidateSha: string;
errors: string[];
toolingSha: string;
};
expect(result.status).toBe(1);
@@ -607,6 +676,7 @@ describe("scripts/lib/plugin-npm-security-scan.mts", () => {
candidateSha: CANDIDATE_SHA,
toolingSha: TOOLING_SHA,
});
expect(report.errors).toContainEqual(expect.stringContaining(expectedError));
expect(`${result.stdout}${result.stderr}${JSON.stringify(report)}`).not.toContain(root);
}
}, 30_000);
@@ -300,7 +300,7 @@ describe("scripts/lib/plugin-prerelease-test-plan.mts", () => {
(step: WorkflowStep) => step.name === "Install trusted scanner dependencies",
);
const runSecurityScan = securityScan.steps.find(
(step: WorkflowStep) => step.name === "Scan inert plugin package inputs",
(step: WorkflowStep) => step.name === "Scan publication-equivalent plugin artifacts",
);
const uploadReport = securityScan.steps.find(
(step: WorkflowStep) => step.name === "Upload plugin npm security scan report",
@@ -310,6 +310,7 @@ describe("scripts/lib/plugin-prerelease-test-plan.mts", () => {
);
const releaseWorkflow = readFullReleaseValidationWorkflow();
const releaseSource = readFileSync(".github/workflows/full-release-validation.yml", "utf8");
const pluginNpmReleaseSource = readFileSync(".github/workflows/plugin-npm-release.yml", "utf8");
const pluginDispatch = releaseWorkflow.jobs.plugin_prerelease.steps.find(
(step: WorkflowStep) => step.name === "Dispatch and monitor plugin prerelease",
);
@@ -368,15 +369,15 @@ describe("scripts/lib/plugin-prerelease-test-plan.mts", () => {
expect(securityScan.needs).not.toContain("preflight");
expect(securityPackage.permissions).toEqual({ contents: "read" });
expect(securityPackage.secrets).toBeUndefined();
expect(JSON.stringify(securityPackage)).not.toContain("plugin-npm-publish.sh");
expect(JSON.stringify(securityPackage)).not.toContain("plugin-npm-runtime-build");
expect(JSON.stringify(securityPackage)).not.toContain("generate-npm-package-lock");
expect(securityPrepareSource).not.toContain("plugin-npm-publish.sh");
expect(securityPrepareSource).not.toContain("plugin-npm-runtime-build");
expect(securityPrepareSource).not.toContain("generate-npm-package-lock");
expect(securityPrepareSource).not.toMatch(/\bnpmArgs:\s*\[\s*["'](?:ci|install)["']/u);
expect(securityPrepareSource).toContain('"--ignore-scripts"');
expect(securityPrepareSource).toContain('"--workspaces=false"');
expect(JSON.stringify(securityPackage)).not.toContain("id-token");
expect(JSON.stringify(securityPackage)).not.toContain("packages: write");
expect(JSON.stringify(securityPackage)).not.toContain("${{ secrets.");
expect(securityPrepareSource).toContain('"scripts", "plugin-npm-publish.sh"');
expect(securityPrepareSource).toContain('"--pack"');
expect(securityPrepareSource).toContain("shell: false");
expect(securityPrepareSource).toContain("resolveCandidatePluginPackageDir");
expect(pluginNpmReleaseSource).toContain("bash .release-tooling/scripts/plugin-npm-publish.sh");
expect(pluginNpmReleaseSource).toContain('--pack "${PACKAGE_DIR}"');
expect(nodeShard.needs).toEqual(["resolve-candidate", "preflight"]);
expect(runNodeShard?.run).toContain('spawnSync("pnpm", ["test", "--", ...configs]');
expect(pluginSource).not.toContain("npm-install-security-scan.release.test.ts");