@driggsby/deploy 0.9.3 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/cli.js +2 -0
- package/dist/distrusted-roots.d.ts +16 -0
- package/dist/distrusted-roots.js +83 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -62,6 +62,16 @@ deploy limits locally.
|
|
|
62
62
|
uploads only content Driggsby doesn't already have, and finalizes the
|
|
63
63
|
version.
|
|
64
64
|
|
|
65
|
+
Call `distrustCompromisedRoots()` once at startup, before any request. Some
|
|
66
|
+
Node.js releases still trust the roots behind the eMudhra certificate
|
|
67
|
+
compromise of October 2026. On official Node.js builds, 22.19.0 and later
|
|
68
|
+
22.x or 24.5.0 and later, it removes them from your process's default CA
|
|
69
|
+
list. Older releases can't change that list. Builds or settings that verify
|
|
70
|
+
against the system's OpenSSL store (such as Homebrew's builds, or
|
|
71
|
+
`--use-openssl-ca`) rely on that store's own update. In those cases, update
|
|
72
|
+
Node.js or your system's CA certificates to a current release. The
|
|
73
|
+
`driggsby` and `driggsby-deploy` commands already call it.
|
|
74
|
+
|
|
65
75
|
Zero runtime dependencies; Node.js 18 or newer; macOS, Linux, and Windows.
|
|
66
76
|
|
|
67
77
|
## License
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
// entrypoint.ts so tests exercise runDeployEntrypoint without triggering a
|
|
3
3
|
// real deploy on import.
|
|
4
4
|
import process from "node:process";
|
|
5
|
+
import { distrustCompromisedRoots } from "./distrusted-roots.js";
|
|
5
6
|
import { runDeployEntrypoint } from "./entrypoint.js";
|
|
7
|
+
distrustCompromisedRoots();
|
|
6
8
|
process.exitCode = await runDeployEntrypoint({
|
|
7
9
|
argv: process.argv.slice(2),
|
|
8
10
|
cwd: process.cwd(),
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare function isDistrustedRoot(pem: string): boolean;
|
|
2
|
+
export interface CaListApi {
|
|
3
|
+
getCACertificates?: (type: "default" | "bundled" | "extra") => string[];
|
|
4
|
+
setDefaultCACertificates?: (certs: string[]) => void;
|
|
5
|
+
}
|
|
6
|
+
export interface BuildConfig {
|
|
7
|
+
variables: {
|
|
8
|
+
node_shared_openssl?: unknown;
|
|
9
|
+
};
|
|
10
|
+
target_defaults: {
|
|
11
|
+
defines?: unknown;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export declare function mayUseOpenSslStore(config?: BuildConfig): boolean;
|
|
15
|
+
export declare function removeDistrustedRoots(api: CaListApi, opensslStore: () => boolean): void;
|
|
16
|
+
export declare function distrustCompromisedRoots(): void;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Roots over the eMudhra intermediate CAs that turned out, in October 2026,
|
|
2
|
+
// to have trivially weak private keys. The intermediates chain to emSign ECC
|
|
3
|
+
// Root CA - G3, and through Sectigo cross-signs to AAA Certificate Services,
|
|
4
|
+
// so anyone can issue a certificate for any host under either root. Current
|
|
5
|
+
// Node.js releases trust neither; older ones trust one or both. Both bins
|
|
6
|
+
// remove them from the process's default CA list before making any request,
|
|
7
|
+
// where Node.js lets them.
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
|
+
import process from "node:process";
|
|
10
|
+
import tls from "node:tls";
|
|
11
|
+
// SHA-256 fingerprints of the two roots' DER encodings.
|
|
12
|
+
const DISTRUSTED_ROOT_FINGERPRINTS = new Set([
|
|
13
|
+
// emSign ECC Root CA - G3
|
|
14
|
+
"86a1ecba089c4a8d3bbe2734c612ba341d813e043cf9e8a862cd5c57a36bbe6b",
|
|
15
|
+
// AAA Certificate Services
|
|
16
|
+
"d7a7a0fb5d7e2731d771e9484ebcdef71d5f0c3e0a2948782bc83ee0ea699ef4",
|
|
17
|
+
]);
|
|
18
|
+
const PEM_BODY = /-----BEGIN CERTIFICATE-----([\s\S]*?)-----END CERTIFICATE-----/;
|
|
19
|
+
// The SHA-256 of a PEM certificate's DER. Node.js re-serializes the list
|
|
20
|
+
// once it has been set, so certificates compare by content, not by text.
|
|
21
|
+
function fingerprint(pem) {
|
|
22
|
+
const body = PEM_BODY.exec(pem)?.[1];
|
|
23
|
+
if (body === undefined) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const der = Buffer.from(body.replace(/\s+/g, ""), "base64");
|
|
27
|
+
return createHash("sha256").update(der).digest("hex");
|
|
28
|
+
}
|
|
29
|
+
export function isDistrustedRoot(pem) {
|
|
30
|
+
const print = fingerprint(pem);
|
|
31
|
+
return print !== undefined && DISTRUSTED_ROOT_FINGERPRINTS.has(print);
|
|
32
|
+
}
|
|
33
|
+
// Builds made to verify against OpenSSL's own store (the build flag that
|
|
34
|
+
// defines NODE_OPENSSL_CERT_STORE, as Homebrew's and many Linux
|
|
35
|
+
// distributions' builds do) still report Node.js's bundled list, so
|
|
36
|
+
// replacing that list would swap their real store, company CAs included,
|
|
37
|
+
// for the bundled roots; their fix comes from that store's own update.
|
|
38
|
+
// Builds linked against a shared OpenSSL are skipped too, to stay on the
|
|
39
|
+
// safe side, as is any runtime whose build configuration doesn't say.
|
|
40
|
+
// Exported for tests.
|
|
41
|
+
export function mayUseOpenSslStore(config = process.config) {
|
|
42
|
+
const defines = config.target_defaults.defines;
|
|
43
|
+
return (config.variables.node_shared_openssl !== false ||
|
|
44
|
+
!Array.isArray(defines) ||
|
|
45
|
+
defines.includes("NODE_OPENSSL_CERT_STORE"));
|
|
46
|
+
}
|
|
47
|
+
// Removes the distrusted roots from the default CA list, where that list is
|
|
48
|
+
// the one Node.js verifies against: it holds every bundled root, and is at
|
|
49
|
+
// least as long as the bundled roots plus the extra CAs, as it is unless
|
|
50
|
+
// --use-openssl-ca is set. It never sets an empty list, and it does nothing
|
|
51
|
+
// where Node.js can't change the list or the list doesn't hold the roots.
|
|
52
|
+
// Exported for tests; callers use distrustCompromisedRoots.
|
|
53
|
+
export function removeDistrustedRoots(api, opensslStore) {
|
|
54
|
+
if (api.getCACertificates === undefined || api.setDefaultCACertificates === undefined) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
if (opensslStore()) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const roots = api.getCACertificates("default");
|
|
62
|
+
const bundled = api.getCACertificates("bundled");
|
|
63
|
+
const listed = new Set(roots.map(fingerprint));
|
|
64
|
+
const holdsBundled = bundled.every((pem) => listed.has(fingerprint(pem)));
|
|
65
|
+
if (!holdsBundled || roots.length < bundled.length + api.getCACertificates("extra").length) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const kept = roots.filter((pem) => !isDistrustedRoot(pem));
|
|
69
|
+
if (kept.length !== roots.length && kept.length > 0) {
|
|
70
|
+
api.setDefaultCACertificates(kept);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Hardening only: if Node.js can't read or replace its list, the CLI
|
|
75
|
+
// carries on with the list Node.js already uses.
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Silently removes the distrusted roots from this process's default CA list,
|
|
79
|
+
// where Node.js lets it. Call it once at startup, before the first HTTPS
|
|
80
|
+
// request: the list applies to TLS connections made after it is set.
|
|
81
|
+
export function distrustCompromisedRoots() {
|
|
82
|
+
removeDistrustedRoots(tls, () => mayUseOpenSslStore());
|
|
83
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { DeployApiError, DeployError } from "./errors.ts";
|
|
2
|
+
export { distrustCompromisedRoots } from "./distrusted-roots.ts";
|
|
2
3
|
export { PUBLIC_BASE_URL, consoleUrlOnOrigin, isLoopbackBaseUrl, resolveBaseUrl } from "./base-url.ts";
|
|
3
4
|
export { MAX_FILE_BYTES, MAX_FILE_COUNT, MAX_PATH_LENGTH, MAX_TOTAL_BYTES, slugProblem, } from "./limits.ts";
|
|
4
5
|
export { type ProjectConfig, readProjectConfig, writeAssignedSlug } from "./project-config.ts";
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
// The driggsby CLI's deploy/rollback/versions commands build on these
|
|
3
3
|
// exports; Node-only sandboxes can use them directly.
|
|
4
4
|
export { DeployApiError, DeployError } from "./errors.js";
|
|
5
|
+
// Drops the roots over the compromised eMudhra intermediates from the
|
|
6
|
+
// process's default CA list. Both bins call it before any request.
|
|
7
|
+
export { distrustCompromisedRoots } from "./distrusted-roots.js";
|
|
5
8
|
// The base-URL policy: which origin the token travels to, and what
|
|
6
9
|
// DRIGGSBY_BASE_URL may override it with. One implementation for both this
|
|
7
10
|
// package's bin and the driggsby CLI.
|