Files
gstack/test/dependency-security.test.ts
Garry Tanandcapy-ai[bot] 86a488b135 v1.93.5.0 fix(deps): pin qs and @hono/node-server past two Medium OSV advisories (#3843) (#3870)
* fix(deps): pin qs 6.16.2 and @hono/node-server 2.1.6 past GHSA-6pq8-jhm2-2vcw and GHSA-rmxm-3fg6-px4f (#3843)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs: changelog fragment and CONTRIBUTING note for the qs and @hono/node-server pins (#3843)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs(changelog): installs older than v1.91.231.0 may still carry the dev toolchain (#3843)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-11-02 16:10:42 +00:00

69 lines
3.2 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { mkdtempSync, mkdirSync, readFileSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import AdmZip from "adm-zip";
import sharp from "sharp";
describe("security dependency overrides", () => {
for (const [name, minimum] of [["sharp", "0.35.4"], ["adm-zip", "0.6.1"], ["qs", "6.16.1"], ["@hono/node-server", "2.1.3"]]) {
test(`${name} resolves only versions at or above ${minimum}`, async () => {
const lock = await Bun.file(join(import.meta.dir, "../bun.lock")).text();
const versions = [...lock.matchAll(new RegExp(`"${name}@([^"]+)"`, "g"))];
expect(versions.length).toBeGreaterThan(0);
for (const match of versions) {
expect(Bun.semver.satisfies(match[1], `>=${minimum}`)).toBe(true);
}
});
}
test("sharp is a declared dependency, and the pinned override satisfies every range that asks for it", async () => {
const pkg = JSON.parse(readFileSync(join(import.meta.dir, "../package.json"), "utf8"));
const declared = pkg.dependencies?.sharp;
expect(typeof declared).toBe("string");
const pinned = pkg.overrides?.sharp;
expect(Bun.semver.satisfies(pinned, declared)).toBe(true);
const lock = await Bun.file(join(import.meta.dir, "../bun.lock")).text();
const askers = [...lock.matchAll(/"([@\w./-]+)": \["[^"]+", "", \{ "dependencies": \{[^}]*"sharp": "([^"]+)"/g)];
expect(askers.map(m => m[1])).toContain("@huggingface/transformers");
for (const [, name, range] of askers) expect({ name, ok: Bun.semver.satisfies(pinned, range) }).toEqual({ name, ok: true });
});
test("sharp loads the patched runtime", () => {
expect(Bun.semver.satisfies(sharp.versions.sharp, ">=0.35.4")).toBe(true);
});
test("adm-zip still extracts ordinary archives", () => {
const root = mkdtempSync(join(tmpdir(), "gstack-zip-safe-"));
try {
const zip = new AdmZip();
zip.addFile("nested/file.txt", Buffer.from("archive contents"));
zip.extractAllTo(root, true);
expect(readFileSync(join(root, "nested/file.txt"), "utf8")).toBe("archive contents");
} finally {
rmSync(root, { recursive: true, force: true });
}
});
for (const target of ["directory", "file"] as const) {
test.skipIf(process.platform === "win32")(`adm-zip refuses an extraction through a ${target} symlink`, () => {
const root = mkdtempSync(join(tmpdir(), "gstack-zip-symlink-"));
try {
const destination = join(root, "destination");
const outside = join(root, "outside");
mkdirSync(destination);
mkdirSync(outside);
const protectedFile = join(outside, "file.txt");
writeFileSync(protectedFile, "original contents");
symlinkSync(target === "directory" ? outside : protectedFile, join(destination, "link"));
const zip = new AdmZip();
zip.addFile(target === "directory" ? "link/file.txt" : "link", Buffer.from("overwritten"));
expect(() => zip.extractAllTo(destination, true)).toThrow();
expect(readFileSync(protectedFile, "utf8")).toBe("original contents");
} finally {
rmSync(root, { recursive: true, force: true });
}
});
}
});