




























@@ -21,6 +21,22 @@ async function listShellScripts(dir: string): Promise<string[]> {
2121return scripts;
2222}
232324+async function extractClawhubSkillInstallVerifier(): Promise<string> {
25+const script = await readFile("scripts/e2e/lib/skills/clawhub-install-proof.sh", "utf8");
26+const marker =
27+'node --input-type=module - "$OPENCLAW_CONFIG_PATH" "$skill_dir" "$origin_json" "$lock_json" "$info_json" "$slug" <<\'NODE\'\n';
28+const start = script.indexOf(marker);
29+if (start === -1) {
30+throw new Error("ClawHub skill install verifier heredoc was not found");
31+}
32+const verifierStart = start + marker.length;
33+const verifierEnd = script.indexOf("\nNODE", verifierStart);
34+if (verifierEnd === -1) {
35+throw new Error("ClawHub skill install verifier heredoc was not terminated");
36+}
37+return script.slice(verifierStart, verifierEnd);
38+}
39+2440describe("e2e shell tempfile hygiene", () => {
2541it("does not allocate FIFO paths with mktemp -u", async () => {
2642const offenders: string[] = [];
@@ -254,4 +270,56 @@ exit 42
254270await rm(tempRoot, { force: true, recursive: true });
255271}
256272});
273+274+it("rejects ClawHub skill info paths that only share a resolved prefix", async () => {
275+const tempRoot = await mkdtemp(path.join(tmpdir(), "openclaw-clawhub-info-path-"));
276+const workspaceDir = path.join(tempRoot, "workspace");
277+const slug = "demo";
278+const skillDir = path.join(workspaceDir, "skills", slug);
279+const escapedInfoPath = path.join(workspaceDir, "skills", `${slug}-escape`, "SKILL.md");
280+const configPath = path.join(tempRoot, "openclaw.json");
281+const originPath = path.join(skillDir, ".clawhub", "origin.json");
282+const lockPath = path.join(workspaceDir, ".clawhub", "lock.json");
283+const infoPath = path.join(tempRoot, "info.json");
284+285+try {
286+await mkdir(path.dirname(originPath), { recursive: true });
287+await mkdir(path.dirname(lockPath), { recursive: true });
288+await writeFile(path.join(skillDir, "SKILL.md"), `---\nname: Demo\n---\n`);
289+await writeFile(
290+configPath,
291+`${JSON.stringify({ skills: { install: { allowUploadedArchives: false } } })}\n`,
292+);
293+await writeFile(
294+originPath,
295+`${JSON.stringify({
296+ installedVersion: "1.0.0",
297+ registry: "https://clawhub.ai",
298+ slug,
299+ })}\n`,
300+);
301+await writeFile(
302+lockPath,
303+`${JSON.stringify({ skills: { [slug]: { version: "1.0.0" } } })}\n`,
304+);
305+await writeFile(
306+infoPath,
307+`${JSON.stringify({ filePath: escapedInfoPath, skillKey: "wrong-skill" })}\n`,
308+);
309+310+const result = spawnSync(
311+process.execPath,
312+["--input-type=module", "-", configPath, skillDir, originPath, lockPath, infoPath, slug],
313+{
314+encoding: "utf8",
315+input: await extractClawhubSkillInstallVerifier(),
316+},
317+);
318+319+expect(result.status).not.toBe(0);
320+expect(result.stderr).toContain("skills info did not report installed skill demo");
321+} finally {
322+await rm(tempRoot, { force: true, recursive: true });
323+}
324+});
257325});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。