

























@@ -1,10 +1,8 @@
11import { execFileSync } from "node:child_process";
2-import { copyFileSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
2+import { copyFileSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync } from "node:fs";
33import { tmpdir } from "node:os";
44import { dirname, join, relative, resolve, sep } from "node:path";
55import { afterEach, describe, expect, it } from "vitest";
6-import { withAugmentedPluginNpmManifestForPackage } from "../../scripts/lib/plugin-npm-package-manifest.mjs";
7-import { collectPublishablePluginPackages } from "../../scripts/lib/plugin-npm-release.ts";
86import { isScannable, scanDirectoryWithSummary } from "../security/skill-scanner.js";
97108type NpmPackFile = {
@@ -15,6 +13,11 @@ type NpmPackResult = {
1513files?: unknown;
1614};
171516+type PublishablePluginPackage = {
17+packageDir: string;
18+packageName: string;
19+};
20+1821const tempDirs: string[] = [];
19222023afterEach(() => {
@@ -41,15 +44,13 @@ function parseNpmPackFiles(raw: string, packageName: string): string[] {
4144}
42454346function collectNpmPackedFiles(packageDir: string, packageName: string): string[] {
44-return withAugmentedPluginNpmManifestForPackage({ packageDir }, ({ packageDir: cwd }) => {
45-const raw = execFileSync("npm", ["pack", "--dry-run", "--json", "--ignore-scripts"], {
46- cwd,
47-encoding: "utf8",
48-maxBuffer: 128 * 1024 * 1024,
49-stdio: ["ignore", "pipe", "pipe"],
50-});
51-return parseNpmPackFiles(raw, packageName);
47+const raw = execFileSync("npm", ["pack", "--dry-run", "--json", "--ignore-scripts"], {
48+cwd: packageDir,
49+encoding: "utf8",
50+maxBuffer: 128 * 1024 * 1024,
51+stdio: ["ignore", "pipe", "pipe"],
5252});
53+return parseNpmPackFiles(raw, packageName);
5354}
54555556function isScannerWalkedPackedPath(packedPath: string): boolean {
@@ -82,6 +83,37 @@ function stageScannerRelevantPackedFiles(
8283return stageDir;
8384}
848586+function collectPublishablePluginPackages(): PublishablePluginPackage[] {
87+return readdirSync("extensions", { withFileTypes: true })
88+.filter((entry) => entry.isDirectory())
89+.flatMap((entry) => {
90+const packageDir = join("extensions", entry.name);
91+const packageJsonPath = join(packageDir, "package.json");
92+let packageJson: {
93+name?: unknown;
94+openclaw?: { release?: { publishToNpm?: unknown } };
95+};
96+try {
97+packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8")) as typeof packageJson;
98+} catch {
99+return [];
100+}
101+if (packageJson.openclaw?.release?.publishToNpm !== true) {
102+return [];
103+}
104+if (typeof packageJson.name !== "string" || !packageJson.name.trim()) {
105+return [];
106+}
107+return [
108+{
109+ packageDir,
110+packageName: packageJson.name,
111+},
112+];
113+})
114+.toSorted((left, right) => left.packageName.localeCompare(right.packageName));
115+}
116+85117describe("publishable plugin npm package install security scan", () => {
86118it("keeps npm-published plugin files clear of env-harvesting hits", async () => {
87119const failures: string[] = [];
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。