@naylence/advanced-security 0.3.14 → 0.3.15
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/browser/index.cjs +1961 -1952
- package/dist/browser/index.mjs +1962 -1953
- package/dist/cjs/browser.js +11 -0
- package/dist/cjs/browser.js.map +1 -1
- package/dist/cjs/node.js +27 -0
- package/dist/cjs/node.js.map +1 -1
- package/dist/cjs/version.js +2 -2
- package/dist/esm/browser.js +11 -0
- package/dist/esm/browser.js.map +1 -1
- package/dist/esm/node.js +27 -0
- package/dist/esm/node.js.map +1 -1
- package/dist/esm/version.js +2 -2
- package/dist/node/index.cjs +2 -2
- package/dist/node/index.mjs +2 -2
- package/dist/node/node.cjs +2009 -1983
- package/dist/node/node.mjs +2011 -1985
- package/dist/types/browser.d.ts.map +1 -1
- package/dist/types/node.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +2 -2
package/dist/node/node.cjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var factory = require('@naylence/factory');
|
|
4
|
+
var runtime = require('@naylence/runtime');
|
|
3
5
|
var asn1Schema = require('@peculiar/asn1-schema');
|
|
4
6
|
var asn1X509 = require('@peculiar/asn1-x509');
|
|
5
7
|
var sha2_js = require('@noble/hashes/sha2.js');
|
|
6
8
|
var ed25519 = require('@noble/ed25519');
|
|
7
|
-
var runtime = require('@naylence/runtime');
|
|
8
9
|
var asn1Csr = require('@peculiar/asn1-csr');
|
|
9
10
|
var x509 = require('@peculiar/x509');
|
|
10
11
|
var core = require('@naylence/core');
|
|
@@ -13,601 +14,1128 @@ var ed25519_js = require('@noble/curves/ed25519.js');
|
|
|
13
14
|
var hkdf_js = require('@noble/hashes/hkdf.js');
|
|
14
15
|
var utils_js = require('@noble/hashes/utils.js');
|
|
15
16
|
var jose = require('jose');
|
|
16
|
-
var factory = require('@naylence/factory');
|
|
17
17
|
var sha256_js = require('@noble/hashes/sha256.js');
|
|
18
18
|
|
|
19
|
-
// This file is auto-generated during build - do not edit manually
|
|
20
|
-
// Generated from package.json version: 0.3.14
|
|
21
19
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* AUTO-GENERATED FILE. DO NOT EDIT DIRECTLY.
|
|
21
|
+
* Generated by scripts/generate-factory-manifest.mjs
|
|
22
|
+
*
|
|
23
|
+
* Provides the list of advanced security factory modules for registration.
|
|
24
24
|
*/
|
|
25
|
-
const
|
|
25
|
+
const MODULES = [
|
|
26
|
+
"./security/cert/default-ca-service-factory.js",
|
|
27
|
+
"./security/cert/default-certificate-manager-factory.js",
|
|
28
|
+
"./security/cert/trust-store/browser-trust-store-provider-factory.js",
|
|
29
|
+
"./security/cert/trust-store/node-trust-store-provider-factory.js",
|
|
30
|
+
"./security/encryption/channel/channel-encryption-manager-factory.js",
|
|
31
|
+
"./security/encryption/composite-encryption-manager-factory.js",
|
|
32
|
+
"./security/encryption/default-secure-channel-manager-factory.js",
|
|
33
|
+
"./security/encryption/sealed/x25519-encryption-manager-factory.js",
|
|
34
|
+
"./security/keys/x5c-key-manager-factory.js",
|
|
35
|
+
"./security/signing/eddsa-envelope-signer-factory.js",
|
|
36
|
+
"./security/signing/eddsa-envelope-verifier-factory.js",
|
|
37
|
+
"./stickiness/aft-load-balancer-stickiness-manager-factory.js",
|
|
38
|
+
"./stickiness/aft-replica-stickiness-manager-factory.js",
|
|
39
|
+
"./welcome/advanced-welcome-service-factory.js"
|
|
40
|
+
];
|
|
41
|
+
const MODULE_LOADERS = {
|
|
42
|
+
"./security/cert/default-ca-service-factory.js": () => Promise.resolve().then(function () { return defaultCaServiceFactory; }),
|
|
43
|
+
"./security/cert/default-certificate-manager-factory.js": () => Promise.resolve().then(function () { return defaultCertificateManagerFactory; }),
|
|
44
|
+
"./security/cert/trust-store/browser-trust-store-provider-factory.js": () => Promise.resolve().then(function () { return browserTrustStoreProviderFactory; }),
|
|
45
|
+
"./security/cert/trust-store/node-trust-store-provider-factory.js": () => Promise.resolve().then(function () { return nodeTrustStoreProviderFactory; }),
|
|
46
|
+
"./security/encryption/channel/channel-encryption-manager-factory.js": () => Promise.resolve().then(function () { return channelEncryptionManagerFactory; }),
|
|
47
|
+
"./security/encryption/composite-encryption-manager-factory.js": () => Promise.resolve().then(function () { return compositeEncryptionManagerFactory; }),
|
|
48
|
+
"./security/encryption/default-secure-channel-manager-factory.js": () => Promise.resolve().then(function () { return defaultSecureChannelManagerFactory; }),
|
|
49
|
+
"./security/encryption/sealed/x25519-encryption-manager-factory.js": () => Promise.resolve().then(function () { return x25519EncryptionManagerFactory; }),
|
|
50
|
+
"./security/keys/x5c-key-manager-factory.js": () => Promise.resolve().then(function () { return x5cKeyManagerFactory; }),
|
|
51
|
+
"./security/signing/eddsa-envelope-signer-factory.js": () => Promise.resolve().then(function () { return eddsaEnvelopeSignerFactory; }),
|
|
52
|
+
"./security/signing/eddsa-envelope-verifier-factory.js": () => Promise.resolve().then(function () { return eddsaEnvelopeVerifierFactory; }),
|
|
53
|
+
"./stickiness/aft-load-balancer-stickiness-manager-factory.js": () => Promise.resolve().then(function () { return aftLoadBalancerStickinessManagerFactory; }),
|
|
54
|
+
"./stickiness/aft-replica-stickiness-manager-factory.js": () => Promise.resolve().then(function () { return aftReplicaStickinessManagerFactory; }),
|
|
55
|
+
"./welcome/advanced-welcome-service-factory.js": () => Promise.resolve().then(function () { return advancedWelcomeServiceFactory; }),
|
|
56
|
+
};
|
|
26
57
|
|
|
27
|
-
const logger$h = runtime.getLogger("naylence.fame.security.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
58
|
+
const logger$h = runtime.getLogger("naylence.fame.security.encryption.encryption_manager_registry");
|
|
59
|
+
class EncryptionManagerFactoryRegistry {
|
|
60
|
+
constructor(autoDiscover = true) {
|
|
61
|
+
this.factories = [];
|
|
62
|
+
this.algorithmToFactory = new Map();
|
|
63
|
+
this.typeToFactories = new Map();
|
|
64
|
+
this.factorySet = new Set();
|
|
65
|
+
this.autoDiscoveredFactories = new Set();
|
|
66
|
+
this.autoDiscovered = false;
|
|
67
|
+
if (autoDiscover) {
|
|
68
|
+
this.autoDiscoverFactories();
|
|
69
|
+
}
|
|
35
70
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
71
|
+
autoDiscoverFactories() {
|
|
72
|
+
if (this.autoDiscovered) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const extensionInfos = factory.ExtensionManager.getExtensionsByType(runtime.ENCRYPTION_MANAGER_FACTORY_BASE_TYPE);
|
|
77
|
+
let registeredCount = 0;
|
|
78
|
+
for (const [factoryName, info] of extensionInfos) {
|
|
79
|
+
if (factoryName === "CompositeEncryptionManager") {
|
|
80
|
+
logger$h.debug("skipping_composite_factory_to_avoid_circular_dependency", {
|
|
81
|
+
factory_name: factoryName,
|
|
82
|
+
});
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const factoryInstance = (info.instance ??
|
|
87
|
+
factory.ExtensionManager.getGlobalFactory(runtime.ENCRYPTION_MANAGER_FACTORY_BASE_TYPE, factoryName));
|
|
88
|
+
this.registerFactory(factoryInstance, { autoDiscovered: true });
|
|
89
|
+
registeredCount += 1;
|
|
90
|
+
logger$h.debug("auto_discovered_factory", {
|
|
91
|
+
factory_name: factoryName,
|
|
92
|
+
factory_class: factoryInstance.constructor.name,
|
|
93
|
+
algorithms: factoryInstance.getSupportedAlgorithms(),
|
|
94
|
+
encryption_type: factoryInstance.getEncryptionType(),
|
|
95
|
+
priority: factoryInstance.getPriority(),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
logger$h.warning("failed_to_auto_register_factory", {
|
|
100
|
+
factory_name: factoryName,
|
|
101
|
+
error: error instanceof Error ? error.message : String(error),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
this.autoDiscovered = true;
|
|
106
|
+
logger$h.debug("completed_auto_discovery", {
|
|
107
|
+
registered_factories: registeredCount,
|
|
108
|
+
total_discovered: extensionInfos.size,
|
|
109
|
+
skipped_composite: true,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
logger$h.warning("failed_auto_discovery_of_factories", {
|
|
114
|
+
error: error instanceof Error ? error.message : String(error),
|
|
56
115
|
});
|
|
57
|
-
return cached;
|
|
58
116
|
}
|
|
59
|
-
logger$h.debug("certificate_cache_miss", {
|
|
60
|
-
call_id: callId,
|
|
61
|
-
cache_key: cacheKey,
|
|
62
|
-
});
|
|
63
117
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
118
|
+
registerFactory(factory, options = {}) {
|
|
119
|
+
if (this.factorySet.has(factory)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
this.factorySet.add(factory);
|
|
123
|
+
this.factories.push(factory);
|
|
124
|
+
if (options.autoDiscovered) {
|
|
125
|
+
this.autoDiscoveredFactories.add(factory);
|
|
126
|
+
}
|
|
127
|
+
for (const algorithm of factory.getSupportedAlgorithms()) {
|
|
128
|
+
const existing = this.algorithmToFactory.get(algorithm);
|
|
129
|
+
if (!existing || factory.getPriority() > existing.getPriority()) {
|
|
130
|
+
this.algorithmToFactory.set(algorithm, factory);
|
|
131
|
+
logger$h.debug("registered_algorithm_mapping", {
|
|
132
|
+
algorithm,
|
|
133
|
+
factory: factory.constructor.name,
|
|
134
|
+
priority: factory.getPriority(),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const encryptionType = factory.getEncryptionType();
|
|
139
|
+
const typeFactories = this.typeToFactories.get(encryptionType) ?? [];
|
|
140
|
+
typeFactories.push(factory);
|
|
141
|
+
typeFactories.sort((a, b) => b.getPriority() - a.getPriority());
|
|
142
|
+
this.typeToFactories.set(encryptionType, typeFactories);
|
|
143
|
+
logger$h.debug("registered_encryption_manager_factory", {
|
|
144
|
+
factory: factory.constructor.name,
|
|
145
|
+
encryption_type: encryptionType,
|
|
146
|
+
algorithms: factory.getSupportedAlgorithms(),
|
|
147
|
+
priority: factory.getPriority(),
|
|
148
|
+
auto_discovered: options.autoDiscovered ?? false,
|
|
149
|
+
});
|
|
67
150
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
certificate: validation.certificate,
|
|
72
|
-
};
|
|
151
|
+
getFactoryForAlgorithm(algorithm) {
|
|
152
|
+
this.ensureAutoDiscovery();
|
|
153
|
+
return this.algorithmToFactory.get(algorithm);
|
|
73
154
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
155
|
+
getFactoryForOptions(opts) {
|
|
156
|
+
this.ensureAutoDiscovery();
|
|
157
|
+
for (const factory of this.factories) {
|
|
158
|
+
if (factory.supportsOptions(opts ?? undefined)) {
|
|
159
|
+
logger$h.debug("found_factory_for_options", {
|
|
160
|
+
factory: factory.constructor.name,
|
|
161
|
+
encryption_type: factory.getEncryptionType(),
|
|
162
|
+
});
|
|
163
|
+
return factory;
|
|
164
|
+
}
|
|
82
165
|
}
|
|
83
|
-
|
|
166
|
+
logger$h.debug("no_factory_found_for_options", { opts });
|
|
167
|
+
return undefined;
|
|
84
168
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return
|
|
169
|
+
getFactoriesByType(encryptionType) {
|
|
170
|
+
this.ensureAutoDiscovery();
|
|
171
|
+
return this.typeToFactories.get(encryptionType) ?? [];
|
|
88
172
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const error = "Invalid x5c field in JWK";
|
|
93
|
-
if (strict) {
|
|
94
|
-
throw new Error(error);
|
|
95
|
-
}
|
|
96
|
-
return { isValid: false, error };
|
|
173
|
+
getAllSupportedAlgorithms() {
|
|
174
|
+
this.ensureAutoDiscovery();
|
|
175
|
+
return Array.from(this.algorithmToFactory.keys());
|
|
97
176
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
177
|
+
getRegistryInfo() {
|
|
178
|
+
return {
|
|
179
|
+
totalFactories: this.factories.length,
|
|
180
|
+
autoDiscovered: this.autoDiscovered,
|
|
181
|
+
algorithmMappings: Object.fromEntries(Array.from(this.algorithmToFactory.entries()).map(([algorithm, factory]) => [algorithm, factory.constructor.name])),
|
|
182
|
+
typeMappings: Object.fromEntries(Array.from(this.typeToFactories.entries()).map(([encType, factories]) => [
|
|
183
|
+
encType,
|
|
184
|
+
factories.map((factory) => factory.constructor.name),
|
|
185
|
+
])),
|
|
186
|
+
};
|
|
104
187
|
}
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
188
|
+
forceRediscovery() {
|
|
189
|
+
const manualFactories = this.factories.filter((factory) => !this.autoDiscoveredFactories.has(factory));
|
|
190
|
+
this.autoDiscovered = false;
|
|
191
|
+
this.algorithmToFactory.clear();
|
|
192
|
+
this.typeToFactories.clear();
|
|
193
|
+
this.factories.length = 0;
|
|
194
|
+
this.factorySet.clear();
|
|
195
|
+
this.autoDiscoveredFactories.clear();
|
|
196
|
+
for (const factory of manualFactories) {
|
|
197
|
+
this.registerFactory(factory);
|
|
110
198
|
}
|
|
111
|
-
|
|
199
|
+
this.autoDiscoverFactories();
|
|
112
200
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const leaf = parsed[0];
|
|
116
|
-
const nowMs = Date.now();
|
|
117
|
-
const notBefore = leaf.certificate.tbsCertificate.validity.notBefore.getTime();
|
|
118
|
-
const notAfter = leaf.certificate.tbsCertificate.validity.notAfter.getTime();
|
|
119
|
-
const notBeforeMs = notBefore.getTime();
|
|
120
|
-
const notAfterMs = notAfter.getTime();
|
|
121
|
-
if (nowMs < notBeforeMs || nowMs > notAfterMs) {
|
|
122
|
-
throw new Error(`Certificate is not currently valid (notBefore: ${notBefore.toISOString()}, notAfter: ${notAfter.toISOString()}, now: ${new Date(nowMs).toISOString()})`);
|
|
201
|
+
isAutoDiscovered() {
|
|
202
|
+
return this.autoDiscovered;
|
|
123
203
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const leafUris = extractUrisFromCert(leaf.certificate);
|
|
127
|
-
validateNameConstraints(issuers, leafUris);
|
|
204
|
+
ensureInitialized() {
|
|
205
|
+
this.ensureAutoDiscovery();
|
|
128
206
|
}
|
|
129
|
-
|
|
130
|
-
|
|
207
|
+
ensureAutoDiscovery() {
|
|
208
|
+
if (!this.autoDiscovered) {
|
|
209
|
+
this.autoDiscoverFactories();
|
|
210
|
+
}
|
|
131
211
|
}
|
|
132
|
-
validateChainContinuity(parsed);
|
|
133
|
-
const publicKey = leaf.subjectPublicKey.slice();
|
|
134
|
-
return {
|
|
135
|
-
publicKey,
|
|
136
|
-
certificate: leaf.certificate,
|
|
137
|
-
notAfter,
|
|
138
|
-
};
|
|
139
212
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
213
|
+
const globalRegistry = new EncryptionManagerFactoryRegistry(true);
|
|
214
|
+
function getEncryptionManagerFactoryRegistry() {
|
|
215
|
+
globalRegistry.ensureInitialized();
|
|
216
|
+
return globalRegistry;
|
|
217
|
+
}
|
|
218
|
+
function registerEncryptionManagerFactory(factory) {
|
|
219
|
+
globalRegistry.registerFactory(factory);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const SECURITY_PREFIX = "./security/";
|
|
223
|
+
const SECURITY_MODULES = MODULES.filter((spec) => spec.startsWith(SECURITY_PREFIX));
|
|
224
|
+
const EXTRA_MODULES = MODULES.filter((spec) => !spec.startsWith(SECURITY_PREFIX));
|
|
225
|
+
const NODE_ONLY_MODULES = new Set([
|
|
226
|
+
"./security/cert/default-ca-service-factory.js",
|
|
227
|
+
"./security/cert/trust-store/node-trust-store-provider-factory.js",
|
|
228
|
+
]);
|
|
229
|
+
const FACTORY_MODULE_PREFIX$1 = "@naylence/advanced-security/naylence/fame/";
|
|
230
|
+
const BROWSER_DIST_SEGMENT = "/dist/browser/";
|
|
231
|
+
function detectModuleUrl() {
|
|
232
|
+
if (typeof __filename === "string") {
|
|
157
233
|
try {
|
|
158
|
-
|
|
234
|
+
return __filename.startsWith("file://")
|
|
235
|
+
? __filename
|
|
236
|
+
: `file://${__filename}`;
|
|
159
237
|
}
|
|
160
|
-
catch
|
|
161
|
-
|
|
162
|
-
throw new Error(`Failed to parse certificate at index ${index}: ${reason}`);
|
|
238
|
+
catch {
|
|
239
|
+
// fall through to stack parsing
|
|
163
240
|
}
|
|
164
|
-
parsed.push(createParsedCertificate(certificate, der));
|
|
165
|
-
derChunks.push(der);
|
|
166
|
-
}
|
|
167
|
-
return { parsed, chainBytes: concatBytes(derChunks) };
|
|
168
|
-
}
|
|
169
|
-
function createParsedCertificate(certificate, raw) {
|
|
170
|
-
return {
|
|
171
|
-
raw,
|
|
172
|
-
certificate,
|
|
173
|
-
serialNumber: toHex(new Uint8Array(certificate.tbsCertificate.serialNumber)).toUpperCase(),
|
|
174
|
-
subjectName: serializeName(certificate.tbsCertificate.subject),
|
|
175
|
-
issuerName: serializeName(certificate.tbsCertificate.issuer),
|
|
176
|
-
subjectPublicKey: new Uint8Array(certificate.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey).slice(),
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
function extractUrisFromCert(cert) {
|
|
180
|
-
const extension = findExtension(cert, asn1X509.id_ce_subjectAltName);
|
|
181
|
-
if (!extension) {
|
|
182
|
-
return [];
|
|
183
241
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
for (const generalName of subjectAlternativeName) {
|
|
187
|
-
if (generalName.uniformResourceIdentifier) {
|
|
188
|
-
uris.push(generalName.uniformResourceIdentifier);
|
|
189
|
-
}
|
|
242
|
+
try {
|
|
243
|
+
throw new Error();
|
|
190
244
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
for (const uri of leafUris) {
|
|
208
|
-
const allowed = permittedUris.some((prefix) => uri.startsWith(prefix));
|
|
209
|
-
if (!allowed) {
|
|
210
|
-
throw new Error(`URI '${uri}' violates name constraints - not in permitted subtrees: ${permittedUris.join(", ")}`);
|
|
245
|
+
catch (error) {
|
|
246
|
+
const stack = typeof error === "object" && error && "stack" in error
|
|
247
|
+
? String(error.stack ?? "")
|
|
248
|
+
: "";
|
|
249
|
+
const lines = stack.split("\n");
|
|
250
|
+
for (const line of lines) {
|
|
251
|
+
const match = line.match(/(https?:\/\/[^\s)]+|file:\/\/[^\s)]+\.(?:js|ts)|\/(?:[^\s)]+\.(?:js|ts)))/u);
|
|
252
|
+
const candidate = match?.[1];
|
|
253
|
+
if (!candidate) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (candidate.startsWith("http://") || candidate.startsWith("https://")) {
|
|
257
|
+
return candidate;
|
|
258
|
+
}
|
|
259
|
+
if (candidate.startsWith("file://")) {
|
|
260
|
+
return candidate;
|
|
211
261
|
}
|
|
262
|
+
return `file://${candidate}`;
|
|
212
263
|
}
|
|
213
264
|
}
|
|
265
|
+
return null;
|
|
214
266
|
}
|
|
215
|
-
function
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (subtree.base.uniformResourceIdentifier &&
|
|
219
|
-
subtree.base.uniformResourceIdentifier.length > 0) {
|
|
220
|
-
uris.push(subtree.base.uniformResourceIdentifier);
|
|
221
|
-
}
|
|
267
|
+
function computeBrowserFactoryBase(rawUrl) {
|
|
268
|
+
if (!rawUrl) {
|
|
269
|
+
return null;
|
|
222
270
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
throw new Error("No valid certificates found in trust store");
|
|
271
|
+
const sanitized = rawUrl.split("?")[0]?.split("#")[0] ?? rawUrl;
|
|
272
|
+
const esmMarker = "/dist/esm/naylence/fame/";
|
|
273
|
+
const distMarker = "/dist/";
|
|
274
|
+
if (sanitized.includes(esmMarker)) {
|
|
275
|
+
return sanitized.slice(0, sanitized.indexOf(esmMarker) + esmMarker.length);
|
|
229
276
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
trust_store_cert_count: trustedCerts.length,
|
|
233
|
-
});
|
|
234
|
-
const chainInfo = chain.map((cert, index) => `[${index}] ${cert.subjectName} (Serial: ${cert.serialNumber})`);
|
|
235
|
-
const trustedInfo = trustedCerts.map((cert, index) => `[${index}] ${cert.subjectName} (Serial: ${cert.serialNumber})`);
|
|
236
|
-
logger$h.debug("certificate_chain_validation", {
|
|
237
|
-
chain_certificates: chainInfo,
|
|
238
|
-
trust_store_certificates: trustedInfo,
|
|
239
|
-
});
|
|
240
|
-
// Strategy 1: direct trust (exact certificate match)
|
|
241
|
-
for (let i = 0; i < chain.length; i += 1) {
|
|
242
|
-
const cert = chain[i];
|
|
243
|
-
const match = trustedCerts.find((trusted) => trusted.serialNumber === cert.serialNumber &&
|
|
244
|
-
namesEqual(trusted.certificate.tbsCertificate.subject, cert.certificate.tbsCertificate.subject));
|
|
245
|
-
if (match) {
|
|
246
|
-
logger$h.debug("certificate_chain_trust_validation_passed", {
|
|
247
|
-
matching_serial: match.serialNumber,
|
|
248
|
-
validation_strategy: `direct_trust_cert_${i}`,
|
|
249
|
-
});
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
277
|
+
if (rawUrl.includes(BROWSER_DIST_SEGMENT)) {
|
|
278
|
+
return new URL("../esm/naylence/fame/", rawUrl).href;
|
|
252
279
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (namesEqual(trusted.certificate.tbsCertificate.subject, leaf.certificate.tbsCertificate.issuer) &&
|
|
257
|
-
trusted.serialNumber !== leaf.serialNumber) {
|
|
258
|
-
verifyCertificateSignature(leaf.certificate, trusted.certificate);
|
|
259
|
-
logger$h.debug("certificate_chain_trust_validation_passed", {
|
|
260
|
-
matching_serial: trusted.serialNumber,
|
|
261
|
-
validation_strategy: "leaf_issuer_trust",
|
|
262
|
-
});
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
280
|
+
if (sanitized.includes(BROWSER_DIST_SEGMENT)) {
|
|
281
|
+
const base = sanitized.slice(0, sanitized.indexOf(BROWSER_DIST_SEGMENT) + BROWSER_DIST_SEGMENT.length);
|
|
282
|
+
return `${base.replace(/browser\/?$/u, "")}esm/naylence/fame/`;
|
|
265
283
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
if (namesEqual(trusted.certificate.tbsCertificate.subject, intermediate.certificate.tbsCertificate.issuer) &&
|
|
271
|
-
trusted.serialNumber !== intermediate.serialNumber) {
|
|
272
|
-
verifyCertificateSignature(intermediate.certificate, trusted.certificate);
|
|
273
|
-
logger$h.debug("certificate_chain_trust_validation_passed", {
|
|
274
|
-
matching_serial: trusted.serialNumber,
|
|
275
|
-
validation_strategy: `intermediate_issuer_trust_cert_${index}`,
|
|
276
|
-
});
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
284
|
+
if (sanitized.includes(distMarker)) {
|
|
285
|
+
const index = sanitized.indexOf(distMarker);
|
|
286
|
+
const base = sanitized.slice(0, index + distMarker.length);
|
|
287
|
+
return `${base}esm/naylence/fame/`;
|
|
280
288
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
});
|
|
289
|
-
throw new Error("Certificate chain is not rooted in a trusted anchor");
|
|
290
|
-
}
|
|
291
|
-
function parseTrustStore(trustStorePem) {
|
|
292
|
-
const normalized = normalizePem$2(trustStorePem);
|
|
293
|
-
const blocks = extractPemBlocks$1(normalized);
|
|
294
|
-
const parsed = [];
|
|
295
|
-
for (const block of blocks) {
|
|
289
|
+
const srcMarker = "/src/naylence/fame/";
|
|
290
|
+
if (sanitized.includes(srcMarker)) {
|
|
291
|
+
const index = sanitized.indexOf(srcMarker);
|
|
292
|
+
const projectRoot = sanitized.slice(0, index);
|
|
293
|
+
return `${projectRoot}/dist/esm/naylence/fame/`;
|
|
294
|
+
}
|
|
295
|
+
if (sanitized.startsWith("http://") || sanitized.startsWith("https://")) {
|
|
296
296
|
try {
|
|
297
|
-
const
|
|
298
|
-
const
|
|
299
|
-
parsed.
|
|
297
|
+
const parsed = new URL(rawUrl);
|
|
298
|
+
const viteDepsSegment = "/node_modules/.vite/deps/";
|
|
299
|
+
if (parsed.pathname.includes(viteDepsSegment)) {
|
|
300
|
+
const baseOrigin = `${parsed.protocol}//${parsed.host}`;
|
|
301
|
+
return `${baseOrigin}/node_modules/@naylence/advanced-security/dist/esm/naylence/fame/`;
|
|
302
|
+
}
|
|
300
303
|
}
|
|
301
|
-
catch
|
|
302
|
-
|
|
303
|
-
logger$h.debug("trust_store_certificate_parse_failed", { reason });
|
|
304
|
+
catch {
|
|
305
|
+
// ignore
|
|
304
306
|
}
|
|
305
307
|
}
|
|
306
|
-
return
|
|
308
|
+
return null;
|
|
307
309
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const body = match[1] ?? "";
|
|
315
|
-
blocks.push(body.replace(/\s+/gu, ""));
|
|
310
|
+
const moduleUrl = detectModuleUrl();
|
|
311
|
+
const browserFactoryBase = computeBrowserFactoryBase(moduleUrl);
|
|
312
|
+
const prefersSource = typeof moduleUrl === "string" && moduleUrl.includes("/src/");
|
|
313
|
+
function resolveFactoryModuleSpecifier$1(specifier) {
|
|
314
|
+
if (specifier.startsWith("../")) {
|
|
315
|
+
return `${FACTORY_MODULE_PREFIX$1}${specifier.slice("../".length)}`;
|
|
316
316
|
}
|
|
317
|
-
|
|
318
|
-
}
|
|
319
|
-
function validateChainContinuity(chain) {
|
|
320
|
-
if (chain.length <= 1) {
|
|
321
|
-
return;
|
|
317
|
+
if (specifier.startsWith("./")) {
|
|
318
|
+
return `${FACTORY_MODULE_PREFIX$1}${specifier.slice("./".length)}`;
|
|
322
319
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
function resolveModuleCandidates(spec) {
|
|
323
|
+
const candidates = [];
|
|
324
|
+
const seen = new Set();
|
|
325
|
+
const addCandidate = (candidate) => {
|
|
326
|
+
if (!candidate) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (!seen.has(candidate)) {
|
|
330
|
+
seen.add(candidate);
|
|
331
|
+
candidates.push(candidate);
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
if (prefersSource && spec.startsWith("./")) {
|
|
335
|
+
const sourceCandidate = `../${spec.slice(2)}`;
|
|
336
|
+
addCandidate(sourceCandidate);
|
|
337
|
+
if (sourceCandidate.endsWith(".js")) {
|
|
338
|
+
addCandidate(sourceCandidate.replace(/\.js$/u, ".ts"));
|
|
336
339
|
}
|
|
340
|
+
}
|
|
341
|
+
if (browserFactoryBase && spec.startsWith("./")) {
|
|
337
342
|
try {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
});
|
|
343
|
+
const browserCandidate = new URL(spec.slice("./".length), browserFactoryBase).href;
|
|
344
|
+
addCandidate(browserCandidate);
|
|
345
|
+
if (browserCandidate.endsWith(".js")) {
|
|
346
|
+
addCandidate(browserCandidate.replace(/\.js$/u, ".ts"));
|
|
347
|
+
}
|
|
344
348
|
}
|
|
345
|
-
catch
|
|
346
|
-
|
|
347
|
-
logger$h.warning("certificate_chain_continuity_failed", {
|
|
348
|
-
cert_index: index,
|
|
349
|
-
cert_subject: cert.subjectName,
|
|
350
|
-
issuer_subject: issuer.subjectName,
|
|
351
|
-
cert_serial: cert.serialNumber,
|
|
352
|
-
issuer_serial: issuer.serialNumber,
|
|
353
|
-
error: reason,
|
|
354
|
-
reason: "signature_verification_failed",
|
|
355
|
-
});
|
|
356
|
-
throw new Error(`Certificate chain continuity broken: certificate at index ${index} was not signed by certificate at index ${index + 1}: ${reason}`);
|
|
349
|
+
catch {
|
|
350
|
+
// ignore resolution failures for browser base
|
|
357
351
|
}
|
|
358
352
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
function verifyCertificateSignature(certificate, issuer) {
|
|
364
|
-
ensureEd25519Support();
|
|
365
|
-
const signatureAlgorithm = certificate.signatureAlgorithm.algorithm;
|
|
366
|
-
const issuerAlgorithm = issuer.tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm;
|
|
367
|
-
if (signatureAlgorithm !== OID_ED25519 || issuerAlgorithm !== OID_ED25519) {
|
|
368
|
-
throw new Error(`Unsupported signature algorithm (certificate: ${signatureAlgorithm}, issuer: ${issuerAlgorithm})`);
|
|
353
|
+
const packageCandidate = resolveFactoryModuleSpecifier$1(spec);
|
|
354
|
+
addCandidate(packageCandidate);
|
|
355
|
+
if (packageCandidate?.endsWith(".js")) {
|
|
356
|
+
addCandidate(packageCandidate.replace(/\.js$/u, ".ts"));
|
|
369
357
|
}
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
throw new Error("Issuer Ed25519 public key must be 32 bytes");
|
|
358
|
+
const fallback = spec.startsWith("./") ? `../${spec.slice(2)}` : spec;
|
|
359
|
+
addCandidate(fallback);
|
|
360
|
+
if (fallback.endsWith(".js")) {
|
|
361
|
+
addCandidate(fallback.replace(/\.js$/u, ".ts"));
|
|
375
362
|
}
|
|
376
|
-
|
|
377
|
-
if (
|
|
378
|
-
|
|
363
|
+
addCandidate(spec);
|
|
364
|
+
if (spec.endsWith(".js")) {
|
|
365
|
+
addCandidate(spec.replace(/\.js$/u, ".ts"));
|
|
379
366
|
}
|
|
367
|
+
return candidates;
|
|
380
368
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
369
|
+
const registeredModules = new Set();
|
|
370
|
+
const inflightModules = new Map();
|
|
371
|
+
const browserSkippedModules = new Set();
|
|
372
|
+
function isNodeEnvironment$5() {
|
|
373
|
+
return (typeof process !== "undefined" &&
|
|
374
|
+
typeof process.release !== "undefined" &&
|
|
375
|
+
process.release?.name === "node");
|
|
376
|
+
}
|
|
377
|
+
function shouldSkipModule(spec) {
|
|
378
|
+
if (isNodeEnvironment$5()) {
|
|
379
|
+
return false;
|
|
385
380
|
}
|
|
386
|
-
if (!
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
381
|
+
if (!NODE_ONLY_MODULES.has(spec)) {
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
if (!browserSkippedModules.has(spec)) {
|
|
385
|
+
// console.warn(
|
|
386
|
+
// "[advanced-security:factory-manifest] skipped browser-incompatible module",
|
|
387
|
+
// spec,
|
|
388
|
+
// );
|
|
389
|
+
browserSkippedModules.add(spec);
|
|
394
390
|
}
|
|
391
|
+
return true;
|
|
395
392
|
}
|
|
396
|
-
function
|
|
397
|
-
|
|
398
|
-
if (!extensions) {
|
|
393
|
+
function getDynamicImporter() {
|
|
394
|
+
if (typeof globalThis === "undefined") {
|
|
399
395
|
return null;
|
|
400
396
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
}
|
|
397
|
+
const candidate = globalThis.__naylenceFactoryDynamicImporter;
|
|
398
|
+
if (typeof candidate === "function") {
|
|
399
|
+
return candidate;
|
|
405
400
|
}
|
|
406
401
|
return null;
|
|
407
402
|
}
|
|
408
|
-
function
|
|
409
|
-
const
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
403
|
+
async function registerModule(spec, registrar) {
|
|
404
|
+
const candidates = resolveModuleCandidates(spec);
|
|
405
|
+
const dynamicImporter = getDynamicImporter();
|
|
406
|
+
const loader = dynamicImporter
|
|
407
|
+
? (specifier) => dynamicImporter(specifier)
|
|
408
|
+
: (specifier) => import(/* @vite-ignore */ specifier);
|
|
409
|
+
const attempts = [];
|
|
410
|
+
const staticLoader = MODULE_LOADERS?.[spec];
|
|
411
|
+
if (staticLoader) {
|
|
412
|
+
attempts.push({ load: () => staticLoader(), candidate: spec });
|
|
413
413
|
}
|
|
414
|
-
for (
|
|
415
|
-
|
|
414
|
+
for (const candidate of candidates) {
|
|
415
|
+
attempts.push({ load: () => loader(candidate), candidate });
|
|
416
|
+
}
|
|
417
|
+
const registerFromModule = (mod) => {
|
|
418
|
+
const meta = mod.FACTORY_META;
|
|
419
|
+
const Ctor = mod.default;
|
|
420
|
+
if (!meta?.base || !meta?.key || typeof Ctor !== "function") {
|
|
421
|
+
console.warn("[debug] invalid factory module", spec, {
|
|
422
|
+
meta,
|
|
423
|
+
hasCtor: typeof Ctor === "function",
|
|
424
|
+
});
|
|
425
|
+
console.warn("[advanced-security:factory-manifest] skipped", spec, "— missing FACTORY_META or default export ctor");
|
|
416
426
|
return false;
|
|
417
427
|
}
|
|
428
|
+
const { base, key, ...metadata } = meta;
|
|
429
|
+
const extraMetadata = Object.keys(metadata).length > 0 ? metadata : undefined;
|
|
430
|
+
// console.log("[debug] registering module", { spec, base, key, metadata: extraMetadata });
|
|
431
|
+
registrar.registerFactory(base, key, Ctor, extraMetadata);
|
|
432
|
+
return true;
|
|
433
|
+
};
|
|
434
|
+
for (const [index, { candidate, load }] of attempts.entries()) {
|
|
435
|
+
try {
|
|
436
|
+
const mod = await load();
|
|
437
|
+
return registerFromModule(mod);
|
|
438
|
+
}
|
|
439
|
+
catch (error) {
|
|
440
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
441
|
+
const moduleNotFound = message.includes("Cannot find module") ||
|
|
442
|
+
message.includes("ERR_MODULE_NOT_FOUND") ||
|
|
443
|
+
message.includes("Unknown file extension") ||
|
|
444
|
+
message.includes("Failed to fetch dynamically imported module") ||
|
|
445
|
+
message.includes("Failed to resolve module specifier") ||
|
|
446
|
+
message.includes("Importing a module script failed");
|
|
447
|
+
const isLastAttempt = index === attempts.length - 1;
|
|
448
|
+
if (!moduleNotFound || isLastAttempt) {
|
|
449
|
+
console.warn("[debug] failed to import candidate", {
|
|
450
|
+
spec,
|
|
451
|
+
candidate,
|
|
452
|
+
message,
|
|
453
|
+
});
|
|
454
|
+
console.warn("[advanced-security:factory-manifest] skipped", spec, "-", message);
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
418
458
|
}
|
|
419
|
-
return
|
|
420
|
-
}
|
|
421
|
-
function serializeName(name) {
|
|
422
|
-
const rdns = Array.from(name);
|
|
423
|
-
return rdns
|
|
424
|
-
.map((rdn) => Array.from(rdn)
|
|
425
|
-
.map((attr) => `${oidToLabel$1(attr.type)}=${attr.value.toString()}`)
|
|
426
|
-
.join("+"))
|
|
427
|
-
.join(",");
|
|
459
|
+
return false;
|
|
428
460
|
}
|
|
429
|
-
function
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
return "CN";
|
|
433
|
-
case "2.5.4.6":
|
|
434
|
-
return "C";
|
|
435
|
-
case "2.5.4.7":
|
|
436
|
-
return "L";
|
|
437
|
-
case "2.5.4.8":
|
|
438
|
-
return "ST";
|
|
439
|
-
case "2.5.4.10":
|
|
440
|
-
return "O";
|
|
441
|
-
case "2.5.4.11":
|
|
442
|
-
return "OU";
|
|
443
|
-
default:
|
|
444
|
-
return oid;
|
|
461
|
+
async function registerModuleOnce(spec, registrar) {
|
|
462
|
+
if (registeredModules.has(spec)) {
|
|
463
|
+
return false;
|
|
445
464
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
465
|
+
const inflight = inflightModules.get(spec);
|
|
466
|
+
if (inflight) {
|
|
467
|
+
return inflight;
|
|
468
|
+
}
|
|
469
|
+
const registration = (async () => {
|
|
470
|
+
const registered = await registerModule(spec, registrar);
|
|
471
|
+
if (registered) {
|
|
472
|
+
registeredModules.add(spec);
|
|
473
|
+
}
|
|
474
|
+
return registered;
|
|
475
|
+
})();
|
|
476
|
+
inflightModules.set(spec, registration);
|
|
477
|
+
try {
|
|
478
|
+
return await registration;
|
|
479
|
+
}
|
|
480
|
+
finally {
|
|
481
|
+
inflightModules.delete(spec);
|
|
454
482
|
}
|
|
455
|
-
return result;
|
|
456
483
|
}
|
|
457
|
-
function
|
|
458
|
-
if (
|
|
459
|
-
|
|
460
|
-
return new Uint8Array(Buffer.from(normalized, "base64"));
|
|
484
|
+
async function registerModules(modules, registrar) {
|
|
485
|
+
if (modules.length === 0) {
|
|
486
|
+
return 0;
|
|
461
487
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
const bytes = new Uint8Array(binary.length);
|
|
466
|
-
for (let i = 0; i < binary.length; i += 1) {
|
|
467
|
-
bytes[i] = binary.charCodeAt(i);
|
|
468
|
-
}
|
|
469
|
-
return bytes;
|
|
488
|
+
const eligibleModules = modules.filter((spec) => !shouldSkipModule(spec));
|
|
489
|
+
if (eligibleModules.length === 0) {
|
|
490
|
+
return 0;
|
|
470
491
|
}
|
|
471
|
-
|
|
492
|
+
const results = await Promise.all(eligibleModules.map((spec) => registerModuleOnce(spec, registrar)));
|
|
493
|
+
return results.reduce((count, registered) => (registered ? count + 1 : count), 0);
|
|
472
494
|
}
|
|
473
|
-
function
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
.
|
|
477
|
-
}
|
|
478
|
-
function buildCacheKey(chainBytes, trustStorePem, enforceNameConstraints) {
|
|
479
|
-
const chainHash = toHex(sha2_js.sha256(chainBytes));
|
|
480
|
-
const trustHash = trustStorePem
|
|
481
|
-
? toHex(sha2_js.sha256(textEncoder.encode(trustStorePem)))
|
|
482
|
-
: "no-trust";
|
|
483
|
-
const constraintFlag = enforceNameConstraints ? "nc1" : "nc0";
|
|
484
|
-
return `${chainHash}|${trustHash}|${constraintFlag}`;
|
|
485
|
-
}
|
|
486
|
-
function getCachedPublicKey(cacheKey) {
|
|
487
|
-
const entry = trustCache.get(cacheKey);
|
|
488
|
-
if (!entry) {
|
|
489
|
-
return null;
|
|
495
|
+
async function registerAdvancedSecurityFactories(registrar = factory.Registry, options) {
|
|
496
|
+
const newlyRegisteredSecurity = await registerModules(SECURITY_MODULES, registrar);
|
|
497
|
+
if (newlyRegisteredSecurity > 0) {
|
|
498
|
+
getEncryptionManagerFactoryRegistry().forceRediscovery();
|
|
490
499
|
}
|
|
491
|
-
if (
|
|
492
|
-
|
|
493
|
-
logger$h.debug("certificate_cache_expired", { cache_key: cacheKey });
|
|
494
|
-
return null;
|
|
500
|
+
if (options?.includeExtras === true) {
|
|
501
|
+
await registerModules(EXTRA_MODULES, registrar);
|
|
495
502
|
}
|
|
496
|
-
return entry.value.slice();
|
|
497
503
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
504
|
+
|
|
505
|
+
// This file is auto-generated during build - do not edit manually
|
|
506
|
+
// Generated from package.json version: 0.3.15
|
|
507
|
+
/**
|
|
508
|
+
* The package version, injected at build time.
|
|
509
|
+
* @internal
|
|
510
|
+
*/
|
|
511
|
+
const VERSION = '0.3.15';
|
|
512
|
+
|
|
513
|
+
async function registerAdvancedSecurityPluginFactories(registrar = factory.Registry) {
|
|
514
|
+
await registerAdvancedSecurityFactories(registrar, { includeExtras: true });
|
|
515
|
+
}
|
|
516
|
+
let initialized = false;
|
|
517
|
+
let initializing = null;
|
|
518
|
+
const advancedSecurityPlugin = {
|
|
519
|
+
name: "naylence:advanced-security",
|
|
520
|
+
version: VERSION,
|
|
521
|
+
async register() {
|
|
522
|
+
// console.log('[naylence:advanced-security] register() called, initialized=', initialized);
|
|
523
|
+
if (initialized) {
|
|
524
|
+
// console.log('[naylence:advanced-security] already initialized, skipping');
|
|
525
|
+
return;
|
|
503
526
|
}
|
|
504
|
-
|
|
505
|
-
|
|
527
|
+
if (initializing) {
|
|
528
|
+
console.log("[naylence:advanced-security] already initializing, awaiting...");
|
|
529
|
+
await initializing;
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
initializing = (async () => {
|
|
533
|
+
try {
|
|
534
|
+
// console.log('[naylence:advanced-security] registering advanced security factories...');
|
|
535
|
+
await registerAdvancedSecurityPluginFactories();
|
|
536
|
+
// console.log('[naylence:advanced-security] advanced security factories registered');
|
|
537
|
+
initialized = true;
|
|
538
|
+
}
|
|
539
|
+
finally {
|
|
540
|
+
initializing = null;
|
|
541
|
+
}
|
|
542
|
+
})();
|
|
543
|
+
await initializing;
|
|
544
|
+
},
|
|
545
|
+
};
|
|
546
|
+
const ADVANCED_SECURITY_PLUGIN_SPECIFIER = advancedSecurityPlugin.name;
|
|
547
|
+
|
|
548
|
+
var plugin = /*#__PURE__*/Object.freeze({
|
|
549
|
+
__proto__: null,
|
|
550
|
+
ADVANCED_SECURITY_PLUGIN_SPECIFIER: ADVANCED_SECURITY_PLUGIN_SPECIFIER,
|
|
551
|
+
default: advancedSecurityPlugin,
|
|
552
|
+
registerAdvancedSecurityPluginFactories: registerAdvancedSecurityPluginFactories
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
const logger$g = runtime.getLogger("naylence.fame.security.cert.util");
|
|
556
|
+
const CACHE_LIMIT = 512;
|
|
557
|
+
const OID_ED25519 = "1.3.101.112";
|
|
558
|
+
const textEncoder = new TextEncoder();
|
|
559
|
+
const trustCache = new Map();
|
|
560
|
+
function publicKeyFromX5c(x5c, options = {}) {
|
|
561
|
+
if (!Array.isArray(x5c) || x5c.length === 0) {
|
|
562
|
+
throw new Error("Empty certificate chain");
|
|
506
563
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
564
|
+
const callId = generateCallId();
|
|
565
|
+
const enforceNameConstraints = options.enforceNameConstraints ?? true;
|
|
566
|
+
const trustStorePem = normalizeTrustStoreOption(options.trustStorePem ?? null);
|
|
567
|
+
const returnCertificate = options.returnCertificate ?? false;
|
|
568
|
+
const { parsed, chainBytes } = parseCertificateChain(x5c);
|
|
569
|
+
logger$g.debug("public_key_from_x5c_called", {
|
|
570
|
+
call_id: callId,
|
|
571
|
+
x5c_count: parsed.length,
|
|
572
|
+
enforce_name_constraints: enforceNameConstraints,
|
|
573
|
+
has_trust_store: Boolean(trustStorePem),
|
|
574
|
+
return_cert: returnCertificate,
|
|
515
575
|
});
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
576
|
+
let cacheKey = null;
|
|
577
|
+
if (!returnCertificate) {
|
|
578
|
+
cacheKey = buildCacheKey(chainBytes, trustStorePem, enforceNameConstraints);
|
|
579
|
+
const cached = getCachedPublicKey(cacheKey);
|
|
580
|
+
if (cached) {
|
|
581
|
+
logger$g.debug("certificate_cache_hit", {
|
|
582
|
+
call_id: callId,
|
|
583
|
+
cache_key: cacheKey,
|
|
584
|
+
});
|
|
585
|
+
return cached;
|
|
586
|
+
}
|
|
587
|
+
logger$g.debug("certificate_cache_miss", {
|
|
588
|
+
call_id: callId,
|
|
589
|
+
cache_key: cacheKey,
|
|
590
|
+
});
|
|
520
591
|
}
|
|
521
|
-
const
|
|
522
|
-
if (
|
|
523
|
-
|
|
592
|
+
const validation = validateCertificateChain(parsed, enforceNameConstraints, trustStorePem);
|
|
593
|
+
if (cacheKey) {
|
|
594
|
+
setCachedPublicKey(cacheKey, validation.publicKey, validation.notAfter);
|
|
524
595
|
}
|
|
525
|
-
if (
|
|
526
|
-
|
|
596
|
+
if (returnCertificate) {
|
|
597
|
+
return {
|
|
598
|
+
publicKey: validation.publicKey.slice(),
|
|
599
|
+
certificate: validation.certificate,
|
|
600
|
+
};
|
|
527
601
|
}
|
|
528
|
-
return
|
|
529
|
-
}
|
|
530
|
-
function normalizePem$2(pem) {
|
|
531
|
-
return pem.replace(/\r/gu, "").trim();
|
|
532
|
-
}
|
|
533
|
-
function generateCallId() {
|
|
534
|
-
return Math.random().toString(36).slice(2, 10);
|
|
602
|
+
return validation.publicKey.slice();
|
|
535
603
|
}
|
|
536
|
-
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
if (!instance) {
|
|
545
|
-
throw new Error("WebCrypto subtle API is required to create a CSR");
|
|
604
|
+
function validateJwkX5cCertificate(options) {
|
|
605
|
+
const { jwk, trustStorePem = null, enforceNameConstraints = true, strict = true, } = options;
|
|
606
|
+
if (!jwk || typeof jwk !== "object") {
|
|
607
|
+
const error = "Invalid JWK object";
|
|
608
|
+
if (strict) {
|
|
609
|
+
throw new Error(error);
|
|
610
|
+
}
|
|
611
|
+
return { isValid: false, error };
|
|
546
612
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
if (!commonName || typeof commonName !== "string") {
|
|
551
|
-
throw new Error("commonName must be a non-empty string");
|
|
613
|
+
const x5c = jwk.x5c;
|
|
614
|
+
if (x5c === undefined) {
|
|
615
|
+
return { isValid: true };
|
|
552
616
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}
|
|
562
|
-
function arrayBufferToBase64(buffer) {
|
|
563
|
-
const bytes = new Uint8Array(buffer);
|
|
564
|
-
if (typeof globalThis.Buffer?.from === "function") {
|
|
565
|
-
return globalThis.Buffer.from(bytes).toString("base64");
|
|
617
|
+
if (!Array.isArray(x5c) ||
|
|
618
|
+
x5c.length === 0 ||
|
|
619
|
+
x5c.some((entry) => typeof entry !== "string")) {
|
|
620
|
+
const error = "Invalid x5c field in JWK";
|
|
621
|
+
if (strict) {
|
|
622
|
+
throw new Error(error);
|
|
623
|
+
}
|
|
624
|
+
return { isValid: false, error };
|
|
566
625
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
626
|
+
try {
|
|
627
|
+
publicKeyFromX5c(x5c, {
|
|
628
|
+
trustStorePem,
|
|
629
|
+
enforceNameConstraints,
|
|
630
|
+
});
|
|
631
|
+
return { isValid: true };
|
|
572
632
|
}
|
|
573
|
-
|
|
574
|
-
|
|
633
|
+
catch (error) {
|
|
634
|
+
const message = error instanceof Error ? error.message : String(error ?? "unknown");
|
|
635
|
+
const normalized = `Certificate validation failed: ${message}`;
|
|
636
|
+
if (strict) {
|
|
637
|
+
throw new Error(normalized);
|
|
638
|
+
}
|
|
639
|
+
return { isValid: false, error: normalized };
|
|
575
640
|
}
|
|
576
|
-
return globalThis.btoa(binary);
|
|
577
641
|
}
|
|
578
|
-
function
|
|
579
|
-
const
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
642
|
+
function validateCertificateChain(parsed, enforceNameConstraints, trustStorePem) {
|
|
643
|
+
const leaf = parsed[0];
|
|
644
|
+
const nowMs = Date.now();
|
|
645
|
+
const notBefore = leaf.certificate.tbsCertificate.validity.notBefore.getTime();
|
|
646
|
+
const notAfter = leaf.certificate.tbsCertificate.validity.notAfter.getTime();
|
|
647
|
+
const notBeforeMs = notBefore.getTime();
|
|
648
|
+
const notAfterMs = notAfter.getTime();
|
|
649
|
+
if (nowMs < notBeforeMs || nowMs > notAfterMs) {
|
|
650
|
+
throw new Error(`Certificate is not currently valid (notBefore: ${notBefore.toISOString()}, notAfter: ${notAfter.toISOString()}, now: ${new Date(nowMs).toISOString()})`);
|
|
583
651
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
const { privateKey, publicKey, commonName } = options;
|
|
589
|
-
if (!(privateKey instanceof CryptoKey) || privateKey.type !== "private") {
|
|
590
|
-
throw new Error("privateKey must be a CryptoKey of type 'private'");
|
|
652
|
+
const issuers = parsed.slice(1);
|
|
653
|
+
if (enforceNameConstraints && issuers.length > 0) {
|
|
654
|
+
const leafUris = extractUrisFromCert(leaf.certificate);
|
|
655
|
+
validateNameConstraints(issuers, leafUris);
|
|
591
656
|
}
|
|
592
|
-
if (
|
|
593
|
-
|
|
657
|
+
if (trustStorePem) {
|
|
658
|
+
validateTrustAnchor(parsed, trustStorePem);
|
|
594
659
|
}
|
|
595
|
-
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
new
|
|
610
|
-
|
|
660
|
+
validateChainContinuity(parsed);
|
|
661
|
+
const publicKey = leaf.subjectPublicKey.slice();
|
|
662
|
+
return {
|
|
663
|
+
publicKey,
|
|
664
|
+
certificate: leaf.certificate,
|
|
665
|
+
notAfter,
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
function parseCertificateChain(x5c) {
|
|
669
|
+
const parsed = [];
|
|
670
|
+
const derChunks = [];
|
|
671
|
+
for (let index = 0; index < x5c.length; index += 1) {
|
|
672
|
+
const entry = x5c[index];
|
|
673
|
+
if (typeof entry !== "string" || entry.trim().length === 0) {
|
|
674
|
+
throw new Error(`Invalid certificate at index ${index}`);
|
|
675
|
+
}
|
|
676
|
+
let der;
|
|
677
|
+
try {
|
|
678
|
+
der = decodeBase64$2(entry);
|
|
679
|
+
}
|
|
680
|
+
catch (error) {
|
|
681
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
682
|
+
throw new Error(`Failed to decode certificate at index ${index}: ${reason}`);
|
|
683
|
+
}
|
|
684
|
+
let certificate;
|
|
685
|
+
try {
|
|
686
|
+
certificate = asn1Schema.AsnConvert.parse(der, asn1X509.Certificate);
|
|
687
|
+
}
|
|
688
|
+
catch (error) {
|
|
689
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
690
|
+
throw new Error(`Failed to parse certificate at index ${index}: ${reason}`);
|
|
691
|
+
}
|
|
692
|
+
parsed.push(createParsedCertificate(certificate, der));
|
|
693
|
+
derChunks.push(der);
|
|
694
|
+
}
|
|
695
|
+
return { parsed, chainBytes: concatBytes(derChunks) };
|
|
696
|
+
}
|
|
697
|
+
function createParsedCertificate(certificate, raw) {
|
|
698
|
+
return {
|
|
699
|
+
raw,
|
|
700
|
+
certificate,
|
|
701
|
+
serialNumber: toHex(new Uint8Array(certificate.tbsCertificate.serialNumber)).toUpperCase(),
|
|
702
|
+
subjectName: serializeName(certificate.tbsCertificate.subject),
|
|
703
|
+
issuerName: serializeName(certificate.tbsCertificate.issuer),
|
|
704
|
+
subjectPublicKey: new Uint8Array(certificate.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey).slice(),
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
function extractUrisFromCert(cert) {
|
|
708
|
+
const extension = findExtension(cert, asn1X509.id_ce_subjectAltName);
|
|
709
|
+
if (!extension) {
|
|
710
|
+
return [];
|
|
711
|
+
}
|
|
712
|
+
const subjectAlternativeName = asn1Schema.AsnConvert.parse(extension.extnValue.buffer, asn1X509.SubjectAlternativeName);
|
|
713
|
+
const uris = [];
|
|
714
|
+
for (const generalName of subjectAlternativeName) {
|
|
715
|
+
if (generalName.uniformResourceIdentifier) {
|
|
716
|
+
uris.push(generalName.uniformResourceIdentifier);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return uris;
|
|
720
|
+
}
|
|
721
|
+
function validateNameConstraints(issuers, leafUris) {
|
|
722
|
+
for (const issuer of issuers) {
|
|
723
|
+
const extension = findExtension(issuer.certificate, asn1X509.id_ce_nameConstraints);
|
|
724
|
+
if (!extension) {
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
const constraints = asn1Schema.AsnConvert.parse(extension.extnValue.buffer, asn1X509.NameConstraints);
|
|
728
|
+
if (!constraints.permittedSubtrees) {
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
const permittedUris = collectPermittedUris(Array.from(constraints.permittedSubtrees));
|
|
732
|
+
if (permittedUris.length === 0) {
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
for (const uri of leafUris) {
|
|
736
|
+
const allowed = permittedUris.some((prefix) => uri.startsWith(prefix));
|
|
737
|
+
if (!allowed) {
|
|
738
|
+
throw new Error(`URI '${uri}' violates name constraints - not in permitted subtrees: ${permittedUris.join(", ")}`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
function collectPermittedUris(subtrees) {
|
|
744
|
+
const uris = [];
|
|
745
|
+
for (const subtree of subtrees) {
|
|
746
|
+
if (subtree.base.uniformResourceIdentifier &&
|
|
747
|
+
subtree.base.uniformResourceIdentifier.length > 0) {
|
|
748
|
+
uris.push(subtree.base.uniformResourceIdentifier);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return uris;
|
|
752
|
+
}
|
|
753
|
+
function validateTrustAnchor(chain, trustStorePem) {
|
|
754
|
+
const trustedCerts = parseTrustStore(trustStorePem);
|
|
755
|
+
if (trustedCerts.length === 0) {
|
|
756
|
+
throw new Error("No valid certificates found in trust store");
|
|
757
|
+
}
|
|
758
|
+
logger$g.debug("trust_anchor_validation_start", {
|
|
759
|
+
chain_length: chain.length,
|
|
760
|
+
trust_store_cert_count: trustedCerts.length,
|
|
761
|
+
});
|
|
762
|
+
const chainInfo = chain.map((cert, index) => `[${index}] ${cert.subjectName} (Serial: ${cert.serialNumber})`);
|
|
763
|
+
const trustedInfo = trustedCerts.map((cert, index) => `[${index}] ${cert.subjectName} (Serial: ${cert.serialNumber})`);
|
|
764
|
+
logger$g.debug("certificate_chain_validation", {
|
|
765
|
+
chain_certificates: chainInfo,
|
|
766
|
+
trust_store_certificates: trustedInfo,
|
|
767
|
+
});
|
|
768
|
+
// Strategy 1: direct trust (exact certificate match)
|
|
769
|
+
for (let i = 0; i < chain.length; i += 1) {
|
|
770
|
+
const cert = chain[i];
|
|
771
|
+
const match = trustedCerts.find((trusted) => trusted.serialNumber === cert.serialNumber &&
|
|
772
|
+
namesEqual(trusted.certificate.tbsCertificate.subject, cert.certificate.tbsCertificate.subject));
|
|
773
|
+
if (match) {
|
|
774
|
+
logger$g.debug("certificate_chain_trust_validation_passed", {
|
|
775
|
+
matching_serial: match.serialNumber,
|
|
776
|
+
validation_strategy: `direct_trust_cert_${i}`,
|
|
777
|
+
});
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
const leaf = chain[0];
|
|
782
|
+
// Strategy 2: leaf issuer in trust store
|
|
783
|
+
for (const trusted of trustedCerts) {
|
|
784
|
+
if (namesEqual(trusted.certificate.tbsCertificate.subject, leaf.certificate.tbsCertificate.issuer) &&
|
|
785
|
+
trusted.serialNumber !== leaf.serialNumber) {
|
|
786
|
+
verifyCertificateSignature(leaf.certificate, trusted.certificate);
|
|
787
|
+
logger$g.debug("certificate_chain_trust_validation_passed", {
|
|
788
|
+
matching_serial: trusted.serialNumber,
|
|
789
|
+
validation_strategy: "leaf_issuer_trust",
|
|
790
|
+
});
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
// Strategy 3: any intermediate issuer in trust store
|
|
795
|
+
for (let index = 1; index < chain.length; index += 1) {
|
|
796
|
+
const intermediate = chain[index];
|
|
797
|
+
for (const trusted of trustedCerts) {
|
|
798
|
+
if (namesEqual(trusted.certificate.tbsCertificate.subject, intermediate.certificate.tbsCertificate.issuer) &&
|
|
799
|
+
trusted.serialNumber !== intermediate.serialNumber) {
|
|
800
|
+
verifyCertificateSignature(intermediate.certificate, trusted.certificate);
|
|
801
|
+
logger$g.debug("certificate_chain_trust_validation_passed", {
|
|
802
|
+
matching_serial: trusted.serialNumber,
|
|
803
|
+
validation_strategy: `intermediate_issuer_trust_cert_${index}`,
|
|
804
|
+
});
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
logger$g.warning("certificate_chain_trust_validation_failed", {
|
|
810
|
+
leaf_subject: leaf.subjectName,
|
|
811
|
+
leaf_issuer: leaf.issuerName,
|
|
812
|
+
leaf_serial: leaf.serialNumber,
|
|
813
|
+
trusted_certificates: trustedInfo,
|
|
814
|
+
chain_certificates: chainInfo,
|
|
815
|
+
reason: "no_matching_trust_anchor",
|
|
816
|
+
});
|
|
817
|
+
throw new Error("Certificate chain is not rooted in a trusted anchor");
|
|
818
|
+
}
|
|
819
|
+
function parseTrustStore(trustStorePem) {
|
|
820
|
+
const normalized = normalizePem$2(trustStorePem);
|
|
821
|
+
const blocks = extractPemBlocks$1(normalized);
|
|
822
|
+
const parsed = [];
|
|
823
|
+
for (const block of blocks) {
|
|
824
|
+
try {
|
|
825
|
+
const der = decodeBase64$2(block);
|
|
826
|
+
const certificate = asn1Schema.AsnConvert.parse(der, asn1X509.Certificate);
|
|
827
|
+
parsed.push(createParsedCertificate(certificate, der));
|
|
828
|
+
}
|
|
829
|
+
catch (error) {
|
|
830
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
831
|
+
logger$g.debug("trust_store_certificate_parse_failed", { reason });
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
return parsed;
|
|
835
|
+
}
|
|
836
|
+
function extractPemBlocks$1(pem) {
|
|
837
|
+
const blocks = [];
|
|
838
|
+
const regex = /-----BEGIN CERTIFICATE-----([\s\S]*?)-----END CERTIFICATE-----/gu;
|
|
839
|
+
let match;
|
|
840
|
+
// eslint-disable-next-line no-cond-assign
|
|
841
|
+
while ((match = regex.exec(pem)) !== null) {
|
|
842
|
+
const body = match[1] ?? "";
|
|
843
|
+
blocks.push(body.replace(/\s+/gu, ""));
|
|
844
|
+
}
|
|
845
|
+
return blocks;
|
|
846
|
+
}
|
|
847
|
+
function validateChainContinuity(chain) {
|
|
848
|
+
if (chain.length <= 1) {
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
logger$g.debug("validating_chain_continuity", { chain_length: chain.length });
|
|
852
|
+
for (let index = 0; index < chain.length - 1; index += 1) {
|
|
853
|
+
const cert = chain[index];
|
|
854
|
+
const issuer = chain[index + 1];
|
|
855
|
+
if (!namesEqual(cert.certificate.tbsCertificate.issuer, issuer.certificate.tbsCertificate.subject)) {
|
|
856
|
+
logger$g.warning("certificate_chain_continuity_failed", {
|
|
857
|
+
cert_index: index,
|
|
858
|
+
cert_subject: cert.subjectName,
|
|
859
|
+
cert_issuer: cert.issuerName,
|
|
860
|
+
expected_issuer_subject: issuer.subjectName,
|
|
861
|
+
reason: "issuer_name_mismatch",
|
|
862
|
+
});
|
|
863
|
+
throw new Error(`Certificate chain continuity broken: certificate at index ${index} issuer does not match next certificate subject`);
|
|
864
|
+
}
|
|
865
|
+
try {
|
|
866
|
+
verifyCertificateSignature(cert.certificate, issuer.certificate);
|
|
867
|
+
logger$g.debug("chain_continuity_verification_success", {
|
|
868
|
+
cert_index: index,
|
|
869
|
+
cert_serial: cert.serialNumber,
|
|
870
|
+
issuer_serial: issuer.serialNumber,
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
catch (error) {
|
|
874
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
875
|
+
logger$g.warning("certificate_chain_continuity_failed", {
|
|
876
|
+
cert_index: index,
|
|
877
|
+
cert_subject: cert.subjectName,
|
|
878
|
+
issuer_subject: issuer.subjectName,
|
|
879
|
+
cert_serial: cert.serialNumber,
|
|
880
|
+
issuer_serial: issuer.serialNumber,
|
|
881
|
+
error: reason,
|
|
882
|
+
reason: "signature_verification_failed",
|
|
883
|
+
});
|
|
884
|
+
throw new Error(`Certificate chain continuity broken: certificate at index ${index} was not signed by certificate at index ${index + 1}: ${reason}`);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
logger$g.debug("chain_continuity_validation_passed", {
|
|
888
|
+
chain_length: chain.length,
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
function verifyCertificateSignature(certificate, issuer) {
|
|
892
|
+
ensureEd25519Support();
|
|
893
|
+
const signatureAlgorithm = certificate.signatureAlgorithm.algorithm;
|
|
894
|
+
const issuerAlgorithm = issuer.tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm;
|
|
895
|
+
if (signatureAlgorithm !== OID_ED25519 || issuerAlgorithm !== OID_ED25519) {
|
|
896
|
+
throw new Error(`Unsupported signature algorithm (certificate: ${signatureAlgorithm}, issuer: ${issuerAlgorithm})`);
|
|
897
|
+
}
|
|
898
|
+
const signatureBytes = new Uint8Array(certificate.signatureValue);
|
|
899
|
+
const tbsBytes = new Uint8Array(asn1Schema.AsnConvert.serialize(certificate.tbsCertificate));
|
|
900
|
+
const issuerKey = new Uint8Array(issuer.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey);
|
|
901
|
+
if (issuerKey.length !== 32) {
|
|
902
|
+
throw new Error("Issuer Ed25519 public key must be 32 bytes");
|
|
903
|
+
}
|
|
904
|
+
const valid = ed25519.verify(signatureBytes, tbsBytes, issuerKey);
|
|
905
|
+
if (!valid) {
|
|
906
|
+
throw new Error("Certificate signature verification failed");
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
function ensureEd25519Support() {
|
|
910
|
+
const etcPatch = ed25519.etc;
|
|
911
|
+
if (!etcPatch.sha512) {
|
|
912
|
+
etcPatch.sha512 = (message) => sha2_js.sha512(message);
|
|
913
|
+
}
|
|
914
|
+
if (!etcPatch.sha512Sync) {
|
|
915
|
+
etcPatch.sha512Sync = (...messages) => {
|
|
916
|
+
if (messages.length === 1) {
|
|
917
|
+
return sha2_js.sha512(messages[0]);
|
|
918
|
+
}
|
|
919
|
+
const combined = ed25519.etc.concatBytes(...messages);
|
|
920
|
+
return sha2_js.sha512(combined);
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
function findExtension(certificate, oid) {
|
|
925
|
+
const extensions = certificate.tbsCertificate.extensions;
|
|
926
|
+
if (!extensions) {
|
|
927
|
+
return null;
|
|
928
|
+
}
|
|
929
|
+
for (const extension of extensions) {
|
|
930
|
+
if (extension.extnID === oid) {
|
|
931
|
+
return extension;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
return null;
|
|
935
|
+
}
|
|
936
|
+
function namesEqual(a, b) {
|
|
937
|
+
const left = new Uint8Array(asn1Schema.AsnConvert.serialize(a));
|
|
938
|
+
const right = new Uint8Array(asn1Schema.AsnConvert.serialize(b));
|
|
939
|
+
if (left.length !== right.length) {
|
|
940
|
+
return false;
|
|
941
|
+
}
|
|
942
|
+
for (let i = 0; i < left.length; i += 1) {
|
|
943
|
+
if (left[i] !== right[i]) {
|
|
944
|
+
return false;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
return true;
|
|
948
|
+
}
|
|
949
|
+
function serializeName(name) {
|
|
950
|
+
const rdns = Array.from(name);
|
|
951
|
+
return rdns
|
|
952
|
+
.map((rdn) => Array.from(rdn)
|
|
953
|
+
.map((attr) => `${oidToLabel$1(attr.type)}=${attr.value.toString()}`)
|
|
954
|
+
.join("+"))
|
|
955
|
+
.join(",");
|
|
956
|
+
}
|
|
957
|
+
function oidToLabel$1(oid) {
|
|
958
|
+
switch (oid) {
|
|
959
|
+
case "2.5.4.3":
|
|
960
|
+
return "CN";
|
|
961
|
+
case "2.5.4.6":
|
|
962
|
+
return "C";
|
|
963
|
+
case "2.5.4.7":
|
|
964
|
+
return "L";
|
|
965
|
+
case "2.5.4.8":
|
|
966
|
+
return "ST";
|
|
967
|
+
case "2.5.4.10":
|
|
968
|
+
return "O";
|
|
969
|
+
case "2.5.4.11":
|
|
970
|
+
return "OU";
|
|
971
|
+
default:
|
|
972
|
+
return oid;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
function concatBytes(chunks) {
|
|
976
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
977
|
+
const result = new Uint8Array(totalLength);
|
|
978
|
+
let offset = 0;
|
|
979
|
+
for (const chunk of chunks) {
|
|
980
|
+
result.set(chunk, offset);
|
|
981
|
+
offset += chunk.length;
|
|
982
|
+
}
|
|
983
|
+
return result;
|
|
984
|
+
}
|
|
985
|
+
function decodeBase64$2(input) {
|
|
986
|
+
if (typeof Buffer !== "undefined") {
|
|
987
|
+
const normalized = input.replace(/\s+/gu, "");
|
|
988
|
+
return new Uint8Array(Buffer.from(normalized, "base64"));
|
|
989
|
+
}
|
|
990
|
+
if (typeof atob === "function") {
|
|
991
|
+
const normalized = input.replace(/\s+/gu, "");
|
|
992
|
+
const binary = atob(normalized);
|
|
993
|
+
const bytes = new Uint8Array(binary.length);
|
|
994
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
995
|
+
bytes[i] = binary.charCodeAt(i);
|
|
996
|
+
}
|
|
997
|
+
return bytes;
|
|
998
|
+
}
|
|
999
|
+
throw new Error("No base64 decoder available in this environment");
|
|
1000
|
+
}
|
|
1001
|
+
function toHex(data) {
|
|
1002
|
+
return Array.from(data)
|
|
1003
|
+
.map((byte) => byte.toString(16).padStart(2, "0"))
|
|
1004
|
+
.join("");
|
|
1005
|
+
}
|
|
1006
|
+
function buildCacheKey(chainBytes, trustStorePem, enforceNameConstraints) {
|
|
1007
|
+
const chainHash = toHex(sha2_js.sha256(chainBytes));
|
|
1008
|
+
const trustHash = trustStorePem
|
|
1009
|
+
? toHex(sha2_js.sha256(textEncoder.encode(trustStorePem)))
|
|
1010
|
+
: "no-trust";
|
|
1011
|
+
const constraintFlag = enforceNameConstraints ? "nc1" : "nc0";
|
|
1012
|
+
return `${chainHash}|${trustHash}|${constraintFlag}`;
|
|
1013
|
+
}
|
|
1014
|
+
function getCachedPublicKey(cacheKey) {
|
|
1015
|
+
const entry = trustCache.get(cacheKey);
|
|
1016
|
+
if (!entry) {
|
|
1017
|
+
return null;
|
|
1018
|
+
}
|
|
1019
|
+
if (Date.now() > entry.expiresAt) {
|
|
1020
|
+
trustCache.delete(cacheKey);
|
|
1021
|
+
logger$g.debug("certificate_cache_expired", { cache_key: cacheKey });
|
|
1022
|
+
return null;
|
|
1023
|
+
}
|
|
1024
|
+
return entry.value.slice();
|
|
1025
|
+
}
|
|
1026
|
+
function setCachedPublicKey(cacheKey, value, notAfter) {
|
|
1027
|
+
while (trustCache.size >= CACHE_LIMIT) {
|
|
1028
|
+
const firstKey = trustCache.keys().next().value;
|
|
1029
|
+
if (firstKey === undefined) {
|
|
1030
|
+
break;
|
|
1031
|
+
}
|
|
1032
|
+
trustCache.delete(firstKey);
|
|
1033
|
+
logger$g.debug("certificate_cache_evicted", { cache_key: firstKey });
|
|
1034
|
+
}
|
|
1035
|
+
trustCache.set(cacheKey, {
|
|
1036
|
+
value: value.slice(),
|
|
1037
|
+
expiresAt: notAfter.getTime(),
|
|
1038
|
+
});
|
|
1039
|
+
logger$g.debug("certificate_cache_stored", {
|
|
1040
|
+
cache_key: cacheKey,
|
|
1041
|
+
expires_at: notAfter.toISOString(),
|
|
1042
|
+
cache_size: trustCache.size,
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
function normalizeTrustStoreOption(value) {
|
|
1046
|
+
if (!value) {
|
|
1047
|
+
return null;
|
|
1048
|
+
}
|
|
1049
|
+
const trimmed = value.trim();
|
|
1050
|
+
if (trimmed.length === 0) {
|
|
1051
|
+
return null;
|
|
1052
|
+
}
|
|
1053
|
+
if (!trimmed.includes("-----BEGIN CERTIFICATE-----")) {
|
|
1054
|
+
throw new Error("trustStorePem must contain PEM-encoded certificates when provided");
|
|
1055
|
+
}
|
|
1056
|
+
return normalizePem$2(trimmed);
|
|
1057
|
+
}
|
|
1058
|
+
function normalizePem$2(pem) {
|
|
1059
|
+
return pem.replace(/\r/gu, "").trim();
|
|
1060
|
+
}
|
|
1061
|
+
function generateCallId() {
|
|
1062
|
+
return Math.random().toString(36).slice(2, 10);
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
const GRANT_PURPOSE_CA_SIGN = "ca_sign";
|
|
1066
|
+
|
|
1067
|
+
const ED25519_OID$2 = "1.3.101.112";
|
|
1068
|
+
const OID_COMMON_NAME$2 = "2.5.4.3";
|
|
1069
|
+
const LOGICAL_URI_PREFIX$1 = "naylence://";
|
|
1070
|
+
function ensureSubtleCrypto() {
|
|
1071
|
+
const instance = globalThis.crypto?.subtle;
|
|
1072
|
+
if (!instance) {
|
|
1073
|
+
throw new Error("WebCrypto subtle API is required to create a CSR");
|
|
1074
|
+
}
|
|
1075
|
+
return instance;
|
|
1076
|
+
}
|
|
1077
|
+
function buildSubject(commonName) {
|
|
1078
|
+
if (!commonName || typeof commonName !== "string") {
|
|
1079
|
+
throw new Error("commonName must be a non-empty string");
|
|
1080
|
+
}
|
|
1081
|
+
return new asn1X509.Name([
|
|
1082
|
+
new asn1X509.RelativeDistinguishedName([
|
|
1083
|
+
new asn1X509.AttributeTypeAndValue({
|
|
1084
|
+
type: OID_COMMON_NAME$2,
|
|
1085
|
+
value: new asn1X509.AttributeValue({ utf8String: commonName }),
|
|
1086
|
+
}),
|
|
1087
|
+
]),
|
|
1088
|
+
]);
|
|
1089
|
+
}
|
|
1090
|
+
function arrayBufferToBase64(buffer) {
|
|
1091
|
+
const bytes = new Uint8Array(buffer);
|
|
1092
|
+
if (typeof globalThis.Buffer?.from === "function") {
|
|
1093
|
+
return globalThis.Buffer.from(bytes).toString("base64");
|
|
1094
|
+
}
|
|
1095
|
+
let binary = "";
|
|
1096
|
+
const chunkSize = 0x8000;
|
|
1097
|
+
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
1098
|
+
const slice = bytes.subarray(offset, offset + chunkSize);
|
|
1099
|
+
binary += String.fromCharCode(...slice);
|
|
1100
|
+
}
|
|
1101
|
+
if (typeof globalThis.btoa !== "function") {
|
|
1102
|
+
throw new Error("Base64 encoding not available in this environment");
|
|
1103
|
+
}
|
|
1104
|
+
return globalThis.btoa(binary);
|
|
1105
|
+
}
|
|
1106
|
+
function derToPem$1(der, label) {
|
|
1107
|
+
const base64 = arrayBufferToBase64(der);
|
|
1108
|
+
const lines = [];
|
|
1109
|
+
for (let index = 0; index < base64.length; index += 64) {
|
|
1110
|
+
lines.push(base64.slice(index, index + 64));
|
|
1111
|
+
}
|
|
1112
|
+
return `-----BEGIN ${label}-----\n${lines.join("\n")}\n-----END ${label}-----\n`;
|
|
1113
|
+
}
|
|
1114
|
+
async function createEd25519Csr(options) {
|
|
1115
|
+
const subtle = ensureSubtleCrypto();
|
|
1116
|
+
const { privateKey, publicKey, commonName } = options;
|
|
1117
|
+
if (!(privateKey instanceof CryptoKey) || privateKey.type !== "private") {
|
|
1118
|
+
throw new Error("privateKey must be a CryptoKey of type 'private'");
|
|
1119
|
+
}
|
|
1120
|
+
if (!(publicKey instanceof CryptoKey) || publicKey.type !== "public") {
|
|
1121
|
+
throw new Error("publicKey must be a CryptoKey of type 'public'");
|
|
1122
|
+
}
|
|
1123
|
+
const subject = buildSubject(commonName);
|
|
1124
|
+
const spkiDer = await subtle.exportKey("spki", publicKey);
|
|
1125
|
+
const subjectPublicKeyInfo = asn1Schema.AsnConvert.parse(spkiDer, asn1X509.SubjectPublicKeyInfo);
|
|
1126
|
+
const attributes = new asn1Csr.Attributes();
|
|
1127
|
+
const sanitizedLogicals = Array.isArray(options.logicals)
|
|
1128
|
+
? options.logicals
|
|
1129
|
+
.map((logical) => logical.trim())
|
|
1130
|
+
.filter((logical) => logical.length > 0)
|
|
1131
|
+
: [];
|
|
1132
|
+
if (sanitizedLogicals.length > 0) {
|
|
1133
|
+
const san = new asn1X509.SubjectAlternativeName(sanitizedLogicals.map((logical) => new asn1X509.GeneralName({
|
|
1134
|
+
uniformResourceIdentifier: `${LOGICAL_URI_PREFIX$1}${logical}`,
|
|
1135
|
+
})));
|
|
1136
|
+
const extensions = new asn1X509.Extensions([
|
|
1137
|
+
new asn1X509.Extension({
|
|
1138
|
+
extnID: asn1X509.id_ce_subjectAltName,
|
|
611
1139
|
critical: false,
|
|
612
1140
|
extnValue: new asn1Schema.OctetString(asn1Schema.AsnConvert.serialize(san)),
|
|
613
1141
|
}),
|
|
@@ -688,7 +1216,7 @@ const NODE_ID_OID = "1.3.6.1.4.1.58530.4";
|
|
|
688
1216
|
* Provides async HTTP client to request certificates from the CA signing service.
|
|
689
1217
|
*/
|
|
690
1218
|
// Simple logger for now - TODO: integrate with runtime logging
|
|
691
|
-
const logger$
|
|
1219
|
+
const logger$f = {
|
|
692
1220
|
debug: (_event, _meta) => {
|
|
693
1221
|
// console.log(`[DEBUG] ${event}`, meta);
|
|
694
1222
|
},
|
|
@@ -1137,7 +1665,7 @@ class CAServiceClient {
|
|
|
1137
1665
|
logicals: logicals || [],
|
|
1138
1666
|
};
|
|
1139
1667
|
const url = `${this.connectionGrant.url.replace(/\/$/, "")}/sign`;
|
|
1140
|
-
logger$
|
|
1668
|
+
logger$f.debug("requesting_certificate", {
|
|
1141
1669
|
requester_id: requesterId,
|
|
1142
1670
|
ca_service_url: url,
|
|
1143
1671
|
physical_path: physicalPath,
|
|
@@ -1166,13 +1694,13 @@ class CAServiceClient {
|
|
|
1166
1694
|
const result = await response.json();
|
|
1167
1695
|
const certificatePem = result.certificate_pem;
|
|
1168
1696
|
const certificateChainPem = result.certificate_chain_pem || certificatePem;
|
|
1169
|
-
logger$
|
|
1697
|
+
logger$f.debug("certificate_request_successful", {
|
|
1170
1698
|
requester_id: requesterId,
|
|
1171
1699
|
expires_at: result.expires_at,
|
|
1172
1700
|
});
|
|
1173
1701
|
// Extract and log certificate information with structured logging
|
|
1174
1702
|
const certInfo = extractCertificateInfo(certificatePem);
|
|
1175
|
-
logger$
|
|
1703
|
+
logger$f.debug("certificate_details", {
|
|
1176
1704
|
requester_id: requesterId,
|
|
1177
1705
|
certificate_type: "issued_certificate",
|
|
1178
1706
|
...certInfo,
|
|
@@ -1191,7 +1719,7 @@ class CAServiceClient {
|
|
|
1191
1719
|
// First cert in chain is usually the issued certificate
|
|
1192
1720
|
if (certPemBlock.trim() !== certificatePem.trim()) {
|
|
1193
1721
|
const chainCertInfo = extractCertificateInfo(certPemBlock);
|
|
1194
|
-
logger$
|
|
1722
|
+
logger$f.debug("certificate_chain_details", {
|
|
1195
1723
|
requester_id: requesterId,
|
|
1196
1724
|
certificate_type: "certificate_chain",
|
|
1197
1725
|
chain_index: i,
|
|
@@ -1202,7 +1730,7 @@ class CAServiceClient {
|
|
|
1202
1730
|
else {
|
|
1203
1731
|
// Subsequent certs are intermediate/root CAs
|
|
1204
1732
|
const caCertInfo = extractCertificateInfo(certPemBlock);
|
|
1205
|
-
logger$
|
|
1733
|
+
logger$f.debug("certificate_chain_details", {
|
|
1206
1734
|
requester_id: requesterId,
|
|
1207
1735
|
certificate_type: "ca_certificate",
|
|
1208
1736
|
chain_index: i,
|
|
@@ -1230,7 +1758,7 @@ class CAServiceClient {
|
|
|
1230
1758
|
// Body read failed entirely
|
|
1231
1759
|
errorDetail = `HTTP ${response.status}`;
|
|
1232
1760
|
}
|
|
1233
|
-
logger$
|
|
1761
|
+
logger$f.error("certificate_request_failed", {
|
|
1234
1762
|
requester_id: requesterId,
|
|
1235
1763
|
status_code: response.status,
|
|
1236
1764
|
error: errorDetail,
|
|
@@ -1247,13 +1775,13 @@ class CAServiceClient {
|
|
|
1247
1775
|
throw error;
|
|
1248
1776
|
}
|
|
1249
1777
|
if (error instanceof Error && error.name === "AbortError") {
|
|
1250
|
-
logger$
|
|
1778
|
+
logger$f.error("certificate_request_timeout", {
|
|
1251
1779
|
requester_id: requesterId,
|
|
1252
1780
|
timeout_seconds: this.timeoutSeconds,
|
|
1253
1781
|
});
|
|
1254
1782
|
throw new CertificateRequestError(`Certificate request timed out after ${this.timeoutSeconds} seconds`);
|
|
1255
1783
|
}
|
|
1256
|
-
logger$
|
|
1784
|
+
logger$f.error("certificate_request_network_error", {
|
|
1257
1785
|
requester_id: requesterId,
|
|
1258
1786
|
error: String(error),
|
|
1259
1787
|
});
|
|
@@ -1262,7 +1790,7 @@ class CAServiceClient {
|
|
|
1262
1790
|
}
|
|
1263
1791
|
}
|
|
1264
1792
|
|
|
1265
|
-
const logger$
|
|
1793
|
+
const logger$e = runtime.getLogger("naylence.fame.security.encryption.sealed.x25519_encryption_manager");
|
|
1266
1794
|
class X25519EncryptionManager {
|
|
1267
1795
|
constructor({ keyProvider, nodeLike = null, cryptoProvider = null, }) {
|
|
1268
1796
|
this.pendingEnvelopes = new Map();
|
|
@@ -1279,7 +1807,7 @@ class X25519EncryptionManager {
|
|
|
1279
1807
|
// KeyManagementHandler will queue the envelope and send KeyRequest.
|
|
1280
1808
|
// X25519 should NOT queue here to avoid dual queueing.
|
|
1281
1809
|
if (opts?.requestAddress) {
|
|
1282
|
-
logger$
|
|
1810
|
+
logger$e.debug("key_not_found_delegating_to_key_management", {
|
|
1283
1811
|
envelope_id: envelope.id,
|
|
1284
1812
|
request_address: String(opts.requestAddress),
|
|
1285
1813
|
});
|
|
@@ -1295,7 +1823,7 @@ class X25519EncryptionManager {
|
|
|
1295
1823
|
return await this.encryptWithKey(envelope, recipPub, recipKid);
|
|
1296
1824
|
}
|
|
1297
1825
|
catch (error) {
|
|
1298
|
-
logger$
|
|
1826
|
+
logger$e.error("x25519_encryption_failed", {
|
|
1299
1827
|
error: error instanceof Error ? error.message : String(error),
|
|
1300
1828
|
});
|
|
1301
1829
|
return runtime.EncryptionResult.skipped(envelope);
|
|
@@ -1333,20 +1861,20 @@ class X25519EncryptionManager {
|
|
|
1333
1861
|
return envelope;
|
|
1334
1862
|
}
|
|
1335
1863
|
catch (error) {
|
|
1336
|
-
logger$
|
|
1864
|
+
logger$e.error("x25519_decryption_failed", {
|
|
1337
1865
|
error: error instanceof Error ? error.message : String(error),
|
|
1338
1866
|
});
|
|
1339
1867
|
return envelope;
|
|
1340
1868
|
}
|
|
1341
1869
|
}
|
|
1342
1870
|
async notifyKeyAvailable(keyId) {
|
|
1343
|
-
logger$
|
|
1871
|
+
logger$e.debug("x25519_notify_key_available_called", {
|
|
1344
1872
|
key_id: keyId,
|
|
1345
1873
|
pending_keys: Array.from(this.pendingEnvelopes.keys()),
|
|
1346
1874
|
});
|
|
1347
1875
|
const queued = this.pendingEnvelopes.get(keyId);
|
|
1348
1876
|
if (!queued || queued.length === 0) {
|
|
1349
|
-
logger$
|
|
1877
|
+
logger$e.debug("no_queued_envelopes_for_key", {
|
|
1350
1878
|
key_id: keyId,
|
|
1351
1879
|
has_queue: this.pendingEnvelopes.has(keyId),
|
|
1352
1880
|
queue_length: queued?.length ?? 0,
|
|
@@ -1358,13 +1886,13 @@ class X25519EncryptionManager {
|
|
|
1358
1886
|
this.keyRequestsInProgress.delete(keyId);
|
|
1359
1887
|
const node = this.nodeLike;
|
|
1360
1888
|
if (!node) {
|
|
1361
|
-
logger$
|
|
1889
|
+
logger$e.debug("discarding_queued_envelopes_no_node", {
|
|
1362
1890
|
key_id: keyId,
|
|
1363
1891
|
count: queued.length,
|
|
1364
1892
|
});
|
|
1365
1893
|
return;
|
|
1366
1894
|
}
|
|
1367
|
-
logger$
|
|
1895
|
+
logger$e.debug("replaying_envelopes_for_key", {
|
|
1368
1896
|
key_id: keyId,
|
|
1369
1897
|
count: queued.length,
|
|
1370
1898
|
});
|
|
@@ -1373,7 +1901,7 @@ class X25519EncryptionManager {
|
|
|
1373
1901
|
await node.deliver(envelope);
|
|
1374
1902
|
}
|
|
1375
1903
|
catch (error) {
|
|
1376
|
-
logger$
|
|
1904
|
+
logger$e.error("failed_to_replay_envelope", {
|
|
1377
1905
|
key_id: keyId,
|
|
1378
1906
|
envelope_id: envelope.id,
|
|
1379
1907
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -1474,7 +2002,7 @@ class X25519EncryptionManager {
|
|
|
1474
2002
|
? this.extractPrivateKeyFromRecord(providerRecord)
|
|
1475
2003
|
: null;
|
|
1476
2004
|
if (providerRecordKey) {
|
|
1477
|
-
logger$
|
|
2005
|
+
logger$e.debug("using_provider_key_record_private_key", {
|
|
1478
2006
|
kid,
|
|
1479
2007
|
provider_key_id: providerKeyId,
|
|
1480
2008
|
mismatched_kid: kid && providerKeyId !== kid ? kid : null,
|
|
@@ -1484,7 +2012,7 @@ class X25519EncryptionManager {
|
|
|
1484
2012
|
}
|
|
1485
2013
|
if (!providerPem) {
|
|
1486
2014
|
if (kid && providerKeyId && providerKeyId !== kid) {
|
|
1487
|
-
logger$
|
|
2015
|
+
logger$e.debug("crypto_provider_key_id_mismatch_no_private_key", {
|
|
1488
2016
|
kid,
|
|
1489
2017
|
provider_key_id: providerKeyId,
|
|
1490
2018
|
});
|
|
@@ -1496,13 +2024,13 @@ class X25519EncryptionManager {
|
|
|
1496
2024
|
return null;
|
|
1497
2025
|
}
|
|
1498
2026
|
if (!kid || providerKeyId === kid) {
|
|
1499
|
-
logger$
|
|
2027
|
+
logger$e.debug("using_crypto_provider_private_key_fallback", {
|
|
1500
2028
|
kid: kid ?? null,
|
|
1501
2029
|
provider_key_id: providerKeyId,
|
|
1502
2030
|
});
|
|
1503
2031
|
}
|
|
1504
2032
|
else {
|
|
1505
|
-
logger$
|
|
2033
|
+
logger$e.warning("crypto_provider_key_id_mismatch_using_private_key", {
|
|
1506
2034
|
kid,
|
|
1507
2035
|
provider_key_id: providerKeyId,
|
|
1508
2036
|
key_record_present: Boolean(record),
|
|
@@ -1511,7 +2039,7 @@ class X25519EncryptionManager {
|
|
|
1511
2039
|
return fallbackKey;
|
|
1512
2040
|
}
|
|
1513
2041
|
async queueEnvelopeForKey(envelope, opts, recipientKeyId) {
|
|
1514
|
-
logger$
|
|
2042
|
+
logger$e.debug("queueing_envelope_for_sealed_encryption", {
|
|
1515
2043
|
envelope_id: envelope.id,
|
|
1516
2044
|
recipient_key_id: recipientKeyId,
|
|
1517
2045
|
request_address: opts?.requestAddress
|
|
@@ -1559,7 +2087,7 @@ class X25519EncryptionManager {
|
|
|
1559
2087
|
await node.deliver(keyRequestEnvelope, context);
|
|
1560
2088
|
}
|
|
1561
2089
|
catch (error) {
|
|
1562
|
-
logger$
|
|
2090
|
+
logger$e.error("failed_to_request_recipient_key", {
|
|
1563
2091
|
recipient_key_id: recipientKeyId,
|
|
1564
2092
|
error: error instanceof Error ? error.message : String(error),
|
|
1565
2093
|
});
|
|
@@ -1572,7 +2100,7 @@ class X25519EncryptionManager {
|
|
|
1572
2100
|
return this.extractPublicKeyFromRecord(record);
|
|
1573
2101
|
}
|
|
1574
2102
|
catch (error) {
|
|
1575
|
-
logger$
|
|
2103
|
+
logger$e.debug("recipient_key_lookup_failed", {
|
|
1576
2104
|
kid,
|
|
1577
2105
|
error: error instanceof Error ? error.message : String(error),
|
|
1578
2106
|
});
|
|
@@ -1587,7 +2115,7 @@ class X25519EncryptionManager {
|
|
|
1587
2115
|
return await this.keyProvider.getKey(kid);
|
|
1588
2116
|
}
|
|
1589
2117
|
catch (error) {
|
|
1590
|
-
logger$
|
|
2118
|
+
logger$e.debug("private_key_lookup_failed", {
|
|
1591
2119
|
kid,
|
|
1592
2120
|
error: error instanceof Error ? error.message : String(error),
|
|
1593
2121
|
});
|
|
@@ -1658,7 +2186,7 @@ class X25519EncryptionManager {
|
|
|
1658
2186
|
const base64 = base64Lines.join("");
|
|
1659
2187
|
const der = this.decodeBase64Flexible(base64);
|
|
1660
2188
|
if (!der) {
|
|
1661
|
-
logger$
|
|
2189
|
+
logger$e.debug("pem_decode_failed", {
|
|
1662
2190
|
key_type: keyType,
|
|
1663
2191
|
});
|
|
1664
2192
|
return null;
|
|
@@ -1891,7 +2419,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
|
|
|
1891
2419
|
X25519EncryptionManagerFactory: X25519EncryptionManagerFactory
|
|
1892
2420
|
});
|
|
1893
2421
|
|
|
1894
|
-
const logger$
|
|
2422
|
+
const logger$d = runtime.getLogger("naylence.fame.security.encryption.channel.channel_encryption_manager");
|
|
1895
2423
|
const SUPPORTED_CHANNEL_ALGORITHMS = ["chacha20-poly1305-channel"];
|
|
1896
2424
|
const CHANNEL_ENCRYPTION_ALGORITHM = "chacha20-poly1305-channel";
|
|
1897
2425
|
const HANDSHAKE_ALGORITHM = "CHACHA20P1305";
|
|
@@ -2042,13 +2570,13 @@ class ChannelEncryptionManager {
|
|
|
2042
2570
|
const destination = opts?.destination ?? envelope.to ?? null;
|
|
2043
2571
|
const destinationStr = toDestinationString(destination);
|
|
2044
2572
|
if (!destinationStr) {
|
|
2045
|
-
logger$
|
|
2573
|
+
logger$d.warning("no_destination_for_channel_encryption", {
|
|
2046
2574
|
envelope_id: envelope.id,
|
|
2047
2575
|
});
|
|
2048
2576
|
return runtime.EncryptionResult.skipped(envelope);
|
|
2049
2577
|
}
|
|
2050
2578
|
if (!this.secureChannelManager) {
|
|
2051
|
-
logger$
|
|
2579
|
+
logger$d.warning("no_secure_channel_manager_available", {
|
|
2052
2580
|
envelope_id: envelope.id,
|
|
2053
2581
|
});
|
|
2054
2582
|
return runtime.EncryptionResult.skipped(envelope);
|
|
@@ -2059,7 +2587,7 @@ class ChannelEncryptionManager {
|
|
|
2059
2587
|
return this.encryptWithChannel(envelope, existingChannelId);
|
|
2060
2588
|
}
|
|
2061
2589
|
catch (error) {
|
|
2062
|
-
logger$
|
|
2590
|
+
logger$d.error("channel_encryption_failed", {
|
|
2063
2591
|
error: error instanceof Error ? error.message : String(error),
|
|
2064
2592
|
channel_id: existingChannelId,
|
|
2065
2593
|
});
|
|
@@ -2087,35 +2615,35 @@ class ChannelEncryptionManager {
|
|
|
2087
2615
|
}
|
|
2088
2616
|
const channelId = encHeader.kid;
|
|
2089
2617
|
if (!channelId) {
|
|
2090
|
-
logger$
|
|
2618
|
+
logger$d.error("missing_channel_id_in_encryption_header", {
|
|
2091
2619
|
envelope_id: envelope.id,
|
|
2092
2620
|
});
|
|
2093
2621
|
return envelope;
|
|
2094
2622
|
}
|
|
2095
2623
|
const nonce = this.decodeNonceValue(encHeader.val ?? "");
|
|
2096
2624
|
if (!nonce) {
|
|
2097
|
-
logger$
|
|
2625
|
+
logger$d.error("invalid_nonce_in_encryption_header", {
|
|
2098
2626
|
envelope_id: envelope.id,
|
|
2099
2627
|
value_present: Boolean(encHeader.val),
|
|
2100
2628
|
});
|
|
2101
2629
|
return envelope;
|
|
2102
2630
|
}
|
|
2103
2631
|
if (!this.secureChannelManager) {
|
|
2104
|
-
logger$
|
|
2632
|
+
logger$d.warning("no_secure_channel_manager_for_decryption", {
|
|
2105
2633
|
envelope_id: envelope.id,
|
|
2106
2634
|
});
|
|
2107
2635
|
return envelope;
|
|
2108
2636
|
}
|
|
2109
2637
|
const channelState = this.getChannelState(channelId);
|
|
2110
2638
|
if (!channelState) {
|
|
2111
|
-
logger$
|
|
2639
|
+
logger$d.error("channel_not_available_for_decryption", {
|
|
2112
2640
|
channel_id: channelId,
|
|
2113
2641
|
});
|
|
2114
2642
|
return envelope;
|
|
2115
2643
|
}
|
|
2116
2644
|
const ciphertext = this.extractCiphertext(frame.payload);
|
|
2117
2645
|
if (!ciphertext) {
|
|
2118
|
-
logger$
|
|
2646
|
+
logger$d.error("invalid_ciphertext_payload", { envelope_id: envelope.id });
|
|
2119
2647
|
return envelope;
|
|
2120
2648
|
}
|
|
2121
2649
|
try {
|
|
@@ -2140,7 +2668,7 @@ class ChannelEncryptionManager {
|
|
|
2140
2668
|
return envelope;
|
|
2141
2669
|
}
|
|
2142
2670
|
catch (error) {
|
|
2143
|
-
logger$
|
|
2671
|
+
logger$d.error("channel_decryption_failed", {
|
|
2144
2672
|
channel_id: channelId,
|
|
2145
2673
|
error: error instanceof Error ? error.message : String(error),
|
|
2146
2674
|
});
|
|
@@ -2148,24 +2676,24 @@ class ChannelEncryptionManager {
|
|
|
2148
2676
|
}
|
|
2149
2677
|
}
|
|
2150
2678
|
async notifyChannelEstablished(channelId) {
|
|
2151
|
-
logger$
|
|
2679
|
+
logger$d.debug("channel_encryption_manager_notified", {
|
|
2152
2680
|
channel_id: channelId,
|
|
2153
2681
|
manager_type: "channel",
|
|
2154
2682
|
});
|
|
2155
2683
|
if (!channelId.startsWith("auto-")) {
|
|
2156
|
-
logger$
|
|
2684
|
+
logger$d.warning("unexpected_channel_id_format", { channel_id: channelId });
|
|
2157
2685
|
return;
|
|
2158
2686
|
}
|
|
2159
2687
|
const destinationStr = this.extractDestinationFromChannelId(channelId);
|
|
2160
2688
|
if (!destinationStr) {
|
|
2161
|
-
logger$
|
|
2689
|
+
logger$d.warning("cannot_parse_destination_from_channel_id", {
|
|
2162
2690
|
channel_id: channelId,
|
|
2163
2691
|
});
|
|
2164
2692
|
return;
|
|
2165
2693
|
}
|
|
2166
2694
|
this.handshakeInProgress.delete(destinationStr);
|
|
2167
2695
|
if (!this.pendingEnvelopes.has(destinationStr)) {
|
|
2168
|
-
logger$
|
|
2696
|
+
logger$d.debug("no_pending_queue_for_destination", {
|
|
2169
2697
|
destination: destinationStr,
|
|
2170
2698
|
});
|
|
2171
2699
|
return;
|
|
@@ -2173,7 +2701,7 @@ class ChannelEncryptionManager {
|
|
|
2173
2701
|
const queuedEnvelopes = this.pendingEnvelopes.get(destinationStr) ?? [];
|
|
2174
2702
|
this.pendingEnvelopes.delete(destinationStr);
|
|
2175
2703
|
if (!this.secureChannelManager) {
|
|
2176
|
-
logger$
|
|
2704
|
+
logger$d.error("no_secure_channel_manager_for_queue_drain", {
|
|
2177
2705
|
channel_id: channelId,
|
|
2178
2706
|
});
|
|
2179
2707
|
return;
|
|
@@ -2182,7 +2710,7 @@ class ChannelEncryptionManager {
|
|
|
2182
2710
|
try {
|
|
2183
2711
|
const result = this.encryptWithChannel(envelope, channelId);
|
|
2184
2712
|
if (!result.envelope) {
|
|
2185
|
-
logger$
|
|
2713
|
+
logger$d.warning("failed_to_encrypt_queued_envelope", {
|
|
2186
2714
|
envelope_id: envelope.id,
|
|
2187
2715
|
channel_id: channelId,
|
|
2188
2716
|
});
|
|
@@ -2192,7 +2720,7 @@ class ChannelEncryptionManager {
|
|
|
2192
2720
|
this.runAsyncTask(() => this.deliverEnvelope(encryptedEnvelope), `deliver-queued-${envelope.id}`);
|
|
2193
2721
|
}
|
|
2194
2722
|
catch (error) {
|
|
2195
|
-
logger$
|
|
2723
|
+
logger$d.error("failed_to_encrypt_queued_envelope", {
|
|
2196
2724
|
envelope_id: envelope.id,
|
|
2197
2725
|
error: error instanceof Error ? error.message : String(error),
|
|
2198
2726
|
});
|
|
@@ -2200,19 +2728,19 @@ class ChannelEncryptionManager {
|
|
|
2200
2728
|
}
|
|
2201
2729
|
}
|
|
2202
2730
|
async notifyChannelFailed(channelId, reason = "handshake_failed") {
|
|
2203
|
-
logger$
|
|
2731
|
+
logger$d.debug("channel_encryption_manager_notified_failure", {
|
|
2204
2732
|
channel_id: channelId,
|
|
2205
2733
|
reason,
|
|
2206
2734
|
});
|
|
2207
2735
|
if (!channelId.startsWith("auto-")) {
|
|
2208
|
-
logger$
|
|
2736
|
+
logger$d.warning("unexpected_channel_id_format_on_failure", {
|
|
2209
2737
|
channel_id: channelId,
|
|
2210
2738
|
});
|
|
2211
2739
|
return;
|
|
2212
2740
|
}
|
|
2213
2741
|
const destinationStr = this.extractDestinationFromChannelId(channelId);
|
|
2214
2742
|
if (!destinationStr) {
|
|
2215
|
-
logger$
|
|
2743
|
+
logger$d.warning("cannot_parse_destination_from_channel_id_on_failure", {
|
|
2216
2744
|
channel_id: channelId,
|
|
2217
2745
|
});
|
|
2218
2746
|
return;
|
|
@@ -2222,14 +2750,14 @@ class ChannelEncryptionManager {
|
|
|
2222
2750
|
const cachedChannelId = this.addrChannelMap.get(destinationStr);
|
|
2223
2751
|
if (cachedChannelId === channelId) {
|
|
2224
2752
|
this.addrChannelMap.delete(destinationStr);
|
|
2225
|
-
logger$
|
|
2753
|
+
logger$d.debug("cleared_channel_cache_for_failed_channel", {
|
|
2226
2754
|
destination: destinationStr,
|
|
2227
2755
|
channel_id: channelId,
|
|
2228
2756
|
});
|
|
2229
2757
|
}
|
|
2230
2758
|
const queuedEnvelopes = this.pendingEnvelopes.get(destinationStr);
|
|
2231
2759
|
if (!queuedEnvelopes || queuedEnvelopes.length === 0) {
|
|
2232
|
-
logger$
|
|
2760
|
+
logger$d.debug("no_pending_queue_for_failed_destination", {
|
|
2233
2761
|
destination: destinationStr,
|
|
2234
2762
|
});
|
|
2235
2763
|
return;
|
|
@@ -2248,7 +2776,7 @@ class ChannelEncryptionManager {
|
|
|
2248
2776
|
const cached = this.addrChannelMap.get(destination);
|
|
2249
2777
|
if (cached) {
|
|
2250
2778
|
this.addrChannelMap.delete(destination);
|
|
2251
|
-
logger$
|
|
2779
|
+
logger$d.debug("cleared_channel_cache_for_destination", {
|
|
2252
2780
|
destination,
|
|
2253
2781
|
cached_channel_id: cached,
|
|
2254
2782
|
});
|
|
@@ -2266,14 +2794,14 @@ class ChannelEncryptionManager {
|
|
|
2266
2794
|
}
|
|
2267
2795
|
const cached = this.addrChannelMap.get(destination);
|
|
2268
2796
|
if (cached && this.getChannelState(cached)) {
|
|
2269
|
-
logger$
|
|
2797
|
+
logger$d.debug("using_cached_channel", { destination, channel_id: cached });
|
|
2270
2798
|
return cached;
|
|
2271
2799
|
}
|
|
2272
2800
|
const channels = this.secureChannelManager.channels;
|
|
2273
2801
|
for (const channelId of Object.keys(channels)) {
|
|
2274
2802
|
if (channelId.startsWith(`auto-${destination}-`)) {
|
|
2275
2803
|
this.addrChannelMap.set(destination, channelId);
|
|
2276
|
-
logger$
|
|
2804
|
+
logger$d.debug("using_existing_channel", {
|
|
2277
2805
|
destination,
|
|
2278
2806
|
channel_id: channelId,
|
|
2279
2807
|
});
|
|
@@ -2286,12 +2814,12 @@ class ChannelEncryptionManager {
|
|
|
2286
2814
|
const queue = this.pendingEnvelopes.get(destinationStr) ?? [];
|
|
2287
2815
|
queue.push(envelope);
|
|
2288
2816
|
this.pendingEnvelopes.set(destinationStr, queue);
|
|
2289
|
-
logger$
|
|
2817
|
+
logger$d.debug("queued_envelope_for_channel_handshake", {
|
|
2290
2818
|
envelope_id: envelope.id,
|
|
2291
2819
|
destination: destinationStr,
|
|
2292
2820
|
});
|
|
2293
2821
|
if (this.handshakeInProgress.has(destinationStr)) {
|
|
2294
|
-
logger$
|
|
2822
|
+
logger$d.debug("handshake_already_in_progress", {
|
|
2295
2823
|
destination: destinationStr,
|
|
2296
2824
|
});
|
|
2297
2825
|
return;
|
|
@@ -2310,7 +2838,7 @@ class ChannelEncryptionManager {
|
|
|
2310
2838
|
async initiateChannelHandshakeAsync(destination, destinationStr, opts) {
|
|
2311
2839
|
void opts;
|
|
2312
2840
|
if (!this.secureChannelManager) {
|
|
2313
|
-
logger$
|
|
2841
|
+
logger$d.error("no_secure_channel_manager_for_async_handshake_initiation");
|
|
2314
2842
|
return;
|
|
2315
2843
|
}
|
|
2316
2844
|
const channelId = this.generateChannelId(destinationStr);
|
|
@@ -2318,19 +2846,19 @@ class ChannelEncryptionManager {
|
|
|
2318
2846
|
const openFrame = this.secureChannelManager.generateOpenFrame(channelId, HANDSHAKE_ALGORITHM);
|
|
2319
2847
|
const success = await this.sendSecureOpenFrameAsync(openFrame, destination);
|
|
2320
2848
|
if (success) {
|
|
2321
|
-
logger$
|
|
2849
|
+
logger$d.debug("sent_secure_open_frame_async", {
|
|
2322
2850
|
channel_id: channelId,
|
|
2323
2851
|
destination: destinationStr,
|
|
2324
2852
|
});
|
|
2325
2853
|
}
|
|
2326
2854
|
else {
|
|
2327
|
-
logger$
|
|
2855
|
+
logger$d.warning("failed_to_send_secure_open_frame_async", {
|
|
2328
2856
|
channel_id: channelId,
|
|
2329
2857
|
});
|
|
2330
2858
|
}
|
|
2331
2859
|
}
|
|
2332
2860
|
catch (error) {
|
|
2333
|
-
logger$
|
|
2861
|
+
logger$d.error("async_channel_handshake_initiation_failed", {
|
|
2334
2862
|
destination: destinationStr,
|
|
2335
2863
|
error: error instanceof Error ? error.message : String(error),
|
|
2336
2864
|
});
|
|
@@ -2339,22 +2867,22 @@ class ChannelEncryptionManager {
|
|
|
2339
2867
|
async sendSecureOpenFrameAsync(openFrame, destination) {
|
|
2340
2868
|
const node = this.nodeLike;
|
|
2341
2869
|
if (!node) {
|
|
2342
|
-
logger$
|
|
2870
|
+
logger$d.error("no_node_available_for_sending_secure_open_async");
|
|
2343
2871
|
return false;
|
|
2344
2872
|
}
|
|
2345
2873
|
const envelopeFactory = node.envelopeFactory;
|
|
2346
2874
|
if (!envelopeFactory) {
|
|
2347
|
-
logger$
|
|
2875
|
+
logger$d.error("no_envelope_factory_available_for_secure_open_async");
|
|
2348
2876
|
return false;
|
|
2349
2877
|
}
|
|
2350
2878
|
const replyTo = this.buildSystemReplyTo();
|
|
2351
2879
|
if (!replyTo) {
|
|
2352
|
-
logger$
|
|
2880
|
+
logger$d.error("no_physical_path_available_for_reply_to_async");
|
|
2353
2881
|
return false;
|
|
2354
2882
|
}
|
|
2355
2883
|
const toAddress = toFameAddress(destination);
|
|
2356
2884
|
if (!toAddress) {
|
|
2357
|
-
logger$
|
|
2885
|
+
logger$d.error("invalid_destination_for_secure_open", {
|
|
2358
2886
|
destination: String(destination),
|
|
2359
2887
|
});
|
|
2360
2888
|
return false;
|
|
@@ -2366,7 +2894,7 @@ class ChannelEncryptionManager {
|
|
|
2366
2894
|
corrId: core.generateId(),
|
|
2367
2895
|
});
|
|
2368
2896
|
await this.deliverEnvelope(envelope);
|
|
2369
|
-
logger$
|
|
2897
|
+
logger$d.debug("delivered_secure_open_frame_async", {
|
|
2370
2898
|
channel_id: openFrame.cid,
|
|
2371
2899
|
});
|
|
2372
2900
|
return true;
|
|
@@ -2374,7 +2902,7 @@ class ChannelEncryptionManager {
|
|
|
2374
2902
|
async deliverEnvelope(envelope) {
|
|
2375
2903
|
const node = this.nodeLike;
|
|
2376
2904
|
if (!node) {
|
|
2377
|
-
logger$
|
|
2905
|
+
logger$d.error("no_node_available_for_delivery", {
|
|
2378
2906
|
envelope_id: envelope.id,
|
|
2379
2907
|
});
|
|
2380
2908
|
return;
|
|
@@ -2384,19 +2912,19 @@ class ChannelEncryptionManager {
|
|
|
2384
2912
|
}
|
|
2385
2913
|
encryptWithChannel(envelope, channelId) {
|
|
2386
2914
|
if (!this.secureChannelManager) {
|
|
2387
|
-
logger$
|
|
2915
|
+
logger$d.error("no_secure_channel_manager_for_encryption");
|
|
2388
2916
|
return runtime.EncryptionResult.skipped(envelope);
|
|
2389
2917
|
}
|
|
2390
2918
|
const frame = envelope.frame;
|
|
2391
2919
|
if (!this.isDataFrame(frame)) {
|
|
2392
|
-
logger$
|
|
2920
|
+
logger$d.error("attempted_to_encrypt_non_dataframe", {
|
|
2393
2921
|
frame_type: frame.type ?? typeof frame,
|
|
2394
2922
|
});
|
|
2395
2923
|
return runtime.EncryptionResult.skipped(envelope);
|
|
2396
2924
|
}
|
|
2397
2925
|
const channelState = this.getChannelState(channelId);
|
|
2398
2926
|
if (!channelState) {
|
|
2399
|
-
logger$
|
|
2927
|
+
logger$d.error("channel_not_in_channels", { channel_id: channelId });
|
|
2400
2928
|
return runtime.EncryptionResult.skipped(envelope);
|
|
2401
2929
|
}
|
|
2402
2930
|
const payloadBytes = this.serializePayload(frame.payload);
|
|
@@ -2455,7 +2983,7 @@ class ChannelEncryptionManager {
|
|
|
2455
2983
|
return decodeBase64$1(payload);
|
|
2456
2984
|
}
|
|
2457
2985
|
catch (error) {
|
|
2458
|
-
logger$
|
|
2986
|
+
logger$d.error("failed_to_decode_base64_ciphertext", {
|
|
2459
2987
|
error: error instanceof Error ? error.message : String(error),
|
|
2460
2988
|
});
|
|
2461
2989
|
return null;
|
|
@@ -2485,7 +3013,7 @@ class ChannelEncryptionManager {
|
|
|
2485
3013
|
return parts.slice(1, -1).join("-");
|
|
2486
3014
|
}
|
|
2487
3015
|
async handleFailedEnvelope(envelope, destinationStr, channelId, reason) {
|
|
2488
|
-
logger$
|
|
3016
|
+
logger$d.warning("envelope_failed_due_to_channel_handshake_failure", {
|
|
2489
3017
|
envelope_id: envelope.id,
|
|
2490
3018
|
destination: destinationStr,
|
|
2491
3019
|
channel_id: channelId,
|
|
@@ -2493,14 +3021,14 @@ class ChannelEncryptionManager {
|
|
|
2493
3021
|
});
|
|
2494
3022
|
const frame = envelope.frame;
|
|
2495
3023
|
if (!this.isDataFrame(frame)) {
|
|
2496
|
-
logger$
|
|
3024
|
+
logger$d.debug("skipping_nack_for_non_dataframe", {
|
|
2497
3025
|
envelope_id: envelope.id,
|
|
2498
3026
|
frame_type: frame.type ?? typeof frame,
|
|
2499
3027
|
});
|
|
2500
3028
|
return;
|
|
2501
3029
|
}
|
|
2502
3030
|
if (!envelope.replyTo) {
|
|
2503
|
-
logger$
|
|
3031
|
+
logger$d.debug("skipping_nack_no_reply_to", { envelope_id: envelope.id });
|
|
2504
3032
|
return;
|
|
2505
3033
|
}
|
|
2506
3034
|
await this.sendDeliveryNack(envelope, `channel_handshake_failed: ${reason}`);
|
|
@@ -2508,17 +3036,17 @@ class ChannelEncryptionManager {
|
|
|
2508
3036
|
async sendDeliveryNack(envelope, failureReason) {
|
|
2509
3037
|
const node = this.nodeLike;
|
|
2510
3038
|
if (!node) {
|
|
2511
|
-
logger$
|
|
3039
|
+
logger$d.error("no_node_available_for_sending_delivery_nack");
|
|
2512
3040
|
return;
|
|
2513
3041
|
}
|
|
2514
3042
|
const envelopeFactory = node.envelopeFactory;
|
|
2515
3043
|
if (!envelopeFactory) {
|
|
2516
|
-
logger$
|
|
3044
|
+
logger$d.error("no_envelope_factory_available_for_delivery_nack");
|
|
2517
3045
|
return;
|
|
2518
3046
|
}
|
|
2519
3047
|
const replyTo = toFameAddress(envelope.replyTo ?? null);
|
|
2520
3048
|
if (!replyTo) {
|
|
2521
|
-
logger$
|
|
3049
|
+
logger$d.error("invalid_reply_to_for_delivery_nack", {
|
|
2522
3050
|
reply_to: envelope.replyTo,
|
|
2523
3051
|
});
|
|
2524
3052
|
return;
|
|
@@ -2535,7 +3063,7 @@ class ChannelEncryptionManager {
|
|
|
2535
3063
|
corrId: envelope.corrId ?? core.generateId(),
|
|
2536
3064
|
});
|
|
2537
3065
|
await this.deliverEnvelope(nackEnvelope);
|
|
2538
|
-
logger$
|
|
3066
|
+
logger$d.debug("delivered_delivery_nack", {
|
|
2539
3067
|
original_envelope_id: envelope.id,
|
|
2540
3068
|
nack_envelope_id: nackEnvelope.id,
|
|
2541
3069
|
});
|
|
@@ -2573,7 +3101,7 @@ class ChannelEncryptionManager {
|
|
|
2573
3101
|
await task();
|
|
2574
3102
|
}
|
|
2575
3103
|
catch (error) {
|
|
2576
|
-
logger$
|
|
3104
|
+
logger$d.error("async_task_failed", {
|
|
2577
3105
|
task_name: name,
|
|
2578
3106
|
error: error instanceof Error ? error.message : String(error),
|
|
2579
3107
|
});
|
|
@@ -2627,7 +3155,7 @@ class ChannelEncryptionManager {
|
|
|
2627
3155
|
}
|
|
2628
3156
|
}
|
|
2629
3157
|
|
|
2630
|
-
const logger$
|
|
3158
|
+
const logger$c = runtime.getLogger("naylence.fame.security.encryption.channel.channel_encryption_manager_factory");
|
|
2631
3159
|
const DEFAULT_SUPPORTED_ALGORITHMS = ["chacha20-poly1305-channel"];
|
|
2632
3160
|
const FACTORY_META$d = {
|
|
2633
3161
|
base: runtime.ENCRYPTION_MANAGER_FACTORY_BASE_TYPE,
|
|
@@ -2659,7 +3187,7 @@ class ChannelEncryptionManagerFactory extends runtime.EncryptionManagerFactory {
|
|
|
2659
3187
|
async create(_config, ...factoryArgs) {
|
|
2660
3188
|
const [dependencies] = factoryArgs;
|
|
2661
3189
|
const resolvedDependencies = this.resolveDependencies(dependencies);
|
|
2662
|
-
logger$
|
|
3190
|
+
logger$c.debug("creating_channel_encryption_manager", {
|
|
2663
3191
|
has_secure_channel_manager: Boolean(resolvedDependencies.secureChannelManager),
|
|
2664
3192
|
has_node_like: Boolean(resolvedDependencies.nodeLike),
|
|
2665
3193
|
has_task_spawner: Boolean(resolvedDependencies.taskSpawner),
|
|
@@ -2721,7 +3249,7 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
2721
3249
|
ChannelEncryptionManagerFactory: ChannelEncryptionManagerFactory
|
|
2722
3250
|
});
|
|
2723
3251
|
|
|
2724
|
-
const logger$
|
|
3252
|
+
const logger$b = runtime.getLogger("naylence.fame.security.encryption.default_secure_channel_manager");
|
|
2725
3253
|
const DEFAULT_ALGORITHM = "CHACHA20P1305";
|
|
2726
3254
|
const CHANNEL_KEY_LENGTH = 32;
|
|
2727
3255
|
const NONCE_PREFIX_LENGTH = 4;
|
|
@@ -2768,7 +3296,7 @@ class DefaultSecureChannelManager {
|
|
|
2768
3296
|
const privateKey = ed25519_js.x25519.utils.randomSecretKey();
|
|
2769
3297
|
const publicKey = ed25519_js.x25519.scalarMultBase(privateKey);
|
|
2770
3298
|
this.ephemeralKeys.set(channelId, privateKey);
|
|
2771
|
-
logger$
|
|
3299
|
+
logger$b.debug("generated_channel_open", { cid: channelId, algorithm });
|
|
2772
3300
|
return {
|
|
2773
3301
|
type: "SecureOpen",
|
|
2774
3302
|
cid: channelId,
|
|
@@ -2781,7 +3309,7 @@ class DefaultSecureChannelManager {
|
|
|
2781
3309
|
runtime.requireCryptoSupport();
|
|
2782
3310
|
const algorithm = frame.alg || DEFAULT_ALGORITHM;
|
|
2783
3311
|
if (!this.isSupportedAlgorithm(algorithm)) {
|
|
2784
|
-
logger$
|
|
3312
|
+
logger$b.warning("unsupported_channel_algorithm", {
|
|
2785
3313
|
cid: frame.cid,
|
|
2786
3314
|
alg: algorithm,
|
|
2787
3315
|
});
|
|
@@ -2799,7 +3327,7 @@ class DefaultSecureChannelManager {
|
|
|
2799
3327
|
peerPublicKey = decodeBase64(frame.ephPub);
|
|
2800
3328
|
}
|
|
2801
3329
|
catch (error) {
|
|
2802
|
-
logger$
|
|
3330
|
+
logger$b.warning("invalid_peer_public_key", {
|
|
2803
3331
|
cid: frame.cid,
|
|
2804
3332
|
error: error instanceof Error ? error.message : String(error),
|
|
2805
3333
|
});
|
|
@@ -2821,7 +3349,7 @@ class DefaultSecureChannelManager {
|
|
|
2821
3349
|
algorithm,
|
|
2822
3350
|
});
|
|
2823
3351
|
this.channelsMap.set(frame.cid, channelState);
|
|
2824
|
-
logger$
|
|
3352
|
+
logger$b.debug("channel_established", { cid: frame.cid, algorithm });
|
|
2825
3353
|
myPrivateKey.fill(0);
|
|
2826
3354
|
sharedSecret.fill(0);
|
|
2827
3355
|
return {
|
|
@@ -2835,7 +3363,7 @@ class DefaultSecureChannelManager {
|
|
|
2835
3363
|
async handleAcceptFrame(frame) {
|
|
2836
3364
|
runtime.requireCryptoSupport();
|
|
2837
3365
|
if (frame.ok === false) {
|
|
2838
|
-
logger$
|
|
3366
|
+
logger$b.warning("channel_rejected", {
|
|
2839
3367
|
cid: frame.cid,
|
|
2840
3368
|
error: frame.reason,
|
|
2841
3369
|
});
|
|
@@ -2844,7 +3372,7 @@ class DefaultSecureChannelManager {
|
|
|
2844
3372
|
}
|
|
2845
3373
|
const privateKey = this.ephemeralKeys.get(frame.cid);
|
|
2846
3374
|
if (!privateKey) {
|
|
2847
|
-
logger$
|
|
3375
|
+
logger$b.error("no_ephemeral_key", { cid: frame.cid });
|
|
2848
3376
|
return false;
|
|
2849
3377
|
}
|
|
2850
3378
|
let peerPublicKey;
|
|
@@ -2852,7 +3380,7 @@ class DefaultSecureChannelManager {
|
|
|
2852
3380
|
peerPublicKey = decodeBase64(frame.ephPub);
|
|
2853
3381
|
}
|
|
2854
3382
|
catch (error) {
|
|
2855
|
-
logger$
|
|
3383
|
+
logger$b.warning("invalid_accept_public_key", {
|
|
2856
3384
|
cid: frame.cid,
|
|
2857
3385
|
error: error instanceof Error ? error.message : String(error),
|
|
2858
3386
|
});
|
|
@@ -2867,17 +3395,17 @@ class DefaultSecureChannelManager {
|
|
|
2867
3395
|
algorithm,
|
|
2868
3396
|
});
|
|
2869
3397
|
this.channelsMap.set(frame.cid, channelState);
|
|
2870
|
-
logger$
|
|
3398
|
+
logger$b.debug("channel_completed", { cid: frame.cid, algorithm });
|
|
2871
3399
|
sharedSecret.fill(0);
|
|
2872
3400
|
this.cleanupEphemeralKey(frame.cid);
|
|
2873
3401
|
return true;
|
|
2874
3402
|
}
|
|
2875
3403
|
handleCloseFrame(frame) {
|
|
2876
3404
|
if (this.channelsMap.delete(frame.cid)) {
|
|
2877
|
-
logger$
|
|
3405
|
+
logger$b.debug("channel_closed", { cid: frame.cid, reason: frame.reason });
|
|
2878
3406
|
}
|
|
2879
3407
|
else {
|
|
2880
|
-
logger$
|
|
3408
|
+
logger$b.warning("close_unknown_channel", { cid: frame.cid });
|
|
2881
3409
|
}
|
|
2882
3410
|
this.cleanupEphemeralKey(frame.cid);
|
|
2883
3411
|
}
|
|
@@ -2904,7 +3432,7 @@ class DefaultSecureChannelManager {
|
|
|
2904
3432
|
}
|
|
2905
3433
|
closeChannel(channelId, reason = "User requested") {
|
|
2906
3434
|
if (this.channelsMap.delete(channelId)) {
|
|
2907
|
-
logger$
|
|
3435
|
+
logger$b.debug("channel_closed_by_user", { cid: channelId, reason });
|
|
2908
3436
|
}
|
|
2909
3437
|
this.cleanupEphemeralKey(channelId);
|
|
2910
3438
|
return {
|
|
@@ -2921,7 +3449,7 @@ class DefaultSecureChannelManager {
|
|
|
2921
3449
|
this.channelsMap.delete(channelId);
|
|
2922
3450
|
this.cleanupEphemeralKey(channelId);
|
|
2923
3451
|
removed += 1;
|
|
2924
|
-
logger$
|
|
3452
|
+
logger$b.debug("channel_expired_cleanup", { cid: channelId });
|
|
2925
3453
|
}
|
|
2926
3454
|
}
|
|
2927
3455
|
return removed;
|
|
@@ -2950,7 +3478,7 @@ class DefaultSecureChannelManager {
|
|
|
2950
3478
|
if (channelId.startsWith(prefix)) {
|
|
2951
3479
|
if (this.removeChannel(channelId)) {
|
|
2952
3480
|
removed += 1;
|
|
2953
|
-
logger$
|
|
3481
|
+
logger$b.debug("removed_channel_for_destination", {
|
|
2954
3482
|
channel_id: channelId,
|
|
2955
3483
|
destination,
|
|
2956
3484
|
});
|
|
@@ -2958,7 +3486,7 @@ class DefaultSecureChannelManager {
|
|
|
2958
3486
|
}
|
|
2959
3487
|
}
|
|
2960
3488
|
if (removed > 0) {
|
|
2961
|
-
logger$
|
|
3489
|
+
logger$b.info("cleanup_channels_for_destination", {
|
|
2962
3490
|
destination,
|
|
2963
3491
|
channels_removed: removed,
|
|
2964
3492
|
});
|
|
@@ -2998,222 +3526,58 @@ const FACTORY_META$c = {
|
|
|
2998
3526
|
base: runtime.SECURE_CHANNEL_MANAGER_FACTORY_BASE_TYPE,
|
|
2999
3527
|
key: "DefaultSecureChannelManager",
|
|
3000
3528
|
};
|
|
3001
|
-
class DefaultSecureChannelManagerFactory extends runtime.SecureChannelManagerFactory {
|
|
3002
|
-
constructor() {
|
|
3003
|
-
super(...arguments);
|
|
3004
|
-
this.type = "DefaultSecureChannelManager";
|
|
3005
|
-
this.isDefault = true;
|
|
3006
|
-
this.priority = 500;
|
|
3007
|
-
}
|
|
3008
|
-
async create(config = null) {
|
|
3009
|
-
const ttl = this.resolveChannelTtl(config);
|
|
3010
|
-
return new DefaultSecureChannelManager(ttl ? { channelTtlSeconds: ttl } : {});
|
|
3011
|
-
}
|
|
3012
|
-
getSupportedAlgorithms() {
|
|
3013
|
-
return ["CHACHA20P1305"];
|
|
3014
|
-
}
|
|
3015
|
-
resolveChannelTtl(config) {
|
|
3016
|
-
if (!config) {
|
|
3017
|
-
return undefined;
|
|
3018
|
-
}
|
|
3019
|
-
const candidates = [
|
|
3020
|
-
config.channelTtlSeconds,
|
|
3021
|
-
config.channelTtl,
|
|
3022
|
-
config.channel_ttl,
|
|
3023
|
-
config.channelTTL,
|
|
3024
|
-
];
|
|
3025
|
-
for (const candidate of candidates) {
|
|
3026
|
-
const normalized = this.toPositiveNumber(candidate);
|
|
3027
|
-
if (typeof normalized === "number") {
|
|
3028
|
-
return normalized;
|
|
3029
|
-
}
|
|
3030
|
-
}
|
|
3031
|
-
return undefined;
|
|
3032
|
-
}
|
|
3033
|
-
toPositiveNumber(value) {
|
|
3034
|
-
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
3035
|
-
return value;
|
|
3036
|
-
}
|
|
3037
|
-
if (typeof value === "string" && value.trim() !== "") {
|
|
3038
|
-
const parsed = Number(value);
|
|
3039
|
-
if (Number.isFinite(parsed) && parsed > 0) {
|
|
3040
|
-
return parsed;
|
|
3041
|
-
}
|
|
3042
|
-
}
|
|
3043
|
-
return undefined;
|
|
3044
|
-
}
|
|
3045
|
-
}
|
|
3046
|
-
|
|
3047
|
-
var defaultSecureChannelManagerFactory = /*#__PURE__*/Object.freeze({
|
|
3048
|
-
__proto__: null,
|
|
3049
|
-
DefaultSecureChannelManagerFactory: DefaultSecureChannelManagerFactory,
|
|
3050
|
-
FACTORY_META: FACTORY_META$c,
|
|
3051
|
-
default: DefaultSecureChannelManagerFactory
|
|
3052
|
-
});
|
|
3053
|
-
|
|
3054
|
-
const logger$b = runtime.getLogger("naylence.fame.security.encryption.encryption_manager_registry");
|
|
3055
|
-
class EncryptionManagerFactoryRegistry {
|
|
3056
|
-
constructor(autoDiscover = true) {
|
|
3057
|
-
this.factories = [];
|
|
3058
|
-
this.algorithmToFactory = new Map();
|
|
3059
|
-
this.typeToFactories = new Map();
|
|
3060
|
-
this.factorySet = new Set();
|
|
3061
|
-
this.autoDiscoveredFactories = new Set();
|
|
3062
|
-
this.autoDiscovered = false;
|
|
3063
|
-
if (autoDiscover) {
|
|
3064
|
-
this.autoDiscoverFactories();
|
|
3065
|
-
}
|
|
3066
|
-
}
|
|
3067
|
-
autoDiscoverFactories() {
|
|
3068
|
-
if (this.autoDiscovered) {
|
|
3069
|
-
return;
|
|
3070
|
-
}
|
|
3071
|
-
try {
|
|
3072
|
-
const extensionInfos = factory.ExtensionManager.getExtensionsByType(runtime.ENCRYPTION_MANAGER_FACTORY_BASE_TYPE);
|
|
3073
|
-
let registeredCount = 0;
|
|
3074
|
-
for (const [factoryName, info] of extensionInfos) {
|
|
3075
|
-
if (factoryName === "CompositeEncryptionManager") {
|
|
3076
|
-
logger$b.debug("skipping_composite_factory_to_avoid_circular_dependency", {
|
|
3077
|
-
factory_name: factoryName,
|
|
3078
|
-
});
|
|
3079
|
-
continue;
|
|
3080
|
-
}
|
|
3081
|
-
try {
|
|
3082
|
-
const factoryInstance = (info.instance ??
|
|
3083
|
-
factory.ExtensionManager.getGlobalFactory(runtime.ENCRYPTION_MANAGER_FACTORY_BASE_TYPE, factoryName));
|
|
3084
|
-
this.registerFactory(factoryInstance, { autoDiscovered: true });
|
|
3085
|
-
registeredCount += 1;
|
|
3086
|
-
logger$b.debug("auto_discovered_factory", {
|
|
3087
|
-
factory_name: factoryName,
|
|
3088
|
-
factory_class: factoryInstance.constructor.name,
|
|
3089
|
-
algorithms: factoryInstance.getSupportedAlgorithms(),
|
|
3090
|
-
encryption_type: factoryInstance.getEncryptionType(),
|
|
3091
|
-
priority: factoryInstance.getPriority(),
|
|
3092
|
-
});
|
|
3093
|
-
}
|
|
3094
|
-
catch (error) {
|
|
3095
|
-
logger$b.warning("failed_to_auto_register_factory", {
|
|
3096
|
-
factory_name: factoryName,
|
|
3097
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3098
|
-
});
|
|
3099
|
-
}
|
|
3100
|
-
}
|
|
3101
|
-
this.autoDiscovered = true;
|
|
3102
|
-
logger$b.debug("completed_auto_discovery", {
|
|
3103
|
-
registered_factories: registeredCount,
|
|
3104
|
-
total_discovered: extensionInfos.size,
|
|
3105
|
-
skipped_composite: true,
|
|
3106
|
-
});
|
|
3107
|
-
}
|
|
3108
|
-
catch (error) {
|
|
3109
|
-
logger$b.warning("failed_auto_discovery_of_factories", {
|
|
3110
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3111
|
-
});
|
|
3112
|
-
}
|
|
3113
|
-
}
|
|
3114
|
-
registerFactory(factory, options = {}) {
|
|
3115
|
-
if (this.factorySet.has(factory)) {
|
|
3116
|
-
return;
|
|
3117
|
-
}
|
|
3118
|
-
this.factorySet.add(factory);
|
|
3119
|
-
this.factories.push(factory);
|
|
3120
|
-
if (options.autoDiscovered) {
|
|
3121
|
-
this.autoDiscoveredFactories.add(factory);
|
|
3122
|
-
}
|
|
3123
|
-
for (const algorithm of factory.getSupportedAlgorithms()) {
|
|
3124
|
-
const existing = this.algorithmToFactory.get(algorithm);
|
|
3125
|
-
if (!existing || factory.getPriority() > existing.getPriority()) {
|
|
3126
|
-
this.algorithmToFactory.set(algorithm, factory);
|
|
3127
|
-
logger$b.debug("registered_algorithm_mapping", {
|
|
3128
|
-
algorithm,
|
|
3129
|
-
factory: factory.constructor.name,
|
|
3130
|
-
priority: factory.getPriority(),
|
|
3131
|
-
});
|
|
3132
|
-
}
|
|
3133
|
-
}
|
|
3134
|
-
const encryptionType = factory.getEncryptionType();
|
|
3135
|
-
const typeFactories = this.typeToFactories.get(encryptionType) ?? [];
|
|
3136
|
-
typeFactories.push(factory);
|
|
3137
|
-
typeFactories.sort((a, b) => b.getPriority() - a.getPriority());
|
|
3138
|
-
this.typeToFactories.set(encryptionType, typeFactories);
|
|
3139
|
-
logger$b.debug("registered_encryption_manager_factory", {
|
|
3140
|
-
factory: factory.constructor.name,
|
|
3141
|
-
encryption_type: encryptionType,
|
|
3142
|
-
algorithms: factory.getSupportedAlgorithms(),
|
|
3143
|
-
priority: factory.getPriority(),
|
|
3144
|
-
auto_discovered: options.autoDiscovered ?? false,
|
|
3145
|
-
});
|
|
3146
|
-
}
|
|
3147
|
-
getFactoryForAlgorithm(algorithm) {
|
|
3148
|
-
this.ensureAutoDiscovery();
|
|
3149
|
-
return this.algorithmToFactory.get(algorithm);
|
|
3150
|
-
}
|
|
3151
|
-
getFactoryForOptions(opts) {
|
|
3152
|
-
this.ensureAutoDiscovery();
|
|
3153
|
-
for (const factory of this.factories) {
|
|
3154
|
-
if (factory.supportsOptions(opts ?? undefined)) {
|
|
3155
|
-
logger$b.debug("found_factory_for_options", {
|
|
3156
|
-
factory: factory.constructor.name,
|
|
3157
|
-
encryption_type: factory.getEncryptionType(),
|
|
3158
|
-
});
|
|
3159
|
-
return factory;
|
|
3160
|
-
}
|
|
3161
|
-
}
|
|
3162
|
-
logger$b.debug("no_factory_found_for_options", { opts });
|
|
3163
|
-
return undefined;
|
|
3164
|
-
}
|
|
3165
|
-
getFactoriesByType(encryptionType) {
|
|
3166
|
-
this.ensureAutoDiscovery();
|
|
3167
|
-
return this.typeToFactories.get(encryptionType) ?? [];
|
|
3168
|
-
}
|
|
3169
|
-
getAllSupportedAlgorithms() {
|
|
3170
|
-
this.ensureAutoDiscovery();
|
|
3171
|
-
return Array.from(this.algorithmToFactory.keys());
|
|
3172
|
-
}
|
|
3173
|
-
getRegistryInfo() {
|
|
3174
|
-
return {
|
|
3175
|
-
totalFactories: this.factories.length,
|
|
3176
|
-
autoDiscovered: this.autoDiscovered,
|
|
3177
|
-
algorithmMappings: Object.fromEntries(Array.from(this.algorithmToFactory.entries()).map(([algorithm, factory]) => [algorithm, factory.constructor.name])),
|
|
3178
|
-
typeMappings: Object.fromEntries(Array.from(this.typeToFactories.entries()).map(([encType, factories]) => [
|
|
3179
|
-
encType,
|
|
3180
|
-
factories.map((factory) => factory.constructor.name),
|
|
3181
|
-
])),
|
|
3182
|
-
};
|
|
3183
|
-
}
|
|
3184
|
-
forceRediscovery() {
|
|
3185
|
-
const manualFactories = this.factories.filter((factory) => !this.autoDiscoveredFactories.has(factory));
|
|
3186
|
-
this.autoDiscovered = false;
|
|
3187
|
-
this.algorithmToFactory.clear();
|
|
3188
|
-
this.typeToFactories.clear();
|
|
3189
|
-
this.factories.length = 0;
|
|
3190
|
-
this.factorySet.clear();
|
|
3191
|
-
this.autoDiscoveredFactories.clear();
|
|
3192
|
-
for (const factory of manualFactories) {
|
|
3193
|
-
this.registerFactory(factory);
|
|
3194
|
-
}
|
|
3195
|
-
this.autoDiscoverFactories();
|
|
3529
|
+
class DefaultSecureChannelManagerFactory extends runtime.SecureChannelManagerFactory {
|
|
3530
|
+
constructor() {
|
|
3531
|
+
super(...arguments);
|
|
3532
|
+
this.type = "DefaultSecureChannelManager";
|
|
3533
|
+
this.isDefault = true;
|
|
3534
|
+
this.priority = 500;
|
|
3196
3535
|
}
|
|
3197
|
-
|
|
3198
|
-
|
|
3536
|
+
async create(config = null) {
|
|
3537
|
+
const ttl = this.resolveChannelTtl(config);
|
|
3538
|
+
return new DefaultSecureChannelManager(ttl ? { channelTtlSeconds: ttl } : {});
|
|
3199
3539
|
}
|
|
3200
|
-
|
|
3201
|
-
|
|
3540
|
+
getSupportedAlgorithms() {
|
|
3541
|
+
return ["CHACHA20P1305"];
|
|
3202
3542
|
}
|
|
3203
|
-
|
|
3204
|
-
if (!
|
|
3205
|
-
|
|
3543
|
+
resolveChannelTtl(config) {
|
|
3544
|
+
if (!config) {
|
|
3545
|
+
return undefined;
|
|
3546
|
+
}
|
|
3547
|
+
const candidates = [
|
|
3548
|
+
config.channelTtlSeconds,
|
|
3549
|
+
config.channelTtl,
|
|
3550
|
+
config.channel_ttl,
|
|
3551
|
+
config.channelTTL,
|
|
3552
|
+
];
|
|
3553
|
+
for (const candidate of candidates) {
|
|
3554
|
+
const normalized = this.toPositiveNumber(candidate);
|
|
3555
|
+
if (typeof normalized === "number") {
|
|
3556
|
+
return normalized;
|
|
3557
|
+
}
|
|
3206
3558
|
}
|
|
3559
|
+
return undefined;
|
|
3560
|
+
}
|
|
3561
|
+
toPositiveNumber(value) {
|
|
3562
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
3563
|
+
return value;
|
|
3564
|
+
}
|
|
3565
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
3566
|
+
const parsed = Number(value);
|
|
3567
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
3568
|
+
return parsed;
|
|
3569
|
+
}
|
|
3570
|
+
}
|
|
3571
|
+
return undefined;
|
|
3207
3572
|
}
|
|
3208
3573
|
}
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
}
|
|
3574
|
+
|
|
3575
|
+
var defaultSecureChannelManagerFactory = /*#__PURE__*/Object.freeze({
|
|
3576
|
+
__proto__: null,
|
|
3577
|
+
DefaultSecureChannelManagerFactory: DefaultSecureChannelManagerFactory,
|
|
3578
|
+
FACTORY_META: FACTORY_META$c,
|
|
3579
|
+
default: DefaultSecureChannelManagerFactory
|
|
3580
|
+
});
|
|
3217
3581
|
|
|
3218
3582
|
const logger$a = runtime.getLogger("naylence.fame.security.encryption.composite_encryption_manager");
|
|
3219
3583
|
const DEFAULT_SEALED_ALGORITHMS = [
|
|
@@ -5135,1275 +5499,953 @@ class AFTLoadBalancerStickinessManager extends runtime.BaseNodeEventListener {
|
|
|
5135
5499
|
}
|
|
5136
5500
|
const verification = await this.verifier.verify(aftToken, envelope.sid ?? undefined);
|
|
5137
5501
|
if (!verification.valid) {
|
|
5138
|
-
this.metrics.verifyFailures += 1;
|
|
5139
|
-
logger$4.warning("aft_verification_failed", {
|
|
5140
|
-
envelope_id: envelope.id,
|
|
5141
|
-
replica_id: replicaId,
|
|
5142
|
-
error: verification.error,
|
|
5143
|
-
});
|
|
5144
|
-
return null;
|
|
5145
|
-
}
|
|
5146
|
-
this.storeAssociation(aftToken, {
|
|
5147
|
-
replicaId,
|
|
5148
|
-
token: aftToken,
|
|
5149
|
-
sid: verification.sid ?? "",
|
|
5150
|
-
exp: verification.exp ?? Math.floor(Date.now() / 1000) + this.defaultTtlSec,
|
|
5151
|
-
trustLevel: verification.trustLevel,
|
|
5152
|
-
scope: verification.scope ?? null,
|
|
5153
|
-
clientSid: verification.clientSid ?? null,
|
|
5154
|
-
});
|
|
5155
|
-
if (verification.clientSid) {
|
|
5156
|
-
this.sidCache.set(verification.clientSid, replicaId);
|
|
5157
|
-
logger$4.debug("sid_cache_updated", {
|
|
5158
|
-
envelope_id: envelope.id,
|
|
5159
|
-
client_sid: verification.clientSid,
|
|
5160
|
-
replica_id: replicaId,
|
|
5161
|
-
});
|
|
5162
|
-
}
|
|
5163
|
-
this.metrics.associationsCreated += 1;
|
|
5164
|
-
logger$4.debug("aft_association_created", {
|
|
5165
|
-
envelope_id: envelope.id,
|
|
5166
|
-
replica_id: replicaId,
|
|
5167
|
-
sid: verification.sid,
|
|
5168
|
-
exp: verification.exp,
|
|
5169
|
-
trust_level: verification.trustLevel,
|
|
5170
|
-
scope: verification.scope,
|
|
5171
|
-
});
|
|
5172
|
-
return this.config.clientEcho ? aftToken : null;
|
|
5173
|
-
}
|
|
5174
|
-
getStickyReplicaSegment(envelope, segments) {
|
|
5175
|
-
if (!this.config.enabled) {
|
|
5176
|
-
logger$4.debug("stickiness_disabled", { envelope_id: envelope.id });
|
|
5177
|
-
return null;
|
|
5178
|
-
}
|
|
5179
|
-
if (envelope.aft) {
|
|
5180
|
-
const replicaId = this.routeByAft(envelope.aft, envelope);
|
|
5181
|
-
if (replicaId) {
|
|
5182
|
-
this.metrics.cacheHits += 1;
|
|
5183
|
-
logger$4.debug("aft_routed_envelope", {
|
|
5184
|
-
envelope_id: envelope.id,
|
|
5185
|
-
replica_id: replicaId,
|
|
5186
|
-
routing_type: "aft_direct",
|
|
5187
|
-
});
|
|
5188
|
-
return replicaId;
|
|
5189
|
-
}
|
|
5190
|
-
}
|
|
5191
|
-
if (envelope.sid) {
|
|
5192
|
-
const cachedReplica = this.sidCache.get(envelope.sid);
|
|
5193
|
-
if (cachedReplica) {
|
|
5194
|
-
if (this.config.securityLevel === exports.StickinessMode.SID_ONLY) {
|
|
5195
|
-
this.metrics.cacheHits += 1;
|
|
5196
|
-
logger$4.debug("sid_cache_routed_envelope", {
|
|
5197
|
-
envelope_id: envelope.id,
|
|
5198
|
-
replica_id: cachedReplica,
|
|
5199
|
-
sid: envelope.sid,
|
|
5200
|
-
routing_type: "sid_only",
|
|
5201
|
-
});
|
|
5202
|
-
return cachedReplica;
|
|
5203
|
-
}
|
|
5204
|
-
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5205
|
-
if (association.replicaId === cachedReplica &&
|
|
5206
|
-
!association.isExpired()) {
|
|
5207
|
-
envelope.aft = token;
|
|
5208
|
-
this.metrics.cacheHits += 1;
|
|
5209
|
-
logger$4.debug("sid_cache_routed_envelope", {
|
|
5210
|
-
envelope_id: envelope.id,
|
|
5211
|
-
replica_id: cachedReplica,
|
|
5212
|
-
sid: envelope.sid,
|
|
5213
|
-
routing_type: "sid_cache_with_aft",
|
|
5214
|
-
});
|
|
5215
|
-
return cachedReplica;
|
|
5216
|
-
}
|
|
5217
|
-
}
|
|
5218
|
-
this.metrics.cacheHits += 1;
|
|
5219
|
-
logger$4.debug("sid_cache_routed_envelope", {
|
|
5220
|
-
envelope_id: envelope.id,
|
|
5221
|
-
replica_id: cachedReplica,
|
|
5222
|
-
sid: envelope.sid,
|
|
5223
|
-
routing_type: "sid_cache_direct",
|
|
5224
|
-
});
|
|
5225
|
-
return cachedReplica;
|
|
5226
|
-
}
|
|
5227
|
-
logger$4.debug("no_cached_replica_for_sid", {
|
|
5228
|
-
envelope_id: envelope.id,
|
|
5229
|
-
sid: envelope.sid,
|
|
5230
|
-
});
|
|
5231
|
-
}
|
|
5232
|
-
if (envelope.sid && Array.isArray(segments) && segments.length > 0) {
|
|
5233
|
-
const index = computeDeterministicIndex(envelope.sid, segments.length);
|
|
5234
|
-
const chosen = segments[index];
|
|
5235
|
-
this.metrics.cacheHits += 1;
|
|
5236
|
-
logger$4.debug("sid_based_deterministic_choice", {
|
|
5237
|
-
envelope_id: envelope.id,
|
|
5238
|
-
sid: envelope.sid,
|
|
5239
|
-
chosen,
|
|
5240
|
-
routing_type: "sid_deterministic",
|
|
5241
|
-
});
|
|
5242
|
-
return chosen;
|
|
5243
|
-
}
|
|
5244
|
-
this.metrics.cacheMisses += 1;
|
|
5245
|
-
logger$4.debug("no_stickiness_routing", {
|
|
5246
|
-
envelope_id: envelope.id,
|
|
5247
|
-
has_aft: Boolean(envelope.aft),
|
|
5248
|
-
has_sid: Boolean(envelope.sid),
|
|
5249
|
-
});
|
|
5250
|
-
return null;
|
|
5251
|
-
}
|
|
5252
|
-
cleanupExpiredAssociations() {
|
|
5253
|
-
const now = Math.floor(Date.now() / 1000);
|
|
5254
|
-
const expiredTokens = [];
|
|
5255
|
-
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5256
|
-
if (association.isExpired(now)) {
|
|
5257
|
-
expiredTokens.push(token);
|
|
5258
|
-
}
|
|
5259
|
-
}
|
|
5260
|
-
for (const token of expiredTokens) {
|
|
5261
|
-
this.removeAssociation(token);
|
|
5262
|
-
}
|
|
5263
|
-
if (expiredTokens.length > 0) {
|
|
5264
|
-
this.metrics.associationsExpired += expiredTokens.length;
|
|
5265
|
-
logger$4.debug("cleaned_expired_associations", {
|
|
5266
|
-
count: expiredTokens.length,
|
|
5267
|
-
});
|
|
5268
|
-
}
|
|
5269
|
-
}
|
|
5270
|
-
replicaLeft(replicaId) {
|
|
5271
|
-
const tokensToRemove = [];
|
|
5272
|
-
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5273
|
-
if (association.replicaId === replicaId) {
|
|
5274
|
-
tokensToRemove.push(token);
|
|
5275
|
-
}
|
|
5276
|
-
}
|
|
5277
|
-
for (const token of tokensToRemove) {
|
|
5278
|
-
this.removeAssociation(token);
|
|
5279
|
-
}
|
|
5280
|
-
if (tokensToRemove.length > 0) {
|
|
5281
|
-
logger$4.debug("removed_associations_for_departed_replica", {
|
|
5282
|
-
replica_id: replicaId,
|
|
5283
|
-
count: tokensToRemove.length,
|
|
5284
|
-
});
|
|
5285
|
-
}
|
|
5286
|
-
}
|
|
5287
|
-
handleReplicaLeft(replicaId) {
|
|
5288
|
-
this.replicaLeft(replicaId);
|
|
5289
|
-
logger$4.debug("stickiness_replica_cleanup", { replica_id: replicaId });
|
|
5290
|
-
}
|
|
5291
|
-
getMetrics() {
|
|
5292
|
-
return {
|
|
5293
|
-
...this.metrics,
|
|
5294
|
-
cacheSize: this.aftAssociations.size,
|
|
5295
|
-
sidCacheSize: this.sidCache.size,
|
|
5296
|
-
};
|
|
5297
|
-
}
|
|
5298
|
-
getAssociations() {
|
|
5299
|
-
const result = {};
|
|
5300
|
-
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5301
|
-
result[token] = {
|
|
5302
|
-
replica_id: association.replicaId,
|
|
5303
|
-
sid: association.sid,
|
|
5304
|
-
client_sid: association.clientSid,
|
|
5305
|
-
exp: association.exp,
|
|
5306
|
-
trust_level: association.trustLevel,
|
|
5307
|
-
scope: association.scope,
|
|
5308
|
-
created_at: association.createdAt,
|
|
5309
|
-
expired: association.isExpired(),
|
|
5310
|
-
};
|
|
5311
|
-
}
|
|
5312
|
-
return result;
|
|
5313
|
-
}
|
|
5314
|
-
getStickinessMetrics() {
|
|
5315
|
-
return this.getMetrics();
|
|
5316
|
-
}
|
|
5317
|
-
logMetrics() {
|
|
5318
|
-
const hits = this.metrics.cacheHits;
|
|
5319
|
-
const misses = this.metrics.cacheMisses;
|
|
5320
|
-
const total = hits + misses;
|
|
5321
|
-
const hitRate = total > 0 ? Math.round((hits / total) * 10000) / 100 : 0;
|
|
5322
|
-
logger$4.info("stickiness_metrics_report", {
|
|
5323
|
-
enabled: this.config.enabled,
|
|
5324
|
-
security_level: this.config.securityLevel,
|
|
5325
|
-
cache_hits: hits,
|
|
5326
|
-
cache_misses: misses,
|
|
5327
|
-
verify_failures: this.metrics.verifyFailures,
|
|
5328
|
-
associations_created: this.metrics.associationsCreated,
|
|
5329
|
-
associations_expired: this.metrics.associationsExpired,
|
|
5330
|
-
active_associations: this.aftAssociations.size,
|
|
5331
|
-
sid_cache_entries: this.sidCache.size,
|
|
5332
|
-
hit_rate: hitRate,
|
|
5502
|
+
this.metrics.verifyFailures += 1;
|
|
5503
|
+
logger$4.warning("aft_verification_failed", {
|
|
5504
|
+
envelope_id: envelope.id,
|
|
5505
|
+
replica_id: replicaId,
|
|
5506
|
+
error: verification.error,
|
|
5507
|
+
});
|
|
5508
|
+
return null;
|
|
5509
|
+
}
|
|
5510
|
+
this.storeAssociation(aftToken, {
|
|
5511
|
+
replicaId,
|
|
5512
|
+
token: aftToken,
|
|
5513
|
+
sid: verification.sid ?? "",
|
|
5514
|
+
exp: verification.exp ?? Math.floor(Date.now() / 1000) + this.defaultTtlSec,
|
|
5515
|
+
trustLevel: verification.trustLevel,
|
|
5516
|
+
scope: verification.scope ?? null,
|
|
5517
|
+
clientSid: verification.clientSid ?? null,
|
|
5333
5518
|
});
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5519
|
+
if (verification.clientSid) {
|
|
5520
|
+
this.sidCache.set(verification.clientSid, replicaId);
|
|
5521
|
+
logger$4.debug("sid_cache_updated", {
|
|
5522
|
+
envelope_id: envelope.id,
|
|
5523
|
+
client_sid: verification.clientSid,
|
|
5524
|
+
replica_id: replicaId,
|
|
5525
|
+
});
|
|
5526
|
+
}
|
|
5527
|
+
this.metrics.associationsCreated += 1;
|
|
5528
|
+
logger$4.debug("aft_association_created", {
|
|
5337
5529
|
envelope_id: envelope.id,
|
|
5338
|
-
|
|
5339
|
-
|
|
5530
|
+
replica_id: replicaId,
|
|
5531
|
+
sid: verification.sid,
|
|
5532
|
+
exp: verification.exp,
|
|
5533
|
+
trust_level: verification.trustLevel,
|
|
5534
|
+
scope: verification.scope,
|
|
5340
5535
|
});
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5536
|
+
return this.config.clientEcho ? aftToken : null;
|
|
5537
|
+
}
|
|
5538
|
+
getStickyReplicaSegment(envelope, segments) {
|
|
5539
|
+
if (!this.config.enabled) {
|
|
5540
|
+
logger$4.debug("stickiness_disabled", { envelope_id: envelope.id });
|
|
5541
|
+
return null;
|
|
5542
|
+
}
|
|
5543
|
+
if (envelope.aft) {
|
|
5544
|
+
const replicaId = this.routeByAft(envelope.aft, envelope);
|
|
5545
|
+
if (replicaId) {
|
|
5546
|
+
this.metrics.cacheHits += 1;
|
|
5547
|
+
logger$4.debug("aft_routed_envelope", {
|
|
5345
5548
|
envelope_id: envelope.id,
|
|
5346
|
-
|
|
5549
|
+
replica_id: replicaId,
|
|
5550
|
+
routing_type: "aft_direct",
|
|
5347
5551
|
});
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5552
|
+
return replicaId;
|
|
5553
|
+
}
|
|
5554
|
+
}
|
|
5555
|
+
if (envelope.sid) {
|
|
5556
|
+
const cachedReplica = this.sidCache.get(envelope.sid);
|
|
5557
|
+
if (cachedReplica) {
|
|
5558
|
+
if (this.config.securityLevel === exports.StickinessMode.SID_ONLY) {
|
|
5559
|
+
this.metrics.cacheHits += 1;
|
|
5560
|
+
logger$4.debug("sid_cache_routed_envelope", {
|
|
5353
5561
|
envelope_id: envelope.id,
|
|
5562
|
+
replica_id: cachedReplica,
|
|
5354
5563
|
sid: envelope.sid,
|
|
5355
|
-
|
|
5356
|
-
});
|
|
5357
|
-
}
|
|
5358
|
-
const hadInstruction = Boolean(extractAftInstruction(envelope));
|
|
5359
|
-
const token = await this.handleOutboundEnvelope(envelope, sourceRoute);
|
|
5360
|
-
if (hadInstruction) {
|
|
5361
|
-
logger$4.debug("processed_aft_setter_instruction", {
|
|
5362
|
-
envelope_id: envelope.id,
|
|
5363
|
-
source_route: sourceRoute,
|
|
5364
|
-
client_echo: Boolean(token),
|
|
5564
|
+
routing_type: "sid_only",
|
|
5365
5565
|
});
|
|
5566
|
+
return cachedReplica;
|
|
5366
5567
|
}
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5568
|
+
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5569
|
+
if (association.replicaId === cachedReplica &&
|
|
5570
|
+
!association.isExpired()) {
|
|
5571
|
+
envelope.aft = token;
|
|
5572
|
+
this.metrics.cacheHits += 1;
|
|
5573
|
+
logger$4.debug("sid_cache_routed_envelope", {
|
|
5574
|
+
envelope_id: envelope.id,
|
|
5575
|
+
replica_id: cachedReplica,
|
|
5576
|
+
sid: envelope.sid,
|
|
5577
|
+
routing_type: "sid_cache_with_aft",
|
|
5578
|
+
});
|
|
5579
|
+
return cachedReplica;
|
|
5580
|
+
}
|
|
5372
5581
|
}
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
logger$4.debug("downstream_envelope_without_source_route", {
|
|
5582
|
+
this.metrics.cacheHits += 1;
|
|
5583
|
+
logger$4.debug("sid_cache_routed_envelope", {
|
|
5376
5584
|
envelope_id: envelope.id,
|
|
5585
|
+
replica_id: cachedReplica,
|
|
5586
|
+
sid: envelope.sid,
|
|
5587
|
+
routing_type: "sid_cache_direct",
|
|
5377
5588
|
});
|
|
5589
|
+
return cachedReplica;
|
|
5378
5590
|
}
|
|
5379
|
-
|
|
5380
|
-
else {
|
|
5381
|
-
logger$4.debug("envelope_not_from_downstream", {
|
|
5591
|
+
logger$4.debug("no_cached_replica_for_sid", {
|
|
5382
5592
|
envelope_id: envelope.id,
|
|
5593
|
+
sid: envelope.sid,
|
|
5383
5594
|
});
|
|
5384
5595
|
}
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
}
|
|
5391
|
-
const association = new AFTAssociation(data);
|
|
5392
|
-
this.aftAssociations.set(token, association);
|
|
5393
|
-
while (this.aftAssociations.size > this.cacheMax) {
|
|
5394
|
-
const oldest = this.aftAssociations.keys().next();
|
|
5395
|
-
if (oldest.done) {
|
|
5396
|
-
break;
|
|
5397
|
-
}
|
|
5398
|
-
const oldestToken = oldest.value;
|
|
5399
|
-
this.removeAssociation(oldestToken);
|
|
5400
|
-
}
|
|
5401
|
-
}
|
|
5402
|
-
removeAssociation(token) {
|
|
5403
|
-
this.aftAssociations.delete(token);
|
|
5404
|
-
for (const [sid, cachedToken] of this.sidCache.entries()) {
|
|
5405
|
-
if (cachedToken === token) {
|
|
5406
|
-
this.sidCache.delete(sid);
|
|
5407
|
-
}
|
|
5408
|
-
}
|
|
5409
|
-
}
|
|
5410
|
-
routeByAft(token, envelope) {
|
|
5411
|
-
const association = this.aftAssociations.get(token);
|
|
5412
|
-
if (!association) {
|
|
5413
|
-
return null;
|
|
5414
|
-
}
|
|
5415
|
-
if (association.isExpired()) {
|
|
5416
|
-
this.metrics.associationsExpired += 1;
|
|
5417
|
-
this.removeAssociation(token);
|
|
5418
|
-
return null;
|
|
5419
|
-
}
|
|
5420
|
-
if (this.verifier.securityLevel === exports.StickinessMode.STRICT &&
|
|
5421
|
-
association.isLowTrust()) {
|
|
5422
|
-
logger$4.warning("rejecting_low_trust_association", {
|
|
5596
|
+
if (envelope.sid && Array.isArray(segments) && segments.length > 0) {
|
|
5597
|
+
const index = computeDeterministicIndex(envelope.sid, segments.length);
|
|
5598
|
+
const chosen = segments[index];
|
|
5599
|
+
this.metrics.cacheHits += 1;
|
|
5600
|
+
logger$4.debug("sid_based_deterministic_choice", {
|
|
5423
5601
|
envelope_id: envelope.id,
|
|
5424
|
-
|
|
5425
|
-
|
|
5602
|
+
sid: envelope.sid,
|
|
5603
|
+
chosen,
|
|
5604
|
+
routing_type: "sid_deterministic",
|
|
5426
5605
|
});
|
|
5427
|
-
return
|
|
5606
|
+
return chosen;
|
|
5428
5607
|
}
|
|
5429
|
-
this.
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
if (!envelope.meta) {
|
|
5608
|
+
this.metrics.cacheMisses += 1;
|
|
5609
|
+
logger$4.debug("no_stickiness_routing", {
|
|
5610
|
+
envelope_id: envelope.id,
|
|
5611
|
+
has_aft: Boolean(envelope.aft),
|
|
5612
|
+
has_sid: Boolean(envelope.sid),
|
|
5613
|
+
});
|
|
5436
5614
|
return null;
|
|
5437
5615
|
}
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
const
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
return 0;
|
|
5454
|
-
}
|
|
5455
|
-
let hash = 0;
|
|
5456
|
-
for (let i = 0; i < key.length; i += 1) {
|
|
5457
|
-
hash = (hash * 31 + key.charCodeAt(i)) >>> 0;
|
|
5458
|
-
}
|
|
5459
|
-
return hash % modulo;
|
|
5460
|
-
}
|
|
5461
|
-
|
|
5462
|
-
const FACTORY_META$7 = {
|
|
5463
|
-
base: runtime.LOAD_BALANCER_STICKINESS_MANAGER_FACTORY_BASE_TYPE,
|
|
5464
|
-
key: "AFTLoadBalancerStickinessManager",
|
|
5465
|
-
};
|
|
5466
|
-
const DEFAULT_VALUES$1 = {
|
|
5467
|
-
enabled: true,
|
|
5468
|
-
clientEcho: false,
|
|
5469
|
-
defaultTtlSec: 30,
|
|
5470
|
-
cacheMax: 100000,
|
|
5471
|
-
securityLevel: exports.StickinessMode.SIGNED_OPTIONAL,
|
|
5472
|
-
maxTtlSec: 7200,
|
|
5473
|
-
};
|
|
5474
|
-
function toBoolean(value, fallback) {
|
|
5475
|
-
return typeof value === "boolean" ? value : fallback;
|
|
5476
|
-
}
|
|
5477
|
-
function toNumber(value, fallback) {
|
|
5478
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5479
|
-
return value;
|
|
5480
|
-
}
|
|
5481
|
-
return fallback;
|
|
5482
|
-
}
|
|
5483
|
-
function normalizeConfig$4(config) {
|
|
5484
|
-
const record = (config ?? {});
|
|
5485
|
-
const normalizedSecurity = record.securityLevel
|
|
5486
|
-
? normalizeStickinessMode(record.securityLevel)
|
|
5487
|
-
: DEFAULT_VALUES$1.securityLevel;
|
|
5488
|
-
return {
|
|
5489
|
-
...record,
|
|
5490
|
-
type: "AFTLoadBalancerStickinessManager",
|
|
5491
|
-
enabled: toBoolean(record.enabled, DEFAULT_VALUES$1.enabled),
|
|
5492
|
-
clientEcho: toBoolean(record.clientEcho, DEFAULT_VALUES$1.clientEcho),
|
|
5493
|
-
defaultTtlSec: toNumber(record.defaultTtlSec, DEFAULT_VALUES$1.defaultTtlSec),
|
|
5494
|
-
cacheMax: toNumber(record.cacheMax, DEFAULT_VALUES$1.cacheMax),
|
|
5495
|
-
securityLevel: normalizedSecurity,
|
|
5496
|
-
maxTtlSec: toNumber(record.maxTtlSec, DEFAULT_VALUES$1.maxTtlSec),
|
|
5497
|
-
};
|
|
5498
|
-
}
|
|
5499
|
-
class AFTLoadBalancerStickinessManagerFactory extends runtime.LoadBalancerStickinessManagerFactory {
|
|
5500
|
-
constructor() {
|
|
5501
|
-
super(...arguments);
|
|
5502
|
-
this.type = "AFTLoadBalancerStickinessManager";
|
|
5503
|
-
this.isDefault = false;
|
|
5504
|
-
}
|
|
5505
|
-
async create(config, keyProvider, verifier) {
|
|
5506
|
-
const resolvedConfig = normalizeConfig$4(config);
|
|
5507
|
-
let effectiveVerifier = verifier ?? null;
|
|
5508
|
-
if (!effectiveVerifier && keyProvider) {
|
|
5509
|
-
effectiveVerifier = createAftVerifier({
|
|
5510
|
-
securityLevel: resolvedConfig.securityLevel ?? DEFAULT_VALUES$1.securityLevel,
|
|
5511
|
-
keyProvider,
|
|
5512
|
-
defaultTtlSec: resolvedConfig.defaultTtlSec ?? DEFAULT_VALUES$1.defaultTtlSec,
|
|
5616
|
+
cleanupExpiredAssociations() {
|
|
5617
|
+
const now = Math.floor(Date.now() / 1000);
|
|
5618
|
+
const expiredTokens = [];
|
|
5619
|
+
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5620
|
+
if (association.isExpired(now)) {
|
|
5621
|
+
expiredTokens.push(token);
|
|
5622
|
+
}
|
|
5623
|
+
}
|
|
5624
|
+
for (const token of expiredTokens) {
|
|
5625
|
+
this.removeAssociation(token);
|
|
5626
|
+
}
|
|
5627
|
+
if (expiredTokens.length > 0) {
|
|
5628
|
+
this.metrics.associationsExpired += expiredTokens.length;
|
|
5629
|
+
logger$4.debug("cleaned_expired_associations", {
|
|
5630
|
+
count: expiredTokens.length,
|
|
5513
5631
|
});
|
|
5514
5632
|
}
|
|
5515
|
-
|
|
5516
|
-
|
|
5633
|
+
}
|
|
5634
|
+
replicaLeft(replicaId) {
|
|
5635
|
+
const tokensToRemove = [];
|
|
5636
|
+
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5637
|
+
if (association.replicaId === replicaId) {
|
|
5638
|
+
tokensToRemove.push(token);
|
|
5639
|
+
}
|
|
5640
|
+
}
|
|
5641
|
+
for (const token of tokensToRemove) {
|
|
5642
|
+
this.removeAssociation(token);
|
|
5643
|
+
}
|
|
5644
|
+
if (tokensToRemove.length > 0) {
|
|
5645
|
+
logger$4.debug("removed_associations_for_departed_replica", {
|
|
5646
|
+
replica_id: replicaId,
|
|
5647
|
+
count: tokensToRemove.length,
|
|
5648
|
+
});
|
|
5517
5649
|
}
|
|
5518
|
-
return new AFTLoadBalancerStickinessManager(resolvedConfig, effectiveVerifier);
|
|
5519
5650
|
}
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
__proto__: null,
|
|
5524
|
-
AFTLoadBalancerStickinessManagerFactory: AFTLoadBalancerStickinessManagerFactory,
|
|
5525
|
-
FACTORY_META: FACTORY_META$7,
|
|
5526
|
-
default: AFTLoadBalancerStickinessManagerFactory
|
|
5527
|
-
});
|
|
5528
|
-
|
|
5529
|
-
const logger$3 = runtime.getLogger("naylence.fame.stickiness.aft_replica_stickiness_manager");
|
|
5530
|
-
function isStickinessRequired(context) {
|
|
5531
|
-
if (typeof context.stickinessRequired === "boolean") {
|
|
5532
|
-
return context.stickinessRequired;
|
|
5651
|
+
handleReplicaLeft(replicaId) {
|
|
5652
|
+
this.replicaLeft(replicaId);
|
|
5653
|
+
logger$4.debug("stickiness_replica_cleanup", { replica_id: replicaId });
|
|
5533
5654
|
}
|
|
5534
|
-
|
|
5535
|
-
return
|
|
5655
|
+
getMetrics() {
|
|
5656
|
+
return {
|
|
5657
|
+
...this.metrics,
|
|
5658
|
+
cacheSize: this.aftAssociations.size,
|
|
5659
|
+
sidCacheSize: this.sidCache.size,
|
|
5660
|
+
};
|
|
5536
5661
|
}
|
|
5537
|
-
|
|
5538
|
-
}
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
helper_type: this.aftHelper.signer.constructor.name,
|
|
5551
|
-
security_level: this.aftHelper.signer.securityLevel,
|
|
5552
|
-
max_ttl_sec: this.aftHelper.maxTtlSec,
|
|
5553
|
-
});
|
|
5554
|
-
}
|
|
5555
|
-
else {
|
|
5556
|
-
logger$3.debug("aft_replica_stickiness_manager_created", {
|
|
5557
|
-
security_level: this.securityLevel,
|
|
5558
|
-
max_ttl_sec: this.maxTtlSec,
|
|
5559
|
-
});
|
|
5662
|
+
getAssociations() {
|
|
5663
|
+
const result = {};
|
|
5664
|
+
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5665
|
+
result[token] = {
|
|
5666
|
+
replica_id: association.replicaId,
|
|
5667
|
+
sid: association.sid,
|
|
5668
|
+
client_sid: association.clientSid,
|
|
5669
|
+
exp: association.exp,
|
|
5670
|
+
trust_level: association.trustLevel,
|
|
5671
|
+
scope: association.scope,
|
|
5672
|
+
created_at: association.createdAt,
|
|
5673
|
+
expired: association.isExpired(),
|
|
5674
|
+
};
|
|
5560
5675
|
}
|
|
5676
|
+
return result;
|
|
5561
5677
|
}
|
|
5562
|
-
|
|
5563
|
-
return
|
|
5678
|
+
getStickinessMetrics() {
|
|
5679
|
+
return this.getMetrics();
|
|
5564
5680
|
}
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5681
|
+
logMetrics() {
|
|
5682
|
+
const hits = this.metrics.cacheHits;
|
|
5683
|
+
const misses = this.metrics.cacheMisses;
|
|
5684
|
+
const total = hits + misses;
|
|
5685
|
+
const hitRate = total > 0 ? Math.round((hits / total) * 10000) / 100 : 0;
|
|
5686
|
+
logger$4.info("stickiness_metrics_report", {
|
|
5687
|
+
enabled: this.config.enabled,
|
|
5688
|
+
security_level: this.config.securityLevel,
|
|
5689
|
+
cache_hits: hits,
|
|
5690
|
+
cache_misses: misses,
|
|
5691
|
+
verify_failures: this.metrics.verifyFailures,
|
|
5692
|
+
associations_created: this.metrics.associationsCreated,
|
|
5693
|
+
associations_expired: this.metrics.associationsExpired,
|
|
5694
|
+
active_associations: this.aftAssociations.size,
|
|
5695
|
+
sid_cache_entries: this.sidCache.size,
|
|
5696
|
+
hit_rate: hitRate,
|
|
5571
5697
|
});
|
|
5572
5698
|
}
|
|
5573
|
-
async
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
if (negotiated.enabled === false ||
|
|
5592
|
-
(negotiated.mode !== null &&
|
|
5593
|
-
negotiated.mode !== undefined &&
|
|
5594
|
-
negotiated.mode !== "aft")) {
|
|
5595
|
-
logger$3.debug("aft_injection_skipped_due_to_policy", {
|
|
5699
|
+
async onDeliver(_node, envelope, context) {
|
|
5700
|
+
logger$4.debug("stickiness_manager_on_deliver", {
|
|
5701
|
+
envelope_id: envelope.id,
|
|
5702
|
+
origin_type: context?.originType ?? "unknown",
|
|
5703
|
+
from_system_id: context?.fromSystemId ?? null,
|
|
5704
|
+
});
|
|
5705
|
+
if (context?.originType === core.DeliveryOriginType.DOWNSTREAM) {
|
|
5706
|
+
const sourceRoute = context.fromSystemId;
|
|
5707
|
+
if (sourceRoute) {
|
|
5708
|
+
logger$4.debug("processing_downstream_envelope", {
|
|
5709
|
+
envelope_id: envelope.id,
|
|
5710
|
+
source_route: sourceRoute,
|
|
5711
|
+
});
|
|
5712
|
+
if (this.config.securityLevel === exports.StickinessMode.SID_ONLY &&
|
|
5713
|
+
envelope.sid &&
|
|
5714
|
+
!this.sidCache.has(envelope.sid)) {
|
|
5715
|
+
this.sidCache.set(envelope.sid, sourceRoute);
|
|
5716
|
+
logger$4.debug("sid_only_association_recorded", {
|
|
5596
5717
|
envelope_id: envelope.id,
|
|
5597
|
-
|
|
5598
|
-
|
|
5718
|
+
sid: envelope.sid,
|
|
5719
|
+
replica_id: sourceRoute,
|
|
5720
|
+
});
|
|
5721
|
+
}
|
|
5722
|
+
const hadInstruction = Boolean(extractAftInstruction(envelope));
|
|
5723
|
+
const token = await this.handleOutboundEnvelope(envelope, sourceRoute);
|
|
5724
|
+
if (hadInstruction) {
|
|
5725
|
+
logger$4.debug("processed_aft_setter_instruction", {
|
|
5726
|
+
envelope_id: envelope.id,
|
|
5727
|
+
source_route: sourceRoute,
|
|
5728
|
+
client_echo: Boolean(token),
|
|
5729
|
+
});
|
|
5730
|
+
}
|
|
5731
|
+
else {
|
|
5732
|
+
logger$4.debug("no_aft_setter_instruction", {
|
|
5733
|
+
envelope_id: envelope.id,
|
|
5734
|
+
source_route: sourceRoute,
|
|
5599
5735
|
});
|
|
5600
|
-
return envelope;
|
|
5601
5736
|
}
|
|
5602
|
-
}
|
|
5603
|
-
logger$3.debug("applying_aft_for_upstream_stickiness_required", {
|
|
5604
|
-
envelope_id: envelope.id,
|
|
5605
|
-
from_system_id: context.fromSystemId ?? null,
|
|
5606
|
-
delivery_origin: context.originType ?? null,
|
|
5607
|
-
});
|
|
5608
|
-
const success = await helper.requestStickiness(envelope, {
|
|
5609
|
-
ttlSec: null,
|
|
5610
|
-
scope: "node",
|
|
5611
|
-
context: stickinessContext,
|
|
5612
|
-
});
|
|
5613
|
-
if (success) {
|
|
5614
|
-
logger$3.debug("aft_token_applied_via_context_flag_upstream", {
|
|
5615
|
-
envelope_id: envelope.id,
|
|
5616
|
-
from_system_id: context.fromSystemId ?? null,
|
|
5617
|
-
delivery_origin: context.originType ?? null,
|
|
5618
|
-
});
|
|
5619
5737
|
}
|
|
5620
5738
|
else {
|
|
5621
|
-
logger$
|
|
5739
|
+
logger$4.debug("downstream_envelope_without_source_route", {
|
|
5622
5740
|
envelope_id: envelope.id,
|
|
5623
|
-
delivery_origin: context.originType ?? null,
|
|
5624
|
-
reason: "helper_returned_false",
|
|
5625
5741
|
});
|
|
5626
5742
|
}
|
|
5627
5743
|
}
|
|
5628
|
-
return envelope;
|
|
5629
|
-
}
|
|
5630
|
-
async onNodeStarted(node) {
|
|
5631
|
-
if (!this.isInitialized) {
|
|
5632
|
-
await this.initializeAftHelper(node);
|
|
5633
|
-
return;
|
|
5634
|
-
}
|
|
5635
|
-
if (this.aftHelper && node.sid) {
|
|
5636
|
-
this.updateNodeSid(node.sid);
|
|
5637
|
-
logger$3.debug("aft_replica_stickiness_manager_sid_updated", {
|
|
5638
|
-
node_id: node.id ?? "unknown",
|
|
5639
|
-
node_sid: node.sid,
|
|
5640
|
-
security_level: this.aftHelper.signer.securityLevel,
|
|
5641
|
-
});
|
|
5642
|
-
}
|
|
5643
|
-
else if (!node.sid) {
|
|
5644
|
-
logger$3.warning("aft_replica_stickiness_manager_no_sid_available", {
|
|
5645
|
-
node_id: node.id ?? "unknown",
|
|
5646
|
-
});
|
|
5647
|
-
}
|
|
5648
5744
|
else {
|
|
5649
|
-
logger$
|
|
5650
|
-
|
|
5745
|
+
logger$4.debug("envelope_not_from_downstream", {
|
|
5746
|
+
envelope_id: envelope.id,
|
|
5651
5747
|
});
|
|
5652
5748
|
}
|
|
5749
|
+
return envelope;
|
|
5653
5750
|
}
|
|
5654
|
-
|
|
5655
|
-
if (this.
|
|
5656
|
-
this.
|
|
5657
|
-
logger$3.debug("aft_replica_stickiness_manager_sid_updated", {
|
|
5658
|
-
new_sid: nodeSid,
|
|
5659
|
-
});
|
|
5751
|
+
storeAssociation(token, data) {
|
|
5752
|
+
if (this.aftAssociations.has(token)) {
|
|
5753
|
+
this.aftAssociations.delete(token);
|
|
5660
5754
|
}
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
}
|
|
5668
|
-
|
|
5755
|
+
const association = new AFTAssociation(data);
|
|
5756
|
+
this.aftAssociations.set(token, association);
|
|
5757
|
+
while (this.aftAssociations.size > this.cacheMax) {
|
|
5758
|
+
const oldest = this.aftAssociations.keys().next();
|
|
5759
|
+
if (oldest.done) {
|
|
5760
|
+
break;
|
|
5761
|
+
}
|
|
5762
|
+
const oldestToken = oldest.value;
|
|
5763
|
+
this.removeAssociation(oldestToken);
|
|
5669
5764
|
}
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5765
|
+
}
|
|
5766
|
+
removeAssociation(token) {
|
|
5767
|
+
this.aftAssociations.delete(token);
|
|
5768
|
+
for (const [sid, cachedToken] of this.sidCache.entries()) {
|
|
5769
|
+
if (cachedToken === token) {
|
|
5770
|
+
this.sidCache.delete(sid);
|
|
5771
|
+
}
|
|
5676
5772
|
}
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
? cryptoProvider.signingPrivatePem
|
|
5683
|
-
: null;
|
|
5684
|
-
if (this.securityLevel === exports.StickinessMode.STRICT && !privateKeyPem) {
|
|
5685
|
-
logger$3.error("aft_replica_stickiness_manager_initialization_failed", {
|
|
5686
|
-
node_id: node.id ?? "unknown",
|
|
5687
|
-
error: "Missing signing private key for strict security level",
|
|
5688
|
-
});
|
|
5689
|
-
return;
|
|
5773
|
+
}
|
|
5774
|
+
routeByAft(token, envelope) {
|
|
5775
|
+
const association = this.aftAssociations.get(token);
|
|
5776
|
+
if (!association) {
|
|
5777
|
+
return null;
|
|
5690
5778
|
}
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
kid: keyId,
|
|
5696
|
-
privateKeyPem,
|
|
5697
|
-
maxTtlSec: this.maxTtlSec,
|
|
5698
|
-
});
|
|
5699
|
-
this.aftHelper = helper;
|
|
5700
|
-
this.isInitialized = true;
|
|
5701
|
-
logger$3.debug("aft_replica_stickiness_manager_initialized", {
|
|
5702
|
-
node_id: node.id ?? "unknown",
|
|
5703
|
-
node_sid: nodeSid,
|
|
5704
|
-
key_id: keyId,
|
|
5705
|
-
security_level: helper.signer.securityLevel,
|
|
5706
|
-
});
|
|
5779
|
+
if (association.isExpired()) {
|
|
5780
|
+
this.metrics.associationsExpired += 1;
|
|
5781
|
+
this.removeAssociation(token);
|
|
5782
|
+
return null;
|
|
5707
5783
|
}
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5784
|
+
if (this.verifier.securityLevel === exports.StickinessMode.STRICT &&
|
|
5785
|
+
association.isLowTrust()) {
|
|
5786
|
+
logger$4.warning("rejecting_low_trust_association", {
|
|
5787
|
+
envelope_id: envelope.id,
|
|
5788
|
+
replica_id: association.replicaId,
|
|
5789
|
+
reason: "strict mode rejects low-trust associations",
|
|
5712
5790
|
});
|
|
5791
|
+
return null;
|
|
5713
5792
|
}
|
|
5793
|
+
this.aftAssociations.delete(token);
|
|
5794
|
+
this.aftAssociations.set(token, association);
|
|
5795
|
+
return association.replicaId;
|
|
5714
5796
|
}
|
|
5715
|
-
|
|
5716
|
-
|
|
5797
|
+
}
|
|
5798
|
+
function extractAftInstruction(envelope) {
|
|
5799
|
+
if (!envelope.meta) {
|
|
5800
|
+
return null;
|
|
5717
5801
|
}
|
|
5718
|
-
|
|
5719
|
-
|
|
5802
|
+
const meta = envelope.meta;
|
|
5803
|
+
const nested = meta.set;
|
|
5804
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
5805
|
+
const aftValue = nested.aft;
|
|
5806
|
+
if (aftValue !== undefined) {
|
|
5807
|
+
return aftValue;
|
|
5808
|
+
}
|
|
5809
|
+
}
|
|
5810
|
+
if (meta["set.aft"] !== undefined) {
|
|
5811
|
+
return meta["set.aft"];
|
|
5720
5812
|
}
|
|
5813
|
+
return null;
|
|
5721
5814
|
}
|
|
5722
|
-
function
|
|
5723
|
-
|
|
5815
|
+
function computeDeterministicIndex(key, modulo) {
|
|
5816
|
+
if (modulo <= 0) {
|
|
5817
|
+
return 0;
|
|
5818
|
+
}
|
|
5819
|
+
let hash = 0;
|
|
5820
|
+
for (let i = 0; i < key.length; i += 1) {
|
|
5821
|
+
hash = (hash * 31 + key.charCodeAt(i)) >>> 0;
|
|
5822
|
+
}
|
|
5823
|
+
return hash % modulo;
|
|
5724
5824
|
}
|
|
5725
5825
|
|
|
5726
|
-
const FACTORY_META$
|
|
5727
|
-
base: runtime.
|
|
5728
|
-
key: "
|
|
5826
|
+
const FACTORY_META$7 = {
|
|
5827
|
+
base: runtime.LOAD_BALANCER_STICKINESS_MANAGER_FACTORY_BASE_TYPE,
|
|
5828
|
+
key: "AFTLoadBalancerStickinessManager",
|
|
5729
5829
|
};
|
|
5730
|
-
const DEFAULT_VALUES = {
|
|
5830
|
+
const DEFAULT_VALUES$1 = {
|
|
5831
|
+
enabled: true,
|
|
5832
|
+
clientEcho: false,
|
|
5833
|
+
defaultTtlSec: 30,
|
|
5834
|
+
cacheMax: 100000,
|
|
5731
5835
|
securityLevel: exports.StickinessMode.SIGNED_OPTIONAL,
|
|
5732
5836
|
maxTtlSec: 7200,
|
|
5733
5837
|
};
|
|
5734
|
-
function
|
|
5838
|
+
function toBoolean(value, fallback) {
|
|
5839
|
+
return typeof value === "boolean" ? value : fallback;
|
|
5840
|
+
}
|
|
5841
|
+
function toNumber(value, fallback) {
|
|
5842
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5843
|
+
return value;
|
|
5844
|
+
}
|
|
5845
|
+
return fallback;
|
|
5846
|
+
}
|
|
5847
|
+
function normalizeConfig$4(config) {
|
|
5735
5848
|
const record = (config ?? {});
|
|
5736
5849
|
const normalizedSecurity = record.securityLevel
|
|
5737
5850
|
? normalizeStickinessMode(record.securityLevel)
|
|
5738
|
-
: DEFAULT_VALUES.securityLevel;
|
|
5739
|
-
const securityLevel = normalizedSecurity ?? DEFAULT_VALUES.securityLevel;
|
|
5740
|
-
const maxTtlSecValue = typeof record.maxTtlSec === "number" && Number.isFinite(record.maxTtlSec)
|
|
5741
|
-
? Math.max(0, Math.floor(record.maxTtlSec))
|
|
5742
|
-
: DEFAULT_VALUES.maxTtlSec;
|
|
5851
|
+
: DEFAULT_VALUES$1.securityLevel;
|
|
5743
5852
|
return {
|
|
5744
5853
|
...record,
|
|
5745
|
-
type: "
|
|
5746
|
-
|
|
5747
|
-
|
|
5854
|
+
type: "AFTLoadBalancerStickinessManager",
|
|
5855
|
+
enabled: toBoolean(record.enabled, DEFAULT_VALUES$1.enabled),
|
|
5856
|
+
clientEcho: toBoolean(record.clientEcho, DEFAULT_VALUES$1.clientEcho),
|
|
5857
|
+
defaultTtlSec: toNumber(record.defaultTtlSec, DEFAULT_VALUES$1.defaultTtlSec),
|
|
5858
|
+
cacheMax: toNumber(record.cacheMax, DEFAULT_VALUES$1.cacheMax),
|
|
5859
|
+
securityLevel: normalizedSecurity,
|
|
5860
|
+
maxTtlSec: toNumber(record.maxTtlSec, DEFAULT_VALUES$1.maxTtlSec),
|
|
5748
5861
|
};
|
|
5749
5862
|
}
|
|
5750
|
-
class
|
|
5863
|
+
class AFTLoadBalancerStickinessManagerFactory extends runtime.LoadBalancerStickinessManagerFactory {
|
|
5751
5864
|
constructor() {
|
|
5752
5865
|
super(...arguments);
|
|
5753
|
-
this.type =
|
|
5754
|
-
this.isDefault =
|
|
5866
|
+
this.type = "AFTLoadBalancerStickinessManager";
|
|
5867
|
+
this.isDefault = false;
|
|
5755
5868
|
}
|
|
5756
|
-
async create(config,
|
|
5757
|
-
const resolvedConfig = normalizeConfig$
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
}
|
|
5869
|
+
async create(config, keyProvider, verifier) {
|
|
5870
|
+
const resolvedConfig = normalizeConfig$4(config);
|
|
5871
|
+
let effectiveVerifier = verifier ?? null;
|
|
5872
|
+
if (!effectiveVerifier && keyProvider) {
|
|
5873
|
+
effectiveVerifier = createAftVerifier({
|
|
5874
|
+
securityLevel: resolvedConfig.securityLevel ?? DEFAULT_VALUES$1.securityLevel,
|
|
5875
|
+
keyProvider,
|
|
5876
|
+
defaultTtlSec: resolvedConfig.defaultTtlSec ?? DEFAULT_VALUES$1.defaultTtlSec,
|
|
5877
|
+
});
|
|
5878
|
+
}
|
|
5879
|
+
if (!effectiveVerifier) {
|
|
5880
|
+
throw new Error("AFTLoadBalancerStickinessManagerFactory requires an AFT verifier or key provider");
|
|
5881
|
+
}
|
|
5882
|
+
return new AFTLoadBalancerStickinessManager(resolvedConfig, effectiveVerifier);
|
|
5769
5883
|
}
|
|
5770
5884
|
}
|
|
5771
5885
|
|
|
5772
|
-
var
|
|
5886
|
+
var aftLoadBalancerStickinessManagerFactory = /*#__PURE__*/Object.freeze({
|
|
5773
5887
|
__proto__: null,
|
|
5774
|
-
|
|
5775
|
-
FACTORY_META: FACTORY_META$
|
|
5776
|
-
default:
|
|
5888
|
+
AFTLoadBalancerStickinessManagerFactory: AFTLoadBalancerStickinessManagerFactory,
|
|
5889
|
+
FACTORY_META: FACTORY_META$7,
|
|
5890
|
+
default: AFTLoadBalancerStickinessManagerFactory
|
|
5777
5891
|
});
|
|
5778
5892
|
|
|
5779
|
-
const logger$
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
process.env?.[ENV_VAR_SHOW_ENVELOPES] === "true";
|
|
5784
|
-
function nowUtc() {
|
|
5785
|
-
return new Date();
|
|
5786
|
-
}
|
|
5787
|
-
function formatTimestampForConsole() {
|
|
5788
|
-
return runtime.color(runtime.formatTimestamp(), runtime.AnsiColor.GRAY);
|
|
5789
|
-
}
|
|
5790
|
-
function prettyModel(value) {
|
|
5791
|
-
try {
|
|
5792
|
-
return runtime.jsonDumps(value);
|
|
5793
|
-
}
|
|
5794
|
-
catch (error) {
|
|
5795
|
-
return String(error);
|
|
5796
|
-
}
|
|
5797
|
-
}
|
|
5798
|
-
function coercePlacementMetadataValue(metadata, camelCaseKey, snakeCaseKey) {
|
|
5799
|
-
if (!metadata) {
|
|
5800
|
-
return undefined;
|
|
5801
|
-
}
|
|
5802
|
-
const record = metadata;
|
|
5803
|
-
if (record[camelCaseKey] !== undefined) {
|
|
5804
|
-
return record[camelCaseKey];
|
|
5893
|
+
const logger$3 = runtime.getLogger("naylence.fame.stickiness.aft_replica_stickiness_manager");
|
|
5894
|
+
function isStickinessRequired(context) {
|
|
5895
|
+
if (typeof context.stickinessRequired === "boolean") {
|
|
5896
|
+
return context.stickinessRequired;
|
|
5805
5897
|
}
|
|
5806
|
-
if (
|
|
5807
|
-
return
|
|
5898
|
+
if (typeof context.stickiness_required === "boolean") {
|
|
5899
|
+
return context.stickiness_required;
|
|
5808
5900
|
}
|
|
5809
|
-
return
|
|
5901
|
+
return false;
|
|
5810
5902
|
}
|
|
5811
|
-
class
|
|
5812
|
-
constructor(options) {
|
|
5813
|
-
|
|
5814
|
-
this.
|
|
5815
|
-
|
|
5816
|
-
this.
|
|
5817
|
-
this.
|
|
5818
|
-
this.
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
const systemId = trimmedSystemId.length > 0 ? trimmedSystemId : core.generateId();
|
|
5833
|
-
const wasAssigned = trimmedSystemId.length === 0;
|
|
5834
|
-
const normalizedHello = {
|
|
5835
|
-
...hello,
|
|
5836
|
-
systemId,
|
|
5837
|
-
};
|
|
5838
|
-
if (showEnvelopes) {
|
|
5839
|
-
// eslint-disable-next-line no-console
|
|
5840
|
-
console.log(`\n${formatTimestampForConsole()} - ${runtime.color("Received envelope 📨", runtime.AnsiColor.BLUE)}\n${prettyModel(normalizedHello)}`);
|
|
5903
|
+
class AFTReplicaStickinessManager extends runtime.BaseNodeEventListener {
|
|
5904
|
+
constructor(options = {}) {
|
|
5905
|
+
super();
|
|
5906
|
+
this.securityLevel =
|
|
5907
|
+
normalizeStickinessMode(options.securityLevel ?? DEFAULT_STICKINESS_SECURITY_LEVEL) ?? DEFAULT_STICKINESS_SECURITY_LEVEL;
|
|
5908
|
+
this.aftHelper = options.aftHelper ?? null;
|
|
5909
|
+
this.maxTtlSec = options.maxTtlSec ?? 7200;
|
|
5910
|
+
this.isInitialized = this.aftHelper !== null;
|
|
5911
|
+
this.negotiatedStickiness = null;
|
|
5912
|
+
if (this.aftHelper) {
|
|
5913
|
+
logger$3.debug("aft_replica_stickiness_manager_initialized", {
|
|
5914
|
+
helper_type: this.aftHelper.signer.constructor.name,
|
|
5915
|
+
security_level: this.aftHelper.signer.securityLevel,
|
|
5916
|
+
max_ttl_sec: this.aftHelper.maxTtlSec,
|
|
5917
|
+
});
|
|
5918
|
+
}
|
|
5919
|
+
else {
|
|
5920
|
+
logger$3.debug("aft_replica_stickiness_manager_created", {
|
|
5921
|
+
security_level: this.securityLevel,
|
|
5922
|
+
max_ttl_sec: this.maxTtlSec,
|
|
5923
|
+
});
|
|
5841
5924
|
}
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5925
|
+
}
|
|
5926
|
+
offer() {
|
|
5927
|
+
return { mode: "aft", supportedModes: ["aft", "attr"], version: 1 };
|
|
5928
|
+
}
|
|
5929
|
+
accept(stickiness) {
|
|
5930
|
+
this.negotiatedStickiness = stickiness ?? null;
|
|
5931
|
+
logger$3.debug("replica_stickiness_policy_set", {
|
|
5932
|
+
enabled: stickiness?.enabled ?? null,
|
|
5933
|
+
mode: stickiness?.mode ?? null,
|
|
5934
|
+
ttl: stickiness?.ttlSec ?? null,
|
|
5848
5935
|
});
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
if (
|
|
5852
|
-
|
|
5853
|
-
fullMetadata.instanceId = normalizedHello.instanceId;
|
|
5854
|
-
}
|
|
5855
|
-
if (fullMetadata.instance_id === undefined) {
|
|
5856
|
-
fullMetadata.instance_id = normalizedHello.instanceId;
|
|
5857
|
-
}
|
|
5936
|
+
}
|
|
5937
|
+
async onForwardUpstream(_node, envelope, context) {
|
|
5938
|
+
if (!context) {
|
|
5939
|
+
return envelope;
|
|
5858
5940
|
}
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
logicals: normalizedHello.logicals,
|
|
5941
|
+
const helper = this.aftHelper;
|
|
5942
|
+
if (!helper) {
|
|
5943
|
+
logger$3.debug("aft_helper_not_ready_skip_injection", {
|
|
5944
|
+
envelope_id: envelope.id,
|
|
5945
|
+
delivery_origin: context.originType ?? null,
|
|
5946
|
+
reason: "not_initialized",
|
|
5866
5947
|
});
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5948
|
+
return envelope;
|
|
5949
|
+
}
|
|
5950
|
+
const stickinessContext = context;
|
|
5951
|
+
if (isStickinessRequired(stickinessContext) &&
|
|
5952
|
+
context.originType === core.DeliveryOriginType.LOCAL) {
|
|
5953
|
+
if (this.negotiatedStickiness) {
|
|
5954
|
+
const negotiated = this.negotiatedStickiness;
|
|
5955
|
+
if (negotiated.enabled === false ||
|
|
5956
|
+
(negotiated.mode !== null &&
|
|
5957
|
+
negotiated.mode !== undefined &&
|
|
5958
|
+
negotiated.mode !== "aft")) {
|
|
5959
|
+
logger$3.debug("aft_injection_skipped_due_to_policy", {
|
|
5960
|
+
envelope_id: envelope.id,
|
|
5961
|
+
policy_mode: negotiated.mode ?? null,
|
|
5962
|
+
policy_enabled: negotiated.enabled ?? null,
|
|
5963
|
+
});
|
|
5964
|
+
return envelope;
|
|
5965
|
+
}
|
|
5966
|
+
}
|
|
5967
|
+
logger$3.debug("applying_aft_for_upstream_stickiness_required", {
|
|
5968
|
+
envelope_id: envelope.id,
|
|
5969
|
+
from_system_id: context.fromSystemId ?? null,
|
|
5970
|
+
delivery_origin: context.originType ?? null,
|
|
5971
|
+
});
|
|
5972
|
+
const success = await helper.requestStickiness(envelope, {
|
|
5973
|
+
ttlSec: null,
|
|
5974
|
+
scope: "node",
|
|
5975
|
+
context: stickinessContext,
|
|
5976
|
+
});
|
|
5977
|
+
if (success) {
|
|
5978
|
+
logger$3.debug("aft_token_applied_via_context_flag_upstream", {
|
|
5979
|
+
envelope_id: envelope.id,
|
|
5980
|
+
from_system_id: context.fromSystemId ?? null,
|
|
5981
|
+
delivery_origin: context.originType ?? null,
|
|
5982
|
+
});
|
|
5983
|
+
}
|
|
5984
|
+
else {
|
|
5985
|
+
logger$3.debug("aft_token_not_applied_upstream", {
|
|
5986
|
+
envelope_id: envelope.id,
|
|
5987
|
+
delivery_origin: context.originType ?? null,
|
|
5988
|
+
reason: "helper_returned_false",
|
|
5872
5989
|
});
|
|
5873
|
-
throw new Error(`Invalid logical format: ${pathError}`);
|
|
5874
5990
|
}
|
|
5875
|
-
logger$2.debug("logicals_validation_successful");
|
|
5876
5991
|
}
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5992
|
+
return envelope;
|
|
5993
|
+
}
|
|
5994
|
+
async onNodeStarted(node) {
|
|
5995
|
+
if (!this.isInitialized) {
|
|
5996
|
+
await this.initializeAftHelper(node);
|
|
5997
|
+
return;
|
|
5998
|
+
}
|
|
5999
|
+
if (this.aftHelper && node.sid) {
|
|
6000
|
+
this.updateNodeSid(node.sid);
|
|
6001
|
+
logger$3.debug("aft_replica_stickiness_manager_sid_updated", {
|
|
6002
|
+
node_id: node.id ?? "unknown",
|
|
6003
|
+
node_sid: node.sid,
|
|
6004
|
+
security_level: this.aftHelper.signer.securityLevel,
|
|
5883
6005
|
});
|
|
5884
|
-
throw new Error(placementResult.reason || "Node not accepted");
|
|
5885
6006
|
}
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
assignedPath,
|
|
5890
|
-
targetPhysicalPath: placementResult.targetPhysicalPath ?? null,
|
|
5891
|
-
targetSystemId: placementResult.targetSystemId ?? null,
|
|
5892
|
-
});
|
|
5893
|
-
const acceptedCapabilities = coercePlacementMetadataValue(placementResult.metadata, "acceptedCapabilities", "accepted_capabilities") ??
|
|
5894
|
-
normalizedHello.capabilities ??
|
|
5895
|
-
null;
|
|
5896
|
-
const acceptedLogicals = coercePlacementMetadataValue(placementResult.metadata, "acceptedLogicals", "accepted_logicals") ??
|
|
5897
|
-
normalizedHello.logicals ??
|
|
5898
|
-
null;
|
|
5899
|
-
logger$2.debug("processing_placement_result_metadata", {
|
|
5900
|
-
acceptedCapabilities,
|
|
5901
|
-
acceptedLogicals,
|
|
5902
|
-
hasPlacementMetadata: placementResult.metadata !== undefined &&
|
|
5903
|
-
placementResult.metadata !== null,
|
|
5904
|
-
});
|
|
5905
|
-
const connectionGrants = [];
|
|
5906
|
-
const metadataInstanceId = (typeof fullMetadata.instanceId === "string" &&
|
|
5907
|
-
fullMetadata.instanceId) ||
|
|
5908
|
-
(typeof fullMetadata.instance_id === "string" &&
|
|
5909
|
-
fullMetadata.instance_id) ||
|
|
5910
|
-
normalizedHello.instanceId ||
|
|
5911
|
-
core.generateId();
|
|
5912
|
-
if (placementResult.targetSystemId) {
|
|
5913
|
-
logger$2.debug("issuing_node_attach_token", {
|
|
5914
|
-
systemId,
|
|
5915
|
-
assignedPath,
|
|
6007
|
+
else if (!node.sid) {
|
|
6008
|
+
logger$3.warning("aft_replica_stickiness_manager_no_sid_available", {
|
|
6009
|
+
node_id: node.id ?? "unknown",
|
|
5916
6010
|
});
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
assigned_path: placementResult.assignedPath,
|
|
5922
|
-
accepted_logicals: acceptedLogicals,
|
|
5923
|
-
instance_id: metadataInstanceId,
|
|
6011
|
+
}
|
|
6012
|
+
else {
|
|
6013
|
+
logger$3.error("aft_replica_stickiness_manager_node_missing_sid", {
|
|
6014
|
+
node_type: node.constructor?.name ?? typeof node,
|
|
5924
6015
|
});
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
? (transportInfo.connectionGrant.type ??
|
|
5933
|
-
"Unknown")
|
|
5934
|
-
: "Unknown",
|
|
6016
|
+
}
|
|
6017
|
+
}
|
|
6018
|
+
updateNodeSid(nodeSid) {
|
|
6019
|
+
if (this.aftHelper) {
|
|
6020
|
+
this.aftHelper.nodeSid = nodeSid;
|
|
6021
|
+
logger$3.debug("aft_replica_stickiness_manager_sid_updated", {
|
|
6022
|
+
new_sid: nodeSid,
|
|
5935
6023
|
});
|
|
5936
|
-
connectionGrants.push(transportInfo.connectionGrant);
|
|
5937
6024
|
}
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
const
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
6025
|
+
}
|
|
6026
|
+
async initializeAftHelper(node) {
|
|
6027
|
+
const nodeSid = node.sid;
|
|
6028
|
+
if (!nodeSid) {
|
|
6029
|
+
logger$3.error("aft_replica_stickiness_manager_cannot_initialize_no_sid", {
|
|
6030
|
+
node_id: node.id ?? "unknown",
|
|
6031
|
+
});
|
|
6032
|
+
return;
|
|
6033
|
+
}
|
|
6034
|
+
const cryptoProvider = node.cryptoProvider ?? null;
|
|
6035
|
+
if (!cryptoProvider) {
|
|
6036
|
+
logger$3.error("aft_replica_stickiness_manager_cannot_initialize_no_crypto_provider", {
|
|
6037
|
+
node_id: node.id ?? "unknown",
|
|
6038
|
+
});
|
|
6039
|
+
return;
|
|
6040
|
+
}
|
|
6041
|
+
const keyId = typeof cryptoProvider.signatureKeyId === "string" &&
|
|
6042
|
+
cryptoProvider.signatureKeyId.length > 0
|
|
6043
|
+
? cryptoProvider.signatureKeyId
|
|
6044
|
+
: "default-key-id";
|
|
6045
|
+
const privateKeyPem = typeof cryptoProvider.signingPrivatePem === "string"
|
|
6046
|
+
? cryptoProvider.signingPrivatePem
|
|
6047
|
+
: null;
|
|
6048
|
+
if (this.securityLevel === exports.StickinessMode.STRICT && !privateKeyPem) {
|
|
6049
|
+
logger$3.error("aft_replica_stickiness_manager_initialization_failed", {
|
|
6050
|
+
node_id: node.id ?? "unknown",
|
|
6051
|
+
error: "Missing signing private key for strict security level",
|
|
6052
|
+
});
|
|
6053
|
+
return;
|
|
6054
|
+
}
|
|
6055
|
+
try {
|
|
6056
|
+
const helper = createAftHelper({
|
|
6057
|
+
securityLevel: this.securityLevel,
|
|
6058
|
+
nodeSid,
|
|
6059
|
+
kid: keyId,
|
|
6060
|
+
privateKeyPem,
|
|
6061
|
+
maxTtlSec: this.maxTtlSec,
|
|
6062
|
+
});
|
|
6063
|
+
this.aftHelper = helper;
|
|
6064
|
+
this.isInitialized = true;
|
|
6065
|
+
logger$3.debug("aft_replica_stickiness_manager_initialized", {
|
|
6066
|
+
node_id: node.id ?? "unknown",
|
|
6067
|
+
node_sid: nodeSid,
|
|
6068
|
+
key_id: keyId,
|
|
6069
|
+
security_level: helper.signer.securityLevel,
|
|
6070
|
+
});
|
|
5983
6071
|
}
|
|
5984
|
-
|
|
6072
|
+
catch (error) {
|
|
6073
|
+
logger$3.error("aft_replica_stickiness_manager_initialization_failed", {
|
|
6074
|
+
node_id: node.id ?? "unknown",
|
|
6075
|
+
error: error instanceof Error ? error.message : String(error),
|
|
6076
|
+
});
|
|
6077
|
+
}
|
|
6078
|
+
}
|
|
6079
|
+
get signer() {
|
|
6080
|
+
return this.aftHelper?.signer ?? null;
|
|
6081
|
+
}
|
|
6082
|
+
getHelper() {
|
|
6083
|
+
return this.aftHelper;
|
|
5985
6084
|
}
|
|
5986
6085
|
}
|
|
6086
|
+
function createAftReplicaStickinessManager(aftHelper) {
|
|
6087
|
+
return new AFTReplicaStickinessManager({ aftHelper });
|
|
6088
|
+
}
|
|
5987
6089
|
|
|
5988
|
-
const FACTORY_META$
|
|
5989
|
-
base: runtime.
|
|
5990
|
-
key: "
|
|
5991
|
-
priority: 100,
|
|
5992
|
-
isDefault: true,
|
|
6090
|
+
const FACTORY_META$6 = {
|
|
6091
|
+
base: runtime.REPLICA_STICKINESS_MANAGER_FACTORY_BASE_TYPE,
|
|
6092
|
+
key: "AFTReplicaStickinessManager",
|
|
5993
6093
|
};
|
|
5994
|
-
|
|
6094
|
+
const DEFAULT_VALUES = {
|
|
6095
|
+
securityLevel: exports.StickinessMode.SIGNED_OPTIONAL,
|
|
6096
|
+
maxTtlSec: 7200,
|
|
6097
|
+
};
|
|
6098
|
+
function normalizeConfig$3(config) {
|
|
6099
|
+
const record = (config ?? {});
|
|
6100
|
+
const normalizedSecurity = record.securityLevel
|
|
6101
|
+
? normalizeStickinessMode(record.securityLevel)
|
|
6102
|
+
: DEFAULT_VALUES.securityLevel;
|
|
6103
|
+
const securityLevel = normalizedSecurity ?? DEFAULT_VALUES.securityLevel;
|
|
6104
|
+
const maxTtlSecValue = typeof record.maxTtlSec === "number" && Number.isFinite(record.maxTtlSec)
|
|
6105
|
+
? Math.max(0, Math.floor(record.maxTtlSec))
|
|
6106
|
+
: DEFAULT_VALUES.maxTtlSec;
|
|
6107
|
+
return {
|
|
6108
|
+
...record,
|
|
6109
|
+
type: "AFTReplicaStickinessManager",
|
|
6110
|
+
securityLevel,
|
|
6111
|
+
maxTtlSec: maxTtlSecValue,
|
|
6112
|
+
};
|
|
6113
|
+
}
|
|
6114
|
+
class AFTReplicaStickinessManagerFactory extends runtime.ReplicaStickinessManagerFactory {
|
|
5995
6115
|
constructor() {
|
|
5996
6116
|
super(...arguments);
|
|
5997
|
-
this.type = FACTORY_META$
|
|
5998
|
-
this.isDefault =
|
|
5999
|
-
this.priority = FACTORY_META$5.priority;
|
|
6000
|
-
}
|
|
6001
|
-
async create(config, ...factoryArgs) {
|
|
6002
|
-
const normalized = normalizeConfig$2(config);
|
|
6003
|
-
// Crypto provider should be passed from upstream (node-welcome-server)
|
|
6004
|
-
// Do not create it here - downstream components should use what's passed in factoryArgs
|
|
6005
|
-
const placementStrategy = await runtime.NodePlacementStrategyFactory.createNodePlacementStrategy(normalized.placementConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
6006
|
-
const transportProvisioner = await runtime.TransportProvisionerFactory.createTransportProvisioner(normalized.transportConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
6007
|
-
const tokenIssuer = await runtime.TokenIssuerFactory.createTokenIssuer(normalized.tokenIssuerConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
6008
|
-
let authorizer = null;
|
|
6009
|
-
if (normalized.authorizerConfig) {
|
|
6010
|
-
authorizer =
|
|
6011
|
-
(await runtime.AuthorizerFactory.createAuthorizer(normalized.authorizerConfig, {
|
|
6012
|
-
factoryArgs,
|
|
6013
|
-
})) ?? null;
|
|
6014
|
-
}
|
|
6015
|
-
const options = {
|
|
6016
|
-
placementStrategy,
|
|
6017
|
-
transportProvisioner,
|
|
6018
|
-
tokenIssuer,
|
|
6019
|
-
authorizer,
|
|
6020
|
-
caServiceUrl: normalized.caServiceUrl,
|
|
6021
|
-
};
|
|
6022
|
-
if (normalized.ttlSec !== undefined) {
|
|
6023
|
-
options.ttlSec = normalized.ttlSec;
|
|
6024
|
-
}
|
|
6025
|
-
return new AdvancedWelcomeService(options);
|
|
6026
|
-
}
|
|
6027
|
-
}
|
|
6028
|
-
function normalizeConfig$2(config) {
|
|
6029
|
-
if (!config) {
|
|
6030
|
-
throw new Error("AdvancedWelcomeService requires configuration");
|
|
6031
|
-
}
|
|
6032
|
-
const source = config;
|
|
6033
|
-
const ttlCandidate = typeof source.ttlSec === "number"
|
|
6034
|
-
? source.ttlSec
|
|
6035
|
-
: typeof source.ttl_sec === "number"
|
|
6036
|
-
? source.ttl_sec
|
|
6037
|
-
: undefined;
|
|
6038
|
-
const caServiceUrlCandidate = typeof source.caServiceUrl === "string" &&
|
|
6039
|
-
source.caServiceUrl.trim().length > 0
|
|
6040
|
-
? source.caServiceUrl.trim()
|
|
6041
|
-
: typeof source.ca_service_url === "string" &&
|
|
6042
|
-
source.ca_service_url.trim().length > 0
|
|
6043
|
-
? source.ca_service_url.trim()
|
|
6044
|
-
: undefined;
|
|
6045
|
-
if (!caServiceUrlCandidate) {
|
|
6046
|
-
throw new Error("AdvancedWelcomeService configuration requires caServiceUrl");
|
|
6047
|
-
}
|
|
6048
|
-
const normalized = {
|
|
6049
|
-
caServiceUrl: caServiceUrlCandidate,
|
|
6050
|
-
};
|
|
6051
|
-
if (source.placement !== undefined) {
|
|
6052
|
-
normalized.placementConfig =
|
|
6053
|
-
source.placement ?? null;
|
|
6054
|
-
}
|
|
6055
|
-
if (source.transport !== undefined) {
|
|
6056
|
-
normalized.transportConfig =
|
|
6057
|
-
source.transport ?? null;
|
|
6058
|
-
}
|
|
6059
|
-
const tokenIssuerConfig = source.tokenIssuer !== undefined
|
|
6060
|
-
? source.tokenIssuer
|
|
6061
|
-
: source.token_issuer !== undefined
|
|
6062
|
-
? source.token_issuer
|
|
6063
|
-
: undefined;
|
|
6064
|
-
if (tokenIssuerConfig !== undefined) {
|
|
6065
|
-
normalized.tokenIssuerConfig =
|
|
6066
|
-
tokenIssuerConfig ?? null;
|
|
6067
|
-
}
|
|
6068
|
-
if (source.authorizer !== undefined) {
|
|
6069
|
-
normalized.authorizerConfig =
|
|
6070
|
-
source.authorizer ?? null;
|
|
6117
|
+
this.type = FACTORY_META$6.key;
|
|
6118
|
+
this.isDefault = true;
|
|
6071
6119
|
}
|
|
6072
|
-
|
|
6073
|
-
|
|
6120
|
+
async create(config, dependencies) {
|
|
6121
|
+
const resolvedConfig = normalizeConfig$3(config);
|
|
6122
|
+
const helper = dependencies?.aftHelper ?? null;
|
|
6123
|
+
const securityLevel = normalizeStickinessMode(resolvedConfig.securityLevel ?? DEFAULT_VALUES.securityLevel) ?? DEFAULT_VALUES.securityLevel;
|
|
6124
|
+
const maxTtlSec = typeof resolvedConfig.maxTtlSec === "number" &&
|
|
6125
|
+
Number.isFinite(resolvedConfig.maxTtlSec)
|
|
6126
|
+
? Math.max(0, Math.floor(resolvedConfig.maxTtlSec))
|
|
6127
|
+
: DEFAULT_VALUES.maxTtlSec;
|
|
6128
|
+
return new AFTReplicaStickinessManager({
|
|
6129
|
+
securityLevel,
|
|
6130
|
+
maxTtlSec,
|
|
6131
|
+
aftHelper: helper,
|
|
6132
|
+
});
|
|
6074
6133
|
}
|
|
6075
|
-
return normalized;
|
|
6076
6134
|
}
|
|
6077
6135
|
|
|
6078
|
-
var
|
|
6136
|
+
var aftReplicaStickinessManagerFactory = /*#__PURE__*/Object.freeze({
|
|
6079
6137
|
__proto__: null,
|
|
6080
|
-
|
|
6081
|
-
FACTORY_META: FACTORY_META$
|
|
6082
|
-
default:
|
|
6138
|
+
AFTReplicaStickinessManagerFactory: AFTReplicaStickinessManagerFactory,
|
|
6139
|
+
FACTORY_META: FACTORY_META$6,
|
|
6140
|
+
default: AFTReplicaStickinessManagerFactory
|
|
6083
6141
|
});
|
|
6084
6142
|
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
"./security/encryption/sealed/x25519-encryption-manager-factory.js",
|
|
6100
|
-
"./security/keys/x5c-key-manager-factory.js",
|
|
6101
|
-
"./security/signing/eddsa-envelope-signer-factory.js",
|
|
6102
|
-
"./security/signing/eddsa-envelope-verifier-factory.js",
|
|
6103
|
-
"./stickiness/aft-load-balancer-stickiness-manager-factory.js",
|
|
6104
|
-
"./stickiness/aft-replica-stickiness-manager-factory.js",
|
|
6105
|
-
"./welcome/advanced-welcome-service-factory.js"
|
|
6106
|
-
];
|
|
6107
|
-
const MODULE_LOADERS = {
|
|
6108
|
-
"./security/cert/default-ca-service-factory.js": () => Promise.resolve().then(function () { return defaultCaServiceFactory; }),
|
|
6109
|
-
"./security/cert/default-certificate-manager-factory.js": () => Promise.resolve().then(function () { return defaultCertificateManagerFactory; }),
|
|
6110
|
-
"./security/cert/trust-store/browser-trust-store-provider-factory.js": () => Promise.resolve().then(function () { return browserTrustStoreProviderFactory; }),
|
|
6111
|
-
"./security/cert/trust-store/node-trust-store-provider-factory.js": () => Promise.resolve().then(function () { return nodeTrustStoreProviderFactory; }),
|
|
6112
|
-
"./security/encryption/channel/channel-encryption-manager-factory.js": () => Promise.resolve().then(function () { return channelEncryptionManagerFactory; }),
|
|
6113
|
-
"./security/encryption/composite-encryption-manager-factory.js": () => Promise.resolve().then(function () { return compositeEncryptionManagerFactory; }),
|
|
6114
|
-
"./security/encryption/default-secure-channel-manager-factory.js": () => Promise.resolve().then(function () { return defaultSecureChannelManagerFactory; }),
|
|
6115
|
-
"./security/encryption/sealed/x25519-encryption-manager-factory.js": () => Promise.resolve().then(function () { return x25519EncryptionManagerFactory; }),
|
|
6116
|
-
"./security/keys/x5c-key-manager-factory.js": () => Promise.resolve().then(function () { return x5cKeyManagerFactory; }),
|
|
6117
|
-
"./security/signing/eddsa-envelope-signer-factory.js": () => Promise.resolve().then(function () { return eddsaEnvelopeSignerFactory; }),
|
|
6118
|
-
"./security/signing/eddsa-envelope-verifier-factory.js": () => Promise.resolve().then(function () { return eddsaEnvelopeVerifierFactory; }),
|
|
6119
|
-
"./stickiness/aft-load-balancer-stickiness-manager-factory.js": () => Promise.resolve().then(function () { return aftLoadBalancerStickinessManagerFactory; }),
|
|
6120
|
-
"./stickiness/aft-replica-stickiness-manager-factory.js": () => Promise.resolve().then(function () { return aftReplicaStickinessManagerFactory; }),
|
|
6121
|
-
"./welcome/advanced-welcome-service-factory.js": () => Promise.resolve().then(function () { return advancedWelcomeServiceFactory; }),
|
|
6122
|
-
};
|
|
6123
|
-
|
|
6124
|
-
const SECURITY_PREFIX = "./security/";
|
|
6125
|
-
const SECURITY_MODULES = MODULES.filter((spec) => spec.startsWith(SECURITY_PREFIX));
|
|
6126
|
-
const EXTRA_MODULES = MODULES.filter((spec) => !spec.startsWith(SECURITY_PREFIX));
|
|
6127
|
-
const NODE_ONLY_MODULES = new Set([
|
|
6128
|
-
"./security/cert/default-ca-service-factory.js",
|
|
6129
|
-
"./security/cert/trust-store/node-trust-store-provider-factory.js",
|
|
6130
|
-
]);
|
|
6131
|
-
const FACTORY_MODULE_PREFIX$1 = "@naylence/advanced-security/naylence/fame/";
|
|
6132
|
-
const BROWSER_DIST_SEGMENT = "/dist/browser/";
|
|
6133
|
-
function detectModuleUrl() {
|
|
6134
|
-
if (typeof __filename === "string") {
|
|
6135
|
-
try {
|
|
6136
|
-
return __filename.startsWith("file://")
|
|
6137
|
-
? __filename
|
|
6138
|
-
: `file://${__filename}`;
|
|
6139
|
-
}
|
|
6140
|
-
catch {
|
|
6141
|
-
// fall through to stack parsing
|
|
6142
|
-
}
|
|
6143
|
-
}
|
|
6144
|
-
try {
|
|
6145
|
-
throw new Error();
|
|
6146
|
-
}
|
|
6147
|
-
catch (error) {
|
|
6148
|
-
const stack = typeof error === "object" && error && "stack" in error
|
|
6149
|
-
? String(error.stack ?? "")
|
|
6150
|
-
: "";
|
|
6151
|
-
const lines = stack.split("\n");
|
|
6152
|
-
for (const line of lines) {
|
|
6153
|
-
const match = line.match(/(https?:\/\/[^\s)]+|file:\/\/[^\s)]+\.(?:js|ts)|\/(?:[^\s)]+\.(?:js|ts)))/u);
|
|
6154
|
-
const candidate = match?.[1];
|
|
6155
|
-
if (!candidate) {
|
|
6156
|
-
continue;
|
|
6157
|
-
}
|
|
6158
|
-
if (candidate.startsWith("http://") || candidate.startsWith("https://")) {
|
|
6159
|
-
return candidate;
|
|
6160
|
-
}
|
|
6161
|
-
if (candidate.startsWith("file://")) {
|
|
6162
|
-
return candidate;
|
|
6163
|
-
}
|
|
6164
|
-
return `file://${candidate}`;
|
|
6165
|
-
}
|
|
6166
|
-
}
|
|
6167
|
-
return null;
|
|
6168
|
-
}
|
|
6169
|
-
function computeBrowserFactoryBase(rawUrl) {
|
|
6170
|
-
if (!rawUrl) {
|
|
6171
|
-
return null;
|
|
6172
|
-
}
|
|
6173
|
-
const sanitized = rawUrl.split("?")[0]?.split("#")[0] ?? rawUrl;
|
|
6174
|
-
const esmMarker = "/dist/esm/naylence/fame/";
|
|
6175
|
-
const distMarker = "/dist/";
|
|
6176
|
-
if (sanitized.includes(esmMarker)) {
|
|
6177
|
-
return sanitized.slice(0, sanitized.indexOf(esmMarker) + esmMarker.length);
|
|
6178
|
-
}
|
|
6179
|
-
if (rawUrl.includes(BROWSER_DIST_SEGMENT)) {
|
|
6180
|
-
return new URL("../esm/naylence/fame/", rawUrl).href;
|
|
6143
|
+
const logger$2 = runtime.getLogger("naylence.fame.welcome.advanced_welcome_service");
|
|
6144
|
+
const ENV_VAR_SHOW_ENVELOPES = "FAME_SHOW_ENVELOPES";
|
|
6145
|
+
const DEFAULT_TTL_SEC = 3600;
|
|
6146
|
+
const showEnvelopes = typeof process !== "undefined" &&
|
|
6147
|
+
process.env?.[ENV_VAR_SHOW_ENVELOPES] === "true";
|
|
6148
|
+
function nowUtc() {
|
|
6149
|
+
return new Date();
|
|
6150
|
+
}
|
|
6151
|
+
function formatTimestampForConsole() {
|
|
6152
|
+
return runtime.color(runtime.formatTimestamp(), runtime.AnsiColor.GRAY);
|
|
6153
|
+
}
|
|
6154
|
+
function prettyModel(value) {
|
|
6155
|
+
try {
|
|
6156
|
+
return runtime.jsonDumps(value);
|
|
6181
6157
|
}
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
return `${base.replace(/browser\/?$/u, "")}esm/naylence/fame/`;
|
|
6158
|
+
catch (error) {
|
|
6159
|
+
return String(error);
|
|
6185
6160
|
}
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
return
|
|
6161
|
+
}
|
|
6162
|
+
function coercePlacementMetadataValue(metadata, camelCaseKey, snakeCaseKey) {
|
|
6163
|
+
if (!metadata) {
|
|
6164
|
+
return undefined;
|
|
6190
6165
|
}
|
|
6191
|
-
const
|
|
6192
|
-
if (
|
|
6193
|
-
|
|
6194
|
-
const projectRoot = sanitized.slice(0, index);
|
|
6195
|
-
return `${projectRoot}/dist/esm/naylence/fame/`;
|
|
6166
|
+
const record = metadata;
|
|
6167
|
+
if (record[camelCaseKey] !== undefined) {
|
|
6168
|
+
return record[camelCaseKey];
|
|
6196
6169
|
}
|
|
6197
|
-
if (
|
|
6198
|
-
|
|
6199
|
-
const parsed = new URL(rawUrl);
|
|
6200
|
-
const viteDepsSegment = "/node_modules/.vite/deps/";
|
|
6201
|
-
if (parsed.pathname.includes(viteDepsSegment)) {
|
|
6202
|
-
const baseOrigin = `${parsed.protocol}//${parsed.host}`;
|
|
6203
|
-
return `${baseOrigin}/node_modules/@naylence/advanced-security/dist/esm/naylence/fame/`;
|
|
6204
|
-
}
|
|
6205
|
-
}
|
|
6206
|
-
catch {
|
|
6207
|
-
// ignore
|
|
6208
|
-
}
|
|
6170
|
+
if (record[snakeCaseKey] !== undefined) {
|
|
6171
|
+
return record[snakeCaseKey];
|
|
6209
6172
|
}
|
|
6210
|
-
return
|
|
6173
|
+
return undefined;
|
|
6211
6174
|
}
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6175
|
+
class AdvancedWelcomeService {
|
|
6176
|
+
constructor(options) {
|
|
6177
|
+
this.placementStrategy = options.placementStrategy;
|
|
6178
|
+
this.transportProvisioner = options.transportProvisioner;
|
|
6179
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
6180
|
+
this.authorizer = options.authorizer ?? null;
|
|
6181
|
+
this.caServiceUrl = options.caServiceUrl;
|
|
6182
|
+
this.ttlSec =
|
|
6183
|
+
typeof options.ttlSec === "number" && Number.isFinite(options.ttlSec)
|
|
6184
|
+
? Math.max(0, options.ttlSec)
|
|
6185
|
+
: DEFAULT_TTL_SEC;
|
|
6186
|
+
logger$2.debug("initialized_advanced_welcome_service", {
|
|
6187
|
+
ca_service_url: this.caServiceUrl,
|
|
6188
|
+
ttl_sec: this.ttlSec,
|
|
6189
|
+
});
|
|
6221
6190
|
}
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6191
|
+
async handleHello(hello, metadata) {
|
|
6192
|
+
const fullMetadata = metadata
|
|
6193
|
+
? { ...metadata }
|
|
6194
|
+
: {};
|
|
6195
|
+
const trimmedSystemId = typeof hello.systemId === "string" ? hello.systemId.trim() : "";
|
|
6196
|
+
const systemId = trimmedSystemId.length > 0 ? trimmedSystemId : core.generateId();
|
|
6197
|
+
const wasAssigned = trimmedSystemId.length === 0;
|
|
6198
|
+
const normalizedHello = {
|
|
6199
|
+
...hello,
|
|
6200
|
+
systemId,
|
|
6201
|
+
};
|
|
6202
|
+
if (showEnvelopes) {
|
|
6203
|
+
// eslint-disable-next-line no-console
|
|
6204
|
+
console.log(`\n${formatTimestampForConsole()} - ${runtime.color("Received envelope 📨", runtime.AnsiColor.BLUE)}\n${prettyModel(normalizedHello)}`);
|
|
6234
6205
|
}
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6206
|
+
logger$2.debug("starting_hello_frame_processing", {
|
|
6207
|
+
instanceId: normalizedHello.instanceId,
|
|
6208
|
+
systemId,
|
|
6209
|
+
logicals: normalizedHello.logicals,
|
|
6210
|
+
capabilities: normalizedHello.capabilities,
|
|
6211
|
+
ttlSec: this.ttlSec,
|
|
6212
|
+
});
|
|
6213
|
+
const now = nowUtc();
|
|
6214
|
+
const expiry = new Date(now.getTime() + this.ttlSec * 1000);
|
|
6215
|
+
if (normalizedHello.instanceId) {
|
|
6216
|
+
if (fullMetadata.instanceId === undefined) {
|
|
6217
|
+
fullMetadata.instanceId = normalizedHello.instanceId;
|
|
6218
|
+
}
|
|
6219
|
+
if (fullMetadata.instance_id === undefined) {
|
|
6220
|
+
fullMetadata.instance_id = normalizedHello.instanceId;
|
|
6221
|
+
}
|
|
6241
6222
|
}
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6223
|
+
logger$2.debug("system_id_assignment_completed", {
|
|
6224
|
+
systemId,
|
|
6225
|
+
wasAssigned,
|
|
6226
|
+
});
|
|
6227
|
+
if (normalizedHello.logicals?.length) {
|
|
6228
|
+
logger$2.debug("validating_logicals_for_dns_compatibility", {
|
|
6229
|
+
logicals: normalizedHello.logicals,
|
|
6230
|
+
});
|
|
6231
|
+
const [pathsValid, pathError] = runtime.validateHostLogicals(normalizedHello.logicals);
|
|
6232
|
+
if (!pathsValid) {
|
|
6233
|
+
logger$2.error("logical_validation_failed", {
|
|
6234
|
+
error: pathError,
|
|
6235
|
+
logicals: normalizedHello.logicals,
|
|
6236
|
+
});
|
|
6237
|
+
throw new Error(`Invalid logical format: ${pathError}`);
|
|
6249
6238
|
}
|
|
6239
|
+
logger$2.debug("logicals_validation_successful");
|
|
6250
6240
|
}
|
|
6251
|
-
|
|
6252
|
-
|
|
6241
|
+
logger$2.debug("requesting_node_placement", { systemId });
|
|
6242
|
+
const placementResult = await this.placementStrategy.place(normalizedHello);
|
|
6243
|
+
if (!placementResult.accept) {
|
|
6244
|
+
logger$2.error("node_placement_rejected", {
|
|
6245
|
+
systemId,
|
|
6246
|
+
reason: placementResult.reason,
|
|
6247
|
+
});
|
|
6248
|
+
throw new Error(placementResult.reason || "Node not accepted");
|
|
6253
6249
|
}
|
|
6250
|
+
const assignedPath = placementResult.assignedPath;
|
|
6251
|
+
logger$2.debug("node_placement_accepted", {
|
|
6252
|
+
systemId,
|
|
6253
|
+
assignedPath,
|
|
6254
|
+
targetPhysicalPath: placementResult.targetPhysicalPath ?? null,
|
|
6255
|
+
targetSystemId: placementResult.targetSystemId ?? null,
|
|
6256
|
+
});
|
|
6257
|
+
const acceptedCapabilities = coercePlacementMetadataValue(placementResult.metadata, "acceptedCapabilities", "accepted_capabilities") ??
|
|
6258
|
+
normalizedHello.capabilities ??
|
|
6259
|
+
null;
|
|
6260
|
+
const acceptedLogicals = coercePlacementMetadataValue(placementResult.metadata, "acceptedLogicals", "accepted_logicals") ??
|
|
6261
|
+
normalizedHello.logicals ??
|
|
6262
|
+
null;
|
|
6263
|
+
logger$2.debug("processing_placement_result_metadata", {
|
|
6264
|
+
acceptedCapabilities,
|
|
6265
|
+
acceptedLogicals,
|
|
6266
|
+
hasPlacementMetadata: placementResult.metadata !== undefined &&
|
|
6267
|
+
placementResult.metadata !== null,
|
|
6268
|
+
});
|
|
6269
|
+
const connectionGrants = [];
|
|
6270
|
+
const metadataInstanceId = (typeof fullMetadata.instanceId === "string" &&
|
|
6271
|
+
fullMetadata.instanceId) ||
|
|
6272
|
+
(typeof fullMetadata.instance_id === "string" &&
|
|
6273
|
+
fullMetadata.instance_id) ||
|
|
6274
|
+
normalizedHello.instanceId ||
|
|
6275
|
+
core.generateId();
|
|
6276
|
+
if (placementResult.targetSystemId) {
|
|
6277
|
+
logger$2.debug("issuing_node_attach_token", {
|
|
6278
|
+
systemId,
|
|
6279
|
+
assignedPath,
|
|
6280
|
+
});
|
|
6281
|
+
const nodeAttachToken = await this.tokenIssuer.issue({
|
|
6282
|
+
aud: placementResult.targetPhysicalPath,
|
|
6283
|
+
system_id: systemId,
|
|
6284
|
+
parent_path: placementResult.targetPhysicalPath,
|
|
6285
|
+
assigned_path: placementResult.assignedPath,
|
|
6286
|
+
accepted_logicals: acceptedLogicals,
|
|
6287
|
+
instance_id: metadataInstanceId,
|
|
6288
|
+
});
|
|
6289
|
+
logger$2.debug("token_issued_successfully");
|
|
6290
|
+
logger$2.debug("provisioning_transport", { systemId });
|
|
6291
|
+
const transportInfo = await this.transportProvisioner.provision(placementResult, normalizedHello, fullMetadata, nodeAttachToken);
|
|
6292
|
+
logger$2.debug("transport_provisioned_successfully", {
|
|
6293
|
+
systemId,
|
|
6294
|
+
directiveType: transportInfo.connectionGrant &&
|
|
6295
|
+
typeof transportInfo.connectionGrant === "object"
|
|
6296
|
+
? (transportInfo.connectionGrant.type ??
|
|
6297
|
+
"Unknown")
|
|
6298
|
+
: "Unknown",
|
|
6299
|
+
});
|
|
6300
|
+
connectionGrants.push(transportInfo.connectionGrant);
|
|
6301
|
+
}
|
|
6302
|
+
const caSignToken = await this.tokenIssuer.issue({
|
|
6303
|
+
aud: "ca",
|
|
6304
|
+
system_id: systemId,
|
|
6305
|
+
assigned_path: assignedPath,
|
|
6306
|
+
accepted_logicals: acceptedLogicals,
|
|
6307
|
+
instance_id: metadataInstanceId,
|
|
6308
|
+
});
|
|
6309
|
+
const caGrant = {
|
|
6310
|
+
type: runtime.HTTP_CONNECTION_GRANT_TYPE,
|
|
6311
|
+
purpose: GRANT_PURPOSE_CA_SIGN,
|
|
6312
|
+
url: this.caServiceUrl,
|
|
6313
|
+
auth: {
|
|
6314
|
+
type: "BearerTokenHeaderAuth",
|
|
6315
|
+
tokenProvider: {
|
|
6316
|
+
type: "StaticTokenProvider",
|
|
6317
|
+
token: caSignToken,
|
|
6318
|
+
},
|
|
6319
|
+
},
|
|
6320
|
+
};
|
|
6321
|
+
connectionGrants.push(caGrant);
|
|
6322
|
+
const welcomeFrame = {
|
|
6323
|
+
type: "NodeWelcome",
|
|
6324
|
+
systemId,
|
|
6325
|
+
targetSystemId: placementResult.targetSystemId ?? undefined,
|
|
6326
|
+
targetPhysicalPath: placementResult.targetPhysicalPath ?? undefined,
|
|
6327
|
+
instanceId: normalizedHello.instanceId,
|
|
6328
|
+
assignedPath,
|
|
6329
|
+
acceptedCapabilities: acceptedCapabilities ?? undefined,
|
|
6330
|
+
acceptedLogicals: acceptedLogicals ?? undefined,
|
|
6331
|
+
rejectedLogicals: undefined,
|
|
6332
|
+
connectionGrants,
|
|
6333
|
+
metadata: Object.keys(fullMetadata).length > 0 ? fullMetadata : undefined,
|
|
6334
|
+
expiresAt: expiry.toISOString(),
|
|
6335
|
+
};
|
|
6336
|
+
logger$2.debug("hello_frame_processing_completed_successfully", {
|
|
6337
|
+
systemId,
|
|
6338
|
+
assignedPath,
|
|
6339
|
+
acceptedLogicals,
|
|
6340
|
+
acceptedCapabilities,
|
|
6341
|
+
expiresAt: welcomeFrame.expiresAt,
|
|
6342
|
+
instanceId: normalizedHello.instanceId,
|
|
6343
|
+
});
|
|
6344
|
+
if (showEnvelopes) {
|
|
6345
|
+
// eslint-disable-next-line no-console
|
|
6346
|
+
console.log(`\n${formatTimestampForConsole()} - ${runtime.color("Sent envelope", runtime.AnsiColor.BLUE)} 🚀\n${prettyModel(welcomeFrame)}`);
|
|
6347
|
+
}
|
|
6348
|
+
return welcomeFrame;
|
|
6254
6349
|
}
|
|
6255
|
-
const packageCandidate = resolveFactoryModuleSpecifier$1(spec);
|
|
6256
|
-
addCandidate(packageCandidate);
|
|
6257
|
-
if (packageCandidate?.endsWith(".js")) {
|
|
6258
|
-
addCandidate(packageCandidate.replace(/\.js$/u, ".ts"));
|
|
6259
|
-
}
|
|
6260
|
-
const fallback = spec.startsWith("./") ? `../${spec.slice(2)}` : spec;
|
|
6261
|
-
addCandidate(fallback);
|
|
6262
|
-
if (fallback.endsWith(".js")) {
|
|
6263
|
-
addCandidate(fallback.replace(/\.js$/u, ".ts"));
|
|
6264
|
-
}
|
|
6265
|
-
addCandidate(spec);
|
|
6266
|
-
if (spec.endsWith(".js")) {
|
|
6267
|
-
addCandidate(spec.replace(/\.js$/u, ".ts"));
|
|
6268
|
-
}
|
|
6269
|
-
return candidates;
|
|
6270
|
-
}
|
|
6271
|
-
const registeredModules = new Set();
|
|
6272
|
-
const inflightModules = new Map();
|
|
6273
|
-
const browserSkippedModules = new Set();
|
|
6274
|
-
function isNodeEnvironment$5() {
|
|
6275
|
-
return (typeof process !== "undefined" &&
|
|
6276
|
-
typeof process.release !== "undefined" &&
|
|
6277
|
-
process.release?.name === "node");
|
|
6278
|
-
}
|
|
6279
|
-
function shouldSkipModule(spec) {
|
|
6280
|
-
if (isNodeEnvironment$5()) {
|
|
6281
|
-
return false;
|
|
6282
|
-
}
|
|
6283
|
-
if (!NODE_ONLY_MODULES.has(spec)) {
|
|
6284
|
-
return false;
|
|
6285
|
-
}
|
|
6286
|
-
if (!browserSkippedModules.has(spec)) {
|
|
6287
|
-
// console.warn(
|
|
6288
|
-
// "[advanced-security:factory-manifest] skipped browser-incompatible module",
|
|
6289
|
-
// spec,
|
|
6290
|
-
// );
|
|
6291
|
-
browserSkippedModules.add(spec);
|
|
6292
|
-
}
|
|
6293
|
-
return true;
|
|
6294
|
-
}
|
|
6295
|
-
function getDynamicImporter() {
|
|
6296
|
-
if (typeof globalThis === "undefined") {
|
|
6297
|
-
return null;
|
|
6298
|
-
}
|
|
6299
|
-
const candidate = globalThis.__naylenceFactoryDynamicImporter;
|
|
6300
|
-
if (typeof candidate === "function") {
|
|
6301
|
-
return candidate;
|
|
6302
|
-
}
|
|
6303
|
-
return null;
|
|
6304
6350
|
}
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6351
|
+
|
|
6352
|
+
const FACTORY_META$5 = {
|
|
6353
|
+
base: runtime.WELCOME_SERVICE_FACTORY_BASE_TYPE,
|
|
6354
|
+
key: "AdvancedWelcomeService",
|
|
6355
|
+
priority: 100,
|
|
6356
|
+
isDefault: true,
|
|
6357
|
+
};
|
|
6358
|
+
class AdvancedWelcomeServiceFactory extends runtime.WelcomeServiceFactory {
|
|
6359
|
+
constructor() {
|
|
6360
|
+
super(...arguments);
|
|
6361
|
+
this.type = FACTORY_META$5.key;
|
|
6362
|
+
this.isDefault = FACTORY_META$5.isDefault;
|
|
6363
|
+
this.priority = FACTORY_META$5.priority;
|
|
6318
6364
|
}
|
|
6319
|
-
|
|
6320
|
-
const
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
// console.log("[debug] registering module", { spec, base, key, metadata: extraMetadata });
|
|
6333
|
-
registrar.registerFactory(base, key, Ctor, extraMetadata);
|
|
6334
|
-
return true;
|
|
6335
|
-
};
|
|
6336
|
-
for (const [index, { candidate, load }] of attempts.entries()) {
|
|
6337
|
-
try {
|
|
6338
|
-
const mod = await load();
|
|
6339
|
-
return registerFromModule(mod);
|
|
6365
|
+
async create(config, ...factoryArgs) {
|
|
6366
|
+
const normalized = normalizeConfig$2(config);
|
|
6367
|
+
// Crypto provider should be passed from upstream (node-welcome-server)
|
|
6368
|
+
// Do not create it here - downstream components should use what's passed in factoryArgs
|
|
6369
|
+
const placementStrategy = await runtime.NodePlacementStrategyFactory.createNodePlacementStrategy(normalized.placementConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
6370
|
+
const transportProvisioner = await runtime.TransportProvisionerFactory.createTransportProvisioner(normalized.transportConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
6371
|
+
const tokenIssuer = await runtime.TokenIssuerFactory.createTokenIssuer(normalized.tokenIssuerConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
6372
|
+
let authorizer = null;
|
|
6373
|
+
if (normalized.authorizerConfig) {
|
|
6374
|
+
authorizer =
|
|
6375
|
+
(await runtime.AuthorizerFactory.createAuthorizer(normalized.authorizerConfig, {
|
|
6376
|
+
factoryArgs,
|
|
6377
|
+
})) ?? null;
|
|
6340
6378
|
}
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
if (!moduleNotFound || isLastAttempt) {
|
|
6351
|
-
console.warn("[debug] failed to import candidate", {
|
|
6352
|
-
spec,
|
|
6353
|
-
candidate,
|
|
6354
|
-
message,
|
|
6355
|
-
});
|
|
6356
|
-
console.warn("[advanced-security:factory-manifest] skipped", spec, "-", message);
|
|
6357
|
-
return false;
|
|
6358
|
-
}
|
|
6379
|
+
const options = {
|
|
6380
|
+
placementStrategy,
|
|
6381
|
+
transportProvisioner,
|
|
6382
|
+
tokenIssuer,
|
|
6383
|
+
authorizer,
|
|
6384
|
+
caServiceUrl: normalized.caServiceUrl,
|
|
6385
|
+
};
|
|
6386
|
+
if (normalized.ttlSec !== undefined) {
|
|
6387
|
+
options.ttlSec = normalized.ttlSec;
|
|
6359
6388
|
}
|
|
6389
|
+
return new AdvancedWelcomeService(options);
|
|
6360
6390
|
}
|
|
6361
|
-
return false;
|
|
6362
6391
|
}
|
|
6363
|
-
|
|
6364
|
-
if (
|
|
6365
|
-
|
|
6366
|
-
}
|
|
6367
|
-
const inflight = inflightModules.get(spec);
|
|
6368
|
-
if (inflight) {
|
|
6369
|
-
return inflight;
|
|
6392
|
+
function normalizeConfig$2(config) {
|
|
6393
|
+
if (!config) {
|
|
6394
|
+
throw new Error("AdvancedWelcomeService requires configuration");
|
|
6370
6395
|
}
|
|
6371
|
-
const
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6396
|
+
const source = config;
|
|
6397
|
+
const ttlCandidate = typeof source.ttlSec === "number"
|
|
6398
|
+
? source.ttlSec
|
|
6399
|
+
: typeof source.ttl_sec === "number"
|
|
6400
|
+
? source.ttl_sec
|
|
6401
|
+
: undefined;
|
|
6402
|
+
const caServiceUrlCandidate = typeof source.caServiceUrl === "string" &&
|
|
6403
|
+
source.caServiceUrl.trim().length > 0
|
|
6404
|
+
? source.caServiceUrl.trim()
|
|
6405
|
+
: typeof source.ca_service_url === "string" &&
|
|
6406
|
+
source.ca_service_url.trim().length > 0
|
|
6407
|
+
? source.ca_service_url.trim()
|
|
6408
|
+
: undefined;
|
|
6409
|
+
if (!caServiceUrlCandidate) {
|
|
6410
|
+
throw new Error("AdvancedWelcomeService configuration requires caServiceUrl");
|
|
6381
6411
|
}
|
|
6382
|
-
|
|
6383
|
-
|
|
6412
|
+
const normalized = {
|
|
6413
|
+
caServiceUrl: caServiceUrlCandidate,
|
|
6414
|
+
};
|
|
6415
|
+
if (source.placement !== undefined) {
|
|
6416
|
+
normalized.placementConfig =
|
|
6417
|
+
source.placement ?? null;
|
|
6384
6418
|
}
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
return 0;
|
|
6419
|
+
if (source.transport !== undefined) {
|
|
6420
|
+
normalized.transportConfig =
|
|
6421
|
+
source.transport ?? null;
|
|
6389
6422
|
}
|
|
6390
|
-
const
|
|
6391
|
-
|
|
6392
|
-
|
|
6423
|
+
const tokenIssuerConfig = source.tokenIssuer !== undefined
|
|
6424
|
+
? source.tokenIssuer
|
|
6425
|
+
: source.token_issuer !== undefined
|
|
6426
|
+
? source.token_issuer
|
|
6427
|
+
: undefined;
|
|
6428
|
+
if (tokenIssuerConfig !== undefined) {
|
|
6429
|
+
normalized.tokenIssuerConfig =
|
|
6430
|
+
tokenIssuerConfig ?? null;
|
|
6393
6431
|
}
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
async function registerAdvancedSecurityFactories(registrar = factory.Registry, options) {
|
|
6398
|
-
const newlyRegisteredSecurity = await registerModules(SECURITY_MODULES, registrar);
|
|
6399
|
-
if (newlyRegisteredSecurity > 0) {
|
|
6400
|
-
getEncryptionManagerFactoryRegistry().forceRediscovery();
|
|
6432
|
+
if (source.authorizer !== undefined) {
|
|
6433
|
+
normalized.authorizerConfig =
|
|
6434
|
+
source.authorizer ?? null;
|
|
6401
6435
|
}
|
|
6402
|
-
if (
|
|
6403
|
-
|
|
6436
|
+
if (ttlCandidate !== undefined && Number.isFinite(ttlCandidate)) {
|
|
6437
|
+
normalized.ttlSec = ttlCandidate;
|
|
6404
6438
|
}
|
|
6439
|
+
return normalized;
|
|
6405
6440
|
}
|
|
6406
6441
|
|
|
6442
|
+
var advancedWelcomeServiceFactory = /*#__PURE__*/Object.freeze({
|
|
6443
|
+
__proto__: null,
|
|
6444
|
+
AdvancedWelcomeServiceFactory: AdvancedWelcomeServiceFactory,
|
|
6445
|
+
FACTORY_META: FACTORY_META$5,
|
|
6446
|
+
default: AdvancedWelcomeServiceFactory
|
|
6447
|
+
});
|
|
6448
|
+
|
|
6407
6449
|
/**
|
|
6408
6450
|
* Isomorphic entry point for Naylence Advanced Security.
|
|
6409
6451
|
*
|
|
@@ -10100,48 +10142,32 @@ var defaultCaServiceFactory = /*#__PURE__*/Object.freeze({
|
|
|
10100
10142
|
* Includes the full factory surface area, including certificate authority
|
|
10101
10143
|
* helpers that rely on Node.js built-ins.
|
|
10102
10144
|
*/
|
|
10103
|
-
|
|
10104
|
-
|
|
10105
|
-
|
|
10145
|
+
// Always register the plugin directly. This ensures it is initialized even if
|
|
10146
|
+
// the dynamic import mechanism (used by FAME_PLUGINS) fails.
|
|
10147
|
+
(async () => {
|
|
10148
|
+
try {
|
|
10149
|
+
await advancedSecurityPlugin.register();
|
|
10150
|
+
}
|
|
10151
|
+
catch (err) {
|
|
10152
|
+
console.error('[naylence:advanced-security] Failed to auto-register plugin:', err);
|
|
10153
|
+
}
|
|
10154
|
+
})();
|
|
10155
|
+
const g = (typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : {});
|
|
10156
|
+
const proc = g.process || (typeof process !== 'undefined' ? process : undefined);
|
|
10157
|
+
const isNode = typeof process !== 'undefined' &&
|
|
10158
|
+
process.versions != null &&
|
|
10159
|
+
process.versions.node != null;
|
|
10160
|
+
// Only in Node.js: populate FAME_PLUGINS so child processes inherit it.
|
|
10161
|
+
if (isNode && proc && proc.env) {
|
|
10162
|
+
const pluginName = '@naylence/advanced-security';
|
|
10163
|
+
const current = proc.env.FAME_PLUGINS || '';
|
|
10164
|
+
const plugins = current.split(',').map((p) => p.trim());
|
|
10165
|
+
if (!plugins.includes(pluginName)) {
|
|
10166
|
+
proc.env.FAME_PLUGINS = current
|
|
10167
|
+
? `${current},${pluginName}`
|
|
10168
|
+
: pluginName;
|
|
10169
|
+
}
|
|
10106
10170
|
}
|
|
10107
|
-
let initialized = false;
|
|
10108
|
-
let initializing = null;
|
|
10109
|
-
const advancedSecurityPlugin = {
|
|
10110
|
-
name: "naylence:advanced-security",
|
|
10111
|
-
version: VERSION,
|
|
10112
|
-
async register() {
|
|
10113
|
-
// console.log('[naylence:advanced-security] register() called, initialized=', initialized);
|
|
10114
|
-
if (initialized) {
|
|
10115
|
-
// console.log('[naylence:advanced-security] already initialized, skipping');
|
|
10116
|
-
return;
|
|
10117
|
-
}
|
|
10118
|
-
if (initializing) {
|
|
10119
|
-
console.log("[naylence:advanced-security] already initializing, awaiting...");
|
|
10120
|
-
await initializing;
|
|
10121
|
-
return;
|
|
10122
|
-
}
|
|
10123
|
-
initializing = (async () => {
|
|
10124
|
-
try {
|
|
10125
|
-
// console.log('[naylence:advanced-security] registering advanced security factories...');
|
|
10126
|
-
await registerAdvancedSecurityPluginFactories();
|
|
10127
|
-
// console.log('[naylence:advanced-security] advanced security factories registered');
|
|
10128
|
-
initialized = true;
|
|
10129
|
-
}
|
|
10130
|
-
finally {
|
|
10131
|
-
initializing = null;
|
|
10132
|
-
}
|
|
10133
|
-
})();
|
|
10134
|
-
await initializing;
|
|
10135
|
-
},
|
|
10136
|
-
};
|
|
10137
|
-
const ADVANCED_SECURITY_PLUGIN_SPECIFIER = advancedSecurityPlugin.name;
|
|
10138
|
-
|
|
10139
|
-
var plugin = /*#__PURE__*/Object.freeze({
|
|
10140
|
-
__proto__: null,
|
|
10141
|
-
ADVANCED_SECURITY_PLUGIN_SPECIFIER: ADVANCED_SECURITY_PLUGIN_SPECIFIER,
|
|
10142
|
-
default: advancedSecurityPlugin,
|
|
10143
|
-
registerAdvancedSecurityPluginFactories: registerAdvancedSecurityPluginFactories
|
|
10144
|
-
});
|
|
10145
10171
|
|
|
10146
10172
|
exports.ADVANCED_EDDSA_ENVELOPE_SIGNER_FACTORY_META = FACTORY_META$a;
|
|
10147
10173
|
exports.ADVANCED_EDDSA_ENVELOPE_VERIFIER_FACTORY_META = FACTORY_META$9;
|