@blamejs/core 0.6.13 → 0.6.20

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.
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+
3
+ var http = require("node:http");
4
+ var https = require("node:https");
5
+ var net = require("node:net");
6
+ var tls = require("node:tls");
7
+
8
+ var C = require("./constants");
9
+ var validateOpts = require("./validate-opts");
10
+ var lazyRequire = require("./lazy-require");
11
+ var { defineClass } = require("./framework-error");
12
+
13
+ var ProxyError = defineClass("ProxyError", { alwaysPermanent: false });
14
+
15
+ var observability = lazyRequire(function () { return require("./observability"); });
16
+
17
+ var STATE = {
18
+ http: null,
19
+ https: null,
20
+ noProxy: [],
21
+ auth: null,
22
+ agentCache: new Map(),
23
+ };
24
+
25
+ function _parseAuth(spec) {
26
+ if (!spec) return null;
27
+ var idx = spec.indexOf(":");
28
+ if (idx === -1) return null;
29
+ return { user: spec.slice(0, idx), pass: spec.slice(idx + 1) };
30
+ }
31
+
32
+ function _parseProxyUrl(value) {
33
+ if (!value) return null;
34
+ var u;
35
+ try { u = new URL(value); } catch (_e) {
36
+ throw new ProxyError("proxy/bad-url", "invalid proxy URL: " + value);
37
+ }
38
+ if (u.protocol !== "http:" && u.protocol !== "https:") {
39
+ throw new ProxyError("proxy/bad-protocol", "proxy URL must be http:// or https://, got " + u.protocol);
40
+ }
41
+ return u;
42
+ }
43
+
44
+ function _parseNoProxy(spec) {
45
+ if (!spec) return [];
46
+ return String(spec).split(",").map(function (s) { return s.trim(); }).filter(Boolean);
47
+ }
48
+
49
+ function set(opts) {
50
+ opts = opts || {};
51
+ validateOpts(opts, ["http", "https", "no", "auth"], "proxy.set");
52
+ if (opts.http !== undefined) STATE.http = opts.http ? _parseProxyUrl(opts.http) : null;
53
+ if (opts.https !== undefined) STATE.https = opts.https ? _parseProxyUrl(opts.https) : null;
54
+ if (opts.no !== undefined) STATE.noProxy = _parseNoProxy(opts.no);
55
+ if (opts.auth !== undefined) STATE.auth = opts.auth ? _parseAuth(opts.auth) : null;
56
+ STATE.agentCache.clear();
57
+ _emitObs("network.proxy.set", {
58
+ httpSet: !!STATE.http,
59
+ httpsSet: !!STATE.https,
60
+ noProxyCount: STATE.noProxy.length,
61
+ });
62
+ }
63
+
64
+ function fromEnv(envObj) {
65
+ var env = envObj || process.env;
66
+ var httpProxy = env.HTTP_PROXY || env.http_proxy;
67
+ var httpsProxy = env.HTTPS_PROXY || env.https_proxy;
68
+ var noProxy = env.NO_PROXY || env.no_proxy;
69
+ var allProxy = env.ALL_PROXY || env.all_proxy;
70
+ var authEnv = env.BLAMEJS_PROXY_AUTH;
71
+ var changed = false;
72
+ if (httpProxy) { STATE.http = _parseProxyUrl(httpProxy); changed = true; }
73
+ else if (allProxy) { STATE.http = _parseProxyUrl(allProxy); changed = true; }
74
+ if (httpsProxy) { STATE.https = _parseProxyUrl(httpsProxy); changed = true; }
75
+ else if (allProxy) { STATE.https = _parseProxyUrl(allProxy); changed = true; }
76
+ if (noProxy) { STATE.noProxy = _parseNoProxy(noProxy); changed = true; }
77
+ if (authEnv) { STATE.auth = _parseAuth(authEnv); changed = true; }
78
+ if (changed) {
79
+ STATE.agentCache.clear();
80
+ _emitObs("network.proxy.from_env", {
81
+ httpSet: !!STATE.http,
82
+ httpsSet: !!STATE.https,
83
+ noProxyCount: STATE.noProxy.length,
84
+ });
85
+ }
86
+ return changed;
87
+ }
88
+
89
+ function _ipv4ToInt(ip) {
90
+ var p = ip.split(".");
91
+ return ((p[0] | 0) << 24 >>> 0) + ((p[1] | 0) << 16) + ((p[2] | 0) << 8) + (p[3] | 0);
92
+ }
93
+
94
+ function _matchCidr(cidr, host) {
95
+ var slash = cidr.indexOf("/");
96
+ if (slash === -1) return false;
97
+ var network = cidr.slice(0, slash);
98
+ var prefix = parseInt(cidr.slice(slash + 1), 10);
99
+ if (net.isIP(host) !== 4 || net.isIP(network) !== 4) return false;
100
+ if (!isFinite(prefix) || prefix < 0 || prefix > 32) return false;
101
+ if (prefix === 0) return true;
102
+ var mask = (0xffffffff << (32 - prefix)) >>> 0;
103
+ return (_ipv4ToInt(host) & mask) === (_ipv4ToInt(network) & mask);
104
+ }
105
+
106
+ function shouldProxy(targetUrl) {
107
+ var u = targetUrl instanceof URL ? targetUrl : new URL(String(targetUrl));
108
+ var host = u.hostname;
109
+ var rules = STATE.noProxy;
110
+ for (var i = 0; i < rules.length; i++) {
111
+ var r = rules[i];
112
+ if (r === "*") return false;
113
+ if (r.indexOf("/") !== -1 && _matchCidr(r, host)) return false;
114
+ var bare = r.charAt(0) === "." ? r.slice(1) : r;
115
+ if (host === bare) return false;
116
+ if (host.length > bare.length && host.slice(host.length - bare.length - 1) === "." + bare) return false;
117
+ }
118
+ if (u.protocol === "https:") return !!STATE.https;
119
+ if (u.protocol === "http:") return !!STATE.http;
120
+ return false;
121
+ }
122
+
123
+ function _proxyForUrl(targetUrl) {
124
+ var u = targetUrl instanceof URL ? targetUrl : new URL(String(targetUrl));
125
+ if (!shouldProxy(u)) return null;
126
+ return u.protocol === "https:" ? STATE.https : STATE.http;
127
+ }
128
+
129
+ function _proxyAuthHeader(proxyUrl) {
130
+ var user = proxyUrl.username || (STATE.auth && STATE.auth.user) || "";
131
+ var pass = proxyUrl.password || (STATE.auth && STATE.auth.pass) || "";
132
+ if (!user && !pass) return null;
133
+ return "Basic " + Buffer.from(decodeURIComponent(user) + ":" + decodeURIComponent(pass), "utf8").toString("base64");
134
+ }
135
+
136
+ function _connectThroughTunnel(proxyUrl, targetHost, targetPort, callback) {
137
+ var proxyPort = proxyUrl.port || (proxyUrl.protocol === "https:" ? 443 : 80);
138
+ var proxySocket = proxyUrl.protocol === "https:"
139
+ ? tls.connect({
140
+ host: proxyUrl.hostname,
141
+ port: proxyPort,
142
+ servername: proxyUrl.hostname,
143
+ minVersion: "TLSv1.3",
144
+ ecdhCurve: C.TLS_GROUP_CURVE_STR,
145
+ })
146
+ : net.connect({ host: proxyUrl.hostname, port: proxyPort });
147
+ var settled = false;
148
+ function done(err, sock) { if (settled) return; settled = true; callback(err, sock); }
149
+ proxySocket.on("error", function (e) { done(e); });
150
+ proxySocket.on(proxyUrl.protocol === "https:" ? "secureConnect" : "connect", function () {
151
+ var lines = [
152
+ "CONNECT " + targetHost + ":" + targetPort + " HTTP/1.1",
153
+ "Host: " + targetHost + ":" + targetPort,
154
+ ];
155
+ var auth = _proxyAuthHeader(proxyUrl);
156
+ if (auth) lines.push("Proxy-Authorization: " + auth);
157
+ lines.push("", "");
158
+ proxySocket.write(lines.join("\r\n"));
159
+ var buf = Buffer.alloc(0);
160
+ function onData(chunk) {
161
+ buf = Buffer.concat([buf, chunk]);
162
+ var idx = buf.indexOf("\r\n\r\n");
163
+ if (idx === -1) return;
164
+ proxySocket.removeListener("data", onData);
165
+ var head = buf.slice(0, idx).toString("ascii");
166
+ var status = head.split("\r\n")[0] || "";
167
+ var match = /HTTP\/1\.[01] (\d{3})/.exec(status);
168
+ if (!match || parseInt(match[1], 10) !== 200) {
169
+ done(new ProxyError("proxy/connect-failed", "proxy CONNECT to " + targetHost + ":" + targetPort + " failed: " + status));
170
+ try { proxySocket.destroy(); } catch (_e) {}
171
+ return;
172
+ }
173
+ done(null, proxySocket);
174
+ }
175
+ proxySocket.on("data", onData);
176
+ });
177
+ }
178
+
179
+ function agentFor(targetUrl) {
180
+ var proxy = _proxyForUrl(targetUrl);
181
+ if (!proxy) return null;
182
+ var u = targetUrl instanceof URL ? targetUrl : new URL(String(targetUrl));
183
+ var key = proxy.toString() + "|" + u.protocol;
184
+ if (STATE.agentCache.has(key)) return STATE.agentCache.get(key);
185
+
186
+ var agent;
187
+ if (u.protocol === "https:") {
188
+ agent = new https.Agent({
189
+ keepAlive: true,
190
+ minVersion: "TLSv1.3",
191
+ ecdhCurve: C.TLS_GROUP_CURVE_STR,
192
+ });
193
+ agent.createConnection = function (options, cb) {
194
+ _connectThroughTunnel(proxy, options.host, options.port, function (err, tunnel) {
195
+ if (err) return cb(err);
196
+ var secure = tls.connect({
197
+ socket: tunnel,
198
+ servername: options.servername || options.host,
199
+ minVersion: "TLSv1.3",
200
+ ecdhCurve: C.TLS_GROUP_CURVE_STR,
201
+ ALPNProtocols: options.ALPNProtocols,
202
+ }, function () { cb(null, secure); });
203
+ secure.on("error", function (e) { cb(e); });
204
+ });
205
+ };
206
+ } else {
207
+ agent = new http.Agent({ keepAlive: true });
208
+ agent.createConnection = function (options, cb) {
209
+ _connectThroughTunnel(proxy, options.host, options.port, function (err, tunnel) {
210
+ if (err) return cb(err);
211
+ cb(null, tunnel);
212
+ });
213
+ };
214
+ }
215
+ STATE.agentCache.set(key, agent);
216
+ _emitObs("network.proxy.agent.created", { protocol: u.protocol });
217
+ return agent;
218
+ }
219
+
220
+ function snapshot() {
221
+ return {
222
+ http: STATE.http ? STATE.http.toString() : null,
223
+ https: STATE.https ? STATE.https.toString() : null,
224
+ noProxy: STATE.noProxy.slice(),
225
+ authSet: !!STATE.auth,
226
+ };
227
+ }
228
+
229
+ function _emitObs(name, fields) {
230
+ try { observability().emit(name, fields || {}); } catch (_e) {}
231
+ }
232
+
233
+ function _resetForTest() {
234
+ STATE.http = null; STATE.https = null; STATE.noProxy = []; STATE.auth = null;
235
+ STATE.agentCache.clear();
236
+ }
237
+
238
+ module.exports = {
239
+ set: set,
240
+ fromEnv: fromEnv,
241
+ shouldProxy: shouldProxy,
242
+ agentFor: agentFor,
243
+ snapshot: snapshot,
244
+ ProxyError: ProxyError,
245
+ _resetForTest: _resetForTest,
246
+ };
@@ -0,0 +1,326 @@
1
+ "use strict";
2
+
3
+ var tls = require("node:tls");
4
+ var fs = require("node:fs");
5
+ var path = require("node:path");
6
+ var nodeCrypto = require("node:crypto");
7
+
8
+ var validateOpts = require("./validate-opts");
9
+ var lazyRequire = require("./lazy-require");
10
+ var { defineClass } = require("./framework-error");
11
+
12
+ var TlsTrustError = defineClass("TlsTrustError", { alwaysPermanent: true });
13
+
14
+ var observability = lazyRequire(function () { return require("./observability"); });
15
+ var audit = lazyRequire(function () { return require("./audit"); });
16
+
17
+ var STATE = {
18
+ cas: [],
19
+ systemTrust: false,
20
+ baselineFingerprints: null,
21
+ };
22
+
23
+ function _normalizePem(pem) {
24
+ if (Buffer.isBuffer(pem)) pem = pem.toString("utf8");
25
+ if (typeof pem !== "string") {
26
+ throw new TlsTrustError("tls/bad-ca", "CA must be a PEM string or path, got " + typeof pem);
27
+ }
28
+ return pem.replace(/\r\n/g, "\n").trim();
29
+ }
30
+
31
+ function _splitPemBundle(pem) {
32
+ var blocks = [];
33
+ var lines = pem.split("\n");
34
+ var current = null;
35
+ for (var i = 0; i < lines.length; i++) {
36
+ var line = lines[i];
37
+ if (line.indexOf("-----BEGIN CERTIFICATE-----") === 0) {
38
+ current = [line];
39
+ } else if (current) {
40
+ current.push(line);
41
+ if (line.indexOf("-----END CERTIFICATE-----") === 0) {
42
+ blocks.push(current.join("\n"));
43
+ current = null;
44
+ }
45
+ }
46
+ }
47
+ return blocks;
48
+ }
49
+
50
+ function _certMetadata(pem) {
51
+ try {
52
+ var x = new nodeCrypto.X509Certificate(pem);
53
+ return {
54
+ subject: x.subject,
55
+ issuer: x.issuer,
56
+ validFrom: x.validFrom,
57
+ validTo: x.validTo,
58
+ fingerprint256: x.fingerprint256,
59
+ serialNumber: x.serialNumber,
60
+ isSelfSigned: x.subject === x.issuer,
61
+ };
62
+ } catch (e) {
63
+ throw new TlsTrustError("tls/bad-ca-pem", "CA PEM not parseable: " + e.message);
64
+ }
65
+ }
66
+
67
+ function _isPathLike(s) {
68
+ if (s.indexOf("-----BEGIN") !== -1) return false;
69
+ if (s.length > 1024) return false;
70
+ if (/[\r\n]/.test(s)) return false;
71
+ return true;
72
+ }
73
+
74
+ function _readPath(p) {
75
+ var stat = fs.statSync(p);
76
+ if (stat.isDirectory()) {
77
+ var files = fs.readdirSync(p)
78
+ .filter(function (f) { return /\.(pem|crt|cer)$/i.test(f); })
79
+ .sort();
80
+ return files.map(function (f) { return fs.readFileSync(path.join(p, f), "utf8"); }).join("\n");
81
+ }
82
+ return fs.readFileSync(p, "utf8");
83
+ }
84
+
85
+ function addCa(pemOrPath, opts) {
86
+ opts = opts || {};
87
+ validateOpts(opts, ["label", "audit"], "tls.addCa");
88
+ var raw = pemOrPath;
89
+ if (typeof pemOrPath === "string" && _isPathLike(pemOrPath)) {
90
+ var stat;
91
+ try { stat = fs.statSync(pemOrPath); } catch (_e) {
92
+ throw new TlsTrustError("tls/empty-pem", "tls.addCa: input has no PEM marker and is not a readable path: " +
93
+ pemOrPath);
94
+ }
95
+ raw = _readPath(pemOrPath);
96
+ if (!stat) raw = "";
97
+ }
98
+ raw = _normalizePem(raw);
99
+ var blocks = _splitPemBundle(raw);
100
+ if (blocks.length === 0) {
101
+ throw new TlsTrustError("tls/empty-pem", "no CERTIFICATE blocks found in PEM input");
102
+ }
103
+ var added = [];
104
+ for (var i = 0; i < blocks.length; i++) {
105
+ var meta = _certMetadata(blocks[i]);
106
+ STATE.cas.push({ pem: blocks[i], meta: meta, label: opts.label || null, addedAt: Date.now() });
107
+ added.push(meta);
108
+ }
109
+ _emitAuditAdd(added, opts);
110
+ _emitObs("network.tls.ca.added", { count: added.length });
111
+ return added;
112
+ }
113
+
114
+ function addCaBundle(p, opts) {
115
+ return addCa(p, opts);
116
+ }
117
+
118
+ function useSystemTrust(enable) {
119
+ STATE.systemTrust = enable !== false;
120
+ _emitObs("network.tls.system_trust.set", { enabled: STATE.systemTrust });
121
+ }
122
+
123
+ function isSystemTrustEnabled() { return !!STATE.systemTrust; }
124
+
125
+ function getTrustStore() {
126
+ return STATE.cas.map(function (entry) {
127
+ return {
128
+ label: entry.label,
129
+ addedAt: entry.addedAt,
130
+ subject: entry.meta.subject,
131
+ issuer: entry.meta.issuer,
132
+ validFrom: entry.meta.validFrom,
133
+ validTo: entry.meta.validTo,
134
+ fingerprint256: entry.meta.fingerprint256,
135
+ serialNumber: entry.meta.serialNumber,
136
+ isSelfSigned: entry.meta.isSelfSigned,
137
+ };
138
+ });
139
+ }
140
+
141
+ function _emitAuditRemove(metaList, reason) {
142
+ var sink;
143
+ try { sink = audit(); } catch (_e) { sink = null; }
144
+ if (!sink || typeof sink.safeEmit !== "function") return;
145
+ for (var i = 0; i < metaList.length; i++) {
146
+ var m = metaList[i];
147
+ try {
148
+ sink.safeEmit({
149
+ action: "network.tls.ca.removed",
150
+ outcome: "success",
151
+ metadata: {
152
+ subject: m.subject,
153
+ issuer: m.issuer,
154
+ fingerprint256: m.fingerprint256,
155
+ validFrom: m.validFrom,
156
+ validTo: m.validTo,
157
+ isSelfSigned: m.isSelfSigned,
158
+ label: m.label,
159
+ reason: reason || "operator",
160
+ },
161
+ });
162
+ } catch (_e) {}
163
+ }
164
+ }
165
+
166
+ function removeCa(fingerprint256, opts) {
167
+ if (typeof fingerprint256 !== "string" || fingerprint256.length === 0) {
168
+ throw new TlsTrustError("tls/bad-fingerprint", "tls.removeCa: fingerprint256 must be a non-empty string");
169
+ }
170
+ var fp = fingerprint256.toUpperCase();
171
+ var removed = [];
172
+ STATE.cas = STATE.cas.filter(function (entry) {
173
+ var entryFp = (entry.meta.fingerprint256 || "").toUpperCase();
174
+ if (entryFp === fp) {
175
+ removed.push(Object.assign({ label: entry.label }, entry.meta));
176
+ return false;
177
+ }
178
+ return true;
179
+ });
180
+ if (removed.length === 0) return 0;
181
+ if (!opts || opts.audit !== false) _emitAuditRemove(removed, "operator-remove");
182
+ _emitObs("network.tls.ca.removed", { count: removed.length, reason: "operator" });
183
+ return removed.length;
184
+ }
185
+
186
+ function removeCaByLabel(label, opts) {
187
+ if (typeof label !== "string" || label.length === 0) {
188
+ throw new TlsTrustError("tls/bad-label", "tls.removeCaByLabel: label must be a non-empty string");
189
+ }
190
+ var removed = [];
191
+ STATE.cas = STATE.cas.filter(function (entry) {
192
+ if (entry.label === label) {
193
+ removed.push(Object.assign({ label: entry.label }, entry.meta));
194
+ return false;
195
+ }
196
+ return true;
197
+ });
198
+ if (removed.length === 0) return 0;
199
+ if (!opts || opts.audit !== false) _emitAuditRemove(removed, "operator-remove-by-label");
200
+ _emitObs("network.tls.ca.removed", { count: removed.length, reason: "label" });
201
+ return removed.length;
202
+ }
203
+
204
+ function clearAll(opts) {
205
+ if (STATE.cas.length === 0) return 0;
206
+ var removed = STATE.cas.map(function (e) { return Object.assign({ label: e.label }, e.meta); });
207
+ STATE.cas = [];
208
+ if (!opts || opts.audit !== false) _emitAuditRemove(removed, "operator-clear-all");
209
+ _emitObs("network.tls.ca.cleared", { count: removed.length });
210
+ return removed.length;
211
+ }
212
+
213
+ function purgeExpired(opts) {
214
+ var nowMs = Date.now();
215
+ var removed = [];
216
+ STATE.cas = STATE.cas.filter(function (entry) {
217
+ var validToMs = entry.meta.validTo ? Date.parse(entry.meta.validTo) : NaN;
218
+ if (isFinite(validToMs) && validToMs < nowMs) {
219
+ removed.push(Object.assign({ label: entry.label }, entry.meta));
220
+ return false;
221
+ }
222
+ return true;
223
+ });
224
+ if (removed.length === 0) return 0;
225
+ if (!opts || opts.audit !== false) _emitAuditRemove(removed, "expired");
226
+ _emitObs("network.tls.ca.purged_expired", { count: removed.length });
227
+ return removed.length;
228
+ }
229
+
230
+ function expiringSoon(windowMs) {
231
+ if (typeof windowMs !== "number" || !isFinite(windowMs) || windowMs < 0) {
232
+ throw new TlsTrustError("tls/bad-window", "tls.expiringSoon: windowMs must be a non-negative finite number");
233
+ }
234
+ var threshold = Date.now() + windowMs;
235
+ return STATE.cas.filter(function (entry) {
236
+ var validToMs = entry.meta.validTo ? Date.parse(entry.meta.validTo) : NaN;
237
+ return isFinite(validToMs) && validToMs <= threshold;
238
+ }).map(function (entry) {
239
+ return Object.assign({ label: entry.label }, entry.meta);
240
+ });
241
+ }
242
+
243
+ function captureBaselineFingerprints() {
244
+ STATE.baselineFingerprints = STATE.cas.map(function (e) { return e.meta.fingerprint256; });
245
+ }
246
+
247
+ function detectBaselineDrift() {
248
+ if (!STATE.baselineFingerprints) return null;
249
+ var current = STATE.cas.map(function (e) { return e.meta.fingerprint256; });
250
+ var added = current.filter(function (fp) { return STATE.baselineFingerprints.indexOf(fp) === -1; });
251
+ var removed = STATE.baselineFingerprints.filter(function (fp) { return current.indexOf(fp) === -1; });
252
+ return { added: added, removed: removed, drifted: added.length > 0 || removed.length > 0 };
253
+ }
254
+
255
+ function applyToContext(opts) {
256
+ opts = opts || {};
257
+ validateOpts(opts, ["base"], "tls.applyToContext");
258
+ var base = Object.assign({}, opts.base || {});
259
+ var caStrings = STATE.cas.map(function (e) { return e.pem; });
260
+ if (STATE.systemTrust) {
261
+ var rootCAs = tls.rootCertificates;
262
+ if (Array.isArray(rootCAs)) {
263
+ caStrings = caStrings.concat(rootCAs);
264
+ }
265
+ }
266
+ if (caStrings.length > 0) base.ca = caStrings;
267
+ return base;
268
+ }
269
+
270
+ function getCaPems() {
271
+ return STATE.cas.map(function (e) { return e.pem; });
272
+ }
273
+
274
+ function _emitAuditAdd(metaList, opts) {
275
+ if (opts.audit === false) return;
276
+ var sink;
277
+ try { sink = audit(); } catch (_e) { sink = null; }
278
+ if (!sink || typeof sink.safeEmit !== "function") return;
279
+ for (var i = 0; i < metaList.length; i++) {
280
+ var m = metaList[i];
281
+ try {
282
+ sink.safeEmit({
283
+ action: "network.tls.ca.added",
284
+ outcome: "success",
285
+ metadata: {
286
+ subject: m.subject,
287
+ issuer: m.issuer,
288
+ fingerprint256: m.fingerprint256,
289
+ validFrom: m.validFrom,
290
+ validTo: m.validTo,
291
+ isSelfSigned: m.isSelfSigned,
292
+ label: opts.label || null,
293
+ },
294
+ });
295
+ } catch (_e) {}
296
+ }
297
+ }
298
+
299
+ function _emitObs(name, fields) {
300
+ try { observability().emit(name, fields || {}); } catch (_e) {}
301
+ }
302
+
303
+ function _resetForTest() {
304
+ STATE.cas = [];
305
+ STATE.systemTrust = false;
306
+ STATE.baselineFingerprints = null;
307
+ }
308
+
309
+ module.exports = {
310
+ addCa: addCa,
311
+ addCaBundle: addCaBundle,
312
+ removeCa: removeCa,
313
+ removeCaByLabel: removeCaByLabel,
314
+ clearAll: clearAll,
315
+ purgeExpired: purgeExpired,
316
+ expiringSoon: expiringSoon,
317
+ useSystemTrust: useSystemTrust,
318
+ isSystemTrustEnabled: isSystemTrustEnabled,
319
+ getTrustStore: getTrustStore,
320
+ captureBaselineFingerprints: captureBaselineFingerprints,
321
+ detectBaselineDrift: detectBaselineDrift,
322
+ applyToContext: applyToContext,
323
+ getCaPems: getCaPems,
324
+ TlsTrustError: TlsTrustError,
325
+ _resetForTest: _resetForTest,
326
+ };