mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 03:45:46 -06:00
234df15a6d
* build(deps): refresh dependencies after cooldown Apply dependency, toolchain, action, image, and exact tool updates released by the inclusive 2026-08-16 seven-day cutoff. Adapt owner boundaries for the resulting CUA, logging, Teams, Markdown, native, and test-harness contract changes while retaining versions blocked by upstream compatibility constraints. * fix(ui): align markdown renderer env typing * fix(deps): align postcss and mistral peer contracts * fix(deps): repair refreshed dependency contracts * fix(deps): retain tslog startup budget * fix(ci): verify Android tools with SHA-256 * fix(ci): fence Android SDK cache version
196 lines
7.1 KiB
TypeScript
196 lines
7.1 KiB
TypeScript
import { execFileSync } from "node:child_process";
|
|
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
import { dirname, join } from "node:path";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js";
|
|
|
|
const SCRIPT = "scripts/install-trufflehog.sh";
|
|
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
|
|
|
|
function runBash(command: string, env: NodeJS.ProcessEnv = {}): string {
|
|
return execFileSync("/bin/bash", ["--noprofile", "--norc", "-c", command], {
|
|
cwd: process.cwd(),
|
|
encoding: "utf8",
|
|
env: {
|
|
...process.env,
|
|
OPENCLAW_TRUFFLEHOG_SOURCE_ONLY: "1",
|
|
...env,
|
|
},
|
|
stdio: ["ignore", "pipe", "pipe"],
|
|
});
|
|
}
|
|
|
|
describe("scripts/install-trufflehog.sh", () => {
|
|
it("is an opt-in shared environment setup capability", () => {
|
|
const action = readFileSync(".github/actions/setup-node-env/action.yml", "utf8");
|
|
expect(action).toContain("install-trufflehog:");
|
|
expect(action).toContain("if: inputs.install-trufflehog == 'true'");
|
|
expect(action).toContain("run: bash scripts/install-trufflehog.sh");
|
|
});
|
|
|
|
it("is enabled during every Linux Testbox hydration before handoff", () => {
|
|
for (const workflow of [
|
|
".github/workflows/ci-check-testbox.yml",
|
|
".github/workflows/ci-check-arm-testbox.yml",
|
|
".github/workflows/ci-build-artifacts-testbox.yml",
|
|
]) {
|
|
const text = readFileSync(workflow, "utf8");
|
|
const install = text.indexOf('install-trufflehog: "true"');
|
|
const handoff = text.indexOf("uses: useblacksmith/run-testbox@");
|
|
|
|
expect(install, `${workflow} must provision TruffleHog`).toBeGreaterThanOrEqual(0);
|
|
expect(handoff, `${workflow} must hand off to run-testbox`).toBeGreaterThan(install);
|
|
}
|
|
});
|
|
|
|
it("pins the reviewed Linux checksums for both Testbox architectures", () => {
|
|
const output = runBash(
|
|
[
|
|
`source ${SCRIPT}`,
|
|
"printf 'amd64=%s\\n' \"$(trufflehog_sha256 amd64)\"",
|
|
"printf 'arm64=%s\\n' \"$(trufflehog_sha256 arm64)\"",
|
|
].join("\n"),
|
|
);
|
|
|
|
expect(output).toContain(
|
|
"amd64=62224de2f9dd7cd418800feb953760a302ed2f82a7c547fe1146a4874fb179e4",
|
|
);
|
|
expect(output).toContain(
|
|
"arm64=f48f57e3d4343377865b1b64653f96d381d61a7792d89d026e85524732039fde",
|
|
);
|
|
});
|
|
|
|
it("does not download TruffleHog again when the pinned version is installed", () => {
|
|
const root = tempDirs.make("openclaw-trufflehog-install-");
|
|
const binDir = join(root, "bin");
|
|
const downloadMarker = join(root, "downloaded");
|
|
mkdirSync(binDir);
|
|
const trufflehog = join(binDir, "trufflehog");
|
|
writeFileSync(trufflehog, "#!/bin/sh\nprintf 'trufflehog 3.97.0\\n'\n");
|
|
chmodSync(trufflehog, 0o755);
|
|
const fakeCurl = join(binDir, "curl");
|
|
writeFileSync(
|
|
fakeCurl,
|
|
`#!/bin/sh\nprintf downloaded >${JSON.stringify(downloadMarker)}\nexit 99\n`,
|
|
);
|
|
chmodSync(fakeCurl, 0o755);
|
|
const fakeUname = join(binDir, "uname");
|
|
writeFileSync(
|
|
fakeUname,
|
|
'#!/bin/sh\nif [ "$1" = "-s" ]; then printf "Linux\\n"; else printf "x86_64\\n"; fi\n',
|
|
);
|
|
chmodSync(fakeUname, 0o755);
|
|
|
|
runBash(`source ${SCRIPT}\ninstall_trufflehog`, {
|
|
OPENCLAW_TRUFFLEHOG_BIN_DIR: binDir,
|
|
PATH: `${binDir}:${process.env.PATH ?? ""}`,
|
|
});
|
|
|
|
expect(existsSync(downloadMarker)).toBe(false);
|
|
expect(readFileSync(trufflehog, "utf8")).toContain("3.97.0");
|
|
});
|
|
|
|
it("creates a missing user-writable install directory without sudo", () => {
|
|
const root = tempDirs.make("openclaw-trufflehog-user-bin-");
|
|
const binDir = join(root, "nested", "bin");
|
|
const fakeBin = join(root, "fake-bin");
|
|
const sudoMarker = join(root, "sudo-used");
|
|
mkdirSync(fakeBin);
|
|
const fakeSudo = join(fakeBin, "sudo");
|
|
writeFileSync(fakeSudo, `#!/bin/sh\nprintf used >${JSON.stringify(sudoMarker)}\nexit 99\n`);
|
|
chmodSync(fakeSudo, 0o755);
|
|
|
|
runBash(`source ${SCRIPT}\nrun_as_root mkdir -p "$OPENCLAW_TRUFFLEHOG_BIN_DIR"`, {
|
|
OPENCLAW_TRUFFLEHOG_BIN_DIR: binDir,
|
|
PATH: `${fakeBin}:${process.env.PATH ?? ""}`,
|
|
});
|
|
|
|
expect(existsSync(binDir)).toBe(true);
|
|
expect(existsSync(sudoMarker)).toBe(false);
|
|
});
|
|
|
|
it("does not change permissions on an existing writable install directory", () => {
|
|
const root = tempDirs.make("openclaw-trufflehog-existing-bin-");
|
|
const binDir = join(root, "bin");
|
|
const fakeBin = join(root, "fake-bin");
|
|
const installMarker = join(root, "install-used");
|
|
mkdirSync(binDir);
|
|
mkdirSync(fakeBin);
|
|
const fakeInstall = join(fakeBin, "install");
|
|
writeFileSync(
|
|
fakeInstall,
|
|
`#!/bin/sh\nprintf used >${JSON.stringify(installMarker)}\nexit 99\n`,
|
|
);
|
|
chmodSync(fakeInstall, 0o755);
|
|
|
|
runBash(`source ${SCRIPT}\nensure_trufflehog_bin_dir`, {
|
|
OPENCLAW_TRUFFLEHOG_BIN_DIR: binDir,
|
|
PATH: `${fakeBin}:${process.env.PATH ?? ""}`,
|
|
});
|
|
|
|
expect(existsSync(installMarker)).toBe(false);
|
|
});
|
|
|
|
it("passes bounded download options to curl and cleans up after curl times out", () => {
|
|
const root = tempDirs.make("openclaw-trufflehog-curl-");
|
|
const binDir = join(root, "bin");
|
|
const argsFile = join(root, "curl-args");
|
|
mkdirSync(binDir);
|
|
writeFileSync(
|
|
join(binDir, "curl"),
|
|
'#!/bin/sh\nprintf "%s\\n" "$@" >"$CURL_ARGS_FILE"\nexit 28\n',
|
|
{ mode: 0o755 },
|
|
);
|
|
|
|
expect(() =>
|
|
runBash(
|
|
`uname() { if [ "$1" = "-s" ]; then printf "Linux\\n"; else printf "x86_64\\n"; fi; }\nsource ${SCRIPT}\ninstall_trufflehog`,
|
|
{
|
|
CURL_ARGS_FILE: argsFile,
|
|
OPENCLAW_TRUFFLEHOG_BIN_DIR: join(root, "install"),
|
|
PATH: `${binDir}:${process.env.PATH ?? ""}`,
|
|
},
|
|
),
|
|
).toThrow();
|
|
|
|
const archive = "trufflehog_3.97.0_linux_amd64.tar.gz";
|
|
const args = readFileSync(argsFile, "utf8").trimEnd().split("\n");
|
|
const outputPath = args[10] ?? "";
|
|
expect(args.slice(0, 10)).toEqual([
|
|
"-fsSL",
|
|
"--connect-timeout",
|
|
"30",
|
|
"--max-time",
|
|
"300",
|
|
"--retry",
|
|
"3",
|
|
"--retry-max-time",
|
|
"300",
|
|
"--output",
|
|
]);
|
|
expect(outputPath).toBe(join(dirname(outputPath), archive));
|
|
expect(args[11]).toBe(
|
|
`https://github.com/trufflesecurity/trufflehog/releases/download/v3.97.0/${archive}`,
|
|
);
|
|
expect(existsSync(dirname(outputPath))).toBe(false);
|
|
});
|
|
|
|
it("verifies the archive before extraction and replaces the binary atomically", () => {
|
|
const script = readFileSync(SCRIPT, "utf8");
|
|
expect(script).toContain('"$binary" --no-update --version');
|
|
const download = script.indexOf("curl -fsSL");
|
|
const verify = script.indexOf("sha256sum -c -");
|
|
const extract = script.indexOf(
|
|
'tar --no-same-owner -xzf "$tmp_dir/$archive" -C "$tmp_dir" trufflehog',
|
|
);
|
|
const validate = script.indexOf('trufflehog_binary_ready "$candidate"');
|
|
const replace = script.indexOf('mv -f "$candidate" "$target"');
|
|
|
|
expect(download).toBeGreaterThanOrEqual(0);
|
|
expect(verify).toBeGreaterThan(download);
|
|
expect(extract).toBeGreaterThan(verify);
|
|
expect(validate).toBeGreaterThan(extract);
|
|
expect(replace).toBeGreaterThan(validate);
|
|
});
|
|
});
|