From fa472623f683114c5c010c8ac97f327e00093f9d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 15 Feb 2026 12:56:31 +0000 Subject: [PATCH] perf(test): use prebuilt hook install fixtures --- src/hooks/install.test.ts | 176 +----------------- .../fixtures/hooks-install/npm-pack-hooks.tgz | Bin 0 -> 421 bytes test/fixtures/hooks-install/tar-evil-id.tar | Bin 0 -> 5632 bytes test/fixtures/hooks-install/tar-hooks.tar | Bin 0 -> 5632 bytes .../hooks-install/tar-reserved-id.tar | Bin 0 -> 5632 bytes test/fixtures/hooks-install/tar-traversal.tar | Bin 0 -> 2048 bytes test/fixtures/hooks-install/zip-hooks.zip | Bin 0 -> 961 bytes test/fixtures/hooks-install/zip-traversal.zip | Bin 0 -> 209 bytes 8 files changed, 10 insertions(+), 166 deletions(-) create mode 100644 test/fixtures/hooks-install/npm-pack-hooks.tgz create mode 100644 test/fixtures/hooks-install/tar-evil-id.tar create mode 100644 test/fixtures/hooks-install/tar-hooks.tar create mode 100644 test/fixtures/hooks-install/tar-reserved-id.tar create mode 100644 test/fixtures/hooks-install/tar-traversal.tar create mode 100644 test/fixtures/hooks-install/zip-hooks.zip create mode 100644 test/fixtures/hooks-install/zip-traversal.zip diff --git a/src/hooks/install.test.ts b/src/hooks/install.test.ts index cb1ec50c29..579bff4172 100644 --- a/src/hooks/install.test.ts +++ b/src/hooks/install.test.ts @@ -1,50 +1,27 @@ -import JSZip from "jszip"; import { randomUUID } from "node:crypto"; import fs from "node:fs"; -import fsPromises from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import * as tar from "tar"; -import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterAll, beforeEach, describe, expect, it, vi } from "vitest"; const fixtureRoot = path.join(os.tmpdir(), `openclaw-hook-install-${randomUUID()}`); let tempDirIndex = 0; -let zipHooksBuffer: Buffer; -let zipTraversalBuffer: Buffer; -let tarHooksBuffer: Buffer; -let tarTraversalBuffer: Buffer; -let tarEvilIdBuffer: Buffer; -let tarReservedIdBuffer: Buffer; -let npmPackHooksBuffer: Buffer; +const fixturesDir = path.resolve(process.cwd(), "test", "fixtures", "hooks-install"); +const zipHooksBuffer = fs.readFileSync(path.join(fixturesDir, "zip-hooks.zip")); +const zipTraversalBuffer = fs.readFileSync(path.join(fixturesDir, "zip-traversal.zip")); +const tarHooksBuffer = fs.readFileSync(path.join(fixturesDir, "tar-hooks.tar")); +const tarTraversalBuffer = fs.readFileSync(path.join(fixturesDir, "tar-traversal.tar")); +const tarEvilIdBuffer = fs.readFileSync(path.join(fixturesDir, "tar-evil-id.tar")); +const tarReservedIdBuffer = fs.readFileSync(path.join(fixturesDir, "tar-reserved-id.tar")); +const npmPackHooksBuffer = fs.readFileSync(path.join(fixturesDir, "npm-pack-hooks.tgz")); vi.mock("../process/exec.js", () => ({ runCommandWithTimeout: vi.fn(), })); -async function packToArchive({ - pkgDir, - outDir, - outName, -}: { - pkgDir: string; - outDir: string; - outName: string; -}) { - const dest = path.join(outDir, outName); - fs.rmSync(dest, { force: true }); - await tar.c( - { - gzip: true, - file: dest, - cwd: path.dirname(pkgDir), - }, - [path.basename(pkgDir)], - ); - return dest; -} - function makeTempDir() { + fs.mkdirSync(fixtureRoot, { recursive: true }); const dir = path.join(fixtureRoot, `case-${tempDirIndex++}`); fs.mkdirSync(dir, { recursive: true }); return dir; @@ -54,139 +31,6 @@ const { runCommandWithTimeout } = await import("../process/exec.js"); const { installHooksFromArchive, installHooksFromNpmSpec, installHooksFromPath } = await import("./install.js"); -beforeAll(async () => { - fs.mkdirSync(fixtureRoot, { recursive: true }); - - const zipHooks = new JSZip(); - zipHooks.file( - "package/package.json", - JSON.stringify({ - name: "@openclaw/zip-hooks", - version: "0.0.1", - openclaw: { hooks: ["./hooks/zip-hook"] }, - }), - ); - zipHooks.file( - "package/hooks/zip-hook/HOOK.md", - [ - "---", - "name: zip-hook", - "description: Zip hook", - 'metadata: {"openclaw":{"events":["command:new"]}}', - "---", - "", - "# Zip Hook", - ].join("\n"), - ); - zipHooks.file("package/hooks/zip-hook/handler.ts", "export default async () => {};\n"); - zipHooksBuffer = await zipHooks.generateAsync({ type: "nodebuffer" }); - - const zipTraversal = new JSZip(); - zipTraversal.file("../pwned.txt", "nope\n"); - zipTraversalBuffer = await zipTraversal.generateAsync({ type: "nodebuffer" }); - - const makeTarWithPackage = async (params: { - packageName: string; - hookName: string; - }): Promise => { - const workDir = makeTempDir(); - const archivePath = path.join(workDir, "hooks.tar"); - const pkgDir = path.join(workDir, "package"); - fs.mkdirSync(path.join(pkgDir, "hooks", params.hookName), { recursive: true }); - fs.writeFileSync( - path.join(pkgDir, "package.json"), - JSON.stringify({ - name: params.packageName, - version: "0.0.1", - openclaw: { hooks: [`./hooks/${params.hookName}`] }, - }), - "utf-8", - ); - fs.writeFileSync( - path.join(pkgDir, "hooks", params.hookName, "HOOK.md"), - [ - "---", - `name: ${params.hookName}`, - `description: ${params.hookName}`, - 'metadata: {"openclaw":{"events":["command:new"]}}', - "---", - "", - "# Hook", - ].join("\n"), - "utf-8", - ); - fs.writeFileSync( - path.join(pkgDir, "hooks", params.hookName, "handler.ts"), - "export default async () => {};\n", - "utf-8", - ); - await tar.c({ cwd: workDir, file: archivePath }, ["package"]); - return await fsPromises.readFile(archivePath); - }; - - tarHooksBuffer = await makeTarWithPackage({ - packageName: "@openclaw/tar-hooks", - hookName: "tar-hook", - }); - tarEvilIdBuffer = await makeTarWithPackage({ packageName: "@evil/..", hookName: "evil-hook" }); - tarReservedIdBuffer = await makeTarWithPackage({ - packageName: "@evil/.", - hookName: "reserved-hook", - }); - - const makeTraversalTar = async (): Promise => { - const workDir = makeTempDir(); - const insideDir = path.join(workDir, "inside"); - fs.mkdirSync(insideDir, { recursive: true }); - fs.writeFileSync(path.join(workDir, "outside.txt"), "nope\n", "utf-8"); - const archivePath = path.join(workDir, "traversal.tar"); - await tar.c({ cwd: insideDir, file: archivePath }, ["../outside.txt"]); - return await fsPromises.readFile(archivePath); - }; - - tarTraversalBuffer = await makeTraversalTar(); - - const makeNpmPackTgz = async (): Promise => { - const workDir = makeTempDir(); - const packedName = "test-hooks-0.0.1.tgz"; - const pkgDir = path.join(workDir, "package"); - fs.mkdirSync(path.join(pkgDir, "hooks", "one-hook"), { recursive: true }); - fs.writeFileSync( - path.join(pkgDir, "package.json"), - JSON.stringify({ - name: "@openclaw/test-hooks", - version: "0.0.1", - openclaw: { hooks: ["./hooks/one-hook"] }, - }), - "utf-8", - ); - fs.writeFileSync( - path.join(pkgDir, "hooks", "one-hook", "HOOK.md"), - [ - "---", - "name: one-hook", - "description: One hook", - 'metadata: {"openclaw":{"events":["command:new"]}}', - "---", - "", - "# One Hook", - ].join("\n"), - "utf-8", - ); - fs.writeFileSync( - path.join(pkgDir, "hooks", "one-hook", "handler.ts"), - "export default async () => {};\n", - "utf-8", - ); - - const packTmpDir = makeTempDir(); - const archivePath = await packToArchive({ pkgDir, outDir: packTmpDir, outName: packedName }); - return await fsPromises.readFile(archivePath); - }; - - npmPackHooksBuffer = await makeNpmPackTgz(); -}); - afterAll(() => { try { fs.rmSync(fixtureRoot, { recursive: true, force: true }); diff --git a/test/fixtures/hooks-install/npm-pack-hooks.tgz b/test/fixtures/hooks-install/npm-pack-hooks.tgz new file mode 100644 index 0000000000000000000000000000000000000000..ee382e1256b7312ed7ab82f3f2d4163d2765a5fc GIT binary patch literal 421 zcmV;W0b2eaiwFP!000006YZ7XO2aS|#(UkT2=PY2cIkhpC|(L)>;c4!&>r2~nv^En zl(Kg>?N-@_bJn#?SbjGrgwy0CU-O-uCKLJ!1EWU>AyE*ZI)N&!QS;LefH&A|#A+knq=anu9d)AFz3+o8wLLwf-Z{C!(jnpuPS<5IS1_ zV`-!RI@)i7v##q<_YwO3Ui}@<+tojeT%|whx{&WUbNx%qXa+dO_dExdOzC1EK}f6i z1AL9=P>3;SDuLLQBh39iT(mbNk{!D4PB!@)M+0K zHHR3+YV`kXomWxhaQqlp=^qB`eE%u`UGH1|hmos&;uxE^#W?f)uW`;Yj$?e!=i_PL zaqu;s^Gb}1Ty+wMIIij`N}eZEw!m?XS5ATBN9^?1rlA-1G{jHKWgFdQ{BJSPhGcH-mB#Tk~r6BM*&!x~6At|zq~{%AeV6DiIU! dd4evl(Crm?VokXPe^&2lE#exW01KBO5tZXd!Natbj#E8oR`nq_l7&|e@a=@@^{^DPyB7^ibE zj5);6R}=r;Xnyi6h3kD_8Gqlc^ZmE{w;cO8|NX$0nn3zEyv@fy&;JsqG+{Wz&somW zXhGK_sfWf^4)8r*v4YRCRIU?~n3kMr%y3xZ+A4g-=BU<>W-ET%Y}$xd>({_QlmCbD z>$ebe*Z;sFvQJR;|FS;>YCgo!>xuY}CzBU5iJ|5BxK00E+3Ugk5t}%H(*N@b-|d8) zLJS=nhM`v#AViIJ>oMa|G0TP2eK4CaL1QXth{}T+z*a039LpjQWl2KQI852P(to{j zt>2)r>@4Al3SScgC-Q$P1G!*@DR>W;ZOMO`?8o_U*@5f{RsVMx`7q}phF;lMo)rSc k?42$b0#W{*M(FkqJwBmw^Podhg%tye0mXn~;14nI0~v1tQ2+n{ literal 0 HcmV?d00001 diff --git a/test/fixtures/hooks-install/tar-reserved-id.tar b/test/fixtures/hooks-install/tar-reserved-id.tar new file mode 100644 index 0000000000000000000000000000000000000000..7c1ea84420dd06622e4cc5ba4bd35a44d68c36a3 GIT binary patch literal 5632 zcmeHJU2DQH6zy|=MaW(@m^J+rIyUww?7<%}_7K{;*=av$qHYxb`x3Py7O_+hI&AaH54>D}u_B_|K$;dT`gaI=A9?$d} zdcim?#nBjtqyP@T106yAA$8kvc^}hNPF1Y#RqXVi=J|~E^cM(9I_mHHfvxpdbvgyB znyXliRqMZNtxuL`>i8wF&_D3D`F<$>ZO3iqe=u@{Cy+~Q-WKD^^S{CwO#z4a8J5X> zXyJRjgpwtBCi;j)EL-&C1!U2ju5ei4jZxt66;L&biR$?(RwEzNe>|DISZS>f{Q}X97=)Hm~tATYOgZD5;D$YX^8SPrCA(iu#yGAl$)40 zXeC`^A6FH|HK_N?thNs$~&RX|MPfPJzvFYRQM|LlA{>j j>0-_iW#3tZZtu|J6ROq^raDz)&46Y=GoTsxM-2P`_(=%O literal 0 HcmV?d00001 diff --git a/test/fixtures/hooks-install/tar-traversal.tar b/test/fixtures/hooks-install/tar-traversal.tar new file mode 100644 index 0000000000000000000000000000000000000000..cf0530ab9d71fc4f6d9f297e078ca634f3a362fc GIT binary patch literal 2048 zcmeH@F%Ez*2t_-43Qtg}(s~{f8C+~*G4c2kyEINNorDGW&;rleA)cJKl`LOuniv2C z#NLRXgMU1XB^OCdH0K@SgbwD(fb$rF^flS$_34>;Ib^lWv?XGCl|QHDl5!kC|M^|R Z#+qpz*CY3ssH+8ym4FgZ0!rW)1a2Wt9Z3KH literal 0 HcmV?d00001 diff --git a/test/fixtures/hooks-install/zip-hooks.zip b/test/fixtures/hooks-install/zip-hooks.zip new file mode 100644 index 0000000000000000000000000000000000000000..444d26c5ab6cb294da147a0cb3e1d5443863bc1c GIT binary patch literal 961 zcmZ`%!A^rf5T#Wc!@-!?q&HpH#AqV4UNIU^rU@E2cxv^qfvu%2y9BUR34h^Fc=hJb z_ydhU;K|v-wgtnGVHbGs?F?^hb7NCgM9c@)!(x-)wqXs3GbT^8Z5L4Ycm3NQd==ST zL*;0F6mgbdMtq75e9Z&OoC%q=-@HH@az2jm8J^NG@;HO<9lfKU!y5_1MuIbx(Z_mw zv0YN}W11Fgf#a9413Ou9#;eA*{$XW#rDi|oKZ6l~MebLVx|U_?zN=}PDjGMClv7b$H^02zfAqY-rsK*g~33wPImk!ADBt|59Wez$yMVD77NiS5}tks)J^