@percy/client 1.32.12-beta.2 → 1.32.12-beta.4
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/dist/trust.js +82 -0
- package/dist/utils.js +6 -1
- package/package.json +5 -5
package/dist/trust.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import tls from 'tls';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
import logger from '@percy/logger';
|
|
5
|
+
|
|
6
|
+
// SHA-256 hashes of the SubjectPublicKeyInfo of root certificates that Node still bundles but that
|
|
7
|
+
// must no longer be trusted. Both chain to eMudhra intermediates whose private keys are publicly
|
|
8
|
+
// known (https://emudhra.we-have-a-problem.com): emSign directly, and AAA Certificate Services via
|
|
9
|
+
// a Sectigo cross-sign. Browsers and operating systems already distrust them.
|
|
10
|
+
export const DISTRUSTED_ROOTS = new Map([['8d417db2dd8bf5e3084d1e3f196d583849d81bdd4c00c70b9d39369e96b8c782', 'emSign ECC Root CA - G3'], ['bd153ed7b0434f6886b17bce8bbe84ed340c7132d702a8f4fa318f756ecbd6f3', 'AAA Certificate Services']]);
|
|
11
|
+
|
|
12
|
+
// Returns true when a PEM certificate is not one of the distrusted roots. Like Node, certificates
|
|
13
|
+
// that cannot be parsed are not trusted.
|
|
14
|
+
export function isTrustedCertificate(pem) {
|
|
15
|
+
try {
|
|
16
|
+
let spki = crypto.createPublicKey(pem).export({
|
|
17
|
+
type: 'spki',
|
|
18
|
+
format: 'der'
|
|
19
|
+
});
|
|
20
|
+
return !DISTRUSTED_ROOTS.has(crypto.createHash('sha256').update(spki).digest('hex'));
|
|
21
|
+
} catch (error) {
|
|
22
|
+
logger('client:trust').debug(`Skipping a CA certificate that cannot be parsed: ${error.message}`);
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Returns the CA certificates Node trusts by default, without the distrusted roots. Older versions
|
|
28
|
+
// of Node cannot report their defaults, so use the bundled roots plus NODE_EXTRA_CA_CERTS, which
|
|
29
|
+
// Node only adds to its default store and would otherwise be lost when providing an explicit list.
|
|
30
|
+
export function trustedCertificates() {
|
|
31
|
+
if (tls.getCACertificates) {
|
|
32
|
+
return tls.getCACertificates('default').filter(isTrustedCertificate);
|
|
33
|
+
}
|
|
34
|
+
let extra = [];
|
|
35
|
+
try {
|
|
36
|
+
extra = fs.readFileSync(process.env.NODE_EXTRA_CA_CERTS, 'utf-8').match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g) ?? [];
|
|
37
|
+
} catch {}
|
|
38
|
+
return tls.rootCertificates.concat(extra).filter(isTrustedCertificate);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Returns true when Node verifies against the OpenSSL store instead of its bundled roots. Distro
|
|
42
|
+
// builds (e.g. Debian and Ubuntu) default to the OpenSSL store, and either default can be switched
|
|
43
|
+
// with --use-openssl-ca or --use-bundled-ca, where the last option wins and command-line options
|
|
44
|
+
// take precedence over NODE_OPTIONS. Unlike these, --use-system-ca adds to the bundled roots.
|
|
45
|
+
export function usesOpenSSLStore(variables = process.config.variables) {
|
|
46
|
+
let openssl = !!variables.node_use_openssl_ca;
|
|
47
|
+
let options = [...(process.env.NODE_OPTIONS ?? '').split(/\s+/), ...process.execArgv];
|
|
48
|
+
for (let option of options) {
|
|
49
|
+
// Node accepts both dashes and underscores in option names
|
|
50
|
+
option = option.replace(/_/g, '-');
|
|
51
|
+
if (option === '--use-openssl-ca') openssl = true;
|
|
52
|
+
if (option === '--use-bundled-ca') openssl = false;
|
|
53
|
+
}
|
|
54
|
+
return openssl;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Removes the distrusted roots from the CA certificates used by every TLS connection in this
|
|
58
|
+
// process that does not provide its own `ca` option (http, https, proxies, fetch).
|
|
59
|
+
export function dropDistrustedRoots() {
|
|
60
|
+
// the OpenSSL store is managed by the OS, and OS vendors have removed the distrusted roots
|
|
61
|
+
if (usesOpenSSLStore()) return;
|
|
62
|
+
|
|
63
|
+
// newer versions of Node can replace the default CA certificates directly
|
|
64
|
+
if (tls.setDefaultCACertificates && tls.getCACertificates) {
|
|
65
|
+
let current = tls.getCACertificates('default');
|
|
66
|
+
let trusted = current.filter(isTrustedCertificate);
|
|
67
|
+
if (trusted.length !== current.length) tls.setDefaultCACertificates(trusted);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (tls.createSecureContext.__percyTrusted) return;
|
|
71
|
+
let ca = trustedCertificates();
|
|
72
|
+
let {
|
|
73
|
+
createSecureContext
|
|
74
|
+
} = tls;
|
|
75
|
+
tls.createSecureContext = function (options) {
|
|
76
|
+
return createSecureContext(options !== null && options !== void 0 && options.ca ? options : {
|
|
77
|
+
...options,
|
|
78
|
+
ca
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
tls.createSecureContext.__percyTrusted = true;
|
|
82
|
+
}
|
package/dist/utils.js
CHANGED
|
@@ -5,6 +5,10 @@ import path from 'path';
|
|
|
5
5
|
import crypto from 'crypto';
|
|
6
6
|
import logger from '@percy/logger';
|
|
7
7
|
import { snakecase } from '@percy/config/utils';
|
|
8
|
+
import { dropDistrustedRoots } from './trust.js';
|
|
9
|
+
|
|
10
|
+
// every network path in the CLI imports these utils, so distrust compromised roots process-wide
|
|
11
|
+
dropDistrustedRoots();
|
|
8
12
|
|
|
9
13
|
// Formats a raw byte integer as a string
|
|
10
14
|
export function formatBytes(int) {
|
|
@@ -305,4 +309,5 @@ export function normalizeBrowsers(browserValues) {
|
|
|
305
309
|
if (!browserValues) return null;
|
|
306
310
|
return browserValues.map(browser => snakecase(browser));
|
|
307
311
|
}
|
|
308
|
-
export { hostnameMatches, getProxy, ProxyHttpAgent, ProxyHttpsAgent, proxyAgentFor } from './proxy.js';
|
|
312
|
+
export { hostnameMatches, getProxy, ProxyHttpAgent, ProxyHttpsAgent, proxyAgentFor } from './proxy.js';
|
|
313
|
+
export { DISTRUSTED_ROOTS, isTrustedCertificate, trustedCertificates, usesOpenSSLStore, dropDistrustedRoots } from './trust.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@percy/client",
|
|
3
|
-
"version": "1.32.12-beta.
|
|
3
|
+
"version": "1.32.12-beta.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
"test:coverage": "yarn test --coverage"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@percy/config": "1.32.12-beta.
|
|
37
|
-
"@percy/env": "1.32.12-beta.
|
|
38
|
-
"@percy/logger": "1.32.12-beta.
|
|
36
|
+
"@percy/config": "1.32.12-beta.4",
|
|
37
|
+
"@percy/env": "1.32.12-beta.4",
|
|
38
|
+
"@percy/logger": "1.32.12-beta.4",
|
|
39
39
|
"pac-proxy-agent": "^7.0.2",
|
|
40
40
|
"pako": "^2.1.0"
|
|
41
41
|
},
|
|
42
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "916cdcd8854848b48c3d658d9d01779176feb2d0"
|
|
43
43
|
}
|