chore: import upstream snapshot with attribution
CI / results (push) Blocked by required conditions
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / e2e-live (push) Waiting to run
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / deadcode (push) Waiting to run
CI / results (push) Blocked by required conditions
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / e2e-live (push) Waiting to run
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / deadcode (push) Waiting to run
This commit is contained in:
@@ -0,0 +1,332 @@
|
||||
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
const { describe, it } = require("node:test");
|
||||
const assert = require("node:assert/strict");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const os = require("os");
|
||||
|
||||
const crypto = require("crypto");
|
||||
|
||||
const { getExpectedChecksum, verifyChecksum, assertAllowedHost, resolveMirrorUrls, isCurlVersionSupported } = require("./install.js");
|
||||
|
||||
describe("getExpectedChecksum", () => {
|
||||
function makeTmpChecksums(content) {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "checksum-test-"));
|
||||
fs.writeFileSync(path.join(dir, "checksums.txt"), content, "utf8");
|
||||
return dir;
|
||||
}
|
||||
|
||||
it("returns correct hash from standard-format checksums.txt", () => {
|
||||
const dir = makeTmpChecksums(
|
||||
"abc123def456 lark-cli-1.0.0-darwin-arm64.tar.gz\n"
|
||||
);
|
||||
const hash = getExpectedChecksum(
|
||||
"lark-cli-1.0.0-darwin-arm64.tar.gz",
|
||||
dir
|
||||
);
|
||||
assert.equal(hash, "abc123def456");
|
||||
});
|
||||
|
||||
it("returns correct entry when multiple entries exist", () => {
|
||||
const dir = makeTmpChecksums(
|
||||
"aaaa lark-cli-1.0.0-linux-amd64.tar.gz\n" +
|
||||
"bbbb lark-cli-1.0.0-darwin-arm64.tar.gz\n" +
|
||||
"cccc lark-cli-1.0.0-windows-amd64.zip\n"
|
||||
);
|
||||
const hash = getExpectedChecksum(
|
||||
"lark-cli-1.0.0-darwin-arm64.tar.gz",
|
||||
dir
|
||||
);
|
||||
assert.equal(hash, "bbbb");
|
||||
});
|
||||
|
||||
it("throws Error when archiveName is not found", () => {
|
||||
const dir = makeTmpChecksums(
|
||||
"aaaa lark-cli-1.0.0-linux-amd64.tar.gz\n"
|
||||
);
|
||||
assert.throws(
|
||||
() => getExpectedChecksum("nonexistent.tar.gz", dir),
|
||||
{ message: /Checksum entry not found for nonexistent\.tar\.gz/ }
|
||||
);
|
||||
});
|
||||
|
||||
it("returns null when checksums.txt does not exist", () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "checksum-test-"));
|
||||
// No checksums.txt in dir
|
||||
const result = getExpectedChecksum("anything.tar.gz", dir);
|
||||
assert.equal(result, null);
|
||||
});
|
||||
|
||||
it("skips malformed lines and still finds valid entry", () => {
|
||||
const dir = makeTmpChecksums(
|
||||
"garbage line without separator\n" +
|
||||
"\n" +
|
||||
"abc123 lark-cli-1.0.0-darwin-arm64.tar.gz\n" +
|
||||
"also garbage\n"
|
||||
);
|
||||
const hash = getExpectedChecksum(
|
||||
"lark-cli-1.0.0-darwin-arm64.tar.gz",
|
||||
dir
|
||||
);
|
||||
assert.equal(hash, "abc123");
|
||||
});
|
||||
|
||||
it("skips tab-separated lines (only double-space is valid)", () => {
|
||||
const dir = makeTmpChecksums(
|
||||
"wrong\tlark-cli-1.0.0-darwin-arm64.tar.gz\n" +
|
||||
"correct lark-cli-1.0.0-darwin-arm64.tar.gz\n"
|
||||
);
|
||||
const hash = getExpectedChecksum(
|
||||
"lark-cli-1.0.0-darwin-arm64.tar.gz",
|
||||
dir
|
||||
);
|
||||
assert.equal(hash, "correct");
|
||||
});
|
||||
});
|
||||
|
||||
describe("verifyChecksum", () => {
|
||||
function makeTmpFile(content) {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "checksum-test-"));
|
||||
const filePath = path.join(dir, "archive.tar.gz");
|
||||
fs.writeFileSync(filePath, content);
|
||||
return filePath;
|
||||
}
|
||||
|
||||
function sha256(content) {
|
||||
return crypto.createHash("sha256").update(content).digest("hex");
|
||||
}
|
||||
|
||||
it("returns normally when hash matches", () => {
|
||||
const content = "binary content here";
|
||||
const filePath = makeTmpFile(content);
|
||||
const hash = sha256(content);
|
||||
// Should not throw
|
||||
verifyChecksum(filePath, hash);
|
||||
});
|
||||
|
||||
it("matches case-insensitively", () => {
|
||||
const content = "case test";
|
||||
const filePath = makeTmpFile(content);
|
||||
const hash = sha256(content).toUpperCase();
|
||||
// Should not throw
|
||||
verifyChecksum(filePath, hash);
|
||||
});
|
||||
|
||||
it("throws [SECURITY]-prefixed Error on mismatch", () => {
|
||||
const filePath = makeTmpFile("real content");
|
||||
assert.throws(
|
||||
() => verifyChecksum(filePath, "0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
(err) => {
|
||||
assert.match(err.message, /^\[SECURITY\]/);
|
||||
assert.match(err.message, /Checksum mismatch/);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("assertAllowedHost", () => {
|
||||
it("accepts github.com", () => {
|
||||
assertAllowedHost("https://github.com/larksuite/cli/releases/download/v1.0.0/archive.tar.gz");
|
||||
});
|
||||
|
||||
it("accepts objects.githubusercontent.com", () => {
|
||||
assertAllowedHost("https://objects.githubusercontent.com/some/path");
|
||||
});
|
||||
|
||||
it("accepts registry.npmmirror.com", () => {
|
||||
assertAllowedHost("https://registry.npmmirror.com/-/binary/lark-cli/v1.0.0/archive.tar.gz");
|
||||
});
|
||||
|
||||
it("rejects unknown host", () => {
|
||||
assert.throws(
|
||||
() => assertAllowedHost("https://evil.example.com/payload"),
|
||||
{ message: /Download host not allowed: evil\.example\.com/ }
|
||||
);
|
||||
});
|
||||
|
||||
it("normalizes hostname to lowercase", () => {
|
||||
// URL constructor lowercases hostnames per spec
|
||||
assertAllowedHost("https://GitHub.COM/larksuite/cli/releases/download/v1.0.0/a.tar.gz");
|
||||
});
|
||||
|
||||
it("ignores port when matching hostname", () => {
|
||||
// URL.hostname does not include port
|
||||
assertAllowedHost("https://github.com:443/larksuite/cli/releases/download/v1.0.0/a.tar.gz");
|
||||
});
|
||||
|
||||
it("throws on invalid URL", () => {
|
||||
assert.throws(
|
||||
() => assertAllowedHost("not-a-url"),
|
||||
TypeError
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveMirrorUrls", () => {
|
||||
const ARCHIVE = "lark-cli-1.0.0-linux-amd64.tar.gz";
|
||||
const VERSION = "1.0.0";
|
||||
const DEFAULT = "https://registry.npmmirror.com/-/binary/lark-cli/v1.0.0/lark-cli-1.0.0-linux-amd64.tar.gz";
|
||||
|
||||
it("returns only the default mirror when no env vars are set", () => {
|
||||
assert.deepEqual(resolveMirrorUrls({}, ARCHIVE, VERSION), [DEFAULT]);
|
||||
});
|
||||
|
||||
it("does not derive from the default npmjs registry", () => {
|
||||
// The public npmjs registry doesn't host /-/binary/<pkg>/..., so we must
|
||||
// not point downloads at it.
|
||||
assert.deepEqual(
|
||||
resolveMirrorUrls(
|
||||
{ npm_config_registry: "https://registry.npmjs.org/" },
|
||||
ARCHIVE,
|
||||
VERSION
|
||||
),
|
||||
[DEFAULT]
|
||||
);
|
||||
});
|
||||
|
||||
it("derives from non-default npm_config_registry AND keeps default as fallback", () => {
|
||||
// Critical: a corporate npm proxy (Verdaccio/Artifactory/Nexus) often
|
||||
// doesn't actually serve /-/binary/<pkg>/..., so we must keep the
|
||||
// public npmmirror as a final fallback or installs regress vs. the
|
||||
// pre-PR "GitHub → npmmirror" behavior.
|
||||
assert.deepEqual(
|
||||
resolveMirrorUrls(
|
||||
{ npm_config_registry: "https://corp.example.com/repository/npm-public/" },
|
||||
ARCHIVE,
|
||||
VERSION
|
||||
),
|
||||
[
|
||||
"https://corp.example.com/repository/npm-public/-/binary/lark-cli/v1.0.0/lark-cli-1.0.0-linux-amd64.tar.gz",
|
||||
DEFAULT,
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
it("derived URL appears before the default in the chain", () => {
|
||||
const urls = resolveMirrorUrls(
|
||||
{ npm_config_registry: "https://corp.example.com/" },
|
||||
ARCHIVE,
|
||||
VERSION
|
||||
);
|
||||
assert.equal(urls.length, 2);
|
||||
assert.match(urls[0], /^https:\/\/corp\.example\.com\//);
|
||||
assert.equal(urls[1], DEFAULT);
|
||||
});
|
||||
|
||||
it("does not duplicate the default if the registry already points at it", () => {
|
||||
// If npm_config_registry happens to be the public npmmirror, we still
|
||||
// want a single entry, not two identical ones.
|
||||
assert.deepEqual(
|
||||
resolveMirrorUrls(
|
||||
{ npm_config_registry: "https://registry.npmmirror.com/" },
|
||||
ARCHIVE,
|
||||
VERSION
|
||||
),
|
||||
[DEFAULT]
|
||||
);
|
||||
});
|
||||
|
||||
it("strips trailing slashes from the registry URL", () => {
|
||||
assert.deepEqual(
|
||||
resolveMirrorUrls(
|
||||
{ npm_config_registry: "https://corp.example.com///" },
|
||||
ARCHIVE,
|
||||
VERSION
|
||||
),
|
||||
[
|
||||
"https://corp.example.com/-/binary/lark-cli/v1.0.0/lark-cli-1.0.0-linux-amd64.tar.gz",
|
||||
DEFAULT,
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
it("ignores empty/whitespace npm_config_registry", () => {
|
||||
assert.deepEqual(
|
||||
resolveMirrorUrls(
|
||||
{ npm_config_registry: "" },
|
||||
ARCHIVE,
|
||||
VERSION
|
||||
),
|
||||
[DEFAULT]
|
||||
);
|
||||
});
|
||||
|
||||
it("silently falls back when npm_config_registry is non-https", () => {
|
||||
// Implicit feature: don't break installs whose npm registry is plain http.
|
||||
// The user didn't opt into binary-mirror behavior, so just use the default.
|
||||
assert.deepEqual(
|
||||
resolveMirrorUrls(
|
||||
{ npm_config_registry: "http://internal.example.com/" },
|
||||
ARCHIVE,
|
||||
VERSION
|
||||
),
|
||||
[DEFAULT]
|
||||
);
|
||||
});
|
||||
|
||||
it("silently falls back when npm_config_registry is file://", () => {
|
||||
assert.deepEqual(
|
||||
resolveMirrorUrls(
|
||||
{ npm_config_registry: "file:///tmp" },
|
||||
ARCHIVE,
|
||||
VERSION
|
||||
),
|
||||
[DEFAULT]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isCurlVersionSupported", () => {
|
||||
// --ssl-revoke-best-effort was introduced in curl 7.70.0; below that the
|
||||
// flag is unknown and `curl` exits non-zero (see issue #1099).
|
||||
it("returns false for curl 7.55.1 (older Windows 10, flag unknown)", () => {
|
||||
assert.equal(
|
||||
isCurlVersionSupported("curl 7.55.1 (x86_64-pc-win32) libcurl/7.55.1"),
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it("returns false for curl 7.69.0 (just below the 7.70.0 threshold)", () => {
|
||||
assert.equal(
|
||||
isCurlVersionSupported("curl 7.69.0 (x86_64-pc-win32) libcurl/7.69.0"),
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it("returns true for curl 7.70.0 (flag introduced here)", () => {
|
||||
assert.equal(
|
||||
isCurlVersionSupported("curl 7.70.0 (x86_64-pc-win32) libcurl/7.70.0"),
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it("returns true for a future major (curl 8.x)", () => {
|
||||
assert.equal(
|
||||
isCurlVersionSupported("curl 8.5.0 (x86_64-apple-darwin) libcurl/8.5.0"),
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it("returns false when no version can be parsed", () => {
|
||||
assert.equal(isCurlVersionSupported("not a curl version string"), false);
|
||||
assert.equal(isCurlVersionSupported(""), false);
|
||||
});
|
||||
|
||||
it("reads the leading 'curl X.Y.Z', not the trailing libcurl/X.Y.Z", () => {
|
||||
// Guards the regex against latching onto "libcurl/7.55.1" when the
|
||||
// curl binary itself is new enough.
|
||||
assert.equal(
|
||||
isCurlVersionSupported("curl 8.0.0 (x86_64) libcurl/7.55.1"),
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it("does not match a 'libcurl X.Y.Z' token (anchored to leading curl)", () => {
|
||||
// "libcurl 8.0.0" contains the substring "curl 8.0.0"; the leading
|
||||
// anchor keeps it from being mistaken for a real curl version line.
|
||||
assert.equal(isCurlVersionSupported("libcurl 8.0.0"), false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user