mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 14:02:43 +08:00
fix(install): detect curl version before using --ssl-revoke-best-effort (#1124)
* fix(install): detect curl version before using --ssl-revoke-best-effort
(cherry picked from commit da14737702)
* test(install): cover curl version gate and refactor for testability
Extract the version comparison out of curlSupportsSslRevokeBestEffort()
into a pure isCurlVersionSupported(output), so the >= 7.70.0 logic is unit
testable without spawning curl. Add cases for 7.55.1 / 7.69.0 / 7.70.0 /
8.x plus the unparseable and libcurl-token edge cases (the regex must read
the leading "curl X.Y.Z", not the trailing "libcurl/X.Y.Z").
Memoize the `curl --version` probe: curl's version is invariant for the
install's lifetime while download() runs once per mirror URL, so probe at
most once instead of re-spawning curl on every attempt.
---------
Co-authored-by: EllienTang <146210093+Ellien-Tang@users.noreply.github.com>
Co-authored-by: liangshuo-1 <266696938+liangshuo-1@users.noreply.github.com>
This commit is contained in:
@@ -110,6 +110,52 @@ function getMirrorUrls(env) {
|
||||
return urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide from a `curl --version` output whether curl is >= 7.70.0 — the
|
||||
* release (2020-04-29) that introduced --ssl-revoke-best-effort. Kept pure
|
||||
* (no I/O) so the version-comparison logic can be unit tested without
|
||||
* spawning a process. Reads the leading "curl X.Y.Z" token, ignoring the
|
||||
* trailing "libcurl/X.Y.Z" that may report a different version.
|
||||
*
|
||||
* @param {string} versionOutput raw stdout of `curl --version`
|
||||
* @returns {boolean} true when the parsed version is >= 7.70.0
|
||||
*/
|
||||
function isCurlVersionSupported(versionOutput) {
|
||||
const match = String(versionOutput).match(/^\s*curl\s+(\d+)\.(\d+)\.(\d+)/i);
|
||||
if (!match) return false;
|
||||
const major = parseInt(match[1], 10);
|
||||
const minor = parseInt(match[2], 10);
|
||||
return major > 7 || (major === 7 && minor >= 70);
|
||||
}
|
||||
|
||||
// Memoized probe result. curl's version is invariant for the lifetime of the
|
||||
// install, while download() runs once per mirror URL — so probe at most once.
|
||||
let _curlSupportsSslRevokeBestEffort;
|
||||
|
||||
/**
|
||||
* Detect whether the system curl supports --ssl-revoke-best-effort. Older
|
||||
* versions (notably the curl 7.55.1 shipped with older Windows 10 builds)
|
||||
* exit with "unknown option" if the flag is passed.
|
||||
*
|
||||
* @returns {boolean} true when curl >= 7.70.0 is available
|
||||
*/
|
||||
function curlSupportsSslRevokeBestEffort() {
|
||||
if (_curlSupportsSslRevokeBestEffort !== undefined) {
|
||||
return _curlSupportsSslRevokeBestEffort;
|
||||
}
|
||||
try {
|
||||
const output = execFileSync("curl", ["--version"], {
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
encoding: "utf8",
|
||||
timeout: 5000,
|
||||
});
|
||||
_curlSupportsSslRevokeBestEffort = isCurlVersionSupported(output);
|
||||
} catch (_) {
|
||||
_curlSupportsSslRevokeBestEffort = false;
|
||||
}
|
||||
return _curlSupportsSslRevokeBestEffort;
|
||||
}
|
||||
|
||||
function download(url, destPath) {
|
||||
assertAllowedHost(url);
|
||||
const args = [
|
||||
@@ -119,8 +165,11 @@ function download(url, destPath) {
|
||||
"--output", destPath,
|
||||
];
|
||||
// --ssl-revoke-best-effort: on Windows (Schannel), avoid CRYPT_E_REVOCATION_OFFLINE
|
||||
// errors when the certificate revocation list server is unreachable
|
||||
if (isWindows) args.unshift("--ssl-revoke-best-effort");
|
||||
// errors when the certificate revocation list server is unreachable.
|
||||
// Only use it when the system curl is new enough (>= 7.70.0).
|
||||
if (isWindows && curlSupportsSslRevokeBestEffort()) {
|
||||
args.unshift("--ssl-revoke-best-effort");
|
||||
}
|
||||
args.push(url);
|
||||
execFileSync("curl", args, { stdio: ["ignore", "ignore", "pipe"] });
|
||||
}
|
||||
@@ -294,4 +343,4 @@ if (require.main === module) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { getExpectedChecksum, verifyChecksum, assertAllowedHost, resolveMirrorUrls };
|
||||
module.exports = { getExpectedChecksum, verifyChecksum, assertAllowedHost, resolveMirrorUrls, curlSupportsSslRevokeBestEffort, isCurlVersionSupported };
|
||||
|
||||
@@ -9,7 +9,7 @@ const os = require("os");
|
||||
|
||||
const crypto = require("crypto");
|
||||
|
||||
const { getExpectedChecksum, verifyChecksum, assertAllowedHost, resolveMirrorUrls } = require("./install.js");
|
||||
const { getExpectedChecksum, verifyChecksum, assertAllowedHost, resolveMirrorUrls, isCurlVersionSupported } = require("./install.js");
|
||||
|
||||
describe("getExpectedChecksum", () => {
|
||||
function makeTmpChecksums(content) {
|
||||
@@ -278,3 +278,55 @@ describe("resolveMirrorUrls", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
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