@naylence/advanced-security 0.3.11 → 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/browser/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ExtensionManager, Registry, AbstractResourceFactory } from '@naylence/factory';
|
|
2
|
+
import { ENCRYPTION_MANAGER_FACTORY_BASE_TYPE, getLogger, EncryptionResult, urlsafeBase64Decode, sealedDecrypt, sealedEncrypt, FIXED_PREFIX_LEN, urlsafeBase64Encode, EncryptionManagerFactory, requireCryptoSupport, SECURE_CHANNEL_MANAGER_FACTORY_BASE_TYPE, SecureChannelManagerFactory, ENVELOPE_SIGNER_FACTORY_BASE_TYPE, EnvelopeSignerFactory, SigningConfigClass, validateSigningKey, JWKValidationError, decodeBase64Url, canonicalJson, secureDigest, frameDigest, immutableHeaders, encodeUtf8, ENVELOPE_VERIFIER_FACTORY_BASE_TYPE, EnvelopeVerifierFactory, TrustStoreProviderFactory, TaskSpawner, getKeyStore, DefaultKeyManager, validateJwkComplete, currentTraceId, DeliveryOriginType, KEY_MANAGER_FACTORY_BASE_TYPE, KeyManagerFactory, KeyStoreFactory, BaseNodeEventListener, LOAD_BALANCER_STICKINESS_MANAGER_FACTORY_BASE_TYPE, LoadBalancerStickinessManagerFactory, REPLICA_STICKINESS_MANAGER_FACTORY_BASE_TYPE, ReplicaStickinessManagerFactory, color, AnsiColor, validateHostLogicals, HTTP_CONNECTION_GRANT_TYPE, formatTimestamp, jsonDumps, WELCOME_SERVICE_FACTORY_BASE_TYPE, WelcomeServiceFactory, NodePlacementStrategyFactory, TransportProvisionerFactory, TokenIssuerFactory, AuthorizerFactory, validateHostLogical, AuthInjectionStrategyFactory, CERTIFICATE_MANAGER_FACTORY_BASE_TYPE, CertificateManagerFactory, TRUST_STORE_PROVIDER_FACTORY_BASE_TYPE } from '@naylence/runtime';
|
|
2
3
|
import { AsnConvert, OctetString } from '@peculiar/asn1-schema';
|
|
3
4
|
import { Attributes, CertificationRequestInfo, CertificationRequest } from '@peculiar/asn1-csr';
|
|
4
5
|
import { Certificate, SubjectAlternativeName, NameConstraints, id_ce_subjectAltName, id_ce_nameConstraints, SubjectPublicKeyInfo, GeneralName, Extensions, Extension, Attribute, AlgorithmIdentifier, Name, RelativeDistinguishedName, AttributeTypeAndValue, AttributeValue, BasicConstraints, id_ce_basicConstraints, KeyUsageFlags, id_ce_keyUsage, KeyUsage, id_ce_subjectKeyIdentifier, SubjectKeyIdentifier, id_ce_authorityKeyIdentifier, AuthorityKeyIdentifier, KeyIdentifier, GeneralSubtrees, GeneralSubtree, TBSCertificate, Validity, Version, id_ce_extKeyUsage, ExtendedKeyUsage, id_kp_clientAuth, id_kp_serverAuth } from '@peculiar/asn1-x509';
|
|
@@ -10,603 +11,1127 @@ import { x25519 } from '@noble/curves/ed25519.js';
|
|
|
10
11
|
import { hkdf } from '@noble/hashes/hkdf.js';
|
|
11
12
|
import { utf8ToBytes, randomBytes as randomBytes$1 } from '@noble/hashes/utils.js';
|
|
12
13
|
import { SignJWT, importPKCS8, compactVerify, importJWK, importSPKI } from 'jose';
|
|
13
|
-
import { ExtensionManager, Registry, AbstractResourceFactory } from '@naylence/factory';
|
|
14
14
|
import { sha256 as sha256$1 } from '@noble/hashes/sha256.js';
|
|
15
15
|
import { X509Certificate } from '@peculiar/x509';
|
|
16
16
|
|
|
17
|
-
// This file is auto-generated during build - do not edit manually
|
|
18
|
-
// Generated from package.json version: 0.3.11
|
|
19
17
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* AUTO-GENERATED FILE. DO NOT EDIT DIRECTLY.
|
|
19
|
+
* Generated by scripts/generate-factory-manifest.mjs
|
|
20
|
+
*
|
|
21
|
+
* Provides the list of advanced security factory modules for registration.
|
|
22
22
|
*/
|
|
23
|
-
const
|
|
23
|
+
const MODULES = [
|
|
24
|
+
"./security/cert/default-ca-service-factory.js",
|
|
25
|
+
"./security/cert/default-certificate-manager-factory.js",
|
|
26
|
+
"./security/cert/trust-store/browser-trust-store-provider-factory.js",
|
|
27
|
+
"./security/cert/trust-store/node-trust-store-provider-factory.js",
|
|
28
|
+
"./security/encryption/channel/channel-encryption-manager-factory.js",
|
|
29
|
+
"./security/encryption/composite-encryption-manager-factory.js",
|
|
30
|
+
"./security/encryption/default-secure-channel-manager-factory.js",
|
|
31
|
+
"./security/encryption/sealed/x25519-encryption-manager-factory.js",
|
|
32
|
+
"./security/keys/x5c-key-manager-factory.js",
|
|
33
|
+
"./security/signing/eddsa-envelope-signer-factory.js",
|
|
34
|
+
"./security/signing/eddsa-envelope-verifier-factory.js",
|
|
35
|
+
"./stickiness/aft-load-balancer-stickiness-manager-factory.js",
|
|
36
|
+
"./stickiness/aft-replica-stickiness-manager-factory.js",
|
|
37
|
+
"./welcome/advanced-welcome-service-factory.js"
|
|
38
|
+
];
|
|
39
|
+
const MODULE_LOADERS = {
|
|
40
|
+
"./security/cert/default-ca-service-factory.js": () => Promise.resolve().then(function () { return defaultCaServiceFactory; }),
|
|
41
|
+
"./security/cert/default-certificate-manager-factory.js": () => Promise.resolve().then(function () { return defaultCertificateManagerFactory; }),
|
|
42
|
+
"./security/cert/trust-store/browser-trust-store-provider-factory.js": () => Promise.resolve().then(function () { return browserTrustStoreProviderFactory; }),
|
|
43
|
+
"./security/cert/trust-store/node-trust-store-provider-factory.js": () => Promise.resolve().then(function () { return nodeTrustStoreProviderFactory; }),
|
|
44
|
+
"./security/encryption/channel/channel-encryption-manager-factory.js": () => Promise.resolve().then(function () { return channelEncryptionManagerFactory; }),
|
|
45
|
+
"./security/encryption/composite-encryption-manager-factory.js": () => Promise.resolve().then(function () { return compositeEncryptionManagerFactory; }),
|
|
46
|
+
"./security/encryption/default-secure-channel-manager-factory.js": () => Promise.resolve().then(function () { return defaultSecureChannelManagerFactory; }),
|
|
47
|
+
"./security/encryption/sealed/x25519-encryption-manager-factory.js": () => Promise.resolve().then(function () { return x25519EncryptionManagerFactory; }),
|
|
48
|
+
"./security/keys/x5c-key-manager-factory.js": () => Promise.resolve().then(function () { return x5cKeyManagerFactory; }),
|
|
49
|
+
"./security/signing/eddsa-envelope-signer-factory.js": () => Promise.resolve().then(function () { return eddsaEnvelopeSignerFactory; }),
|
|
50
|
+
"./security/signing/eddsa-envelope-verifier-factory.js": () => Promise.resolve().then(function () { return eddsaEnvelopeVerifierFactory; }),
|
|
51
|
+
"./stickiness/aft-load-balancer-stickiness-manager-factory.js": () => Promise.resolve().then(function () { return aftLoadBalancerStickinessManagerFactory; }),
|
|
52
|
+
"./stickiness/aft-replica-stickiness-manager-factory.js": () => Promise.resolve().then(function () { return aftReplicaStickinessManagerFactory; }),
|
|
53
|
+
"./welcome/advanced-welcome-service-factory.js": () => Promise.resolve().then(function () { return advancedWelcomeServiceFactory; }),
|
|
54
|
+
};
|
|
24
55
|
|
|
25
|
-
const logger$h = getLogger("naylence.fame.security.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
56
|
+
const logger$h = getLogger("naylence.fame.security.encryption.encryption_manager_registry");
|
|
57
|
+
class EncryptionManagerFactoryRegistry {
|
|
58
|
+
constructor(autoDiscover = true) {
|
|
59
|
+
this.factories = [];
|
|
60
|
+
this.algorithmToFactory = new Map();
|
|
61
|
+
this.typeToFactories = new Map();
|
|
62
|
+
this.factorySet = new Set();
|
|
63
|
+
this.autoDiscoveredFactories = new Set();
|
|
64
|
+
this.autoDiscovered = false;
|
|
65
|
+
if (autoDiscover) {
|
|
66
|
+
this.autoDiscoverFactories();
|
|
67
|
+
}
|
|
33
68
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
69
|
+
autoDiscoverFactories() {
|
|
70
|
+
if (this.autoDiscovered) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const extensionInfos = ExtensionManager.getExtensionsByType(ENCRYPTION_MANAGER_FACTORY_BASE_TYPE);
|
|
75
|
+
let registeredCount = 0;
|
|
76
|
+
for (const [factoryName, info] of extensionInfos) {
|
|
77
|
+
if (factoryName === "CompositeEncryptionManager") {
|
|
78
|
+
logger$h.debug("skipping_composite_factory_to_avoid_circular_dependency", {
|
|
79
|
+
factory_name: factoryName,
|
|
80
|
+
});
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const factoryInstance = (info.instance ??
|
|
85
|
+
ExtensionManager.getGlobalFactory(ENCRYPTION_MANAGER_FACTORY_BASE_TYPE, factoryName));
|
|
86
|
+
this.registerFactory(factoryInstance, { autoDiscovered: true });
|
|
87
|
+
registeredCount += 1;
|
|
88
|
+
logger$h.debug("auto_discovered_factory", {
|
|
89
|
+
factory_name: factoryName,
|
|
90
|
+
factory_class: factoryInstance.constructor.name,
|
|
91
|
+
algorithms: factoryInstance.getSupportedAlgorithms(),
|
|
92
|
+
encryption_type: factoryInstance.getEncryptionType(),
|
|
93
|
+
priority: factoryInstance.getPriority(),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
logger$h.warning("failed_to_auto_register_factory", {
|
|
98
|
+
factory_name: factoryName,
|
|
99
|
+
error: error instanceof Error ? error.message : String(error),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
this.autoDiscovered = true;
|
|
104
|
+
logger$h.debug("completed_auto_discovery", {
|
|
105
|
+
registered_factories: registeredCount,
|
|
106
|
+
total_discovered: extensionInfos.size,
|
|
107
|
+
skipped_composite: true,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
logger$h.warning("failed_auto_discovery_of_factories", {
|
|
112
|
+
error: error instanceof Error ? error.message : String(error),
|
|
54
113
|
});
|
|
55
|
-
return cached;
|
|
56
114
|
}
|
|
57
|
-
logger$h.debug("certificate_cache_miss", {
|
|
58
|
-
call_id: callId,
|
|
59
|
-
cache_key: cacheKey,
|
|
60
|
-
});
|
|
61
115
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
116
|
+
registerFactory(factory, options = {}) {
|
|
117
|
+
if (this.factorySet.has(factory)) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
this.factorySet.add(factory);
|
|
121
|
+
this.factories.push(factory);
|
|
122
|
+
if (options.autoDiscovered) {
|
|
123
|
+
this.autoDiscoveredFactories.add(factory);
|
|
124
|
+
}
|
|
125
|
+
for (const algorithm of factory.getSupportedAlgorithms()) {
|
|
126
|
+
const existing = this.algorithmToFactory.get(algorithm);
|
|
127
|
+
if (!existing || factory.getPriority() > existing.getPriority()) {
|
|
128
|
+
this.algorithmToFactory.set(algorithm, factory);
|
|
129
|
+
logger$h.debug("registered_algorithm_mapping", {
|
|
130
|
+
algorithm,
|
|
131
|
+
factory: factory.constructor.name,
|
|
132
|
+
priority: factory.getPriority(),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const encryptionType = factory.getEncryptionType();
|
|
137
|
+
const typeFactories = this.typeToFactories.get(encryptionType) ?? [];
|
|
138
|
+
typeFactories.push(factory);
|
|
139
|
+
typeFactories.sort((a, b) => b.getPriority() - a.getPriority());
|
|
140
|
+
this.typeToFactories.set(encryptionType, typeFactories);
|
|
141
|
+
logger$h.debug("registered_encryption_manager_factory", {
|
|
142
|
+
factory: factory.constructor.name,
|
|
143
|
+
encryption_type: encryptionType,
|
|
144
|
+
algorithms: factory.getSupportedAlgorithms(),
|
|
145
|
+
priority: factory.getPriority(),
|
|
146
|
+
auto_discovered: options.autoDiscovered ?? false,
|
|
147
|
+
});
|
|
65
148
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
certificate: validation.certificate,
|
|
70
|
-
};
|
|
149
|
+
getFactoryForAlgorithm(algorithm) {
|
|
150
|
+
this.ensureAutoDiscovery();
|
|
151
|
+
return this.algorithmToFactory.get(algorithm);
|
|
71
152
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
153
|
+
getFactoryForOptions(opts) {
|
|
154
|
+
this.ensureAutoDiscovery();
|
|
155
|
+
for (const factory of this.factories) {
|
|
156
|
+
if (factory.supportsOptions(opts ?? undefined)) {
|
|
157
|
+
logger$h.debug("found_factory_for_options", {
|
|
158
|
+
factory: factory.constructor.name,
|
|
159
|
+
encryption_type: factory.getEncryptionType(),
|
|
160
|
+
});
|
|
161
|
+
return factory;
|
|
162
|
+
}
|
|
80
163
|
}
|
|
81
|
-
|
|
164
|
+
logger$h.debug("no_factory_found_for_options", { opts });
|
|
165
|
+
return undefined;
|
|
82
166
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return
|
|
167
|
+
getFactoriesByType(encryptionType) {
|
|
168
|
+
this.ensureAutoDiscovery();
|
|
169
|
+
return this.typeToFactories.get(encryptionType) ?? [];
|
|
86
170
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const error = "Invalid x5c field in JWK";
|
|
91
|
-
if (strict) {
|
|
92
|
-
throw new Error(error);
|
|
93
|
-
}
|
|
94
|
-
return { isValid: false, error };
|
|
171
|
+
getAllSupportedAlgorithms() {
|
|
172
|
+
this.ensureAutoDiscovery();
|
|
173
|
+
return Array.from(this.algorithmToFactory.keys());
|
|
95
174
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
175
|
+
getRegistryInfo() {
|
|
176
|
+
return {
|
|
177
|
+
totalFactories: this.factories.length,
|
|
178
|
+
autoDiscovered: this.autoDiscovered,
|
|
179
|
+
algorithmMappings: Object.fromEntries(Array.from(this.algorithmToFactory.entries()).map(([algorithm, factory]) => [algorithm, factory.constructor.name])),
|
|
180
|
+
typeMappings: Object.fromEntries(Array.from(this.typeToFactories.entries()).map(([encType, factories]) => [
|
|
181
|
+
encType,
|
|
182
|
+
factories.map((factory) => factory.constructor.name),
|
|
183
|
+
])),
|
|
184
|
+
};
|
|
102
185
|
}
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
186
|
+
forceRediscovery() {
|
|
187
|
+
const manualFactories = this.factories.filter((factory) => !this.autoDiscoveredFactories.has(factory));
|
|
188
|
+
this.autoDiscovered = false;
|
|
189
|
+
this.algorithmToFactory.clear();
|
|
190
|
+
this.typeToFactories.clear();
|
|
191
|
+
this.factories.length = 0;
|
|
192
|
+
this.factorySet.clear();
|
|
193
|
+
this.autoDiscoveredFactories.clear();
|
|
194
|
+
for (const factory of manualFactories) {
|
|
195
|
+
this.registerFactory(factory);
|
|
108
196
|
}
|
|
109
|
-
|
|
197
|
+
this.autoDiscoverFactories();
|
|
110
198
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const leaf = parsed[0];
|
|
114
|
-
const nowMs = Date.now();
|
|
115
|
-
const notBefore = leaf.certificate.tbsCertificate.validity.notBefore.getTime();
|
|
116
|
-
const notAfter = leaf.certificate.tbsCertificate.validity.notAfter.getTime();
|
|
117
|
-
const notBeforeMs = notBefore.getTime();
|
|
118
|
-
const notAfterMs = notAfter.getTime();
|
|
119
|
-
if (nowMs < notBeforeMs || nowMs > notAfterMs) {
|
|
120
|
-
throw new Error(`Certificate is not currently valid (notBefore: ${notBefore.toISOString()}, notAfter: ${notAfter.toISOString()}, now: ${new Date(nowMs).toISOString()})`);
|
|
199
|
+
isAutoDiscovered() {
|
|
200
|
+
return this.autoDiscovered;
|
|
121
201
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const leafUris = extractUrisFromCert(leaf.certificate);
|
|
125
|
-
validateNameConstraints(issuers, leafUris);
|
|
202
|
+
ensureInitialized() {
|
|
203
|
+
this.ensureAutoDiscovery();
|
|
126
204
|
}
|
|
127
|
-
|
|
128
|
-
|
|
205
|
+
ensureAutoDiscovery() {
|
|
206
|
+
if (!this.autoDiscovered) {
|
|
207
|
+
this.autoDiscoverFactories();
|
|
208
|
+
}
|
|
129
209
|
}
|
|
130
|
-
validateChainContinuity(parsed);
|
|
131
|
-
const publicKey = leaf.subjectPublicKey.slice();
|
|
132
|
-
return {
|
|
133
|
-
publicKey,
|
|
134
|
-
certificate: leaf.certificate,
|
|
135
|
-
notAfter,
|
|
136
|
-
};
|
|
137
210
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
211
|
+
const globalRegistry = new EncryptionManagerFactoryRegistry(true);
|
|
212
|
+
function getEncryptionManagerFactoryRegistry() {
|
|
213
|
+
globalRegistry.ensureInitialized();
|
|
214
|
+
return globalRegistry;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const SECURITY_PREFIX = "./security/";
|
|
218
|
+
const SECURITY_MODULES = MODULES.filter((spec) => spec.startsWith(SECURITY_PREFIX));
|
|
219
|
+
const EXTRA_MODULES = MODULES.filter((spec) => !spec.startsWith(SECURITY_PREFIX));
|
|
220
|
+
const NODE_ONLY_MODULES = new Set([
|
|
221
|
+
"./security/cert/default-ca-service-factory.js",
|
|
222
|
+
"./security/cert/trust-store/node-trust-store-provider-factory.js",
|
|
223
|
+
]);
|
|
224
|
+
const FACTORY_MODULE_PREFIX$1 = "@naylence/advanced-security/naylence/fame/";
|
|
225
|
+
const BROWSER_DIST_SEGMENT = "/dist/browser/";
|
|
226
|
+
function detectModuleUrl() {
|
|
227
|
+
if (typeof __filename === "string") {
|
|
155
228
|
try {
|
|
156
|
-
|
|
229
|
+
return __filename.startsWith("file://")
|
|
230
|
+
? __filename
|
|
231
|
+
: `file://${__filename}`;
|
|
157
232
|
}
|
|
158
|
-
catch
|
|
159
|
-
|
|
160
|
-
throw new Error(`Failed to parse certificate at index ${index}: ${reason}`);
|
|
233
|
+
catch {
|
|
234
|
+
// fall through to stack parsing
|
|
161
235
|
}
|
|
162
|
-
parsed.push(createParsedCertificate(certificate, der));
|
|
163
|
-
derChunks.push(der);
|
|
164
236
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
function createParsedCertificate(certificate, raw) {
|
|
168
|
-
return {
|
|
169
|
-
raw,
|
|
170
|
-
certificate,
|
|
171
|
-
serialNumber: toHex(new Uint8Array(certificate.tbsCertificate.serialNumber)).toUpperCase(),
|
|
172
|
-
subjectName: serializeName(certificate.tbsCertificate.subject),
|
|
173
|
-
issuerName: serializeName(certificate.tbsCertificate.issuer),
|
|
174
|
-
subjectPublicKey: new Uint8Array(certificate.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey).slice(),
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
function extractUrisFromCert(cert) {
|
|
178
|
-
const extension = findExtension(cert, id_ce_subjectAltName);
|
|
179
|
-
if (!extension) {
|
|
180
|
-
return [];
|
|
237
|
+
try {
|
|
238
|
+
throw new Error();
|
|
181
239
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
240
|
+
catch (error) {
|
|
241
|
+
const stack = typeof error === "object" && error && "stack" in error
|
|
242
|
+
? String(error.stack ?? "")
|
|
243
|
+
: "";
|
|
244
|
+
const lines = stack.split("\n");
|
|
245
|
+
for (const line of lines) {
|
|
246
|
+
const match = line.match(/(https?:\/\/[^\s)]+|file:\/\/[^\s)]+\.(?:js|ts)|\/(?:[^\s)]+\.(?:js|ts)))/u);
|
|
247
|
+
const candidate = match?.[1];
|
|
248
|
+
if (!candidate) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (candidate.startsWith("http://") || candidate.startsWith("https://")) {
|
|
252
|
+
return candidate;
|
|
253
|
+
}
|
|
254
|
+
if (candidate.startsWith("file://")) {
|
|
255
|
+
return candidate;
|
|
256
|
+
}
|
|
257
|
+
return `file://${candidate}`;
|
|
187
258
|
}
|
|
188
259
|
}
|
|
189
|
-
return
|
|
260
|
+
return null;
|
|
190
261
|
}
|
|
191
|
-
function
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
262
|
+
function computeBrowserFactoryBase(rawUrl) {
|
|
263
|
+
if (!rawUrl) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
const sanitized = rawUrl.split("?")[0]?.split("#")[0] ?? rawUrl;
|
|
267
|
+
const esmMarker = "/dist/esm/naylence/fame/";
|
|
268
|
+
const distMarker = "/dist/";
|
|
269
|
+
if (sanitized.includes(esmMarker)) {
|
|
270
|
+
return sanitized.slice(0, sanitized.indexOf(esmMarker) + esmMarker.length);
|
|
271
|
+
}
|
|
272
|
+
if (rawUrl.includes(BROWSER_DIST_SEGMENT)) {
|
|
273
|
+
return new URL("../esm/naylence/fame/", rawUrl).href;
|
|
274
|
+
}
|
|
275
|
+
if (sanitized.includes(BROWSER_DIST_SEGMENT)) {
|
|
276
|
+
const base = sanitized.slice(0, sanitized.indexOf(BROWSER_DIST_SEGMENT) + BROWSER_DIST_SEGMENT.length);
|
|
277
|
+
return `${base.replace(/browser\/?$/u, "")}esm/naylence/fame/`;
|
|
278
|
+
}
|
|
279
|
+
if (sanitized.includes(distMarker)) {
|
|
280
|
+
const index = sanitized.indexOf(distMarker);
|
|
281
|
+
const base = sanitized.slice(0, index + distMarker.length);
|
|
282
|
+
return `${base}esm/naylence/fame/`;
|
|
283
|
+
}
|
|
284
|
+
const srcMarker = "/src/naylence/fame/";
|
|
285
|
+
if (sanitized.includes(srcMarker)) {
|
|
286
|
+
const index = sanitized.indexOf(srcMarker);
|
|
287
|
+
const projectRoot = sanitized.slice(0, index);
|
|
288
|
+
return `${projectRoot}/dist/esm/naylence/fame/`;
|
|
289
|
+
}
|
|
290
|
+
if (sanitized.startsWith("http://") || sanitized.startsWith("https://")) {
|
|
291
|
+
try {
|
|
292
|
+
const parsed = new URL(rawUrl);
|
|
293
|
+
const viteDepsSegment = "/node_modules/.vite/deps/";
|
|
294
|
+
if (parsed.pathname.includes(viteDepsSegment)) {
|
|
295
|
+
const baseOrigin = `${parsed.protocol}//${parsed.host}`;
|
|
296
|
+
return `${baseOrigin}/node_modules/@naylence/advanced-security/dist/esm/naylence/fame/`;
|
|
209
297
|
}
|
|
210
298
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
function collectPermittedUris(subtrees) {
|
|
214
|
-
const uris = [];
|
|
215
|
-
for (const subtree of subtrees) {
|
|
216
|
-
if (subtree.base.uniformResourceIdentifier &&
|
|
217
|
-
subtree.base.uniformResourceIdentifier.length > 0) {
|
|
218
|
-
uris.push(subtree.base.uniformResourceIdentifier);
|
|
299
|
+
catch {
|
|
300
|
+
// ignore
|
|
219
301
|
}
|
|
220
302
|
}
|
|
221
|
-
return
|
|
303
|
+
return null;
|
|
222
304
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
305
|
+
const moduleUrl = detectModuleUrl();
|
|
306
|
+
const browserFactoryBase = computeBrowserFactoryBase(moduleUrl);
|
|
307
|
+
const prefersSource = typeof moduleUrl === "string" && moduleUrl.includes("/src/");
|
|
308
|
+
function resolveFactoryModuleSpecifier$1(specifier) {
|
|
309
|
+
if (specifier.startsWith("../")) {
|
|
310
|
+
return `${FACTORY_MODULE_PREFIX$1}${specifier.slice("../".length)}`;
|
|
227
311
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
trust_store_cert_count: trustedCerts.length,
|
|
231
|
-
});
|
|
232
|
-
const chainInfo = chain.map((cert, index) => `[${index}] ${cert.subjectName} (Serial: ${cert.serialNumber})`);
|
|
233
|
-
const trustedInfo = trustedCerts.map((cert, index) => `[${index}] ${cert.subjectName} (Serial: ${cert.serialNumber})`);
|
|
234
|
-
logger$h.debug("certificate_chain_validation", {
|
|
235
|
-
chain_certificates: chainInfo,
|
|
236
|
-
trust_store_certificates: trustedInfo,
|
|
237
|
-
});
|
|
238
|
-
// Strategy 1: direct trust (exact certificate match)
|
|
239
|
-
for (let i = 0; i < chain.length; i += 1) {
|
|
240
|
-
const cert = chain[i];
|
|
241
|
-
const match = trustedCerts.find((trusted) => trusted.serialNumber === cert.serialNumber &&
|
|
242
|
-
namesEqual(trusted.certificate.tbsCertificate.subject, cert.certificate.tbsCertificate.subject));
|
|
243
|
-
if (match) {
|
|
244
|
-
logger$h.debug("certificate_chain_trust_validation_passed", {
|
|
245
|
-
matching_serial: match.serialNumber,
|
|
246
|
-
validation_strategy: `direct_trust_cert_${i}`,
|
|
247
|
-
});
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
312
|
+
if (specifier.startsWith("./")) {
|
|
313
|
+
return `${FACTORY_MODULE_PREFIX$1}${specifier.slice("./".length)}`;
|
|
250
314
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
matching_serial: trusted.serialNumber,
|
|
259
|
-
validation_strategy: "leaf_issuer_trust",
|
|
260
|
-
});
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
function resolveModuleCandidates(spec) {
|
|
318
|
+
const candidates = [];
|
|
319
|
+
const seen = new Set();
|
|
320
|
+
const addCandidate = (candidate) => {
|
|
321
|
+
if (!candidate) {
|
|
261
322
|
return;
|
|
262
323
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
validation_strategy: `intermediate_issuer_trust_cert_${index}`,
|
|
274
|
-
});
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
324
|
+
if (!seen.has(candidate)) {
|
|
325
|
+
seen.add(candidate);
|
|
326
|
+
candidates.push(candidate);
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
if (prefersSource && spec.startsWith("./")) {
|
|
330
|
+
const sourceCandidate = `../${spec.slice(2)}`;
|
|
331
|
+
addCandidate(sourceCandidate);
|
|
332
|
+
if (sourceCandidate.endsWith(".js")) {
|
|
333
|
+
addCandidate(sourceCandidate.replace(/\.js$/u, ".ts"));
|
|
277
334
|
}
|
|
278
335
|
}
|
|
279
|
-
|
|
280
|
-
leaf_subject: leaf.subjectName,
|
|
281
|
-
leaf_issuer: leaf.issuerName,
|
|
282
|
-
leaf_serial: leaf.serialNumber,
|
|
283
|
-
trusted_certificates: trustedInfo,
|
|
284
|
-
chain_certificates: chainInfo,
|
|
285
|
-
reason: "no_matching_trust_anchor",
|
|
286
|
-
});
|
|
287
|
-
throw new Error("Certificate chain is not rooted in a trusted anchor");
|
|
288
|
-
}
|
|
289
|
-
function parseTrustStore(trustStorePem) {
|
|
290
|
-
const normalized = normalizePem$2(trustStorePem);
|
|
291
|
-
const blocks = extractPemBlocks$1(normalized);
|
|
292
|
-
const parsed = [];
|
|
293
|
-
for (const block of blocks) {
|
|
336
|
+
if (browserFactoryBase && spec.startsWith("./")) {
|
|
294
337
|
try {
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
338
|
+
const browserCandidate = new URL(spec.slice("./".length), browserFactoryBase).href;
|
|
339
|
+
addCandidate(browserCandidate);
|
|
340
|
+
if (browserCandidate.endsWith(".js")) {
|
|
341
|
+
addCandidate(browserCandidate.replace(/\.js$/u, ".ts"));
|
|
342
|
+
}
|
|
298
343
|
}
|
|
299
|
-
catch
|
|
300
|
-
|
|
301
|
-
logger$h.debug("trust_store_certificate_parse_failed", { reason });
|
|
344
|
+
catch {
|
|
345
|
+
// ignore resolution failures for browser base
|
|
302
346
|
}
|
|
303
347
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const regex = /-----BEGIN CERTIFICATE-----([\s\S]*?)-----END CERTIFICATE-----/gu;
|
|
309
|
-
let match;
|
|
310
|
-
// eslint-disable-next-line no-cond-assign
|
|
311
|
-
while ((match = regex.exec(pem)) !== null) {
|
|
312
|
-
const body = match[1] ?? "";
|
|
313
|
-
blocks.push(body.replace(/\s+/gu, ""));
|
|
348
|
+
const packageCandidate = resolveFactoryModuleSpecifier$1(spec);
|
|
349
|
+
addCandidate(packageCandidate);
|
|
350
|
+
if (packageCandidate?.endsWith(".js")) {
|
|
351
|
+
addCandidate(packageCandidate.replace(/\.js$/u, ".ts"));
|
|
314
352
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
return;
|
|
353
|
+
const fallback = spec.startsWith("./") ? `../${spec.slice(2)}` : spec;
|
|
354
|
+
addCandidate(fallback);
|
|
355
|
+
if (fallback.endsWith(".js")) {
|
|
356
|
+
addCandidate(fallback.replace(/\.js$/u, ".ts"));
|
|
320
357
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const issuer = chain[index + 1];
|
|
325
|
-
if (!namesEqual(cert.certificate.tbsCertificate.issuer, issuer.certificate.tbsCertificate.subject)) {
|
|
326
|
-
logger$h.warning("certificate_chain_continuity_failed", {
|
|
327
|
-
cert_index: index,
|
|
328
|
-
cert_subject: cert.subjectName,
|
|
329
|
-
cert_issuer: cert.issuerName,
|
|
330
|
-
expected_issuer_subject: issuer.subjectName,
|
|
331
|
-
reason: "issuer_name_mismatch",
|
|
332
|
-
});
|
|
333
|
-
throw new Error(`Certificate chain continuity broken: certificate at index ${index} issuer does not match next certificate subject`);
|
|
334
|
-
}
|
|
335
|
-
try {
|
|
336
|
-
verifyCertificateSignature(cert.certificate, issuer.certificate);
|
|
337
|
-
logger$h.debug("chain_continuity_verification_success", {
|
|
338
|
-
cert_index: index,
|
|
339
|
-
cert_serial: cert.serialNumber,
|
|
340
|
-
issuer_serial: issuer.serialNumber,
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
catch (error) {
|
|
344
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
345
|
-
logger$h.warning("certificate_chain_continuity_failed", {
|
|
346
|
-
cert_index: index,
|
|
347
|
-
cert_subject: cert.subjectName,
|
|
348
|
-
issuer_subject: issuer.subjectName,
|
|
349
|
-
cert_serial: cert.serialNumber,
|
|
350
|
-
issuer_serial: issuer.serialNumber,
|
|
351
|
-
error: reason,
|
|
352
|
-
reason: "signature_verification_failed",
|
|
353
|
-
});
|
|
354
|
-
throw new Error(`Certificate chain continuity broken: certificate at index ${index} was not signed by certificate at index ${index + 1}: ${reason}`);
|
|
355
|
-
}
|
|
358
|
+
addCandidate(spec);
|
|
359
|
+
if (spec.endsWith(".js")) {
|
|
360
|
+
addCandidate(spec.replace(/\.js$/u, ".ts"));
|
|
356
361
|
}
|
|
357
|
-
|
|
358
|
-
chain_length: chain.length,
|
|
359
|
-
});
|
|
362
|
+
return candidates;
|
|
360
363
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
const signatureBytes = new Uint8Array(certificate.signatureValue);
|
|
369
|
-
const tbsBytes = new Uint8Array(AsnConvert.serialize(certificate.tbsCertificate));
|
|
370
|
-
const issuerKey = new Uint8Array(issuer.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey);
|
|
371
|
-
if (issuerKey.length !== 32) {
|
|
372
|
-
throw new Error("Issuer Ed25519 public key must be 32 bytes");
|
|
373
|
-
}
|
|
374
|
-
const valid = verify(signatureBytes, tbsBytes, issuerKey);
|
|
375
|
-
if (!valid) {
|
|
376
|
-
throw new Error("Certificate signature verification failed");
|
|
377
|
-
}
|
|
364
|
+
const registeredModules = new Set();
|
|
365
|
+
const inflightModules = new Map();
|
|
366
|
+
const browserSkippedModules = new Set();
|
|
367
|
+
function isNodeEnvironment$5() {
|
|
368
|
+
return (typeof process !== "undefined" &&
|
|
369
|
+
typeof process.release !== "undefined" &&
|
|
370
|
+
process.release?.name === "node");
|
|
378
371
|
}
|
|
379
|
-
function
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
etcPatch.sha512 = (message) => sha512(message);
|
|
372
|
+
function shouldSkipModule(spec) {
|
|
373
|
+
if (isNodeEnvironment$5()) {
|
|
374
|
+
return false;
|
|
383
375
|
}
|
|
384
|
-
if (!
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
376
|
+
if (!NODE_ONLY_MODULES.has(spec)) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
if (!browserSkippedModules.has(spec)) {
|
|
380
|
+
// console.warn(
|
|
381
|
+
// "[advanced-security:factory-manifest] skipped browser-incompatible module",
|
|
382
|
+
// spec,
|
|
383
|
+
// );
|
|
384
|
+
browserSkippedModules.add(spec);
|
|
392
385
|
}
|
|
386
|
+
return true;
|
|
393
387
|
}
|
|
394
|
-
function
|
|
395
|
-
|
|
396
|
-
if (!extensions) {
|
|
388
|
+
function getDynamicImporter() {
|
|
389
|
+
if (typeof globalThis === "undefined") {
|
|
397
390
|
return null;
|
|
398
391
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
392
|
+
const candidate = globalThis.__naylenceFactoryDynamicImporter;
|
|
393
|
+
if (typeof candidate === "function") {
|
|
394
|
+
return candidate;
|
|
403
395
|
}
|
|
404
396
|
return null;
|
|
405
397
|
}
|
|
406
|
-
function
|
|
407
|
-
const
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
398
|
+
async function registerModule(spec, registrar) {
|
|
399
|
+
const candidates = resolveModuleCandidates(spec);
|
|
400
|
+
const dynamicImporter = getDynamicImporter();
|
|
401
|
+
const loader = dynamicImporter
|
|
402
|
+
? (specifier) => dynamicImporter(specifier)
|
|
403
|
+
: (specifier) => import(/* @vite-ignore */ specifier);
|
|
404
|
+
const attempts = [];
|
|
405
|
+
const staticLoader = MODULE_LOADERS?.[spec];
|
|
406
|
+
if (staticLoader) {
|
|
407
|
+
attempts.push({ load: () => staticLoader(), candidate: spec });
|
|
411
408
|
}
|
|
412
|
-
for (
|
|
413
|
-
|
|
409
|
+
for (const candidate of candidates) {
|
|
410
|
+
attempts.push({ load: () => loader(candidate), candidate });
|
|
411
|
+
}
|
|
412
|
+
const registerFromModule = (mod) => {
|
|
413
|
+
const meta = mod.FACTORY_META;
|
|
414
|
+
const Ctor = mod.default;
|
|
415
|
+
if (!meta?.base || !meta?.key || typeof Ctor !== "function") {
|
|
416
|
+
console.warn("[debug] invalid factory module", spec, {
|
|
417
|
+
meta,
|
|
418
|
+
hasCtor: typeof Ctor === "function",
|
|
419
|
+
});
|
|
420
|
+
console.warn("[advanced-security:factory-manifest] skipped", spec, "— missing FACTORY_META or default export ctor");
|
|
414
421
|
return false;
|
|
415
422
|
}
|
|
423
|
+
const { base, key, ...metadata } = meta;
|
|
424
|
+
const extraMetadata = Object.keys(metadata).length > 0 ? metadata : undefined;
|
|
425
|
+
// console.log("[debug] registering module", { spec, base, key, metadata: extraMetadata });
|
|
426
|
+
registrar.registerFactory(base, key, Ctor, extraMetadata);
|
|
427
|
+
return true;
|
|
428
|
+
};
|
|
429
|
+
for (const [index, { candidate, load }] of attempts.entries()) {
|
|
430
|
+
try {
|
|
431
|
+
const mod = await load();
|
|
432
|
+
return registerFromModule(mod);
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
436
|
+
const moduleNotFound = message.includes("Cannot find module") ||
|
|
437
|
+
message.includes("ERR_MODULE_NOT_FOUND") ||
|
|
438
|
+
message.includes("Unknown file extension") ||
|
|
439
|
+
message.includes("Failed to fetch dynamically imported module") ||
|
|
440
|
+
message.includes("Failed to resolve module specifier") ||
|
|
441
|
+
message.includes("Importing a module script failed");
|
|
442
|
+
const isLastAttempt = index === attempts.length - 1;
|
|
443
|
+
if (!moduleNotFound || isLastAttempt) {
|
|
444
|
+
console.warn("[debug] failed to import candidate", {
|
|
445
|
+
spec,
|
|
446
|
+
candidate,
|
|
447
|
+
message,
|
|
448
|
+
});
|
|
449
|
+
console.warn("[advanced-security:factory-manifest] skipped", spec, "-", message);
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
416
453
|
}
|
|
417
|
-
return
|
|
418
|
-
}
|
|
419
|
-
function serializeName(name) {
|
|
420
|
-
const rdns = Array.from(name);
|
|
421
|
-
return rdns
|
|
422
|
-
.map((rdn) => Array.from(rdn)
|
|
423
|
-
.map((attr) => `${oidToLabel$1(attr.type)}=${attr.value.toString()}`)
|
|
424
|
-
.join("+"))
|
|
425
|
-
.join(",");
|
|
454
|
+
return false;
|
|
426
455
|
}
|
|
427
|
-
function
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
return "CN";
|
|
431
|
-
case "2.5.4.6":
|
|
432
|
-
return "C";
|
|
433
|
-
case "2.5.4.7":
|
|
434
|
-
return "L";
|
|
435
|
-
case "2.5.4.8":
|
|
436
|
-
return "ST";
|
|
437
|
-
case "2.5.4.10":
|
|
438
|
-
return "O";
|
|
439
|
-
case "2.5.4.11":
|
|
440
|
-
return "OU";
|
|
441
|
-
default:
|
|
442
|
-
return oid;
|
|
456
|
+
async function registerModuleOnce(spec, registrar) {
|
|
457
|
+
if (registeredModules.has(spec)) {
|
|
458
|
+
return false;
|
|
443
459
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
460
|
+
const inflight = inflightModules.get(spec);
|
|
461
|
+
if (inflight) {
|
|
462
|
+
return inflight;
|
|
463
|
+
}
|
|
464
|
+
const registration = (async () => {
|
|
465
|
+
const registered = await registerModule(spec, registrar);
|
|
466
|
+
if (registered) {
|
|
467
|
+
registeredModules.add(spec);
|
|
468
|
+
}
|
|
469
|
+
return registered;
|
|
470
|
+
})();
|
|
471
|
+
inflightModules.set(spec, registration);
|
|
472
|
+
try {
|
|
473
|
+
return await registration;
|
|
474
|
+
}
|
|
475
|
+
finally {
|
|
476
|
+
inflightModules.delete(spec);
|
|
452
477
|
}
|
|
453
|
-
return result;
|
|
454
478
|
}
|
|
455
|
-
function
|
|
456
|
-
if (
|
|
457
|
-
|
|
458
|
-
return new Uint8Array(Buffer.from(normalized, "base64"));
|
|
479
|
+
async function registerModules(modules, registrar) {
|
|
480
|
+
if (modules.length === 0) {
|
|
481
|
+
return 0;
|
|
459
482
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
const bytes = new Uint8Array(binary.length);
|
|
464
|
-
for (let i = 0; i < binary.length; i += 1) {
|
|
465
|
-
bytes[i] = binary.charCodeAt(i);
|
|
466
|
-
}
|
|
467
|
-
return bytes;
|
|
483
|
+
const eligibleModules = modules.filter((spec) => !shouldSkipModule(spec));
|
|
484
|
+
if (eligibleModules.length === 0) {
|
|
485
|
+
return 0;
|
|
468
486
|
}
|
|
469
|
-
|
|
487
|
+
const results = await Promise.all(eligibleModules.map((spec) => registerModuleOnce(spec, registrar)));
|
|
488
|
+
return results.reduce((count, registered) => (registered ? count + 1 : count), 0);
|
|
470
489
|
}
|
|
471
|
-
function
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
.
|
|
475
|
-
}
|
|
476
|
-
function buildCacheKey(chainBytes, trustStorePem, enforceNameConstraints) {
|
|
477
|
-
const chainHash = toHex(sha256(chainBytes));
|
|
478
|
-
const trustHash = trustStorePem
|
|
479
|
-
? toHex(sha256(textEncoder.encode(trustStorePem)))
|
|
480
|
-
: "no-trust";
|
|
481
|
-
const constraintFlag = enforceNameConstraints ? "nc1" : "nc0";
|
|
482
|
-
return `${chainHash}|${trustHash}|${constraintFlag}`;
|
|
483
|
-
}
|
|
484
|
-
function getCachedPublicKey(cacheKey) {
|
|
485
|
-
const entry = trustCache.get(cacheKey);
|
|
486
|
-
if (!entry) {
|
|
487
|
-
return null;
|
|
490
|
+
async function registerAdvancedSecurityFactories(registrar = Registry, options) {
|
|
491
|
+
const newlyRegisteredSecurity = await registerModules(SECURITY_MODULES, registrar);
|
|
492
|
+
if (newlyRegisteredSecurity > 0) {
|
|
493
|
+
getEncryptionManagerFactoryRegistry().forceRediscovery();
|
|
488
494
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
logger$h.debug("certificate_cache_expired", { cache_key: cacheKey });
|
|
492
|
-
return null;
|
|
495
|
+
{
|
|
496
|
+
await registerModules(EXTRA_MODULES, registrar);
|
|
493
497
|
}
|
|
494
|
-
return entry.value.slice();
|
|
495
498
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
499
|
+
|
|
500
|
+
// This file is auto-generated during build - do not edit manually
|
|
501
|
+
// Generated from package.json version: 0.3.15
|
|
502
|
+
/**
|
|
503
|
+
* The package version, injected at build time.
|
|
504
|
+
* @internal
|
|
505
|
+
*/
|
|
506
|
+
const VERSION = '0.3.15';
|
|
507
|
+
|
|
508
|
+
async function registerAdvancedSecurityPluginFactories(registrar = Registry) {
|
|
509
|
+
await registerAdvancedSecurityFactories(registrar);
|
|
510
|
+
}
|
|
511
|
+
let initialized = false;
|
|
512
|
+
let initializing = null;
|
|
513
|
+
const advancedSecurityPlugin = {
|
|
514
|
+
name: "naylence:advanced-security",
|
|
515
|
+
version: VERSION,
|
|
516
|
+
async register() {
|
|
517
|
+
// console.log('[naylence:advanced-security] register() called, initialized=', initialized);
|
|
518
|
+
if (initialized) {
|
|
519
|
+
// console.log('[naylence:advanced-security] already initialized, skipping');
|
|
520
|
+
return;
|
|
501
521
|
}
|
|
502
|
-
|
|
503
|
-
|
|
522
|
+
if (initializing) {
|
|
523
|
+
console.log("[naylence:advanced-security] already initializing, awaiting...");
|
|
524
|
+
await initializing;
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
initializing = (async () => {
|
|
528
|
+
try {
|
|
529
|
+
// console.log('[naylence:advanced-security] registering advanced security factories...');
|
|
530
|
+
await registerAdvancedSecurityPluginFactories();
|
|
531
|
+
// console.log('[naylence:advanced-security] advanced security factories registered');
|
|
532
|
+
initialized = true;
|
|
533
|
+
}
|
|
534
|
+
finally {
|
|
535
|
+
initializing = null;
|
|
536
|
+
}
|
|
537
|
+
})();
|
|
538
|
+
await initializing;
|
|
539
|
+
},
|
|
540
|
+
};
|
|
541
|
+
const ADVANCED_SECURITY_PLUGIN_SPECIFIER = advancedSecurityPlugin.name;
|
|
542
|
+
|
|
543
|
+
var plugin = /*#__PURE__*/Object.freeze({
|
|
544
|
+
__proto__: null,
|
|
545
|
+
ADVANCED_SECURITY_PLUGIN_SPECIFIER: ADVANCED_SECURITY_PLUGIN_SPECIFIER,
|
|
546
|
+
default: advancedSecurityPlugin,
|
|
547
|
+
registerAdvancedSecurityPluginFactories: registerAdvancedSecurityPluginFactories
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
const logger$g = getLogger("naylence.fame.security.cert.util");
|
|
551
|
+
const CACHE_LIMIT = 512;
|
|
552
|
+
const OID_ED25519 = "1.3.101.112";
|
|
553
|
+
const textEncoder = new TextEncoder();
|
|
554
|
+
const trustCache = new Map();
|
|
555
|
+
function publicKeyFromX5c(x5c, options = {}) {
|
|
556
|
+
if (!Array.isArray(x5c) || x5c.length === 0) {
|
|
557
|
+
throw new Error("Empty certificate chain");
|
|
504
558
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
559
|
+
const callId = generateCallId();
|
|
560
|
+
const enforceNameConstraints = options.enforceNameConstraints ?? true;
|
|
561
|
+
const trustStorePem = normalizeTrustStoreOption(options.trustStorePem ?? null);
|
|
562
|
+
const returnCertificate = options.returnCertificate ?? false;
|
|
563
|
+
const { parsed, chainBytes } = parseCertificateChain(x5c);
|
|
564
|
+
logger$g.debug("public_key_from_x5c_called", {
|
|
565
|
+
call_id: callId,
|
|
566
|
+
x5c_count: parsed.length,
|
|
567
|
+
enforce_name_constraints: enforceNameConstraints,
|
|
568
|
+
has_trust_store: Boolean(trustStorePem),
|
|
569
|
+
return_cert: returnCertificate,
|
|
513
570
|
});
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
571
|
+
let cacheKey = null;
|
|
572
|
+
if (!returnCertificate) {
|
|
573
|
+
cacheKey = buildCacheKey(chainBytes, trustStorePem, enforceNameConstraints);
|
|
574
|
+
const cached = getCachedPublicKey(cacheKey);
|
|
575
|
+
if (cached) {
|
|
576
|
+
logger$g.debug("certificate_cache_hit", {
|
|
577
|
+
call_id: callId,
|
|
578
|
+
cache_key: cacheKey,
|
|
579
|
+
});
|
|
580
|
+
return cached;
|
|
581
|
+
}
|
|
582
|
+
logger$g.debug("certificate_cache_miss", {
|
|
583
|
+
call_id: callId,
|
|
584
|
+
cache_key: cacheKey,
|
|
585
|
+
});
|
|
518
586
|
}
|
|
519
|
-
const
|
|
520
|
-
if (
|
|
521
|
-
|
|
587
|
+
const validation = validateCertificateChain(parsed, enforceNameConstraints, trustStorePem);
|
|
588
|
+
if (cacheKey) {
|
|
589
|
+
setCachedPublicKey(cacheKey, validation.publicKey, validation.notAfter);
|
|
522
590
|
}
|
|
523
|
-
if (
|
|
524
|
-
|
|
591
|
+
if (returnCertificate) {
|
|
592
|
+
return {
|
|
593
|
+
publicKey: validation.publicKey.slice(),
|
|
594
|
+
certificate: validation.certificate,
|
|
595
|
+
};
|
|
525
596
|
}
|
|
526
|
-
return
|
|
527
|
-
}
|
|
528
|
-
function normalizePem$2(pem) {
|
|
529
|
-
return pem.replace(/\r/gu, "").trim();
|
|
530
|
-
}
|
|
531
|
-
function generateCallId() {
|
|
532
|
-
return Math.random().toString(36).slice(2, 10);
|
|
597
|
+
return validation.publicKey.slice();
|
|
533
598
|
}
|
|
534
|
-
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
if (!instance) {
|
|
543
|
-
throw new Error("WebCrypto subtle API is required to create a CSR");
|
|
599
|
+
function validateJwkX5cCertificate(options) {
|
|
600
|
+
const { jwk, trustStorePem = null, enforceNameConstraints = true, strict = true, } = options;
|
|
601
|
+
if (!jwk || typeof jwk !== "object") {
|
|
602
|
+
const error = "Invalid JWK object";
|
|
603
|
+
if (strict) {
|
|
604
|
+
throw new Error(error);
|
|
605
|
+
}
|
|
606
|
+
return { isValid: false, error };
|
|
544
607
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
if (!commonName || typeof commonName !== "string") {
|
|
549
|
-
throw new Error("commonName must be a non-empty string");
|
|
608
|
+
const x5c = jwk.x5c;
|
|
609
|
+
if (x5c === undefined) {
|
|
610
|
+
return { isValid: true };
|
|
550
611
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
}
|
|
560
|
-
function arrayBufferToBase64(buffer) {
|
|
561
|
-
const bytes = new Uint8Array(buffer);
|
|
562
|
-
if (typeof globalThis.Buffer?.from === "function") {
|
|
563
|
-
return globalThis.Buffer.from(bytes).toString("base64");
|
|
612
|
+
if (!Array.isArray(x5c) ||
|
|
613
|
+
x5c.length === 0 ||
|
|
614
|
+
x5c.some((entry) => typeof entry !== "string")) {
|
|
615
|
+
const error = "Invalid x5c field in JWK";
|
|
616
|
+
if (strict) {
|
|
617
|
+
throw new Error(error);
|
|
618
|
+
}
|
|
619
|
+
return { isValid: false, error };
|
|
564
620
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
621
|
+
try {
|
|
622
|
+
publicKeyFromX5c(x5c, {
|
|
623
|
+
trustStorePem,
|
|
624
|
+
enforceNameConstraints,
|
|
625
|
+
});
|
|
626
|
+
return { isValid: true };
|
|
570
627
|
}
|
|
571
|
-
|
|
572
|
-
|
|
628
|
+
catch (error) {
|
|
629
|
+
const message = error instanceof Error ? error.message : String(error ?? "unknown");
|
|
630
|
+
const normalized = `Certificate validation failed: ${message}`;
|
|
631
|
+
if (strict) {
|
|
632
|
+
throw new Error(normalized);
|
|
633
|
+
}
|
|
634
|
+
return { isValid: false, error: normalized };
|
|
573
635
|
}
|
|
574
|
-
return globalThis.btoa(binary);
|
|
575
636
|
}
|
|
576
|
-
function
|
|
577
|
-
const
|
|
578
|
-
const
|
|
579
|
-
|
|
580
|
-
|
|
637
|
+
function validateCertificateChain(parsed, enforceNameConstraints, trustStorePem) {
|
|
638
|
+
const leaf = parsed[0];
|
|
639
|
+
const nowMs = Date.now();
|
|
640
|
+
const notBefore = leaf.certificate.tbsCertificate.validity.notBefore.getTime();
|
|
641
|
+
const notAfter = leaf.certificate.tbsCertificate.validity.notAfter.getTime();
|
|
642
|
+
const notBeforeMs = notBefore.getTime();
|
|
643
|
+
const notAfterMs = notAfter.getTime();
|
|
644
|
+
if (nowMs < notBeforeMs || nowMs > notAfterMs) {
|
|
645
|
+
throw new Error(`Certificate is not currently valid (notBefore: ${notBefore.toISOString()}, notAfter: ${notAfter.toISOString()}, now: ${new Date(nowMs).toISOString()})`);
|
|
581
646
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
const { privateKey, publicKey, commonName } = options;
|
|
587
|
-
if (!(privateKey instanceof CryptoKey) || privateKey.type !== "private") {
|
|
588
|
-
throw new Error("privateKey must be a CryptoKey of type 'private'");
|
|
647
|
+
const issuers = parsed.slice(1);
|
|
648
|
+
if (enforceNameConstraints && issuers.length > 0) {
|
|
649
|
+
const leafUris = extractUrisFromCert(leaf.certificate);
|
|
650
|
+
validateNameConstraints(issuers, leafUris);
|
|
589
651
|
}
|
|
590
|
-
if (
|
|
591
|
-
|
|
652
|
+
if (trustStorePem) {
|
|
653
|
+
validateTrustAnchor(parsed, trustStorePem);
|
|
592
654
|
}
|
|
593
|
-
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
new
|
|
608
|
-
|
|
609
|
-
|
|
655
|
+
validateChainContinuity(parsed);
|
|
656
|
+
const publicKey = leaf.subjectPublicKey.slice();
|
|
657
|
+
return {
|
|
658
|
+
publicKey,
|
|
659
|
+
certificate: leaf.certificate,
|
|
660
|
+
notAfter,
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
function parseCertificateChain(x5c) {
|
|
664
|
+
const parsed = [];
|
|
665
|
+
const derChunks = [];
|
|
666
|
+
for (let index = 0; index < x5c.length; index += 1) {
|
|
667
|
+
const entry = x5c[index];
|
|
668
|
+
if (typeof entry !== "string" || entry.trim().length === 0) {
|
|
669
|
+
throw new Error(`Invalid certificate at index ${index}`);
|
|
670
|
+
}
|
|
671
|
+
let der;
|
|
672
|
+
try {
|
|
673
|
+
der = decodeBase64$2(entry);
|
|
674
|
+
}
|
|
675
|
+
catch (error) {
|
|
676
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
677
|
+
throw new Error(`Failed to decode certificate at index ${index}: ${reason}`);
|
|
678
|
+
}
|
|
679
|
+
let certificate;
|
|
680
|
+
try {
|
|
681
|
+
certificate = AsnConvert.parse(der, Certificate);
|
|
682
|
+
}
|
|
683
|
+
catch (error) {
|
|
684
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
685
|
+
throw new Error(`Failed to parse certificate at index ${index}: ${reason}`);
|
|
686
|
+
}
|
|
687
|
+
parsed.push(createParsedCertificate(certificate, der));
|
|
688
|
+
derChunks.push(der);
|
|
689
|
+
}
|
|
690
|
+
return { parsed, chainBytes: concatBytes(derChunks) };
|
|
691
|
+
}
|
|
692
|
+
function createParsedCertificate(certificate, raw) {
|
|
693
|
+
return {
|
|
694
|
+
raw,
|
|
695
|
+
certificate,
|
|
696
|
+
serialNumber: toHex(new Uint8Array(certificate.tbsCertificate.serialNumber)).toUpperCase(),
|
|
697
|
+
subjectName: serializeName(certificate.tbsCertificate.subject),
|
|
698
|
+
issuerName: serializeName(certificate.tbsCertificate.issuer),
|
|
699
|
+
subjectPublicKey: new Uint8Array(certificate.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey).slice(),
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
function extractUrisFromCert(cert) {
|
|
703
|
+
const extension = findExtension(cert, id_ce_subjectAltName);
|
|
704
|
+
if (!extension) {
|
|
705
|
+
return [];
|
|
706
|
+
}
|
|
707
|
+
const subjectAlternativeName = AsnConvert.parse(extension.extnValue.buffer, SubjectAlternativeName);
|
|
708
|
+
const uris = [];
|
|
709
|
+
for (const generalName of subjectAlternativeName) {
|
|
710
|
+
if (generalName.uniformResourceIdentifier) {
|
|
711
|
+
uris.push(generalName.uniformResourceIdentifier);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
return uris;
|
|
715
|
+
}
|
|
716
|
+
function validateNameConstraints(issuers, leafUris) {
|
|
717
|
+
for (const issuer of issuers) {
|
|
718
|
+
const extension = findExtension(issuer.certificate, id_ce_nameConstraints);
|
|
719
|
+
if (!extension) {
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
const constraints = AsnConvert.parse(extension.extnValue.buffer, NameConstraints);
|
|
723
|
+
if (!constraints.permittedSubtrees) {
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
const permittedUris = collectPermittedUris(Array.from(constraints.permittedSubtrees));
|
|
727
|
+
if (permittedUris.length === 0) {
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
for (const uri of leafUris) {
|
|
731
|
+
const allowed = permittedUris.some((prefix) => uri.startsWith(prefix));
|
|
732
|
+
if (!allowed) {
|
|
733
|
+
throw new Error(`URI '${uri}' violates name constraints - not in permitted subtrees: ${permittedUris.join(", ")}`);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function collectPermittedUris(subtrees) {
|
|
739
|
+
const uris = [];
|
|
740
|
+
for (const subtree of subtrees) {
|
|
741
|
+
if (subtree.base.uniformResourceIdentifier &&
|
|
742
|
+
subtree.base.uniformResourceIdentifier.length > 0) {
|
|
743
|
+
uris.push(subtree.base.uniformResourceIdentifier);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
return uris;
|
|
747
|
+
}
|
|
748
|
+
function validateTrustAnchor(chain, trustStorePem) {
|
|
749
|
+
const trustedCerts = parseTrustStore(trustStorePem);
|
|
750
|
+
if (trustedCerts.length === 0) {
|
|
751
|
+
throw new Error("No valid certificates found in trust store");
|
|
752
|
+
}
|
|
753
|
+
logger$g.debug("trust_anchor_validation_start", {
|
|
754
|
+
chain_length: chain.length,
|
|
755
|
+
trust_store_cert_count: trustedCerts.length,
|
|
756
|
+
});
|
|
757
|
+
const chainInfo = chain.map((cert, index) => `[${index}] ${cert.subjectName} (Serial: ${cert.serialNumber})`);
|
|
758
|
+
const trustedInfo = trustedCerts.map((cert, index) => `[${index}] ${cert.subjectName} (Serial: ${cert.serialNumber})`);
|
|
759
|
+
logger$g.debug("certificate_chain_validation", {
|
|
760
|
+
chain_certificates: chainInfo,
|
|
761
|
+
trust_store_certificates: trustedInfo,
|
|
762
|
+
});
|
|
763
|
+
// Strategy 1: direct trust (exact certificate match)
|
|
764
|
+
for (let i = 0; i < chain.length; i += 1) {
|
|
765
|
+
const cert = chain[i];
|
|
766
|
+
const match = trustedCerts.find((trusted) => trusted.serialNumber === cert.serialNumber &&
|
|
767
|
+
namesEqual(trusted.certificate.tbsCertificate.subject, cert.certificate.tbsCertificate.subject));
|
|
768
|
+
if (match) {
|
|
769
|
+
logger$g.debug("certificate_chain_trust_validation_passed", {
|
|
770
|
+
matching_serial: match.serialNumber,
|
|
771
|
+
validation_strategy: `direct_trust_cert_${i}`,
|
|
772
|
+
});
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
const leaf = chain[0];
|
|
777
|
+
// Strategy 2: leaf issuer in trust store
|
|
778
|
+
for (const trusted of trustedCerts) {
|
|
779
|
+
if (namesEqual(trusted.certificate.tbsCertificate.subject, leaf.certificate.tbsCertificate.issuer) &&
|
|
780
|
+
trusted.serialNumber !== leaf.serialNumber) {
|
|
781
|
+
verifyCertificateSignature(leaf.certificate, trusted.certificate);
|
|
782
|
+
logger$g.debug("certificate_chain_trust_validation_passed", {
|
|
783
|
+
matching_serial: trusted.serialNumber,
|
|
784
|
+
validation_strategy: "leaf_issuer_trust",
|
|
785
|
+
});
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
// Strategy 3: any intermediate issuer in trust store
|
|
790
|
+
for (let index = 1; index < chain.length; index += 1) {
|
|
791
|
+
const intermediate = chain[index];
|
|
792
|
+
for (const trusted of trustedCerts) {
|
|
793
|
+
if (namesEqual(trusted.certificate.tbsCertificate.subject, intermediate.certificate.tbsCertificate.issuer) &&
|
|
794
|
+
trusted.serialNumber !== intermediate.serialNumber) {
|
|
795
|
+
verifyCertificateSignature(intermediate.certificate, trusted.certificate);
|
|
796
|
+
logger$g.debug("certificate_chain_trust_validation_passed", {
|
|
797
|
+
matching_serial: trusted.serialNumber,
|
|
798
|
+
validation_strategy: `intermediate_issuer_trust_cert_${index}`,
|
|
799
|
+
});
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
logger$g.warning("certificate_chain_trust_validation_failed", {
|
|
805
|
+
leaf_subject: leaf.subjectName,
|
|
806
|
+
leaf_issuer: leaf.issuerName,
|
|
807
|
+
leaf_serial: leaf.serialNumber,
|
|
808
|
+
trusted_certificates: trustedInfo,
|
|
809
|
+
chain_certificates: chainInfo,
|
|
810
|
+
reason: "no_matching_trust_anchor",
|
|
811
|
+
});
|
|
812
|
+
throw new Error("Certificate chain is not rooted in a trusted anchor");
|
|
813
|
+
}
|
|
814
|
+
function parseTrustStore(trustStorePem) {
|
|
815
|
+
const normalized = normalizePem$2(trustStorePem);
|
|
816
|
+
const blocks = extractPemBlocks$1(normalized);
|
|
817
|
+
const parsed = [];
|
|
818
|
+
for (const block of blocks) {
|
|
819
|
+
try {
|
|
820
|
+
const der = decodeBase64$2(block);
|
|
821
|
+
const certificate = AsnConvert.parse(der, Certificate);
|
|
822
|
+
parsed.push(createParsedCertificate(certificate, der));
|
|
823
|
+
}
|
|
824
|
+
catch (error) {
|
|
825
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
826
|
+
logger$g.debug("trust_store_certificate_parse_failed", { reason });
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return parsed;
|
|
830
|
+
}
|
|
831
|
+
function extractPemBlocks$1(pem) {
|
|
832
|
+
const blocks = [];
|
|
833
|
+
const regex = /-----BEGIN CERTIFICATE-----([\s\S]*?)-----END CERTIFICATE-----/gu;
|
|
834
|
+
let match;
|
|
835
|
+
// eslint-disable-next-line no-cond-assign
|
|
836
|
+
while ((match = regex.exec(pem)) !== null) {
|
|
837
|
+
const body = match[1] ?? "";
|
|
838
|
+
blocks.push(body.replace(/\s+/gu, ""));
|
|
839
|
+
}
|
|
840
|
+
return blocks;
|
|
841
|
+
}
|
|
842
|
+
function validateChainContinuity(chain) {
|
|
843
|
+
if (chain.length <= 1) {
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
logger$g.debug("validating_chain_continuity", { chain_length: chain.length });
|
|
847
|
+
for (let index = 0; index < chain.length - 1; index += 1) {
|
|
848
|
+
const cert = chain[index];
|
|
849
|
+
const issuer = chain[index + 1];
|
|
850
|
+
if (!namesEqual(cert.certificate.tbsCertificate.issuer, issuer.certificate.tbsCertificate.subject)) {
|
|
851
|
+
logger$g.warning("certificate_chain_continuity_failed", {
|
|
852
|
+
cert_index: index,
|
|
853
|
+
cert_subject: cert.subjectName,
|
|
854
|
+
cert_issuer: cert.issuerName,
|
|
855
|
+
expected_issuer_subject: issuer.subjectName,
|
|
856
|
+
reason: "issuer_name_mismatch",
|
|
857
|
+
});
|
|
858
|
+
throw new Error(`Certificate chain continuity broken: certificate at index ${index} issuer does not match next certificate subject`);
|
|
859
|
+
}
|
|
860
|
+
try {
|
|
861
|
+
verifyCertificateSignature(cert.certificate, issuer.certificate);
|
|
862
|
+
logger$g.debug("chain_continuity_verification_success", {
|
|
863
|
+
cert_index: index,
|
|
864
|
+
cert_serial: cert.serialNumber,
|
|
865
|
+
issuer_serial: issuer.serialNumber,
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
catch (error) {
|
|
869
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
870
|
+
logger$g.warning("certificate_chain_continuity_failed", {
|
|
871
|
+
cert_index: index,
|
|
872
|
+
cert_subject: cert.subjectName,
|
|
873
|
+
issuer_subject: issuer.subjectName,
|
|
874
|
+
cert_serial: cert.serialNumber,
|
|
875
|
+
issuer_serial: issuer.serialNumber,
|
|
876
|
+
error: reason,
|
|
877
|
+
reason: "signature_verification_failed",
|
|
878
|
+
});
|
|
879
|
+
throw new Error(`Certificate chain continuity broken: certificate at index ${index} was not signed by certificate at index ${index + 1}: ${reason}`);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
logger$g.debug("chain_continuity_validation_passed", {
|
|
883
|
+
chain_length: chain.length,
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
function verifyCertificateSignature(certificate, issuer) {
|
|
887
|
+
ensureEd25519Support();
|
|
888
|
+
const signatureAlgorithm = certificate.signatureAlgorithm.algorithm;
|
|
889
|
+
const issuerAlgorithm = issuer.tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm;
|
|
890
|
+
if (signatureAlgorithm !== OID_ED25519 || issuerAlgorithm !== OID_ED25519) {
|
|
891
|
+
throw new Error(`Unsupported signature algorithm (certificate: ${signatureAlgorithm}, issuer: ${issuerAlgorithm})`);
|
|
892
|
+
}
|
|
893
|
+
const signatureBytes = new Uint8Array(certificate.signatureValue);
|
|
894
|
+
const tbsBytes = new Uint8Array(AsnConvert.serialize(certificate.tbsCertificate));
|
|
895
|
+
const issuerKey = new Uint8Array(issuer.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey);
|
|
896
|
+
if (issuerKey.length !== 32) {
|
|
897
|
+
throw new Error("Issuer Ed25519 public key must be 32 bytes");
|
|
898
|
+
}
|
|
899
|
+
const valid = verify(signatureBytes, tbsBytes, issuerKey);
|
|
900
|
+
if (!valid) {
|
|
901
|
+
throw new Error("Certificate signature verification failed");
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
function ensureEd25519Support() {
|
|
905
|
+
const etcPatch = etc;
|
|
906
|
+
if (!etcPatch.sha512) {
|
|
907
|
+
etcPatch.sha512 = (message) => sha512(message);
|
|
908
|
+
}
|
|
909
|
+
if (!etcPatch.sha512Sync) {
|
|
910
|
+
etcPatch.sha512Sync = (...messages) => {
|
|
911
|
+
if (messages.length === 1) {
|
|
912
|
+
return sha512(messages[0]);
|
|
913
|
+
}
|
|
914
|
+
const combined = etc.concatBytes(...messages);
|
|
915
|
+
return sha512(combined);
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
function findExtension(certificate, oid) {
|
|
920
|
+
const extensions = certificate.tbsCertificate.extensions;
|
|
921
|
+
if (!extensions) {
|
|
922
|
+
return null;
|
|
923
|
+
}
|
|
924
|
+
for (const extension of extensions) {
|
|
925
|
+
if (extension.extnID === oid) {
|
|
926
|
+
return extension;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
return null;
|
|
930
|
+
}
|
|
931
|
+
function namesEqual(a, b) {
|
|
932
|
+
const left = new Uint8Array(AsnConvert.serialize(a));
|
|
933
|
+
const right = new Uint8Array(AsnConvert.serialize(b));
|
|
934
|
+
if (left.length !== right.length) {
|
|
935
|
+
return false;
|
|
936
|
+
}
|
|
937
|
+
for (let i = 0; i < left.length; i += 1) {
|
|
938
|
+
if (left[i] !== right[i]) {
|
|
939
|
+
return false;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
return true;
|
|
943
|
+
}
|
|
944
|
+
function serializeName(name) {
|
|
945
|
+
const rdns = Array.from(name);
|
|
946
|
+
return rdns
|
|
947
|
+
.map((rdn) => Array.from(rdn)
|
|
948
|
+
.map((attr) => `${oidToLabel$1(attr.type)}=${attr.value.toString()}`)
|
|
949
|
+
.join("+"))
|
|
950
|
+
.join(",");
|
|
951
|
+
}
|
|
952
|
+
function oidToLabel$1(oid) {
|
|
953
|
+
switch (oid) {
|
|
954
|
+
case "2.5.4.3":
|
|
955
|
+
return "CN";
|
|
956
|
+
case "2.5.4.6":
|
|
957
|
+
return "C";
|
|
958
|
+
case "2.5.4.7":
|
|
959
|
+
return "L";
|
|
960
|
+
case "2.5.4.8":
|
|
961
|
+
return "ST";
|
|
962
|
+
case "2.5.4.10":
|
|
963
|
+
return "O";
|
|
964
|
+
case "2.5.4.11":
|
|
965
|
+
return "OU";
|
|
966
|
+
default:
|
|
967
|
+
return oid;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
function concatBytes(chunks) {
|
|
971
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
972
|
+
const result = new Uint8Array(totalLength);
|
|
973
|
+
let offset = 0;
|
|
974
|
+
for (const chunk of chunks) {
|
|
975
|
+
result.set(chunk, offset);
|
|
976
|
+
offset += chunk.length;
|
|
977
|
+
}
|
|
978
|
+
return result;
|
|
979
|
+
}
|
|
980
|
+
function decodeBase64$2(input) {
|
|
981
|
+
if (typeof Buffer !== "undefined") {
|
|
982
|
+
const normalized = input.replace(/\s+/gu, "");
|
|
983
|
+
return new Uint8Array(Buffer.from(normalized, "base64"));
|
|
984
|
+
}
|
|
985
|
+
if (typeof atob === "function") {
|
|
986
|
+
const normalized = input.replace(/\s+/gu, "");
|
|
987
|
+
const binary = atob(normalized);
|
|
988
|
+
const bytes = new Uint8Array(binary.length);
|
|
989
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
990
|
+
bytes[i] = binary.charCodeAt(i);
|
|
991
|
+
}
|
|
992
|
+
return bytes;
|
|
993
|
+
}
|
|
994
|
+
throw new Error("No base64 decoder available in this environment");
|
|
995
|
+
}
|
|
996
|
+
function toHex(data) {
|
|
997
|
+
return Array.from(data)
|
|
998
|
+
.map((byte) => byte.toString(16).padStart(2, "0"))
|
|
999
|
+
.join("");
|
|
1000
|
+
}
|
|
1001
|
+
function buildCacheKey(chainBytes, trustStorePem, enforceNameConstraints) {
|
|
1002
|
+
const chainHash = toHex(sha256(chainBytes));
|
|
1003
|
+
const trustHash = trustStorePem
|
|
1004
|
+
? toHex(sha256(textEncoder.encode(trustStorePem)))
|
|
1005
|
+
: "no-trust";
|
|
1006
|
+
const constraintFlag = enforceNameConstraints ? "nc1" : "nc0";
|
|
1007
|
+
return `${chainHash}|${trustHash}|${constraintFlag}`;
|
|
1008
|
+
}
|
|
1009
|
+
function getCachedPublicKey(cacheKey) {
|
|
1010
|
+
const entry = trustCache.get(cacheKey);
|
|
1011
|
+
if (!entry) {
|
|
1012
|
+
return null;
|
|
1013
|
+
}
|
|
1014
|
+
if (Date.now() > entry.expiresAt) {
|
|
1015
|
+
trustCache.delete(cacheKey);
|
|
1016
|
+
logger$g.debug("certificate_cache_expired", { cache_key: cacheKey });
|
|
1017
|
+
return null;
|
|
1018
|
+
}
|
|
1019
|
+
return entry.value.slice();
|
|
1020
|
+
}
|
|
1021
|
+
function setCachedPublicKey(cacheKey, value, notAfter) {
|
|
1022
|
+
while (trustCache.size >= CACHE_LIMIT) {
|
|
1023
|
+
const firstKey = trustCache.keys().next().value;
|
|
1024
|
+
if (firstKey === undefined) {
|
|
1025
|
+
break;
|
|
1026
|
+
}
|
|
1027
|
+
trustCache.delete(firstKey);
|
|
1028
|
+
logger$g.debug("certificate_cache_evicted", { cache_key: firstKey });
|
|
1029
|
+
}
|
|
1030
|
+
trustCache.set(cacheKey, {
|
|
1031
|
+
value: value.slice(),
|
|
1032
|
+
expiresAt: notAfter.getTime(),
|
|
1033
|
+
});
|
|
1034
|
+
logger$g.debug("certificate_cache_stored", {
|
|
1035
|
+
cache_key: cacheKey,
|
|
1036
|
+
expires_at: notAfter.toISOString(),
|
|
1037
|
+
cache_size: trustCache.size,
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
function normalizeTrustStoreOption(value) {
|
|
1041
|
+
if (!value) {
|
|
1042
|
+
return null;
|
|
1043
|
+
}
|
|
1044
|
+
const trimmed = value.trim();
|
|
1045
|
+
if (trimmed.length === 0) {
|
|
1046
|
+
return null;
|
|
1047
|
+
}
|
|
1048
|
+
if (!trimmed.includes("-----BEGIN CERTIFICATE-----")) {
|
|
1049
|
+
throw new Error("trustStorePem must contain PEM-encoded certificates when provided");
|
|
1050
|
+
}
|
|
1051
|
+
return normalizePem$2(trimmed);
|
|
1052
|
+
}
|
|
1053
|
+
function normalizePem$2(pem) {
|
|
1054
|
+
return pem.replace(/\r/gu, "").trim();
|
|
1055
|
+
}
|
|
1056
|
+
function generateCallId() {
|
|
1057
|
+
return Math.random().toString(36).slice(2, 10);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
const GRANT_PURPOSE_CA_SIGN = "ca_sign";
|
|
1061
|
+
|
|
1062
|
+
const ED25519_OID$2 = "1.3.101.112";
|
|
1063
|
+
const OID_COMMON_NAME$2 = "2.5.4.3";
|
|
1064
|
+
const LOGICAL_URI_PREFIX$1 = "naylence://";
|
|
1065
|
+
function ensureSubtleCrypto() {
|
|
1066
|
+
const instance = globalThis.crypto?.subtle;
|
|
1067
|
+
if (!instance) {
|
|
1068
|
+
throw new Error("WebCrypto subtle API is required to create a CSR");
|
|
1069
|
+
}
|
|
1070
|
+
return instance;
|
|
1071
|
+
}
|
|
1072
|
+
function buildSubject(commonName) {
|
|
1073
|
+
if (!commonName || typeof commonName !== "string") {
|
|
1074
|
+
throw new Error("commonName must be a non-empty string");
|
|
1075
|
+
}
|
|
1076
|
+
return new Name([
|
|
1077
|
+
new RelativeDistinguishedName([
|
|
1078
|
+
new AttributeTypeAndValue({
|
|
1079
|
+
type: OID_COMMON_NAME$2,
|
|
1080
|
+
value: new AttributeValue({ utf8String: commonName }),
|
|
1081
|
+
}),
|
|
1082
|
+
]),
|
|
1083
|
+
]);
|
|
1084
|
+
}
|
|
1085
|
+
function arrayBufferToBase64(buffer) {
|
|
1086
|
+
const bytes = new Uint8Array(buffer);
|
|
1087
|
+
if (typeof globalThis.Buffer?.from === "function") {
|
|
1088
|
+
return globalThis.Buffer.from(bytes).toString("base64");
|
|
1089
|
+
}
|
|
1090
|
+
let binary = "";
|
|
1091
|
+
const chunkSize = 0x8000;
|
|
1092
|
+
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
1093
|
+
const slice = bytes.subarray(offset, offset + chunkSize);
|
|
1094
|
+
binary += String.fromCharCode(...slice);
|
|
1095
|
+
}
|
|
1096
|
+
if (typeof globalThis.btoa !== "function") {
|
|
1097
|
+
throw new Error("Base64 encoding not available in this environment");
|
|
1098
|
+
}
|
|
1099
|
+
return globalThis.btoa(binary);
|
|
1100
|
+
}
|
|
1101
|
+
function derToPem$1(der, label) {
|
|
1102
|
+
const base64 = arrayBufferToBase64(der);
|
|
1103
|
+
const lines = [];
|
|
1104
|
+
for (let index = 0; index < base64.length; index += 64) {
|
|
1105
|
+
lines.push(base64.slice(index, index + 64));
|
|
1106
|
+
}
|
|
1107
|
+
return `-----BEGIN ${label}-----\n${lines.join("\n")}\n-----END ${label}-----\n`;
|
|
1108
|
+
}
|
|
1109
|
+
async function createEd25519Csr(options) {
|
|
1110
|
+
const subtle = ensureSubtleCrypto();
|
|
1111
|
+
const { privateKey, publicKey, commonName } = options;
|
|
1112
|
+
if (!(privateKey instanceof CryptoKey) || privateKey.type !== "private") {
|
|
1113
|
+
throw new Error("privateKey must be a CryptoKey of type 'private'");
|
|
1114
|
+
}
|
|
1115
|
+
if (!(publicKey instanceof CryptoKey) || publicKey.type !== "public") {
|
|
1116
|
+
throw new Error("publicKey must be a CryptoKey of type 'public'");
|
|
1117
|
+
}
|
|
1118
|
+
const subject = buildSubject(commonName);
|
|
1119
|
+
const spkiDer = await subtle.exportKey("spki", publicKey);
|
|
1120
|
+
const subjectPublicKeyInfo = AsnConvert.parse(spkiDer, SubjectPublicKeyInfo);
|
|
1121
|
+
const attributes = new Attributes();
|
|
1122
|
+
const sanitizedLogicals = Array.isArray(options.logicals)
|
|
1123
|
+
? options.logicals
|
|
1124
|
+
.map((logical) => logical.trim())
|
|
1125
|
+
.filter((logical) => logical.length > 0)
|
|
1126
|
+
: [];
|
|
1127
|
+
if (sanitizedLogicals.length > 0) {
|
|
1128
|
+
const san = new SubjectAlternativeName(sanitizedLogicals.map((logical) => new GeneralName({
|
|
1129
|
+
uniformResourceIdentifier: `${LOGICAL_URI_PREFIX$1}${logical}`,
|
|
1130
|
+
})));
|
|
1131
|
+
const extensions = new Extensions([
|
|
1132
|
+
new Extension({
|
|
1133
|
+
extnID: id_ce_subjectAltName,
|
|
1134
|
+
critical: false,
|
|
610
1135
|
extnValue: new OctetString(AsnConvert.serialize(san)),
|
|
611
1136
|
}),
|
|
612
1137
|
]);
|
|
@@ -686,7 +1211,7 @@ const NODE_ID_OID = "1.3.6.1.4.1.58530.4";
|
|
|
686
1211
|
* Provides async HTTP client to request certificates from the CA signing service.
|
|
687
1212
|
*/
|
|
688
1213
|
// Simple logger for now - TODO: integrate with runtime logging
|
|
689
|
-
const logger$
|
|
1214
|
+
const logger$f = {
|
|
690
1215
|
debug: (_event, _meta) => {
|
|
691
1216
|
// console.log(`[DEBUG] ${event}`, meta);
|
|
692
1217
|
},
|
|
@@ -1158,13 +1683,13 @@ class CAServiceClient {
|
|
|
1158
1683
|
const result = await response.json();
|
|
1159
1684
|
const certificatePem = result.certificate_pem;
|
|
1160
1685
|
const certificateChainPem = result.certificate_chain_pem || certificatePem;
|
|
1161
|
-
logger$
|
|
1686
|
+
logger$f.debug("certificate_request_successful", {
|
|
1162
1687
|
requester_id: requesterId,
|
|
1163
1688
|
expires_at: result.expires_at,
|
|
1164
1689
|
});
|
|
1165
1690
|
// Extract and log certificate information with structured logging
|
|
1166
1691
|
const certInfo = extractCertificateInfo(certificatePem);
|
|
1167
|
-
logger$
|
|
1692
|
+
logger$f.debug("certificate_details", {
|
|
1168
1693
|
requester_id: requesterId,
|
|
1169
1694
|
certificate_type: "issued_certificate",
|
|
1170
1695
|
...certInfo,
|
|
@@ -1183,7 +1708,7 @@ class CAServiceClient {
|
|
|
1183
1708
|
// First cert in chain is usually the issued certificate
|
|
1184
1709
|
if (certPemBlock.trim() !== certificatePem.trim()) {
|
|
1185
1710
|
const chainCertInfo = extractCertificateInfo(certPemBlock);
|
|
1186
|
-
logger$
|
|
1711
|
+
logger$f.debug("certificate_chain_details", {
|
|
1187
1712
|
requester_id: requesterId,
|
|
1188
1713
|
certificate_type: "certificate_chain",
|
|
1189
1714
|
chain_index: i,
|
|
@@ -1194,7 +1719,7 @@ class CAServiceClient {
|
|
|
1194
1719
|
else {
|
|
1195
1720
|
// Subsequent certs are intermediate/root CAs
|
|
1196
1721
|
const caCertInfo = extractCertificateInfo(certPemBlock);
|
|
1197
|
-
logger$
|
|
1722
|
+
logger$f.debug("certificate_chain_details", {
|
|
1198
1723
|
requester_id: requesterId,
|
|
1199
1724
|
certificate_type: "ca_certificate",
|
|
1200
1725
|
chain_index: i,
|
|
@@ -1222,7 +1747,7 @@ class CAServiceClient {
|
|
|
1222
1747
|
// Body read failed entirely
|
|
1223
1748
|
errorDetail = `HTTP ${response.status}`;
|
|
1224
1749
|
}
|
|
1225
|
-
logger$
|
|
1750
|
+
logger$f.error("certificate_request_failed", {
|
|
1226
1751
|
requester_id: requesterId,
|
|
1227
1752
|
status_code: response.status,
|
|
1228
1753
|
error: errorDetail,
|
|
@@ -1239,13 +1764,13 @@ class CAServiceClient {
|
|
|
1239
1764
|
throw error;
|
|
1240
1765
|
}
|
|
1241
1766
|
if (error instanceof Error && error.name === "AbortError") {
|
|
1242
|
-
logger$
|
|
1767
|
+
logger$f.error("certificate_request_timeout", {
|
|
1243
1768
|
requester_id: requesterId,
|
|
1244
1769
|
timeout_seconds: this.timeoutSeconds,
|
|
1245
1770
|
});
|
|
1246
1771
|
throw new CertificateRequestError(`Certificate request timed out after ${this.timeoutSeconds} seconds`);
|
|
1247
1772
|
}
|
|
1248
|
-
logger$
|
|
1773
|
+
logger$f.error("certificate_request_network_error", {
|
|
1249
1774
|
requester_id: requesterId,
|
|
1250
1775
|
error: String(error),
|
|
1251
1776
|
});
|
|
@@ -1254,7 +1779,7 @@ class CAServiceClient {
|
|
|
1254
1779
|
}
|
|
1255
1780
|
}
|
|
1256
1781
|
|
|
1257
|
-
const logger$
|
|
1782
|
+
const logger$e = getLogger("naylence.fame.security.encryption.sealed.x25519_encryption_manager");
|
|
1258
1783
|
class X25519EncryptionManager {
|
|
1259
1784
|
constructor({ keyProvider, nodeLike = null, cryptoProvider = null, }) {
|
|
1260
1785
|
this.pendingEnvelopes = new Map();
|
|
@@ -1271,7 +1796,7 @@ class X25519EncryptionManager {
|
|
|
1271
1796
|
// KeyManagementHandler will queue the envelope and send KeyRequest.
|
|
1272
1797
|
// X25519 should NOT queue here to avoid dual queueing.
|
|
1273
1798
|
if (opts?.requestAddress) {
|
|
1274
|
-
logger$
|
|
1799
|
+
logger$e.debug("key_not_found_delegating_to_key_management", {
|
|
1275
1800
|
envelope_id: envelope.id,
|
|
1276
1801
|
request_address: String(opts.requestAddress),
|
|
1277
1802
|
});
|
|
@@ -1287,7 +1812,7 @@ class X25519EncryptionManager {
|
|
|
1287
1812
|
return await this.encryptWithKey(envelope, recipPub, recipKid);
|
|
1288
1813
|
}
|
|
1289
1814
|
catch (error) {
|
|
1290
|
-
logger$
|
|
1815
|
+
logger$e.error("x25519_encryption_failed", {
|
|
1291
1816
|
error: error instanceof Error ? error.message : String(error),
|
|
1292
1817
|
});
|
|
1293
1818
|
return EncryptionResult.skipped(envelope);
|
|
@@ -1325,20 +1850,20 @@ class X25519EncryptionManager {
|
|
|
1325
1850
|
return envelope;
|
|
1326
1851
|
}
|
|
1327
1852
|
catch (error) {
|
|
1328
|
-
logger$
|
|
1853
|
+
logger$e.error("x25519_decryption_failed", {
|
|
1329
1854
|
error: error instanceof Error ? error.message : String(error),
|
|
1330
1855
|
});
|
|
1331
1856
|
return envelope;
|
|
1332
1857
|
}
|
|
1333
1858
|
}
|
|
1334
1859
|
async notifyKeyAvailable(keyId) {
|
|
1335
|
-
logger$
|
|
1860
|
+
logger$e.debug("x25519_notify_key_available_called", {
|
|
1336
1861
|
key_id: keyId,
|
|
1337
1862
|
pending_keys: Array.from(this.pendingEnvelopes.keys()),
|
|
1338
1863
|
});
|
|
1339
1864
|
const queued = this.pendingEnvelopes.get(keyId);
|
|
1340
1865
|
if (!queued || queued.length === 0) {
|
|
1341
|
-
logger$
|
|
1866
|
+
logger$e.debug("no_queued_envelopes_for_key", {
|
|
1342
1867
|
key_id: keyId,
|
|
1343
1868
|
has_queue: this.pendingEnvelopes.has(keyId),
|
|
1344
1869
|
queue_length: queued?.length ?? 0,
|
|
@@ -1350,13 +1875,13 @@ class X25519EncryptionManager {
|
|
|
1350
1875
|
this.keyRequestsInProgress.delete(keyId);
|
|
1351
1876
|
const node = this.nodeLike;
|
|
1352
1877
|
if (!node) {
|
|
1353
|
-
logger$
|
|
1878
|
+
logger$e.debug("discarding_queued_envelopes_no_node", {
|
|
1354
1879
|
key_id: keyId,
|
|
1355
1880
|
count: queued.length,
|
|
1356
1881
|
});
|
|
1357
1882
|
return;
|
|
1358
1883
|
}
|
|
1359
|
-
logger$
|
|
1884
|
+
logger$e.debug("replaying_envelopes_for_key", {
|
|
1360
1885
|
key_id: keyId,
|
|
1361
1886
|
count: queued.length,
|
|
1362
1887
|
});
|
|
@@ -1365,7 +1890,7 @@ class X25519EncryptionManager {
|
|
|
1365
1890
|
await node.deliver(envelope);
|
|
1366
1891
|
}
|
|
1367
1892
|
catch (error) {
|
|
1368
|
-
logger$
|
|
1893
|
+
logger$e.error("failed_to_replay_envelope", {
|
|
1369
1894
|
key_id: keyId,
|
|
1370
1895
|
envelope_id: envelope.id,
|
|
1371
1896
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -1466,7 +1991,7 @@ class X25519EncryptionManager {
|
|
|
1466
1991
|
? this.extractPrivateKeyFromRecord(providerRecord)
|
|
1467
1992
|
: null;
|
|
1468
1993
|
if (providerRecordKey) {
|
|
1469
|
-
logger$
|
|
1994
|
+
logger$e.debug("using_provider_key_record_private_key", {
|
|
1470
1995
|
kid,
|
|
1471
1996
|
provider_key_id: providerKeyId,
|
|
1472
1997
|
mismatched_kid: kid && providerKeyId !== kid ? kid : null,
|
|
@@ -1476,7 +2001,7 @@ class X25519EncryptionManager {
|
|
|
1476
2001
|
}
|
|
1477
2002
|
if (!providerPem) {
|
|
1478
2003
|
if (kid && providerKeyId && providerKeyId !== kid) {
|
|
1479
|
-
logger$
|
|
2004
|
+
logger$e.debug("crypto_provider_key_id_mismatch_no_private_key", {
|
|
1480
2005
|
kid,
|
|
1481
2006
|
provider_key_id: providerKeyId,
|
|
1482
2007
|
});
|
|
@@ -1488,13 +2013,13 @@ class X25519EncryptionManager {
|
|
|
1488
2013
|
return null;
|
|
1489
2014
|
}
|
|
1490
2015
|
if (!kid || providerKeyId === kid) {
|
|
1491
|
-
logger$
|
|
2016
|
+
logger$e.debug("using_crypto_provider_private_key_fallback", {
|
|
1492
2017
|
kid: kid ?? null,
|
|
1493
2018
|
provider_key_id: providerKeyId,
|
|
1494
2019
|
});
|
|
1495
2020
|
}
|
|
1496
2021
|
else {
|
|
1497
|
-
logger$
|
|
2022
|
+
logger$e.warning("crypto_provider_key_id_mismatch_using_private_key", {
|
|
1498
2023
|
kid,
|
|
1499
2024
|
provider_key_id: providerKeyId,
|
|
1500
2025
|
key_record_present: Boolean(record),
|
|
@@ -1503,7 +2028,7 @@ class X25519EncryptionManager {
|
|
|
1503
2028
|
return fallbackKey;
|
|
1504
2029
|
}
|
|
1505
2030
|
async queueEnvelopeForKey(envelope, opts, recipientKeyId) {
|
|
1506
|
-
logger$
|
|
2031
|
+
logger$e.debug("queueing_envelope_for_sealed_encryption", {
|
|
1507
2032
|
envelope_id: envelope.id,
|
|
1508
2033
|
recipient_key_id: recipientKeyId,
|
|
1509
2034
|
request_address: opts?.requestAddress
|
|
@@ -1551,7 +2076,7 @@ class X25519EncryptionManager {
|
|
|
1551
2076
|
await node.deliver(keyRequestEnvelope, context);
|
|
1552
2077
|
}
|
|
1553
2078
|
catch (error) {
|
|
1554
|
-
logger$
|
|
2079
|
+
logger$e.error("failed_to_request_recipient_key", {
|
|
1555
2080
|
recipient_key_id: recipientKeyId,
|
|
1556
2081
|
error: error instanceof Error ? error.message : String(error),
|
|
1557
2082
|
});
|
|
@@ -1564,7 +2089,7 @@ class X25519EncryptionManager {
|
|
|
1564
2089
|
return this.extractPublicKeyFromRecord(record);
|
|
1565
2090
|
}
|
|
1566
2091
|
catch (error) {
|
|
1567
|
-
logger$
|
|
2092
|
+
logger$e.debug("recipient_key_lookup_failed", {
|
|
1568
2093
|
kid,
|
|
1569
2094
|
error: error instanceof Error ? error.message : String(error),
|
|
1570
2095
|
});
|
|
@@ -1579,7 +2104,7 @@ class X25519EncryptionManager {
|
|
|
1579
2104
|
return await this.keyProvider.getKey(kid);
|
|
1580
2105
|
}
|
|
1581
2106
|
catch (error) {
|
|
1582
|
-
logger$
|
|
2107
|
+
logger$e.debug("private_key_lookup_failed", {
|
|
1583
2108
|
kid,
|
|
1584
2109
|
error: error instanceof Error ? error.message : String(error),
|
|
1585
2110
|
});
|
|
@@ -1650,7 +2175,7 @@ class X25519EncryptionManager {
|
|
|
1650
2175
|
const base64 = base64Lines.join("");
|
|
1651
2176
|
const der = this.decodeBase64Flexible(base64);
|
|
1652
2177
|
if (!der) {
|
|
1653
|
-
logger$
|
|
2178
|
+
logger$e.debug("pem_decode_failed", {
|
|
1654
2179
|
key_type: keyType,
|
|
1655
2180
|
});
|
|
1656
2181
|
return null;
|
|
@@ -1883,7 +2408,7 @@ var index$1 = /*#__PURE__*/Object.freeze({
|
|
|
1883
2408
|
X25519EncryptionManagerFactory: X25519EncryptionManagerFactory
|
|
1884
2409
|
});
|
|
1885
2410
|
|
|
1886
|
-
const logger$
|
|
2411
|
+
const logger$d = getLogger("naylence.fame.security.encryption.channel.channel_encryption_manager");
|
|
1887
2412
|
const SUPPORTED_CHANNEL_ALGORITHMS = ["chacha20-poly1305-channel"];
|
|
1888
2413
|
const CHANNEL_ENCRYPTION_ALGORITHM = "chacha20-poly1305-channel";
|
|
1889
2414
|
const HANDSHAKE_ALGORITHM = "CHACHA20P1305";
|
|
@@ -2034,13 +2559,13 @@ class ChannelEncryptionManager {
|
|
|
2034
2559
|
const destination = opts?.destination ?? envelope.to ?? null;
|
|
2035
2560
|
const destinationStr = toDestinationString(destination);
|
|
2036
2561
|
if (!destinationStr) {
|
|
2037
|
-
logger$
|
|
2562
|
+
logger$d.warning("no_destination_for_channel_encryption", {
|
|
2038
2563
|
envelope_id: envelope.id,
|
|
2039
2564
|
});
|
|
2040
2565
|
return EncryptionResult.skipped(envelope);
|
|
2041
2566
|
}
|
|
2042
2567
|
if (!this.secureChannelManager) {
|
|
2043
|
-
logger$
|
|
2568
|
+
logger$d.warning("no_secure_channel_manager_available", {
|
|
2044
2569
|
envelope_id: envelope.id,
|
|
2045
2570
|
});
|
|
2046
2571
|
return EncryptionResult.skipped(envelope);
|
|
@@ -2051,7 +2576,7 @@ class ChannelEncryptionManager {
|
|
|
2051
2576
|
return this.encryptWithChannel(envelope, existingChannelId);
|
|
2052
2577
|
}
|
|
2053
2578
|
catch (error) {
|
|
2054
|
-
logger$
|
|
2579
|
+
logger$d.error("channel_encryption_failed", {
|
|
2055
2580
|
error: error instanceof Error ? error.message : String(error),
|
|
2056
2581
|
channel_id: existingChannelId,
|
|
2057
2582
|
});
|
|
@@ -2078,35 +2603,35 @@ class ChannelEncryptionManager {
|
|
|
2078
2603
|
}
|
|
2079
2604
|
const channelId = encHeader.kid;
|
|
2080
2605
|
if (!channelId) {
|
|
2081
|
-
logger$
|
|
2606
|
+
logger$d.error("missing_channel_id_in_encryption_header", {
|
|
2082
2607
|
envelope_id: envelope.id,
|
|
2083
2608
|
});
|
|
2084
2609
|
return envelope;
|
|
2085
2610
|
}
|
|
2086
2611
|
const nonce = this.decodeNonceValue(encHeader.val ?? "");
|
|
2087
2612
|
if (!nonce) {
|
|
2088
|
-
logger$
|
|
2613
|
+
logger$d.error("invalid_nonce_in_encryption_header", {
|
|
2089
2614
|
envelope_id: envelope.id,
|
|
2090
2615
|
value_present: Boolean(encHeader.val),
|
|
2091
2616
|
});
|
|
2092
2617
|
return envelope;
|
|
2093
2618
|
}
|
|
2094
2619
|
if (!this.secureChannelManager) {
|
|
2095
|
-
logger$
|
|
2620
|
+
logger$d.warning("no_secure_channel_manager_for_decryption", {
|
|
2096
2621
|
envelope_id: envelope.id,
|
|
2097
2622
|
});
|
|
2098
2623
|
return envelope;
|
|
2099
2624
|
}
|
|
2100
2625
|
const channelState = this.getChannelState(channelId);
|
|
2101
2626
|
if (!channelState) {
|
|
2102
|
-
logger$
|
|
2627
|
+
logger$d.error("channel_not_available_for_decryption", {
|
|
2103
2628
|
channel_id: channelId,
|
|
2104
2629
|
});
|
|
2105
2630
|
return envelope;
|
|
2106
2631
|
}
|
|
2107
2632
|
const ciphertext = this.extractCiphertext(frame.payload);
|
|
2108
2633
|
if (!ciphertext) {
|
|
2109
|
-
logger$
|
|
2634
|
+
logger$d.error("invalid_ciphertext_payload", { envelope_id: envelope.id });
|
|
2110
2635
|
return envelope;
|
|
2111
2636
|
}
|
|
2112
2637
|
try {
|
|
@@ -2131,7 +2656,7 @@ class ChannelEncryptionManager {
|
|
|
2131
2656
|
return envelope;
|
|
2132
2657
|
}
|
|
2133
2658
|
catch (error) {
|
|
2134
|
-
logger$
|
|
2659
|
+
logger$d.error("channel_decryption_failed", {
|
|
2135
2660
|
channel_id: channelId,
|
|
2136
2661
|
error: error instanceof Error ? error.message : String(error),
|
|
2137
2662
|
});
|
|
@@ -2139,24 +2664,24 @@ class ChannelEncryptionManager {
|
|
|
2139
2664
|
}
|
|
2140
2665
|
}
|
|
2141
2666
|
async notifyChannelEstablished(channelId) {
|
|
2142
|
-
logger$
|
|
2667
|
+
logger$d.debug("channel_encryption_manager_notified", {
|
|
2143
2668
|
channel_id: channelId,
|
|
2144
2669
|
manager_type: "channel",
|
|
2145
2670
|
});
|
|
2146
2671
|
if (!channelId.startsWith("auto-")) {
|
|
2147
|
-
logger$
|
|
2672
|
+
logger$d.warning("unexpected_channel_id_format", { channel_id: channelId });
|
|
2148
2673
|
return;
|
|
2149
2674
|
}
|
|
2150
2675
|
const destinationStr = this.extractDestinationFromChannelId(channelId);
|
|
2151
2676
|
if (!destinationStr) {
|
|
2152
|
-
logger$
|
|
2677
|
+
logger$d.warning("cannot_parse_destination_from_channel_id", {
|
|
2153
2678
|
channel_id: channelId,
|
|
2154
2679
|
});
|
|
2155
2680
|
return;
|
|
2156
2681
|
}
|
|
2157
2682
|
this.handshakeInProgress.delete(destinationStr);
|
|
2158
2683
|
if (!this.pendingEnvelopes.has(destinationStr)) {
|
|
2159
|
-
logger$
|
|
2684
|
+
logger$d.debug("no_pending_queue_for_destination", {
|
|
2160
2685
|
destination: destinationStr,
|
|
2161
2686
|
});
|
|
2162
2687
|
return;
|
|
@@ -2164,7 +2689,7 @@ class ChannelEncryptionManager {
|
|
|
2164
2689
|
const queuedEnvelopes = this.pendingEnvelopes.get(destinationStr) ?? [];
|
|
2165
2690
|
this.pendingEnvelopes.delete(destinationStr);
|
|
2166
2691
|
if (!this.secureChannelManager) {
|
|
2167
|
-
logger$
|
|
2692
|
+
logger$d.error("no_secure_channel_manager_for_queue_drain", {
|
|
2168
2693
|
channel_id: channelId,
|
|
2169
2694
|
});
|
|
2170
2695
|
return;
|
|
@@ -2173,7 +2698,7 @@ class ChannelEncryptionManager {
|
|
|
2173
2698
|
try {
|
|
2174
2699
|
const result = this.encryptWithChannel(envelope, channelId);
|
|
2175
2700
|
if (!result.envelope) {
|
|
2176
|
-
logger$
|
|
2701
|
+
logger$d.warning("failed_to_encrypt_queued_envelope", {
|
|
2177
2702
|
envelope_id: envelope.id,
|
|
2178
2703
|
channel_id: channelId,
|
|
2179
2704
|
});
|
|
@@ -2183,7 +2708,7 @@ class ChannelEncryptionManager {
|
|
|
2183
2708
|
this.runAsyncTask(() => this.deliverEnvelope(encryptedEnvelope), `deliver-queued-${envelope.id}`);
|
|
2184
2709
|
}
|
|
2185
2710
|
catch (error) {
|
|
2186
|
-
logger$
|
|
2711
|
+
logger$d.error("failed_to_encrypt_queued_envelope", {
|
|
2187
2712
|
envelope_id: envelope.id,
|
|
2188
2713
|
error: error instanceof Error ? error.message : String(error),
|
|
2189
2714
|
});
|
|
@@ -2191,19 +2716,19 @@ class ChannelEncryptionManager {
|
|
|
2191
2716
|
}
|
|
2192
2717
|
}
|
|
2193
2718
|
async notifyChannelFailed(channelId, reason = "handshake_failed") {
|
|
2194
|
-
logger$
|
|
2719
|
+
logger$d.debug("channel_encryption_manager_notified_failure", {
|
|
2195
2720
|
channel_id: channelId,
|
|
2196
2721
|
reason,
|
|
2197
2722
|
});
|
|
2198
2723
|
if (!channelId.startsWith("auto-")) {
|
|
2199
|
-
logger$
|
|
2724
|
+
logger$d.warning("unexpected_channel_id_format_on_failure", {
|
|
2200
2725
|
channel_id: channelId,
|
|
2201
2726
|
});
|
|
2202
2727
|
return;
|
|
2203
2728
|
}
|
|
2204
2729
|
const destinationStr = this.extractDestinationFromChannelId(channelId);
|
|
2205
2730
|
if (!destinationStr) {
|
|
2206
|
-
logger$
|
|
2731
|
+
logger$d.warning("cannot_parse_destination_from_channel_id_on_failure", {
|
|
2207
2732
|
channel_id: channelId,
|
|
2208
2733
|
});
|
|
2209
2734
|
return;
|
|
@@ -2213,14 +2738,14 @@ class ChannelEncryptionManager {
|
|
|
2213
2738
|
const cachedChannelId = this.addrChannelMap.get(destinationStr);
|
|
2214
2739
|
if (cachedChannelId === channelId) {
|
|
2215
2740
|
this.addrChannelMap.delete(destinationStr);
|
|
2216
|
-
logger$
|
|
2741
|
+
logger$d.debug("cleared_channel_cache_for_failed_channel", {
|
|
2217
2742
|
destination: destinationStr,
|
|
2218
2743
|
channel_id: channelId,
|
|
2219
2744
|
});
|
|
2220
2745
|
}
|
|
2221
2746
|
const queuedEnvelopes = this.pendingEnvelopes.get(destinationStr);
|
|
2222
2747
|
if (!queuedEnvelopes || queuedEnvelopes.length === 0) {
|
|
2223
|
-
logger$
|
|
2748
|
+
logger$d.debug("no_pending_queue_for_failed_destination", {
|
|
2224
2749
|
destination: destinationStr,
|
|
2225
2750
|
});
|
|
2226
2751
|
return;
|
|
@@ -2239,7 +2764,7 @@ class ChannelEncryptionManager {
|
|
|
2239
2764
|
const cached = this.addrChannelMap.get(destination);
|
|
2240
2765
|
if (cached) {
|
|
2241
2766
|
this.addrChannelMap.delete(destination);
|
|
2242
|
-
logger$
|
|
2767
|
+
logger$d.debug("cleared_channel_cache_for_destination", {
|
|
2243
2768
|
destination,
|
|
2244
2769
|
cached_channel_id: cached,
|
|
2245
2770
|
});
|
|
@@ -2257,14 +2782,14 @@ class ChannelEncryptionManager {
|
|
|
2257
2782
|
}
|
|
2258
2783
|
const cached = this.addrChannelMap.get(destination);
|
|
2259
2784
|
if (cached && this.getChannelState(cached)) {
|
|
2260
|
-
logger$
|
|
2785
|
+
logger$d.debug("using_cached_channel", { destination, channel_id: cached });
|
|
2261
2786
|
return cached;
|
|
2262
2787
|
}
|
|
2263
2788
|
const channels = this.secureChannelManager.channels;
|
|
2264
2789
|
for (const channelId of Object.keys(channels)) {
|
|
2265
2790
|
if (channelId.startsWith(`auto-${destination}-`)) {
|
|
2266
2791
|
this.addrChannelMap.set(destination, channelId);
|
|
2267
|
-
logger$
|
|
2792
|
+
logger$d.debug("using_existing_channel", {
|
|
2268
2793
|
destination,
|
|
2269
2794
|
channel_id: channelId,
|
|
2270
2795
|
});
|
|
@@ -2277,12 +2802,12 @@ class ChannelEncryptionManager {
|
|
|
2277
2802
|
const queue = this.pendingEnvelopes.get(destinationStr) ?? [];
|
|
2278
2803
|
queue.push(envelope);
|
|
2279
2804
|
this.pendingEnvelopes.set(destinationStr, queue);
|
|
2280
|
-
logger$
|
|
2805
|
+
logger$d.debug("queued_envelope_for_channel_handshake", {
|
|
2281
2806
|
envelope_id: envelope.id,
|
|
2282
2807
|
destination: destinationStr,
|
|
2283
2808
|
});
|
|
2284
2809
|
if (this.handshakeInProgress.has(destinationStr)) {
|
|
2285
|
-
logger$
|
|
2810
|
+
logger$d.debug("handshake_already_in_progress", {
|
|
2286
2811
|
destination: destinationStr,
|
|
2287
2812
|
});
|
|
2288
2813
|
return;
|
|
@@ -2300,7 +2825,7 @@ class ChannelEncryptionManager {
|
|
|
2300
2825
|
}
|
|
2301
2826
|
async initiateChannelHandshakeAsync(destination, destinationStr, opts) {
|
|
2302
2827
|
if (!this.secureChannelManager) {
|
|
2303
|
-
logger$
|
|
2828
|
+
logger$d.error("no_secure_channel_manager_for_async_handshake_initiation");
|
|
2304
2829
|
return;
|
|
2305
2830
|
}
|
|
2306
2831
|
const channelId = this.generateChannelId(destinationStr);
|
|
@@ -2308,19 +2833,19 @@ class ChannelEncryptionManager {
|
|
|
2308
2833
|
const openFrame = this.secureChannelManager.generateOpenFrame(channelId, HANDSHAKE_ALGORITHM);
|
|
2309
2834
|
const success = await this.sendSecureOpenFrameAsync(openFrame, destination);
|
|
2310
2835
|
if (success) {
|
|
2311
|
-
logger$
|
|
2836
|
+
logger$d.debug("sent_secure_open_frame_async", {
|
|
2312
2837
|
channel_id: channelId,
|
|
2313
2838
|
destination: destinationStr,
|
|
2314
2839
|
});
|
|
2315
2840
|
}
|
|
2316
2841
|
else {
|
|
2317
|
-
logger$
|
|
2842
|
+
logger$d.warning("failed_to_send_secure_open_frame_async", {
|
|
2318
2843
|
channel_id: channelId,
|
|
2319
2844
|
});
|
|
2320
2845
|
}
|
|
2321
2846
|
}
|
|
2322
2847
|
catch (error) {
|
|
2323
|
-
logger$
|
|
2848
|
+
logger$d.error("async_channel_handshake_initiation_failed", {
|
|
2324
2849
|
destination: destinationStr,
|
|
2325
2850
|
error: error instanceof Error ? error.message : String(error),
|
|
2326
2851
|
});
|
|
@@ -2329,22 +2854,22 @@ class ChannelEncryptionManager {
|
|
|
2329
2854
|
async sendSecureOpenFrameAsync(openFrame, destination) {
|
|
2330
2855
|
const node = this.nodeLike;
|
|
2331
2856
|
if (!node) {
|
|
2332
|
-
logger$
|
|
2857
|
+
logger$d.error("no_node_available_for_sending_secure_open_async");
|
|
2333
2858
|
return false;
|
|
2334
2859
|
}
|
|
2335
2860
|
const envelopeFactory = node.envelopeFactory;
|
|
2336
2861
|
if (!envelopeFactory) {
|
|
2337
|
-
logger$
|
|
2862
|
+
logger$d.error("no_envelope_factory_available_for_secure_open_async");
|
|
2338
2863
|
return false;
|
|
2339
2864
|
}
|
|
2340
2865
|
const replyTo = this.buildSystemReplyTo();
|
|
2341
2866
|
if (!replyTo) {
|
|
2342
|
-
logger$
|
|
2867
|
+
logger$d.error("no_physical_path_available_for_reply_to_async");
|
|
2343
2868
|
return false;
|
|
2344
2869
|
}
|
|
2345
2870
|
const toAddress = toFameAddress(destination);
|
|
2346
2871
|
if (!toAddress) {
|
|
2347
|
-
logger$
|
|
2872
|
+
logger$d.error("invalid_destination_for_secure_open", {
|
|
2348
2873
|
destination: String(destination),
|
|
2349
2874
|
});
|
|
2350
2875
|
return false;
|
|
@@ -2356,7 +2881,7 @@ class ChannelEncryptionManager {
|
|
|
2356
2881
|
corrId: generateId(),
|
|
2357
2882
|
});
|
|
2358
2883
|
await this.deliverEnvelope(envelope);
|
|
2359
|
-
logger$
|
|
2884
|
+
logger$d.debug("delivered_secure_open_frame_async", {
|
|
2360
2885
|
channel_id: openFrame.cid,
|
|
2361
2886
|
});
|
|
2362
2887
|
return true;
|
|
@@ -2364,7 +2889,7 @@ class ChannelEncryptionManager {
|
|
|
2364
2889
|
async deliverEnvelope(envelope) {
|
|
2365
2890
|
const node = this.nodeLike;
|
|
2366
2891
|
if (!node) {
|
|
2367
|
-
logger$
|
|
2892
|
+
logger$d.error("no_node_available_for_delivery", {
|
|
2368
2893
|
envelope_id: envelope.id,
|
|
2369
2894
|
});
|
|
2370
2895
|
return;
|
|
@@ -2374,19 +2899,19 @@ class ChannelEncryptionManager {
|
|
|
2374
2899
|
}
|
|
2375
2900
|
encryptWithChannel(envelope, channelId) {
|
|
2376
2901
|
if (!this.secureChannelManager) {
|
|
2377
|
-
logger$
|
|
2902
|
+
logger$d.error("no_secure_channel_manager_for_encryption");
|
|
2378
2903
|
return EncryptionResult.skipped(envelope);
|
|
2379
2904
|
}
|
|
2380
2905
|
const frame = envelope.frame;
|
|
2381
2906
|
if (!this.isDataFrame(frame)) {
|
|
2382
|
-
logger$
|
|
2907
|
+
logger$d.error("attempted_to_encrypt_non_dataframe", {
|
|
2383
2908
|
frame_type: frame.type ?? typeof frame,
|
|
2384
2909
|
});
|
|
2385
2910
|
return EncryptionResult.skipped(envelope);
|
|
2386
2911
|
}
|
|
2387
2912
|
const channelState = this.getChannelState(channelId);
|
|
2388
2913
|
if (!channelState) {
|
|
2389
|
-
logger$
|
|
2914
|
+
logger$d.error("channel_not_in_channels", { channel_id: channelId });
|
|
2390
2915
|
return EncryptionResult.skipped(envelope);
|
|
2391
2916
|
}
|
|
2392
2917
|
const payloadBytes = this.serializePayload(frame.payload);
|
|
@@ -2445,7 +2970,7 @@ class ChannelEncryptionManager {
|
|
|
2445
2970
|
return decodeBase64$1(payload);
|
|
2446
2971
|
}
|
|
2447
2972
|
catch (error) {
|
|
2448
|
-
logger$
|
|
2973
|
+
logger$d.error("failed_to_decode_base64_ciphertext", {
|
|
2449
2974
|
error: error instanceof Error ? error.message : String(error),
|
|
2450
2975
|
});
|
|
2451
2976
|
return null;
|
|
@@ -2475,7 +3000,7 @@ class ChannelEncryptionManager {
|
|
|
2475
3000
|
return parts.slice(1, -1).join("-");
|
|
2476
3001
|
}
|
|
2477
3002
|
async handleFailedEnvelope(envelope, destinationStr, channelId, reason) {
|
|
2478
|
-
logger$
|
|
3003
|
+
logger$d.warning("envelope_failed_due_to_channel_handshake_failure", {
|
|
2479
3004
|
envelope_id: envelope.id,
|
|
2480
3005
|
destination: destinationStr,
|
|
2481
3006
|
channel_id: channelId,
|
|
@@ -2483,14 +3008,14 @@ class ChannelEncryptionManager {
|
|
|
2483
3008
|
});
|
|
2484
3009
|
const frame = envelope.frame;
|
|
2485
3010
|
if (!this.isDataFrame(frame)) {
|
|
2486
|
-
logger$
|
|
3011
|
+
logger$d.debug("skipping_nack_for_non_dataframe", {
|
|
2487
3012
|
envelope_id: envelope.id,
|
|
2488
3013
|
frame_type: frame.type ?? typeof frame,
|
|
2489
3014
|
});
|
|
2490
3015
|
return;
|
|
2491
3016
|
}
|
|
2492
3017
|
if (!envelope.replyTo) {
|
|
2493
|
-
logger$
|
|
3018
|
+
logger$d.debug("skipping_nack_no_reply_to", { envelope_id: envelope.id });
|
|
2494
3019
|
return;
|
|
2495
3020
|
}
|
|
2496
3021
|
await this.sendDeliveryNack(envelope, `channel_handshake_failed: ${reason}`);
|
|
@@ -2498,17 +3023,17 @@ class ChannelEncryptionManager {
|
|
|
2498
3023
|
async sendDeliveryNack(envelope, failureReason) {
|
|
2499
3024
|
const node = this.nodeLike;
|
|
2500
3025
|
if (!node) {
|
|
2501
|
-
logger$
|
|
3026
|
+
logger$d.error("no_node_available_for_sending_delivery_nack");
|
|
2502
3027
|
return;
|
|
2503
3028
|
}
|
|
2504
3029
|
const envelopeFactory = node.envelopeFactory;
|
|
2505
3030
|
if (!envelopeFactory) {
|
|
2506
|
-
logger$
|
|
3031
|
+
logger$d.error("no_envelope_factory_available_for_delivery_nack");
|
|
2507
3032
|
return;
|
|
2508
3033
|
}
|
|
2509
3034
|
const replyTo = toFameAddress(envelope.replyTo ?? null);
|
|
2510
3035
|
if (!replyTo) {
|
|
2511
|
-
logger$
|
|
3036
|
+
logger$d.error("invalid_reply_to_for_delivery_nack", {
|
|
2512
3037
|
reply_to: envelope.replyTo,
|
|
2513
3038
|
});
|
|
2514
3039
|
return;
|
|
@@ -2525,7 +3050,7 @@ class ChannelEncryptionManager {
|
|
|
2525
3050
|
corrId: envelope.corrId ?? generateId(),
|
|
2526
3051
|
});
|
|
2527
3052
|
await this.deliverEnvelope(nackEnvelope);
|
|
2528
|
-
logger$
|
|
3053
|
+
logger$d.debug("delivered_delivery_nack", {
|
|
2529
3054
|
original_envelope_id: envelope.id,
|
|
2530
3055
|
nack_envelope_id: nackEnvelope.id,
|
|
2531
3056
|
});
|
|
@@ -2563,7 +3088,7 @@ class ChannelEncryptionManager {
|
|
|
2563
3088
|
await task();
|
|
2564
3089
|
}
|
|
2565
3090
|
catch (error) {
|
|
2566
|
-
logger$
|
|
3091
|
+
logger$d.error("async_task_failed", {
|
|
2567
3092
|
task_name: name,
|
|
2568
3093
|
error: error instanceof Error ? error.message : String(error),
|
|
2569
3094
|
});
|
|
@@ -2617,7 +3142,7 @@ class ChannelEncryptionManager {
|
|
|
2617
3142
|
}
|
|
2618
3143
|
}
|
|
2619
3144
|
|
|
2620
|
-
const logger$
|
|
3145
|
+
const logger$c = getLogger("naylence.fame.security.encryption.channel.channel_encryption_manager_factory");
|
|
2621
3146
|
const DEFAULT_SUPPORTED_ALGORITHMS = ["chacha20-poly1305-channel"];
|
|
2622
3147
|
const FACTORY_META$c = {
|
|
2623
3148
|
base: ENCRYPTION_MANAGER_FACTORY_BASE_TYPE,
|
|
@@ -2649,7 +3174,7 @@ class ChannelEncryptionManagerFactory extends EncryptionManagerFactory {
|
|
|
2649
3174
|
async create(_config, ...factoryArgs) {
|
|
2650
3175
|
const [dependencies] = factoryArgs;
|
|
2651
3176
|
const resolvedDependencies = this.resolveDependencies(dependencies);
|
|
2652
|
-
logger$
|
|
3177
|
+
logger$c.debug("creating_channel_encryption_manager", {
|
|
2653
3178
|
has_secure_channel_manager: Boolean(resolvedDependencies.secureChannelManager),
|
|
2654
3179
|
has_node_like: Boolean(resolvedDependencies.nodeLike),
|
|
2655
3180
|
has_task_spawner: Boolean(resolvedDependencies.taskSpawner),
|
|
@@ -2711,7 +3236,7 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
2711
3236
|
ChannelEncryptionManagerFactory: ChannelEncryptionManagerFactory
|
|
2712
3237
|
});
|
|
2713
3238
|
|
|
2714
|
-
const logger$
|
|
3239
|
+
const logger$b = getLogger("naylence.fame.security.encryption.default_secure_channel_manager");
|
|
2715
3240
|
const DEFAULT_ALGORITHM = "CHACHA20P1305";
|
|
2716
3241
|
const CHANNEL_KEY_LENGTH = 32;
|
|
2717
3242
|
const NONCE_PREFIX_LENGTH = 4;
|
|
@@ -2758,7 +3283,7 @@ class DefaultSecureChannelManager {
|
|
|
2758
3283
|
const privateKey = x25519.utils.randomSecretKey();
|
|
2759
3284
|
const publicKey = x25519.scalarMultBase(privateKey);
|
|
2760
3285
|
this.ephemeralKeys.set(channelId, privateKey);
|
|
2761
|
-
logger$
|
|
3286
|
+
logger$b.debug("generated_channel_open", { cid: channelId, algorithm });
|
|
2762
3287
|
return {
|
|
2763
3288
|
type: "SecureOpen",
|
|
2764
3289
|
cid: channelId,
|
|
@@ -2771,7 +3296,7 @@ class DefaultSecureChannelManager {
|
|
|
2771
3296
|
requireCryptoSupport();
|
|
2772
3297
|
const algorithm = frame.alg || DEFAULT_ALGORITHM;
|
|
2773
3298
|
if (!this.isSupportedAlgorithm(algorithm)) {
|
|
2774
|
-
logger$
|
|
3299
|
+
logger$b.warning("unsupported_channel_algorithm", {
|
|
2775
3300
|
cid: frame.cid,
|
|
2776
3301
|
alg: algorithm,
|
|
2777
3302
|
});
|
|
@@ -2789,7 +3314,7 @@ class DefaultSecureChannelManager {
|
|
|
2789
3314
|
peerPublicKey = decodeBase64(frame.ephPub);
|
|
2790
3315
|
}
|
|
2791
3316
|
catch (error) {
|
|
2792
|
-
logger$
|
|
3317
|
+
logger$b.warning("invalid_peer_public_key", {
|
|
2793
3318
|
cid: frame.cid,
|
|
2794
3319
|
error: error instanceof Error ? error.message : String(error),
|
|
2795
3320
|
});
|
|
@@ -2811,7 +3336,7 @@ class DefaultSecureChannelManager {
|
|
|
2811
3336
|
algorithm,
|
|
2812
3337
|
});
|
|
2813
3338
|
this.channelsMap.set(frame.cid, channelState);
|
|
2814
|
-
logger$
|
|
3339
|
+
logger$b.debug("channel_established", { cid: frame.cid, algorithm });
|
|
2815
3340
|
myPrivateKey.fill(0);
|
|
2816
3341
|
sharedSecret.fill(0);
|
|
2817
3342
|
return {
|
|
@@ -2825,7 +3350,7 @@ class DefaultSecureChannelManager {
|
|
|
2825
3350
|
async handleAcceptFrame(frame) {
|
|
2826
3351
|
requireCryptoSupport();
|
|
2827
3352
|
if (frame.ok === false) {
|
|
2828
|
-
logger$
|
|
3353
|
+
logger$b.warning("channel_rejected", {
|
|
2829
3354
|
cid: frame.cid,
|
|
2830
3355
|
error: frame.reason,
|
|
2831
3356
|
});
|
|
@@ -2834,7 +3359,7 @@ class DefaultSecureChannelManager {
|
|
|
2834
3359
|
}
|
|
2835
3360
|
const privateKey = this.ephemeralKeys.get(frame.cid);
|
|
2836
3361
|
if (!privateKey) {
|
|
2837
|
-
logger$
|
|
3362
|
+
logger$b.error("no_ephemeral_key", { cid: frame.cid });
|
|
2838
3363
|
return false;
|
|
2839
3364
|
}
|
|
2840
3365
|
let peerPublicKey;
|
|
@@ -2842,7 +3367,7 @@ class DefaultSecureChannelManager {
|
|
|
2842
3367
|
peerPublicKey = decodeBase64(frame.ephPub);
|
|
2843
3368
|
}
|
|
2844
3369
|
catch (error) {
|
|
2845
|
-
logger$
|
|
3370
|
+
logger$b.warning("invalid_accept_public_key", {
|
|
2846
3371
|
cid: frame.cid,
|
|
2847
3372
|
error: error instanceof Error ? error.message : String(error),
|
|
2848
3373
|
});
|
|
@@ -2857,17 +3382,17 @@ class DefaultSecureChannelManager {
|
|
|
2857
3382
|
algorithm,
|
|
2858
3383
|
});
|
|
2859
3384
|
this.channelsMap.set(frame.cid, channelState);
|
|
2860
|
-
logger$
|
|
3385
|
+
logger$b.debug("channel_completed", { cid: frame.cid, algorithm });
|
|
2861
3386
|
sharedSecret.fill(0);
|
|
2862
3387
|
this.cleanupEphemeralKey(frame.cid);
|
|
2863
3388
|
return true;
|
|
2864
3389
|
}
|
|
2865
3390
|
handleCloseFrame(frame) {
|
|
2866
3391
|
if (this.channelsMap.delete(frame.cid)) {
|
|
2867
|
-
logger$
|
|
3392
|
+
logger$b.debug("channel_closed", { cid: frame.cid, reason: frame.reason });
|
|
2868
3393
|
}
|
|
2869
3394
|
else {
|
|
2870
|
-
logger$
|
|
3395
|
+
logger$b.warning("close_unknown_channel", { cid: frame.cid });
|
|
2871
3396
|
}
|
|
2872
3397
|
this.cleanupEphemeralKey(frame.cid);
|
|
2873
3398
|
}
|
|
@@ -2894,7 +3419,7 @@ class DefaultSecureChannelManager {
|
|
|
2894
3419
|
}
|
|
2895
3420
|
closeChannel(channelId, reason = "User requested") {
|
|
2896
3421
|
if (this.channelsMap.delete(channelId)) {
|
|
2897
|
-
logger$
|
|
3422
|
+
logger$b.debug("channel_closed_by_user", { cid: channelId, reason });
|
|
2898
3423
|
}
|
|
2899
3424
|
this.cleanupEphemeralKey(channelId);
|
|
2900
3425
|
return {
|
|
@@ -2911,7 +3436,7 @@ class DefaultSecureChannelManager {
|
|
|
2911
3436
|
this.channelsMap.delete(channelId);
|
|
2912
3437
|
this.cleanupEphemeralKey(channelId);
|
|
2913
3438
|
removed += 1;
|
|
2914
|
-
logger$
|
|
3439
|
+
logger$b.debug("channel_expired_cleanup", { cid: channelId });
|
|
2915
3440
|
}
|
|
2916
3441
|
}
|
|
2917
3442
|
return removed;
|
|
@@ -2940,7 +3465,7 @@ class DefaultSecureChannelManager {
|
|
|
2940
3465
|
if (channelId.startsWith(prefix)) {
|
|
2941
3466
|
if (this.removeChannel(channelId)) {
|
|
2942
3467
|
removed += 1;
|
|
2943
|
-
logger$
|
|
3468
|
+
logger$b.debug("removed_channel_for_destination", {
|
|
2944
3469
|
channel_id: channelId,
|
|
2945
3470
|
destination,
|
|
2946
3471
|
});
|
|
@@ -2948,7 +3473,7 @@ class DefaultSecureChannelManager {
|
|
|
2948
3473
|
}
|
|
2949
3474
|
}
|
|
2950
3475
|
if (removed > 0) {
|
|
2951
|
-
logger$
|
|
3476
|
+
logger$b.info("cleanup_channels_for_destination", {
|
|
2952
3477
|
destination,
|
|
2953
3478
|
channels_removed: removed,
|
|
2954
3479
|
});
|
|
@@ -3020,187 +3545,26 @@ class DefaultSecureChannelManagerFactory extends SecureChannelManagerFactory {
|
|
|
3020
3545
|
}
|
|
3021
3546
|
return undefined;
|
|
3022
3547
|
}
|
|
3023
|
-
toPositiveNumber(value) {
|
|
3024
|
-
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
3025
|
-
return value;
|
|
3026
|
-
}
|
|
3027
|
-
if (typeof value === "string" && value.trim() !== "") {
|
|
3028
|
-
const parsed = Number(value);
|
|
3029
|
-
if (Number.isFinite(parsed) && parsed > 0) {
|
|
3030
|
-
return parsed;
|
|
3031
|
-
}
|
|
3032
|
-
}
|
|
3033
|
-
return undefined;
|
|
3034
|
-
}
|
|
3035
|
-
}
|
|
3036
|
-
|
|
3037
|
-
var defaultSecureChannelManagerFactory = /*#__PURE__*/Object.freeze({
|
|
3038
|
-
__proto__: null,
|
|
3039
|
-
DefaultSecureChannelManagerFactory: DefaultSecureChannelManagerFactory,
|
|
3040
|
-
FACTORY_META: FACTORY_META$b,
|
|
3041
|
-
default: DefaultSecureChannelManagerFactory
|
|
3042
|
-
});
|
|
3043
|
-
|
|
3044
|
-
const logger$b = getLogger("naylence.fame.security.encryption.encryption_manager_registry");
|
|
3045
|
-
class EncryptionManagerFactoryRegistry {
|
|
3046
|
-
constructor(autoDiscover = true) {
|
|
3047
|
-
this.factories = [];
|
|
3048
|
-
this.algorithmToFactory = new Map();
|
|
3049
|
-
this.typeToFactories = new Map();
|
|
3050
|
-
this.factorySet = new Set();
|
|
3051
|
-
this.autoDiscoveredFactories = new Set();
|
|
3052
|
-
this.autoDiscovered = false;
|
|
3053
|
-
if (autoDiscover) {
|
|
3054
|
-
this.autoDiscoverFactories();
|
|
3055
|
-
}
|
|
3056
|
-
}
|
|
3057
|
-
autoDiscoverFactories() {
|
|
3058
|
-
if (this.autoDiscovered) {
|
|
3059
|
-
return;
|
|
3060
|
-
}
|
|
3061
|
-
try {
|
|
3062
|
-
const extensionInfos = ExtensionManager.getExtensionsByType(ENCRYPTION_MANAGER_FACTORY_BASE_TYPE);
|
|
3063
|
-
let registeredCount = 0;
|
|
3064
|
-
for (const [factoryName, info] of extensionInfos) {
|
|
3065
|
-
if (factoryName === "CompositeEncryptionManager") {
|
|
3066
|
-
logger$b.debug("skipping_composite_factory_to_avoid_circular_dependency", {
|
|
3067
|
-
factory_name: factoryName,
|
|
3068
|
-
});
|
|
3069
|
-
continue;
|
|
3070
|
-
}
|
|
3071
|
-
try {
|
|
3072
|
-
const factoryInstance = (info.instance ??
|
|
3073
|
-
ExtensionManager.getGlobalFactory(ENCRYPTION_MANAGER_FACTORY_BASE_TYPE, factoryName));
|
|
3074
|
-
this.registerFactory(factoryInstance, { autoDiscovered: true });
|
|
3075
|
-
registeredCount += 1;
|
|
3076
|
-
logger$b.debug("auto_discovered_factory", {
|
|
3077
|
-
factory_name: factoryName,
|
|
3078
|
-
factory_class: factoryInstance.constructor.name,
|
|
3079
|
-
algorithms: factoryInstance.getSupportedAlgorithms(),
|
|
3080
|
-
encryption_type: factoryInstance.getEncryptionType(),
|
|
3081
|
-
priority: factoryInstance.getPriority(),
|
|
3082
|
-
});
|
|
3083
|
-
}
|
|
3084
|
-
catch (error) {
|
|
3085
|
-
logger$b.warning("failed_to_auto_register_factory", {
|
|
3086
|
-
factory_name: factoryName,
|
|
3087
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3088
|
-
});
|
|
3089
|
-
}
|
|
3090
|
-
}
|
|
3091
|
-
this.autoDiscovered = true;
|
|
3092
|
-
logger$b.debug("completed_auto_discovery", {
|
|
3093
|
-
registered_factories: registeredCount,
|
|
3094
|
-
total_discovered: extensionInfos.size,
|
|
3095
|
-
skipped_composite: true,
|
|
3096
|
-
});
|
|
3097
|
-
}
|
|
3098
|
-
catch (error) {
|
|
3099
|
-
logger$b.warning("failed_auto_discovery_of_factories", {
|
|
3100
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3101
|
-
});
|
|
3102
|
-
}
|
|
3103
|
-
}
|
|
3104
|
-
registerFactory(factory, options = {}) {
|
|
3105
|
-
if (this.factorySet.has(factory)) {
|
|
3106
|
-
return;
|
|
3107
|
-
}
|
|
3108
|
-
this.factorySet.add(factory);
|
|
3109
|
-
this.factories.push(factory);
|
|
3110
|
-
if (options.autoDiscovered) {
|
|
3111
|
-
this.autoDiscoveredFactories.add(factory);
|
|
3112
|
-
}
|
|
3113
|
-
for (const algorithm of factory.getSupportedAlgorithms()) {
|
|
3114
|
-
const existing = this.algorithmToFactory.get(algorithm);
|
|
3115
|
-
if (!existing || factory.getPriority() > existing.getPriority()) {
|
|
3116
|
-
this.algorithmToFactory.set(algorithm, factory);
|
|
3117
|
-
logger$b.debug("registered_algorithm_mapping", {
|
|
3118
|
-
algorithm,
|
|
3119
|
-
factory: factory.constructor.name,
|
|
3120
|
-
priority: factory.getPriority(),
|
|
3121
|
-
});
|
|
3122
|
-
}
|
|
3123
|
-
}
|
|
3124
|
-
const encryptionType = factory.getEncryptionType();
|
|
3125
|
-
const typeFactories = this.typeToFactories.get(encryptionType) ?? [];
|
|
3126
|
-
typeFactories.push(factory);
|
|
3127
|
-
typeFactories.sort((a, b) => b.getPriority() - a.getPriority());
|
|
3128
|
-
this.typeToFactories.set(encryptionType, typeFactories);
|
|
3129
|
-
logger$b.debug("registered_encryption_manager_factory", {
|
|
3130
|
-
factory: factory.constructor.name,
|
|
3131
|
-
encryption_type: encryptionType,
|
|
3132
|
-
algorithms: factory.getSupportedAlgorithms(),
|
|
3133
|
-
priority: factory.getPriority(),
|
|
3134
|
-
auto_discovered: options.autoDiscovered ?? false,
|
|
3135
|
-
});
|
|
3136
|
-
}
|
|
3137
|
-
getFactoryForAlgorithm(algorithm) {
|
|
3138
|
-
this.ensureAutoDiscovery();
|
|
3139
|
-
return this.algorithmToFactory.get(algorithm);
|
|
3140
|
-
}
|
|
3141
|
-
getFactoryForOptions(opts) {
|
|
3142
|
-
this.ensureAutoDiscovery();
|
|
3143
|
-
for (const factory of this.factories) {
|
|
3144
|
-
if (factory.supportsOptions(opts ?? undefined)) {
|
|
3145
|
-
logger$b.debug("found_factory_for_options", {
|
|
3146
|
-
factory: factory.constructor.name,
|
|
3147
|
-
encryption_type: factory.getEncryptionType(),
|
|
3148
|
-
});
|
|
3149
|
-
return factory;
|
|
3150
|
-
}
|
|
3151
|
-
}
|
|
3152
|
-
logger$b.debug("no_factory_found_for_options", { opts });
|
|
3153
|
-
return undefined;
|
|
3154
|
-
}
|
|
3155
|
-
getFactoriesByType(encryptionType) {
|
|
3156
|
-
this.ensureAutoDiscovery();
|
|
3157
|
-
return this.typeToFactories.get(encryptionType) ?? [];
|
|
3158
|
-
}
|
|
3159
|
-
getAllSupportedAlgorithms() {
|
|
3160
|
-
this.ensureAutoDiscovery();
|
|
3161
|
-
return Array.from(this.algorithmToFactory.keys());
|
|
3162
|
-
}
|
|
3163
|
-
getRegistryInfo() {
|
|
3164
|
-
return {
|
|
3165
|
-
totalFactories: this.factories.length,
|
|
3166
|
-
autoDiscovered: this.autoDiscovered,
|
|
3167
|
-
algorithmMappings: Object.fromEntries(Array.from(this.algorithmToFactory.entries()).map(([algorithm, factory]) => [algorithm, factory.constructor.name])),
|
|
3168
|
-
typeMappings: Object.fromEntries(Array.from(this.typeToFactories.entries()).map(([encType, factories]) => [
|
|
3169
|
-
encType,
|
|
3170
|
-
factories.map((factory) => factory.constructor.name),
|
|
3171
|
-
])),
|
|
3172
|
-
};
|
|
3173
|
-
}
|
|
3174
|
-
forceRediscovery() {
|
|
3175
|
-
const manualFactories = this.factories.filter((factory) => !this.autoDiscoveredFactories.has(factory));
|
|
3176
|
-
this.autoDiscovered = false;
|
|
3177
|
-
this.algorithmToFactory.clear();
|
|
3178
|
-
this.typeToFactories.clear();
|
|
3179
|
-
this.factories.length = 0;
|
|
3180
|
-
this.factorySet.clear();
|
|
3181
|
-
this.autoDiscoveredFactories.clear();
|
|
3182
|
-
for (const factory of manualFactories) {
|
|
3183
|
-
this.registerFactory(factory);
|
|
3548
|
+
toPositiveNumber(value) {
|
|
3549
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
3550
|
+
return value;
|
|
3184
3551
|
}
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
ensureInitialized() {
|
|
3191
|
-
this.ensureAutoDiscovery();
|
|
3192
|
-
}
|
|
3193
|
-
ensureAutoDiscovery() {
|
|
3194
|
-
if (!this.autoDiscovered) {
|
|
3195
|
-
this.autoDiscoverFactories();
|
|
3552
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
3553
|
+
const parsed = Number(value);
|
|
3554
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
3555
|
+
return parsed;
|
|
3556
|
+
}
|
|
3196
3557
|
}
|
|
3558
|
+
return undefined;
|
|
3197
3559
|
}
|
|
3198
3560
|
}
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3561
|
+
|
|
3562
|
+
var defaultSecureChannelManagerFactory = /*#__PURE__*/Object.freeze({
|
|
3563
|
+
__proto__: null,
|
|
3564
|
+
DefaultSecureChannelManagerFactory: DefaultSecureChannelManagerFactory,
|
|
3565
|
+
FACTORY_META: FACTORY_META$b,
|
|
3566
|
+
default: DefaultSecureChannelManagerFactory
|
|
3567
|
+
});
|
|
3204
3568
|
|
|
3205
3569
|
const logger$a = getLogger("naylence.fame.security.encryption.composite_encryption_manager");
|
|
3206
3570
|
const DEFAULT_SEALED_ALGORITHMS = [
|
|
@@ -5115,1275 +5479,953 @@ class AFTLoadBalancerStickinessManager extends BaseNodeEventListener {
|
|
|
5115
5479
|
}
|
|
5116
5480
|
const verification = await this.verifier.verify(aftToken, envelope.sid ?? undefined);
|
|
5117
5481
|
if (!verification.valid) {
|
|
5118
|
-
this.metrics.verifyFailures += 1;
|
|
5119
|
-
logger$4.warning("aft_verification_failed", {
|
|
5120
|
-
envelope_id: envelope.id,
|
|
5121
|
-
replica_id: replicaId,
|
|
5122
|
-
error: verification.error,
|
|
5123
|
-
});
|
|
5124
|
-
return null;
|
|
5125
|
-
}
|
|
5126
|
-
this.storeAssociation(aftToken, {
|
|
5127
|
-
replicaId,
|
|
5128
|
-
token: aftToken,
|
|
5129
|
-
sid: verification.sid ?? "",
|
|
5130
|
-
exp: verification.exp ?? Math.floor(Date.now() / 1000) + this.defaultTtlSec,
|
|
5131
|
-
trustLevel: verification.trustLevel,
|
|
5132
|
-
scope: verification.scope ?? null,
|
|
5133
|
-
clientSid: verification.clientSid ?? null,
|
|
5134
|
-
});
|
|
5135
|
-
if (verification.clientSid) {
|
|
5136
|
-
this.sidCache.set(verification.clientSid, replicaId);
|
|
5137
|
-
logger$4.debug("sid_cache_updated", {
|
|
5138
|
-
envelope_id: envelope.id,
|
|
5139
|
-
client_sid: verification.clientSid,
|
|
5140
|
-
replica_id: replicaId,
|
|
5141
|
-
});
|
|
5142
|
-
}
|
|
5143
|
-
this.metrics.associationsCreated += 1;
|
|
5144
|
-
logger$4.debug("aft_association_created", {
|
|
5145
|
-
envelope_id: envelope.id,
|
|
5146
|
-
replica_id: replicaId,
|
|
5147
|
-
sid: verification.sid,
|
|
5148
|
-
exp: verification.exp,
|
|
5149
|
-
trust_level: verification.trustLevel,
|
|
5150
|
-
scope: verification.scope,
|
|
5151
|
-
});
|
|
5152
|
-
return this.config.clientEcho ? aftToken : null;
|
|
5153
|
-
}
|
|
5154
|
-
getStickyReplicaSegment(envelope, segments) {
|
|
5155
|
-
if (!this.config.enabled) {
|
|
5156
|
-
logger$4.debug("stickiness_disabled", { envelope_id: envelope.id });
|
|
5157
|
-
return null;
|
|
5158
|
-
}
|
|
5159
|
-
if (envelope.aft) {
|
|
5160
|
-
const replicaId = this.routeByAft(envelope.aft, envelope);
|
|
5161
|
-
if (replicaId) {
|
|
5162
|
-
this.metrics.cacheHits += 1;
|
|
5163
|
-
logger$4.debug("aft_routed_envelope", {
|
|
5164
|
-
envelope_id: envelope.id,
|
|
5165
|
-
replica_id: replicaId,
|
|
5166
|
-
routing_type: "aft_direct",
|
|
5167
|
-
});
|
|
5168
|
-
return replicaId;
|
|
5169
|
-
}
|
|
5170
|
-
}
|
|
5171
|
-
if (envelope.sid) {
|
|
5172
|
-
const cachedReplica = this.sidCache.get(envelope.sid);
|
|
5173
|
-
if (cachedReplica) {
|
|
5174
|
-
if (this.config.securityLevel === StickinessMode.SID_ONLY) {
|
|
5175
|
-
this.metrics.cacheHits += 1;
|
|
5176
|
-
logger$4.debug("sid_cache_routed_envelope", {
|
|
5177
|
-
envelope_id: envelope.id,
|
|
5178
|
-
replica_id: cachedReplica,
|
|
5179
|
-
sid: envelope.sid,
|
|
5180
|
-
routing_type: "sid_only",
|
|
5181
|
-
});
|
|
5182
|
-
return cachedReplica;
|
|
5183
|
-
}
|
|
5184
|
-
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5185
|
-
if (association.replicaId === cachedReplica &&
|
|
5186
|
-
!association.isExpired()) {
|
|
5187
|
-
envelope.aft = token;
|
|
5188
|
-
this.metrics.cacheHits += 1;
|
|
5189
|
-
logger$4.debug("sid_cache_routed_envelope", {
|
|
5190
|
-
envelope_id: envelope.id,
|
|
5191
|
-
replica_id: cachedReplica,
|
|
5192
|
-
sid: envelope.sid,
|
|
5193
|
-
routing_type: "sid_cache_with_aft",
|
|
5194
|
-
});
|
|
5195
|
-
return cachedReplica;
|
|
5196
|
-
}
|
|
5197
|
-
}
|
|
5198
|
-
this.metrics.cacheHits += 1;
|
|
5199
|
-
logger$4.debug("sid_cache_routed_envelope", {
|
|
5200
|
-
envelope_id: envelope.id,
|
|
5201
|
-
replica_id: cachedReplica,
|
|
5202
|
-
sid: envelope.sid,
|
|
5203
|
-
routing_type: "sid_cache_direct",
|
|
5204
|
-
});
|
|
5205
|
-
return cachedReplica;
|
|
5206
|
-
}
|
|
5207
|
-
logger$4.debug("no_cached_replica_for_sid", {
|
|
5208
|
-
envelope_id: envelope.id,
|
|
5209
|
-
sid: envelope.sid,
|
|
5210
|
-
});
|
|
5211
|
-
}
|
|
5212
|
-
if (envelope.sid && Array.isArray(segments) && segments.length > 0) {
|
|
5213
|
-
const index = computeDeterministicIndex(envelope.sid, segments.length);
|
|
5214
|
-
const chosen = segments[index];
|
|
5215
|
-
this.metrics.cacheHits += 1;
|
|
5216
|
-
logger$4.debug("sid_based_deterministic_choice", {
|
|
5217
|
-
envelope_id: envelope.id,
|
|
5218
|
-
sid: envelope.sid,
|
|
5219
|
-
chosen,
|
|
5220
|
-
routing_type: "sid_deterministic",
|
|
5221
|
-
});
|
|
5222
|
-
return chosen;
|
|
5223
|
-
}
|
|
5224
|
-
this.metrics.cacheMisses += 1;
|
|
5225
|
-
logger$4.debug("no_stickiness_routing", {
|
|
5226
|
-
envelope_id: envelope.id,
|
|
5227
|
-
has_aft: Boolean(envelope.aft),
|
|
5228
|
-
has_sid: Boolean(envelope.sid),
|
|
5229
|
-
});
|
|
5230
|
-
return null;
|
|
5231
|
-
}
|
|
5232
|
-
cleanupExpiredAssociations() {
|
|
5233
|
-
const now = Math.floor(Date.now() / 1000);
|
|
5234
|
-
const expiredTokens = [];
|
|
5235
|
-
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5236
|
-
if (association.isExpired(now)) {
|
|
5237
|
-
expiredTokens.push(token);
|
|
5238
|
-
}
|
|
5239
|
-
}
|
|
5240
|
-
for (const token of expiredTokens) {
|
|
5241
|
-
this.removeAssociation(token);
|
|
5242
|
-
}
|
|
5243
|
-
if (expiredTokens.length > 0) {
|
|
5244
|
-
this.metrics.associationsExpired += expiredTokens.length;
|
|
5245
|
-
logger$4.debug("cleaned_expired_associations", {
|
|
5246
|
-
count: expiredTokens.length,
|
|
5247
|
-
});
|
|
5248
|
-
}
|
|
5249
|
-
}
|
|
5250
|
-
replicaLeft(replicaId) {
|
|
5251
|
-
const tokensToRemove = [];
|
|
5252
|
-
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5253
|
-
if (association.replicaId === replicaId) {
|
|
5254
|
-
tokensToRemove.push(token);
|
|
5255
|
-
}
|
|
5256
|
-
}
|
|
5257
|
-
for (const token of tokensToRemove) {
|
|
5258
|
-
this.removeAssociation(token);
|
|
5259
|
-
}
|
|
5260
|
-
if (tokensToRemove.length > 0) {
|
|
5261
|
-
logger$4.debug("removed_associations_for_departed_replica", {
|
|
5262
|
-
replica_id: replicaId,
|
|
5263
|
-
count: tokensToRemove.length,
|
|
5264
|
-
});
|
|
5265
|
-
}
|
|
5266
|
-
}
|
|
5267
|
-
handleReplicaLeft(replicaId) {
|
|
5268
|
-
this.replicaLeft(replicaId);
|
|
5269
|
-
logger$4.debug("stickiness_replica_cleanup", { replica_id: replicaId });
|
|
5270
|
-
}
|
|
5271
|
-
getMetrics() {
|
|
5272
|
-
return {
|
|
5273
|
-
...this.metrics,
|
|
5274
|
-
cacheSize: this.aftAssociations.size,
|
|
5275
|
-
sidCacheSize: this.sidCache.size,
|
|
5276
|
-
};
|
|
5277
|
-
}
|
|
5278
|
-
getAssociations() {
|
|
5279
|
-
const result = {};
|
|
5280
|
-
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5281
|
-
result[token] = {
|
|
5282
|
-
replica_id: association.replicaId,
|
|
5283
|
-
sid: association.sid,
|
|
5284
|
-
client_sid: association.clientSid,
|
|
5285
|
-
exp: association.exp,
|
|
5286
|
-
trust_level: association.trustLevel,
|
|
5287
|
-
scope: association.scope,
|
|
5288
|
-
created_at: association.createdAt,
|
|
5289
|
-
expired: association.isExpired(),
|
|
5290
|
-
};
|
|
5291
|
-
}
|
|
5292
|
-
return result;
|
|
5293
|
-
}
|
|
5294
|
-
getStickinessMetrics() {
|
|
5295
|
-
return this.getMetrics();
|
|
5296
|
-
}
|
|
5297
|
-
logMetrics() {
|
|
5298
|
-
const hits = this.metrics.cacheHits;
|
|
5299
|
-
const misses = this.metrics.cacheMisses;
|
|
5300
|
-
const total = hits + misses;
|
|
5301
|
-
const hitRate = total > 0 ? Math.round((hits / total) * 10000) / 100 : 0;
|
|
5302
|
-
logger$4.info("stickiness_metrics_report", {
|
|
5303
|
-
enabled: this.config.enabled,
|
|
5304
|
-
security_level: this.config.securityLevel,
|
|
5305
|
-
cache_hits: hits,
|
|
5306
|
-
cache_misses: misses,
|
|
5307
|
-
verify_failures: this.metrics.verifyFailures,
|
|
5308
|
-
associations_created: this.metrics.associationsCreated,
|
|
5309
|
-
associations_expired: this.metrics.associationsExpired,
|
|
5310
|
-
active_associations: this.aftAssociations.size,
|
|
5311
|
-
sid_cache_entries: this.sidCache.size,
|
|
5312
|
-
hit_rate: hitRate,
|
|
5482
|
+
this.metrics.verifyFailures += 1;
|
|
5483
|
+
logger$4.warning("aft_verification_failed", {
|
|
5484
|
+
envelope_id: envelope.id,
|
|
5485
|
+
replica_id: replicaId,
|
|
5486
|
+
error: verification.error,
|
|
5487
|
+
});
|
|
5488
|
+
return null;
|
|
5489
|
+
}
|
|
5490
|
+
this.storeAssociation(aftToken, {
|
|
5491
|
+
replicaId,
|
|
5492
|
+
token: aftToken,
|
|
5493
|
+
sid: verification.sid ?? "",
|
|
5494
|
+
exp: verification.exp ?? Math.floor(Date.now() / 1000) + this.defaultTtlSec,
|
|
5495
|
+
trustLevel: verification.trustLevel,
|
|
5496
|
+
scope: verification.scope ?? null,
|
|
5497
|
+
clientSid: verification.clientSid ?? null,
|
|
5313
5498
|
});
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5499
|
+
if (verification.clientSid) {
|
|
5500
|
+
this.sidCache.set(verification.clientSid, replicaId);
|
|
5501
|
+
logger$4.debug("sid_cache_updated", {
|
|
5502
|
+
envelope_id: envelope.id,
|
|
5503
|
+
client_sid: verification.clientSid,
|
|
5504
|
+
replica_id: replicaId,
|
|
5505
|
+
});
|
|
5506
|
+
}
|
|
5507
|
+
this.metrics.associationsCreated += 1;
|
|
5508
|
+
logger$4.debug("aft_association_created", {
|
|
5317
5509
|
envelope_id: envelope.id,
|
|
5318
|
-
|
|
5319
|
-
|
|
5510
|
+
replica_id: replicaId,
|
|
5511
|
+
sid: verification.sid,
|
|
5512
|
+
exp: verification.exp,
|
|
5513
|
+
trust_level: verification.trustLevel,
|
|
5514
|
+
scope: verification.scope,
|
|
5320
5515
|
});
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5516
|
+
return this.config.clientEcho ? aftToken : null;
|
|
5517
|
+
}
|
|
5518
|
+
getStickyReplicaSegment(envelope, segments) {
|
|
5519
|
+
if (!this.config.enabled) {
|
|
5520
|
+
logger$4.debug("stickiness_disabled", { envelope_id: envelope.id });
|
|
5521
|
+
return null;
|
|
5522
|
+
}
|
|
5523
|
+
if (envelope.aft) {
|
|
5524
|
+
const replicaId = this.routeByAft(envelope.aft, envelope);
|
|
5525
|
+
if (replicaId) {
|
|
5526
|
+
this.metrics.cacheHits += 1;
|
|
5527
|
+
logger$4.debug("aft_routed_envelope", {
|
|
5325
5528
|
envelope_id: envelope.id,
|
|
5326
|
-
|
|
5529
|
+
replica_id: replicaId,
|
|
5530
|
+
routing_type: "aft_direct",
|
|
5327
5531
|
});
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5532
|
+
return replicaId;
|
|
5533
|
+
}
|
|
5534
|
+
}
|
|
5535
|
+
if (envelope.sid) {
|
|
5536
|
+
const cachedReplica = this.sidCache.get(envelope.sid);
|
|
5537
|
+
if (cachedReplica) {
|
|
5538
|
+
if (this.config.securityLevel === StickinessMode.SID_ONLY) {
|
|
5539
|
+
this.metrics.cacheHits += 1;
|
|
5540
|
+
logger$4.debug("sid_cache_routed_envelope", {
|
|
5333
5541
|
envelope_id: envelope.id,
|
|
5542
|
+
replica_id: cachedReplica,
|
|
5334
5543
|
sid: envelope.sid,
|
|
5335
|
-
|
|
5336
|
-
});
|
|
5337
|
-
}
|
|
5338
|
-
const hadInstruction = Boolean(extractAftInstruction(envelope));
|
|
5339
|
-
const token = await this.handleOutboundEnvelope(envelope, sourceRoute);
|
|
5340
|
-
if (hadInstruction) {
|
|
5341
|
-
logger$4.debug("processed_aft_setter_instruction", {
|
|
5342
|
-
envelope_id: envelope.id,
|
|
5343
|
-
source_route: sourceRoute,
|
|
5344
|
-
client_echo: Boolean(token),
|
|
5544
|
+
routing_type: "sid_only",
|
|
5345
5545
|
});
|
|
5546
|
+
return cachedReplica;
|
|
5346
5547
|
}
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5548
|
+
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5549
|
+
if (association.replicaId === cachedReplica &&
|
|
5550
|
+
!association.isExpired()) {
|
|
5551
|
+
envelope.aft = token;
|
|
5552
|
+
this.metrics.cacheHits += 1;
|
|
5553
|
+
logger$4.debug("sid_cache_routed_envelope", {
|
|
5554
|
+
envelope_id: envelope.id,
|
|
5555
|
+
replica_id: cachedReplica,
|
|
5556
|
+
sid: envelope.sid,
|
|
5557
|
+
routing_type: "sid_cache_with_aft",
|
|
5558
|
+
});
|
|
5559
|
+
return cachedReplica;
|
|
5560
|
+
}
|
|
5352
5561
|
}
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
logger$4.debug("downstream_envelope_without_source_route", {
|
|
5562
|
+
this.metrics.cacheHits += 1;
|
|
5563
|
+
logger$4.debug("sid_cache_routed_envelope", {
|
|
5356
5564
|
envelope_id: envelope.id,
|
|
5565
|
+
replica_id: cachedReplica,
|
|
5566
|
+
sid: envelope.sid,
|
|
5567
|
+
routing_type: "sid_cache_direct",
|
|
5357
5568
|
});
|
|
5569
|
+
return cachedReplica;
|
|
5358
5570
|
}
|
|
5359
|
-
|
|
5360
|
-
else {
|
|
5361
|
-
logger$4.debug("envelope_not_from_downstream", {
|
|
5571
|
+
logger$4.debug("no_cached_replica_for_sid", {
|
|
5362
5572
|
envelope_id: envelope.id,
|
|
5573
|
+
sid: envelope.sid,
|
|
5363
5574
|
});
|
|
5364
5575
|
}
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
}
|
|
5371
|
-
const association = new AFTAssociation(data);
|
|
5372
|
-
this.aftAssociations.set(token, association);
|
|
5373
|
-
while (this.aftAssociations.size > this.cacheMax) {
|
|
5374
|
-
const oldest = this.aftAssociations.keys().next();
|
|
5375
|
-
if (oldest.done) {
|
|
5376
|
-
break;
|
|
5377
|
-
}
|
|
5378
|
-
const oldestToken = oldest.value;
|
|
5379
|
-
this.removeAssociation(oldestToken);
|
|
5380
|
-
}
|
|
5381
|
-
}
|
|
5382
|
-
removeAssociation(token) {
|
|
5383
|
-
this.aftAssociations.delete(token);
|
|
5384
|
-
for (const [sid, cachedToken] of this.sidCache.entries()) {
|
|
5385
|
-
if (cachedToken === token) {
|
|
5386
|
-
this.sidCache.delete(sid);
|
|
5387
|
-
}
|
|
5388
|
-
}
|
|
5389
|
-
}
|
|
5390
|
-
routeByAft(token, envelope) {
|
|
5391
|
-
const association = this.aftAssociations.get(token);
|
|
5392
|
-
if (!association) {
|
|
5393
|
-
return null;
|
|
5394
|
-
}
|
|
5395
|
-
if (association.isExpired()) {
|
|
5396
|
-
this.metrics.associationsExpired += 1;
|
|
5397
|
-
this.removeAssociation(token);
|
|
5398
|
-
return null;
|
|
5399
|
-
}
|
|
5400
|
-
if (this.verifier.securityLevel === StickinessMode.STRICT &&
|
|
5401
|
-
association.isLowTrust()) {
|
|
5402
|
-
logger$4.warning("rejecting_low_trust_association", {
|
|
5576
|
+
if (envelope.sid && Array.isArray(segments) && segments.length > 0) {
|
|
5577
|
+
const index = computeDeterministicIndex(envelope.sid, segments.length);
|
|
5578
|
+
const chosen = segments[index];
|
|
5579
|
+
this.metrics.cacheHits += 1;
|
|
5580
|
+
logger$4.debug("sid_based_deterministic_choice", {
|
|
5403
5581
|
envelope_id: envelope.id,
|
|
5404
|
-
|
|
5405
|
-
|
|
5582
|
+
sid: envelope.sid,
|
|
5583
|
+
chosen,
|
|
5584
|
+
routing_type: "sid_deterministic",
|
|
5406
5585
|
});
|
|
5407
|
-
return
|
|
5586
|
+
return chosen;
|
|
5408
5587
|
}
|
|
5409
|
-
this.
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
if (!envelope.meta) {
|
|
5588
|
+
this.metrics.cacheMisses += 1;
|
|
5589
|
+
logger$4.debug("no_stickiness_routing", {
|
|
5590
|
+
envelope_id: envelope.id,
|
|
5591
|
+
has_aft: Boolean(envelope.aft),
|
|
5592
|
+
has_sid: Boolean(envelope.sid),
|
|
5593
|
+
});
|
|
5416
5594
|
return null;
|
|
5417
5595
|
}
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
const
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
return 0;
|
|
5434
|
-
}
|
|
5435
|
-
let hash = 0;
|
|
5436
|
-
for (let i = 0; i < key.length; i += 1) {
|
|
5437
|
-
hash = (hash * 31 + key.charCodeAt(i)) >>> 0;
|
|
5438
|
-
}
|
|
5439
|
-
return hash % modulo;
|
|
5440
|
-
}
|
|
5441
|
-
|
|
5442
|
-
const FACTORY_META$6 = {
|
|
5443
|
-
base: LOAD_BALANCER_STICKINESS_MANAGER_FACTORY_BASE_TYPE,
|
|
5444
|
-
key: "AFTLoadBalancerStickinessManager",
|
|
5445
|
-
};
|
|
5446
|
-
const DEFAULT_VALUES$1 = {
|
|
5447
|
-
enabled: true,
|
|
5448
|
-
clientEcho: false,
|
|
5449
|
-
defaultTtlSec: 30,
|
|
5450
|
-
cacheMax: 100000,
|
|
5451
|
-
securityLevel: StickinessMode.SIGNED_OPTIONAL,
|
|
5452
|
-
maxTtlSec: 7200,
|
|
5453
|
-
};
|
|
5454
|
-
function toBoolean(value, fallback) {
|
|
5455
|
-
return typeof value === "boolean" ? value : fallback;
|
|
5456
|
-
}
|
|
5457
|
-
function toNumber(value, fallback) {
|
|
5458
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5459
|
-
return value;
|
|
5460
|
-
}
|
|
5461
|
-
return fallback;
|
|
5462
|
-
}
|
|
5463
|
-
function normalizeConfig$4(config) {
|
|
5464
|
-
const record = (config ?? {});
|
|
5465
|
-
const normalizedSecurity = record.securityLevel
|
|
5466
|
-
? normalizeStickinessMode(record.securityLevel)
|
|
5467
|
-
: DEFAULT_VALUES$1.securityLevel;
|
|
5468
|
-
return {
|
|
5469
|
-
...record,
|
|
5470
|
-
type: "AFTLoadBalancerStickinessManager",
|
|
5471
|
-
enabled: toBoolean(record.enabled, DEFAULT_VALUES$1.enabled),
|
|
5472
|
-
clientEcho: toBoolean(record.clientEcho, DEFAULT_VALUES$1.clientEcho),
|
|
5473
|
-
defaultTtlSec: toNumber(record.defaultTtlSec, DEFAULT_VALUES$1.defaultTtlSec),
|
|
5474
|
-
cacheMax: toNumber(record.cacheMax, DEFAULT_VALUES$1.cacheMax),
|
|
5475
|
-
securityLevel: normalizedSecurity,
|
|
5476
|
-
maxTtlSec: toNumber(record.maxTtlSec, DEFAULT_VALUES$1.maxTtlSec),
|
|
5477
|
-
};
|
|
5478
|
-
}
|
|
5479
|
-
class AFTLoadBalancerStickinessManagerFactory extends LoadBalancerStickinessManagerFactory {
|
|
5480
|
-
constructor() {
|
|
5481
|
-
super(...arguments);
|
|
5482
|
-
this.type = "AFTLoadBalancerStickinessManager";
|
|
5483
|
-
this.isDefault = false;
|
|
5484
|
-
}
|
|
5485
|
-
async create(config, keyProvider, verifier) {
|
|
5486
|
-
const resolvedConfig = normalizeConfig$4(config);
|
|
5487
|
-
let effectiveVerifier = verifier ?? null;
|
|
5488
|
-
if (!effectiveVerifier && keyProvider) {
|
|
5489
|
-
effectiveVerifier = createAftVerifier({
|
|
5490
|
-
securityLevel: resolvedConfig.securityLevel ?? DEFAULT_VALUES$1.securityLevel,
|
|
5491
|
-
keyProvider,
|
|
5492
|
-
defaultTtlSec: resolvedConfig.defaultTtlSec ?? DEFAULT_VALUES$1.defaultTtlSec,
|
|
5596
|
+
cleanupExpiredAssociations() {
|
|
5597
|
+
const now = Math.floor(Date.now() / 1000);
|
|
5598
|
+
const expiredTokens = [];
|
|
5599
|
+
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5600
|
+
if (association.isExpired(now)) {
|
|
5601
|
+
expiredTokens.push(token);
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5604
|
+
for (const token of expiredTokens) {
|
|
5605
|
+
this.removeAssociation(token);
|
|
5606
|
+
}
|
|
5607
|
+
if (expiredTokens.length > 0) {
|
|
5608
|
+
this.metrics.associationsExpired += expiredTokens.length;
|
|
5609
|
+
logger$4.debug("cleaned_expired_associations", {
|
|
5610
|
+
count: expiredTokens.length,
|
|
5493
5611
|
});
|
|
5494
5612
|
}
|
|
5495
|
-
|
|
5496
|
-
|
|
5613
|
+
}
|
|
5614
|
+
replicaLeft(replicaId) {
|
|
5615
|
+
const tokensToRemove = [];
|
|
5616
|
+
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5617
|
+
if (association.replicaId === replicaId) {
|
|
5618
|
+
tokensToRemove.push(token);
|
|
5619
|
+
}
|
|
5620
|
+
}
|
|
5621
|
+
for (const token of tokensToRemove) {
|
|
5622
|
+
this.removeAssociation(token);
|
|
5623
|
+
}
|
|
5624
|
+
if (tokensToRemove.length > 0) {
|
|
5625
|
+
logger$4.debug("removed_associations_for_departed_replica", {
|
|
5626
|
+
replica_id: replicaId,
|
|
5627
|
+
count: tokensToRemove.length,
|
|
5628
|
+
});
|
|
5497
5629
|
}
|
|
5498
|
-
return new AFTLoadBalancerStickinessManager(resolvedConfig, effectiveVerifier);
|
|
5499
5630
|
}
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
__proto__: null,
|
|
5504
|
-
AFTLoadBalancerStickinessManagerFactory: AFTLoadBalancerStickinessManagerFactory,
|
|
5505
|
-
FACTORY_META: FACTORY_META$6,
|
|
5506
|
-
default: AFTLoadBalancerStickinessManagerFactory
|
|
5507
|
-
});
|
|
5508
|
-
|
|
5509
|
-
const logger$3 = getLogger("naylence.fame.stickiness.aft_replica_stickiness_manager");
|
|
5510
|
-
function isStickinessRequired(context) {
|
|
5511
|
-
if (typeof context.stickinessRequired === "boolean") {
|
|
5512
|
-
return context.stickinessRequired;
|
|
5631
|
+
handleReplicaLeft(replicaId) {
|
|
5632
|
+
this.replicaLeft(replicaId);
|
|
5633
|
+
logger$4.debug("stickiness_replica_cleanup", { replica_id: replicaId });
|
|
5513
5634
|
}
|
|
5514
|
-
|
|
5515
|
-
return
|
|
5635
|
+
getMetrics() {
|
|
5636
|
+
return {
|
|
5637
|
+
...this.metrics,
|
|
5638
|
+
cacheSize: this.aftAssociations.size,
|
|
5639
|
+
sidCacheSize: this.sidCache.size,
|
|
5640
|
+
};
|
|
5516
5641
|
}
|
|
5517
|
-
|
|
5518
|
-
}
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
helper_type: this.aftHelper.signer.constructor.name,
|
|
5531
|
-
security_level: this.aftHelper.signer.securityLevel,
|
|
5532
|
-
max_ttl_sec: this.aftHelper.maxTtlSec,
|
|
5533
|
-
});
|
|
5534
|
-
}
|
|
5535
|
-
else {
|
|
5536
|
-
logger$3.debug("aft_replica_stickiness_manager_created", {
|
|
5537
|
-
security_level: this.securityLevel,
|
|
5538
|
-
max_ttl_sec: this.maxTtlSec,
|
|
5539
|
-
});
|
|
5642
|
+
getAssociations() {
|
|
5643
|
+
const result = {};
|
|
5644
|
+
for (const [token, association] of this.aftAssociations.entries()) {
|
|
5645
|
+
result[token] = {
|
|
5646
|
+
replica_id: association.replicaId,
|
|
5647
|
+
sid: association.sid,
|
|
5648
|
+
client_sid: association.clientSid,
|
|
5649
|
+
exp: association.exp,
|
|
5650
|
+
trust_level: association.trustLevel,
|
|
5651
|
+
scope: association.scope,
|
|
5652
|
+
created_at: association.createdAt,
|
|
5653
|
+
expired: association.isExpired(),
|
|
5654
|
+
};
|
|
5540
5655
|
}
|
|
5656
|
+
return result;
|
|
5541
5657
|
}
|
|
5542
|
-
|
|
5543
|
-
return
|
|
5658
|
+
getStickinessMetrics() {
|
|
5659
|
+
return this.getMetrics();
|
|
5544
5660
|
}
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5661
|
+
logMetrics() {
|
|
5662
|
+
const hits = this.metrics.cacheHits;
|
|
5663
|
+
const misses = this.metrics.cacheMisses;
|
|
5664
|
+
const total = hits + misses;
|
|
5665
|
+
const hitRate = total > 0 ? Math.round((hits / total) * 10000) / 100 : 0;
|
|
5666
|
+
logger$4.info("stickiness_metrics_report", {
|
|
5667
|
+
enabled: this.config.enabled,
|
|
5668
|
+
security_level: this.config.securityLevel,
|
|
5669
|
+
cache_hits: hits,
|
|
5670
|
+
cache_misses: misses,
|
|
5671
|
+
verify_failures: this.metrics.verifyFailures,
|
|
5672
|
+
associations_created: this.metrics.associationsCreated,
|
|
5673
|
+
associations_expired: this.metrics.associationsExpired,
|
|
5674
|
+
active_associations: this.aftAssociations.size,
|
|
5675
|
+
sid_cache_entries: this.sidCache.size,
|
|
5676
|
+
hit_rate: hitRate,
|
|
5551
5677
|
});
|
|
5552
5678
|
}
|
|
5553
|
-
async
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
if (negotiated.enabled === false ||
|
|
5572
|
-
(negotiated.mode !== null &&
|
|
5573
|
-
negotiated.mode !== undefined &&
|
|
5574
|
-
negotiated.mode !== "aft")) {
|
|
5575
|
-
logger$3.debug("aft_injection_skipped_due_to_policy", {
|
|
5679
|
+
async onDeliver(_node, envelope, context) {
|
|
5680
|
+
logger$4.debug("stickiness_manager_on_deliver", {
|
|
5681
|
+
envelope_id: envelope.id,
|
|
5682
|
+
origin_type: context?.originType ?? "unknown",
|
|
5683
|
+
from_system_id: context?.fromSystemId ?? null,
|
|
5684
|
+
});
|
|
5685
|
+
if (context?.originType === DeliveryOriginType$1.DOWNSTREAM) {
|
|
5686
|
+
const sourceRoute = context.fromSystemId;
|
|
5687
|
+
if (sourceRoute) {
|
|
5688
|
+
logger$4.debug("processing_downstream_envelope", {
|
|
5689
|
+
envelope_id: envelope.id,
|
|
5690
|
+
source_route: sourceRoute,
|
|
5691
|
+
});
|
|
5692
|
+
if (this.config.securityLevel === StickinessMode.SID_ONLY &&
|
|
5693
|
+
envelope.sid &&
|
|
5694
|
+
!this.sidCache.has(envelope.sid)) {
|
|
5695
|
+
this.sidCache.set(envelope.sid, sourceRoute);
|
|
5696
|
+
logger$4.debug("sid_only_association_recorded", {
|
|
5576
5697
|
envelope_id: envelope.id,
|
|
5577
|
-
|
|
5578
|
-
|
|
5698
|
+
sid: envelope.sid,
|
|
5699
|
+
replica_id: sourceRoute,
|
|
5700
|
+
});
|
|
5701
|
+
}
|
|
5702
|
+
const hadInstruction = Boolean(extractAftInstruction(envelope));
|
|
5703
|
+
const token = await this.handleOutboundEnvelope(envelope, sourceRoute);
|
|
5704
|
+
if (hadInstruction) {
|
|
5705
|
+
logger$4.debug("processed_aft_setter_instruction", {
|
|
5706
|
+
envelope_id: envelope.id,
|
|
5707
|
+
source_route: sourceRoute,
|
|
5708
|
+
client_echo: Boolean(token),
|
|
5709
|
+
});
|
|
5710
|
+
}
|
|
5711
|
+
else {
|
|
5712
|
+
logger$4.debug("no_aft_setter_instruction", {
|
|
5713
|
+
envelope_id: envelope.id,
|
|
5714
|
+
source_route: sourceRoute,
|
|
5579
5715
|
});
|
|
5580
|
-
return envelope;
|
|
5581
5716
|
}
|
|
5582
|
-
}
|
|
5583
|
-
logger$3.debug("applying_aft_for_upstream_stickiness_required", {
|
|
5584
|
-
envelope_id: envelope.id,
|
|
5585
|
-
from_system_id: context.fromSystemId ?? null,
|
|
5586
|
-
delivery_origin: context.originType ?? null,
|
|
5587
|
-
});
|
|
5588
|
-
const success = await helper.requestStickiness(envelope, {
|
|
5589
|
-
ttlSec: null,
|
|
5590
|
-
scope: "node",
|
|
5591
|
-
context: stickinessContext,
|
|
5592
|
-
});
|
|
5593
|
-
if (success) {
|
|
5594
|
-
logger$3.debug("aft_token_applied_via_context_flag_upstream", {
|
|
5595
|
-
envelope_id: envelope.id,
|
|
5596
|
-
from_system_id: context.fromSystemId ?? null,
|
|
5597
|
-
delivery_origin: context.originType ?? null,
|
|
5598
|
-
});
|
|
5599
5717
|
}
|
|
5600
5718
|
else {
|
|
5601
|
-
logger$
|
|
5719
|
+
logger$4.debug("downstream_envelope_without_source_route", {
|
|
5602
5720
|
envelope_id: envelope.id,
|
|
5603
|
-
delivery_origin: context.originType ?? null,
|
|
5604
|
-
reason: "helper_returned_false",
|
|
5605
5721
|
});
|
|
5606
5722
|
}
|
|
5607
5723
|
}
|
|
5608
|
-
return envelope;
|
|
5609
|
-
}
|
|
5610
|
-
async onNodeStarted(node) {
|
|
5611
|
-
if (!this.isInitialized) {
|
|
5612
|
-
await this.initializeAftHelper(node);
|
|
5613
|
-
return;
|
|
5614
|
-
}
|
|
5615
|
-
if (this.aftHelper && node.sid) {
|
|
5616
|
-
this.updateNodeSid(node.sid);
|
|
5617
|
-
logger$3.debug("aft_replica_stickiness_manager_sid_updated", {
|
|
5618
|
-
node_id: node.id ?? "unknown",
|
|
5619
|
-
node_sid: node.sid,
|
|
5620
|
-
security_level: this.aftHelper.signer.securityLevel,
|
|
5621
|
-
});
|
|
5622
|
-
}
|
|
5623
|
-
else if (!node.sid) {
|
|
5624
|
-
logger$3.warning("aft_replica_stickiness_manager_no_sid_available", {
|
|
5625
|
-
node_id: node.id ?? "unknown",
|
|
5626
|
-
});
|
|
5627
|
-
}
|
|
5628
5724
|
else {
|
|
5629
|
-
logger$
|
|
5630
|
-
|
|
5725
|
+
logger$4.debug("envelope_not_from_downstream", {
|
|
5726
|
+
envelope_id: envelope.id,
|
|
5631
5727
|
});
|
|
5632
5728
|
}
|
|
5729
|
+
return envelope;
|
|
5633
5730
|
}
|
|
5634
|
-
|
|
5635
|
-
if (this.
|
|
5636
|
-
this.
|
|
5637
|
-
logger$3.debug("aft_replica_stickiness_manager_sid_updated", {
|
|
5638
|
-
new_sid: nodeSid,
|
|
5639
|
-
});
|
|
5731
|
+
storeAssociation(token, data) {
|
|
5732
|
+
if (this.aftAssociations.has(token)) {
|
|
5733
|
+
this.aftAssociations.delete(token);
|
|
5640
5734
|
}
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
}
|
|
5648
|
-
|
|
5735
|
+
const association = new AFTAssociation(data);
|
|
5736
|
+
this.aftAssociations.set(token, association);
|
|
5737
|
+
while (this.aftAssociations.size > this.cacheMax) {
|
|
5738
|
+
const oldest = this.aftAssociations.keys().next();
|
|
5739
|
+
if (oldest.done) {
|
|
5740
|
+
break;
|
|
5741
|
+
}
|
|
5742
|
+
const oldestToken = oldest.value;
|
|
5743
|
+
this.removeAssociation(oldestToken);
|
|
5649
5744
|
}
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5745
|
+
}
|
|
5746
|
+
removeAssociation(token) {
|
|
5747
|
+
this.aftAssociations.delete(token);
|
|
5748
|
+
for (const [sid, cachedToken] of this.sidCache.entries()) {
|
|
5749
|
+
if (cachedToken === token) {
|
|
5750
|
+
this.sidCache.delete(sid);
|
|
5751
|
+
}
|
|
5656
5752
|
}
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
? cryptoProvider.signingPrivatePem
|
|
5663
|
-
: null;
|
|
5664
|
-
if (this.securityLevel === StickinessMode.STRICT && !privateKeyPem) {
|
|
5665
|
-
logger$3.error("aft_replica_stickiness_manager_initialization_failed", {
|
|
5666
|
-
node_id: node.id ?? "unknown",
|
|
5667
|
-
error: "Missing signing private key for strict security level",
|
|
5668
|
-
});
|
|
5669
|
-
return;
|
|
5753
|
+
}
|
|
5754
|
+
routeByAft(token, envelope) {
|
|
5755
|
+
const association = this.aftAssociations.get(token);
|
|
5756
|
+
if (!association) {
|
|
5757
|
+
return null;
|
|
5670
5758
|
}
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
kid: keyId,
|
|
5676
|
-
privateKeyPem,
|
|
5677
|
-
maxTtlSec: this.maxTtlSec,
|
|
5678
|
-
});
|
|
5679
|
-
this.aftHelper = helper;
|
|
5680
|
-
this.isInitialized = true;
|
|
5681
|
-
logger$3.debug("aft_replica_stickiness_manager_initialized", {
|
|
5682
|
-
node_id: node.id ?? "unknown",
|
|
5683
|
-
node_sid: nodeSid,
|
|
5684
|
-
key_id: keyId,
|
|
5685
|
-
security_level: helper.signer.securityLevel,
|
|
5686
|
-
});
|
|
5759
|
+
if (association.isExpired()) {
|
|
5760
|
+
this.metrics.associationsExpired += 1;
|
|
5761
|
+
this.removeAssociation(token);
|
|
5762
|
+
return null;
|
|
5687
5763
|
}
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5764
|
+
if (this.verifier.securityLevel === StickinessMode.STRICT &&
|
|
5765
|
+
association.isLowTrust()) {
|
|
5766
|
+
logger$4.warning("rejecting_low_trust_association", {
|
|
5767
|
+
envelope_id: envelope.id,
|
|
5768
|
+
replica_id: association.replicaId,
|
|
5769
|
+
reason: "strict mode rejects low-trust associations",
|
|
5692
5770
|
});
|
|
5771
|
+
return null;
|
|
5693
5772
|
}
|
|
5773
|
+
this.aftAssociations.delete(token);
|
|
5774
|
+
this.aftAssociations.set(token, association);
|
|
5775
|
+
return association.replicaId;
|
|
5694
5776
|
}
|
|
5695
|
-
|
|
5696
|
-
|
|
5777
|
+
}
|
|
5778
|
+
function extractAftInstruction(envelope) {
|
|
5779
|
+
if (!envelope.meta) {
|
|
5780
|
+
return null;
|
|
5697
5781
|
}
|
|
5698
|
-
|
|
5699
|
-
|
|
5782
|
+
const meta = envelope.meta;
|
|
5783
|
+
const nested = meta.set;
|
|
5784
|
+
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
5785
|
+
const aftValue = nested.aft;
|
|
5786
|
+
if (aftValue !== undefined) {
|
|
5787
|
+
return aftValue;
|
|
5788
|
+
}
|
|
5789
|
+
}
|
|
5790
|
+
if (meta["set.aft"] !== undefined) {
|
|
5791
|
+
return meta["set.aft"];
|
|
5700
5792
|
}
|
|
5793
|
+
return null;
|
|
5701
5794
|
}
|
|
5702
|
-
function
|
|
5703
|
-
|
|
5795
|
+
function computeDeterministicIndex(key, modulo) {
|
|
5796
|
+
if (modulo <= 0) {
|
|
5797
|
+
return 0;
|
|
5798
|
+
}
|
|
5799
|
+
let hash = 0;
|
|
5800
|
+
for (let i = 0; i < key.length; i += 1) {
|
|
5801
|
+
hash = (hash * 31 + key.charCodeAt(i)) >>> 0;
|
|
5802
|
+
}
|
|
5803
|
+
return hash % modulo;
|
|
5704
5804
|
}
|
|
5705
5805
|
|
|
5706
|
-
const FACTORY_META$
|
|
5707
|
-
base:
|
|
5708
|
-
key: "
|
|
5806
|
+
const FACTORY_META$6 = {
|
|
5807
|
+
base: LOAD_BALANCER_STICKINESS_MANAGER_FACTORY_BASE_TYPE,
|
|
5808
|
+
key: "AFTLoadBalancerStickinessManager",
|
|
5709
5809
|
};
|
|
5710
|
-
const DEFAULT_VALUES = {
|
|
5810
|
+
const DEFAULT_VALUES$1 = {
|
|
5811
|
+
enabled: true,
|
|
5812
|
+
clientEcho: false,
|
|
5813
|
+
defaultTtlSec: 30,
|
|
5814
|
+
cacheMax: 100000,
|
|
5711
5815
|
securityLevel: StickinessMode.SIGNED_OPTIONAL,
|
|
5712
5816
|
maxTtlSec: 7200,
|
|
5713
5817
|
};
|
|
5714
|
-
function
|
|
5818
|
+
function toBoolean(value, fallback) {
|
|
5819
|
+
return typeof value === "boolean" ? value : fallback;
|
|
5820
|
+
}
|
|
5821
|
+
function toNumber(value, fallback) {
|
|
5822
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
5823
|
+
return value;
|
|
5824
|
+
}
|
|
5825
|
+
return fallback;
|
|
5826
|
+
}
|
|
5827
|
+
function normalizeConfig$4(config) {
|
|
5715
5828
|
const record = (config ?? {});
|
|
5716
5829
|
const normalizedSecurity = record.securityLevel
|
|
5717
5830
|
? normalizeStickinessMode(record.securityLevel)
|
|
5718
|
-
: DEFAULT_VALUES.securityLevel;
|
|
5719
|
-
const securityLevel = normalizedSecurity ?? DEFAULT_VALUES.securityLevel;
|
|
5720
|
-
const maxTtlSecValue = typeof record.maxTtlSec === "number" && Number.isFinite(record.maxTtlSec)
|
|
5721
|
-
? Math.max(0, Math.floor(record.maxTtlSec))
|
|
5722
|
-
: DEFAULT_VALUES.maxTtlSec;
|
|
5831
|
+
: DEFAULT_VALUES$1.securityLevel;
|
|
5723
5832
|
return {
|
|
5724
5833
|
...record,
|
|
5725
|
-
type: "
|
|
5726
|
-
|
|
5727
|
-
|
|
5834
|
+
type: "AFTLoadBalancerStickinessManager",
|
|
5835
|
+
enabled: toBoolean(record.enabled, DEFAULT_VALUES$1.enabled),
|
|
5836
|
+
clientEcho: toBoolean(record.clientEcho, DEFAULT_VALUES$1.clientEcho),
|
|
5837
|
+
defaultTtlSec: toNumber(record.defaultTtlSec, DEFAULT_VALUES$1.defaultTtlSec),
|
|
5838
|
+
cacheMax: toNumber(record.cacheMax, DEFAULT_VALUES$1.cacheMax),
|
|
5839
|
+
securityLevel: normalizedSecurity,
|
|
5840
|
+
maxTtlSec: toNumber(record.maxTtlSec, DEFAULT_VALUES$1.maxTtlSec),
|
|
5728
5841
|
};
|
|
5729
5842
|
}
|
|
5730
|
-
class
|
|
5843
|
+
class AFTLoadBalancerStickinessManagerFactory extends LoadBalancerStickinessManagerFactory {
|
|
5731
5844
|
constructor() {
|
|
5732
5845
|
super(...arguments);
|
|
5733
|
-
this.type =
|
|
5734
|
-
this.isDefault =
|
|
5846
|
+
this.type = "AFTLoadBalancerStickinessManager";
|
|
5847
|
+
this.isDefault = false;
|
|
5735
5848
|
}
|
|
5736
|
-
async create(config,
|
|
5737
|
-
const resolvedConfig = normalizeConfig$
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
}
|
|
5849
|
+
async create(config, keyProvider, verifier) {
|
|
5850
|
+
const resolvedConfig = normalizeConfig$4(config);
|
|
5851
|
+
let effectiveVerifier = verifier ?? null;
|
|
5852
|
+
if (!effectiveVerifier && keyProvider) {
|
|
5853
|
+
effectiveVerifier = createAftVerifier({
|
|
5854
|
+
securityLevel: resolvedConfig.securityLevel ?? DEFAULT_VALUES$1.securityLevel,
|
|
5855
|
+
keyProvider,
|
|
5856
|
+
defaultTtlSec: resolvedConfig.defaultTtlSec ?? DEFAULT_VALUES$1.defaultTtlSec,
|
|
5857
|
+
});
|
|
5858
|
+
}
|
|
5859
|
+
if (!effectiveVerifier) {
|
|
5860
|
+
throw new Error("AFTLoadBalancerStickinessManagerFactory requires an AFT verifier or key provider");
|
|
5861
|
+
}
|
|
5862
|
+
return new AFTLoadBalancerStickinessManager(resolvedConfig, effectiveVerifier);
|
|
5749
5863
|
}
|
|
5750
5864
|
}
|
|
5751
5865
|
|
|
5752
|
-
var
|
|
5866
|
+
var aftLoadBalancerStickinessManagerFactory = /*#__PURE__*/Object.freeze({
|
|
5753
5867
|
__proto__: null,
|
|
5754
|
-
|
|
5755
|
-
FACTORY_META: FACTORY_META$
|
|
5756
|
-
default:
|
|
5868
|
+
AFTLoadBalancerStickinessManagerFactory: AFTLoadBalancerStickinessManagerFactory,
|
|
5869
|
+
FACTORY_META: FACTORY_META$6,
|
|
5870
|
+
default: AFTLoadBalancerStickinessManagerFactory
|
|
5757
5871
|
});
|
|
5758
5872
|
|
|
5759
|
-
const logger$
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
process.env?.[ENV_VAR_SHOW_ENVELOPES] === "true";
|
|
5764
|
-
function nowUtc() {
|
|
5765
|
-
return new Date();
|
|
5766
|
-
}
|
|
5767
|
-
function formatTimestampForConsole() {
|
|
5768
|
-
return color(formatTimestamp(), AnsiColor.GRAY);
|
|
5769
|
-
}
|
|
5770
|
-
function prettyModel(value) {
|
|
5771
|
-
try {
|
|
5772
|
-
return jsonDumps(value);
|
|
5773
|
-
}
|
|
5774
|
-
catch (error) {
|
|
5775
|
-
return String(error);
|
|
5776
|
-
}
|
|
5777
|
-
}
|
|
5778
|
-
function coercePlacementMetadataValue(metadata, camelCaseKey, snakeCaseKey) {
|
|
5779
|
-
if (!metadata) {
|
|
5780
|
-
return undefined;
|
|
5781
|
-
}
|
|
5782
|
-
const record = metadata;
|
|
5783
|
-
if (record[camelCaseKey] !== undefined) {
|
|
5784
|
-
return record[camelCaseKey];
|
|
5873
|
+
const logger$3 = getLogger("naylence.fame.stickiness.aft_replica_stickiness_manager");
|
|
5874
|
+
function isStickinessRequired(context) {
|
|
5875
|
+
if (typeof context.stickinessRequired === "boolean") {
|
|
5876
|
+
return context.stickinessRequired;
|
|
5785
5877
|
}
|
|
5786
|
-
if (
|
|
5787
|
-
return
|
|
5878
|
+
if (typeof context.stickiness_required === "boolean") {
|
|
5879
|
+
return context.stickiness_required;
|
|
5788
5880
|
}
|
|
5789
|
-
return
|
|
5881
|
+
return false;
|
|
5790
5882
|
}
|
|
5791
|
-
class
|
|
5792
|
-
constructor(options) {
|
|
5793
|
-
|
|
5794
|
-
this.
|
|
5795
|
-
|
|
5796
|
-
this.
|
|
5797
|
-
this.
|
|
5798
|
-
this.
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
const systemId = trimmedSystemId.length > 0 ? trimmedSystemId : generateId();
|
|
5813
|
-
const wasAssigned = trimmedSystemId.length === 0;
|
|
5814
|
-
const normalizedHello = {
|
|
5815
|
-
...hello,
|
|
5816
|
-
systemId,
|
|
5817
|
-
};
|
|
5818
|
-
if (showEnvelopes) {
|
|
5819
|
-
// eslint-disable-next-line no-console
|
|
5820
|
-
console.log(`\n${formatTimestampForConsole()} - ${color("Received envelope 📨", AnsiColor.BLUE)}\n${prettyModel(normalizedHello)}`);
|
|
5883
|
+
class AFTReplicaStickinessManager extends BaseNodeEventListener {
|
|
5884
|
+
constructor(options = {}) {
|
|
5885
|
+
super();
|
|
5886
|
+
this.securityLevel =
|
|
5887
|
+
normalizeStickinessMode(options.securityLevel ?? DEFAULT_STICKINESS_SECURITY_LEVEL) ?? DEFAULT_STICKINESS_SECURITY_LEVEL;
|
|
5888
|
+
this.aftHelper = options.aftHelper ?? null;
|
|
5889
|
+
this.maxTtlSec = options.maxTtlSec ?? 7200;
|
|
5890
|
+
this.isInitialized = this.aftHelper !== null;
|
|
5891
|
+
this.negotiatedStickiness = null;
|
|
5892
|
+
if (this.aftHelper) {
|
|
5893
|
+
logger$3.debug("aft_replica_stickiness_manager_initialized", {
|
|
5894
|
+
helper_type: this.aftHelper.signer.constructor.name,
|
|
5895
|
+
security_level: this.aftHelper.signer.securityLevel,
|
|
5896
|
+
max_ttl_sec: this.aftHelper.maxTtlSec,
|
|
5897
|
+
});
|
|
5898
|
+
}
|
|
5899
|
+
else {
|
|
5900
|
+
logger$3.debug("aft_replica_stickiness_manager_created", {
|
|
5901
|
+
security_level: this.securityLevel,
|
|
5902
|
+
max_ttl_sec: this.maxTtlSec,
|
|
5903
|
+
});
|
|
5821
5904
|
}
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5905
|
+
}
|
|
5906
|
+
offer() {
|
|
5907
|
+
return { mode: "aft", supportedModes: ["aft", "attr"], version: 1 };
|
|
5908
|
+
}
|
|
5909
|
+
accept(stickiness) {
|
|
5910
|
+
this.negotiatedStickiness = stickiness ?? null;
|
|
5911
|
+
logger$3.debug("replica_stickiness_policy_set", {
|
|
5912
|
+
enabled: stickiness?.enabled ?? null,
|
|
5913
|
+
mode: stickiness?.mode ?? null,
|
|
5914
|
+
ttl: stickiness?.ttlSec ?? null,
|
|
5828
5915
|
});
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
if (
|
|
5832
|
-
|
|
5833
|
-
fullMetadata.instanceId = normalizedHello.instanceId;
|
|
5834
|
-
}
|
|
5835
|
-
if (fullMetadata.instance_id === undefined) {
|
|
5836
|
-
fullMetadata.instance_id = normalizedHello.instanceId;
|
|
5837
|
-
}
|
|
5916
|
+
}
|
|
5917
|
+
async onForwardUpstream(_node, envelope, context) {
|
|
5918
|
+
if (!context) {
|
|
5919
|
+
return envelope;
|
|
5838
5920
|
}
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
logicals: normalizedHello.logicals,
|
|
5921
|
+
const helper = this.aftHelper;
|
|
5922
|
+
if (!helper) {
|
|
5923
|
+
logger$3.debug("aft_helper_not_ready_skip_injection", {
|
|
5924
|
+
envelope_id: envelope.id,
|
|
5925
|
+
delivery_origin: context.originType ?? null,
|
|
5926
|
+
reason: "not_initialized",
|
|
5846
5927
|
});
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5928
|
+
return envelope;
|
|
5929
|
+
}
|
|
5930
|
+
const stickinessContext = context;
|
|
5931
|
+
if (isStickinessRequired(stickinessContext) &&
|
|
5932
|
+
context.originType === DeliveryOriginType$1.LOCAL) {
|
|
5933
|
+
if (this.negotiatedStickiness) {
|
|
5934
|
+
const negotiated = this.negotiatedStickiness;
|
|
5935
|
+
if (negotiated.enabled === false ||
|
|
5936
|
+
(negotiated.mode !== null &&
|
|
5937
|
+
negotiated.mode !== undefined &&
|
|
5938
|
+
negotiated.mode !== "aft")) {
|
|
5939
|
+
logger$3.debug("aft_injection_skipped_due_to_policy", {
|
|
5940
|
+
envelope_id: envelope.id,
|
|
5941
|
+
policy_mode: negotiated.mode ?? null,
|
|
5942
|
+
policy_enabled: negotiated.enabled ?? null,
|
|
5943
|
+
});
|
|
5944
|
+
return envelope;
|
|
5945
|
+
}
|
|
5946
|
+
}
|
|
5947
|
+
logger$3.debug("applying_aft_for_upstream_stickiness_required", {
|
|
5948
|
+
envelope_id: envelope.id,
|
|
5949
|
+
from_system_id: context.fromSystemId ?? null,
|
|
5950
|
+
delivery_origin: context.originType ?? null,
|
|
5951
|
+
});
|
|
5952
|
+
const success = await helper.requestStickiness(envelope, {
|
|
5953
|
+
ttlSec: null,
|
|
5954
|
+
scope: "node",
|
|
5955
|
+
context: stickinessContext,
|
|
5956
|
+
});
|
|
5957
|
+
if (success) {
|
|
5958
|
+
logger$3.debug("aft_token_applied_via_context_flag_upstream", {
|
|
5959
|
+
envelope_id: envelope.id,
|
|
5960
|
+
from_system_id: context.fromSystemId ?? null,
|
|
5961
|
+
delivery_origin: context.originType ?? null,
|
|
5962
|
+
});
|
|
5963
|
+
}
|
|
5964
|
+
else {
|
|
5965
|
+
logger$3.debug("aft_token_not_applied_upstream", {
|
|
5966
|
+
envelope_id: envelope.id,
|
|
5967
|
+
delivery_origin: context.originType ?? null,
|
|
5968
|
+
reason: "helper_returned_false",
|
|
5852
5969
|
});
|
|
5853
|
-
throw new Error(`Invalid logical format: ${pathError}`);
|
|
5854
5970
|
}
|
|
5855
|
-
logger$2.debug("logicals_validation_successful");
|
|
5856
5971
|
}
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5972
|
+
return envelope;
|
|
5973
|
+
}
|
|
5974
|
+
async onNodeStarted(node) {
|
|
5975
|
+
if (!this.isInitialized) {
|
|
5976
|
+
await this.initializeAftHelper(node);
|
|
5977
|
+
return;
|
|
5978
|
+
}
|
|
5979
|
+
if (this.aftHelper && node.sid) {
|
|
5980
|
+
this.updateNodeSid(node.sid);
|
|
5981
|
+
logger$3.debug("aft_replica_stickiness_manager_sid_updated", {
|
|
5982
|
+
node_id: node.id ?? "unknown",
|
|
5983
|
+
node_sid: node.sid,
|
|
5984
|
+
security_level: this.aftHelper.signer.securityLevel,
|
|
5863
5985
|
});
|
|
5864
|
-
throw new Error(placementResult.reason || "Node not accepted");
|
|
5865
5986
|
}
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
assignedPath,
|
|
5870
|
-
targetPhysicalPath: placementResult.targetPhysicalPath ?? null,
|
|
5871
|
-
targetSystemId: placementResult.targetSystemId ?? null,
|
|
5872
|
-
});
|
|
5873
|
-
const acceptedCapabilities = coercePlacementMetadataValue(placementResult.metadata, "acceptedCapabilities", "accepted_capabilities") ??
|
|
5874
|
-
normalizedHello.capabilities ??
|
|
5875
|
-
null;
|
|
5876
|
-
const acceptedLogicals = coercePlacementMetadataValue(placementResult.metadata, "acceptedLogicals", "accepted_logicals") ??
|
|
5877
|
-
normalizedHello.logicals ??
|
|
5878
|
-
null;
|
|
5879
|
-
logger$2.debug("processing_placement_result_metadata", {
|
|
5880
|
-
acceptedCapabilities,
|
|
5881
|
-
acceptedLogicals,
|
|
5882
|
-
hasPlacementMetadata: placementResult.metadata !== undefined &&
|
|
5883
|
-
placementResult.metadata !== null,
|
|
5884
|
-
});
|
|
5885
|
-
const connectionGrants = [];
|
|
5886
|
-
const metadataInstanceId = (typeof fullMetadata.instanceId === "string" &&
|
|
5887
|
-
fullMetadata.instanceId) ||
|
|
5888
|
-
(typeof fullMetadata.instance_id === "string" &&
|
|
5889
|
-
fullMetadata.instance_id) ||
|
|
5890
|
-
normalizedHello.instanceId ||
|
|
5891
|
-
generateId();
|
|
5892
|
-
if (placementResult.targetSystemId) {
|
|
5893
|
-
logger$2.debug("issuing_node_attach_token", {
|
|
5894
|
-
systemId,
|
|
5895
|
-
assignedPath,
|
|
5987
|
+
else if (!node.sid) {
|
|
5988
|
+
logger$3.warning("aft_replica_stickiness_manager_no_sid_available", {
|
|
5989
|
+
node_id: node.id ?? "unknown",
|
|
5896
5990
|
});
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
assigned_path: placementResult.assignedPath,
|
|
5902
|
-
accepted_logicals: acceptedLogicals,
|
|
5903
|
-
instance_id: metadataInstanceId,
|
|
5991
|
+
}
|
|
5992
|
+
else {
|
|
5993
|
+
logger$3.error("aft_replica_stickiness_manager_node_missing_sid", {
|
|
5994
|
+
node_type: node.constructor?.name ?? typeof node,
|
|
5904
5995
|
});
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
? (transportInfo.connectionGrant.type ??
|
|
5913
|
-
"Unknown")
|
|
5914
|
-
: "Unknown",
|
|
5996
|
+
}
|
|
5997
|
+
}
|
|
5998
|
+
updateNodeSid(nodeSid) {
|
|
5999
|
+
if (this.aftHelper) {
|
|
6000
|
+
this.aftHelper.nodeSid = nodeSid;
|
|
6001
|
+
logger$3.debug("aft_replica_stickiness_manager_sid_updated", {
|
|
6002
|
+
new_sid: nodeSid,
|
|
5915
6003
|
});
|
|
5916
|
-
connectionGrants.push(transportInfo.connectionGrant);
|
|
5917
6004
|
}
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
const
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
6005
|
+
}
|
|
6006
|
+
async initializeAftHelper(node) {
|
|
6007
|
+
const nodeSid = node.sid;
|
|
6008
|
+
if (!nodeSid) {
|
|
6009
|
+
logger$3.error("aft_replica_stickiness_manager_cannot_initialize_no_sid", {
|
|
6010
|
+
node_id: node.id ?? "unknown",
|
|
6011
|
+
});
|
|
6012
|
+
return;
|
|
6013
|
+
}
|
|
6014
|
+
const cryptoProvider = node.cryptoProvider ?? null;
|
|
6015
|
+
if (!cryptoProvider) {
|
|
6016
|
+
logger$3.error("aft_replica_stickiness_manager_cannot_initialize_no_crypto_provider", {
|
|
6017
|
+
node_id: node.id ?? "unknown",
|
|
6018
|
+
});
|
|
6019
|
+
return;
|
|
6020
|
+
}
|
|
6021
|
+
const keyId = typeof cryptoProvider.signatureKeyId === "string" &&
|
|
6022
|
+
cryptoProvider.signatureKeyId.length > 0
|
|
6023
|
+
? cryptoProvider.signatureKeyId
|
|
6024
|
+
: "default-key-id";
|
|
6025
|
+
const privateKeyPem = typeof cryptoProvider.signingPrivatePem === "string"
|
|
6026
|
+
? cryptoProvider.signingPrivatePem
|
|
6027
|
+
: null;
|
|
6028
|
+
if (this.securityLevel === StickinessMode.STRICT && !privateKeyPem) {
|
|
6029
|
+
logger$3.error("aft_replica_stickiness_manager_initialization_failed", {
|
|
6030
|
+
node_id: node.id ?? "unknown",
|
|
6031
|
+
error: "Missing signing private key for strict security level",
|
|
6032
|
+
});
|
|
6033
|
+
return;
|
|
6034
|
+
}
|
|
6035
|
+
try {
|
|
6036
|
+
const helper = createAftHelper({
|
|
6037
|
+
securityLevel: this.securityLevel,
|
|
6038
|
+
nodeSid,
|
|
6039
|
+
kid: keyId,
|
|
6040
|
+
privateKeyPem,
|
|
6041
|
+
maxTtlSec: this.maxTtlSec,
|
|
6042
|
+
});
|
|
6043
|
+
this.aftHelper = helper;
|
|
6044
|
+
this.isInitialized = true;
|
|
6045
|
+
logger$3.debug("aft_replica_stickiness_manager_initialized", {
|
|
6046
|
+
node_id: node.id ?? "unknown",
|
|
6047
|
+
node_sid: nodeSid,
|
|
6048
|
+
key_id: keyId,
|
|
6049
|
+
security_level: helper.signer.securityLevel,
|
|
6050
|
+
});
|
|
5963
6051
|
}
|
|
5964
|
-
|
|
6052
|
+
catch (error) {
|
|
6053
|
+
logger$3.error("aft_replica_stickiness_manager_initialization_failed", {
|
|
6054
|
+
node_id: node.id ?? "unknown",
|
|
6055
|
+
error: error instanceof Error ? error.message : String(error),
|
|
6056
|
+
});
|
|
6057
|
+
}
|
|
6058
|
+
}
|
|
6059
|
+
get signer() {
|
|
6060
|
+
return this.aftHelper?.signer ?? null;
|
|
6061
|
+
}
|
|
6062
|
+
getHelper() {
|
|
6063
|
+
return this.aftHelper;
|
|
5965
6064
|
}
|
|
5966
6065
|
}
|
|
6066
|
+
function createAftReplicaStickinessManager(aftHelper) {
|
|
6067
|
+
return new AFTReplicaStickinessManager({ aftHelper });
|
|
6068
|
+
}
|
|
5967
6069
|
|
|
5968
|
-
const FACTORY_META$
|
|
5969
|
-
base:
|
|
5970
|
-
key: "
|
|
5971
|
-
priority: 100,
|
|
5972
|
-
isDefault: true,
|
|
6070
|
+
const FACTORY_META$5 = {
|
|
6071
|
+
base: REPLICA_STICKINESS_MANAGER_FACTORY_BASE_TYPE,
|
|
6072
|
+
key: "AFTReplicaStickinessManager",
|
|
5973
6073
|
};
|
|
5974
|
-
|
|
6074
|
+
const DEFAULT_VALUES = {
|
|
6075
|
+
securityLevel: StickinessMode.SIGNED_OPTIONAL,
|
|
6076
|
+
maxTtlSec: 7200,
|
|
6077
|
+
};
|
|
6078
|
+
function normalizeConfig$3(config) {
|
|
6079
|
+
const record = (config ?? {});
|
|
6080
|
+
const normalizedSecurity = record.securityLevel
|
|
6081
|
+
? normalizeStickinessMode(record.securityLevel)
|
|
6082
|
+
: DEFAULT_VALUES.securityLevel;
|
|
6083
|
+
const securityLevel = normalizedSecurity ?? DEFAULT_VALUES.securityLevel;
|
|
6084
|
+
const maxTtlSecValue = typeof record.maxTtlSec === "number" && Number.isFinite(record.maxTtlSec)
|
|
6085
|
+
? Math.max(0, Math.floor(record.maxTtlSec))
|
|
6086
|
+
: DEFAULT_VALUES.maxTtlSec;
|
|
6087
|
+
return {
|
|
6088
|
+
...record,
|
|
6089
|
+
type: "AFTReplicaStickinessManager",
|
|
6090
|
+
securityLevel,
|
|
6091
|
+
maxTtlSec: maxTtlSecValue,
|
|
6092
|
+
};
|
|
6093
|
+
}
|
|
6094
|
+
class AFTReplicaStickinessManagerFactory extends ReplicaStickinessManagerFactory {
|
|
5975
6095
|
constructor() {
|
|
5976
6096
|
super(...arguments);
|
|
5977
|
-
this.type = FACTORY_META$
|
|
5978
|
-
this.isDefault =
|
|
5979
|
-
this.priority = FACTORY_META$4.priority;
|
|
5980
|
-
}
|
|
5981
|
-
async create(config, ...factoryArgs) {
|
|
5982
|
-
const normalized = normalizeConfig$2(config);
|
|
5983
|
-
// Crypto provider should be passed from upstream (node-welcome-server)
|
|
5984
|
-
// Do not create it here - downstream components should use what's passed in factoryArgs
|
|
5985
|
-
const placementStrategy = await NodePlacementStrategyFactory.createNodePlacementStrategy(normalized.placementConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
5986
|
-
const transportProvisioner = await TransportProvisionerFactory.createTransportProvisioner(normalized.transportConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
5987
|
-
const tokenIssuer = await TokenIssuerFactory.createTokenIssuer(normalized.tokenIssuerConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
5988
|
-
let authorizer = null;
|
|
5989
|
-
if (normalized.authorizerConfig) {
|
|
5990
|
-
authorizer =
|
|
5991
|
-
(await AuthorizerFactory.createAuthorizer(normalized.authorizerConfig, {
|
|
5992
|
-
factoryArgs,
|
|
5993
|
-
})) ?? null;
|
|
5994
|
-
}
|
|
5995
|
-
const options = {
|
|
5996
|
-
placementStrategy,
|
|
5997
|
-
transportProvisioner,
|
|
5998
|
-
tokenIssuer,
|
|
5999
|
-
authorizer,
|
|
6000
|
-
caServiceUrl: normalized.caServiceUrl,
|
|
6001
|
-
};
|
|
6002
|
-
if (normalized.ttlSec !== undefined) {
|
|
6003
|
-
options.ttlSec = normalized.ttlSec;
|
|
6004
|
-
}
|
|
6005
|
-
return new AdvancedWelcomeService(options);
|
|
6006
|
-
}
|
|
6007
|
-
}
|
|
6008
|
-
function normalizeConfig$2(config) {
|
|
6009
|
-
if (!config) {
|
|
6010
|
-
throw new Error("AdvancedWelcomeService requires configuration");
|
|
6011
|
-
}
|
|
6012
|
-
const source = config;
|
|
6013
|
-
const ttlCandidate = typeof source.ttlSec === "number"
|
|
6014
|
-
? source.ttlSec
|
|
6015
|
-
: typeof source.ttl_sec === "number"
|
|
6016
|
-
? source.ttl_sec
|
|
6017
|
-
: undefined;
|
|
6018
|
-
const caServiceUrlCandidate = typeof source.caServiceUrl === "string" &&
|
|
6019
|
-
source.caServiceUrl.trim().length > 0
|
|
6020
|
-
? source.caServiceUrl.trim()
|
|
6021
|
-
: typeof source.ca_service_url === "string" &&
|
|
6022
|
-
source.ca_service_url.trim().length > 0
|
|
6023
|
-
? source.ca_service_url.trim()
|
|
6024
|
-
: undefined;
|
|
6025
|
-
if (!caServiceUrlCandidate) {
|
|
6026
|
-
throw new Error("AdvancedWelcomeService configuration requires caServiceUrl");
|
|
6027
|
-
}
|
|
6028
|
-
const normalized = {
|
|
6029
|
-
caServiceUrl: caServiceUrlCandidate,
|
|
6030
|
-
};
|
|
6031
|
-
if (source.placement !== undefined) {
|
|
6032
|
-
normalized.placementConfig =
|
|
6033
|
-
source.placement ?? null;
|
|
6034
|
-
}
|
|
6035
|
-
if (source.transport !== undefined) {
|
|
6036
|
-
normalized.transportConfig =
|
|
6037
|
-
source.transport ?? null;
|
|
6038
|
-
}
|
|
6039
|
-
const tokenIssuerConfig = source.tokenIssuer !== undefined
|
|
6040
|
-
? source.tokenIssuer
|
|
6041
|
-
: source.token_issuer !== undefined
|
|
6042
|
-
? source.token_issuer
|
|
6043
|
-
: undefined;
|
|
6044
|
-
if (tokenIssuerConfig !== undefined) {
|
|
6045
|
-
normalized.tokenIssuerConfig =
|
|
6046
|
-
tokenIssuerConfig ?? null;
|
|
6047
|
-
}
|
|
6048
|
-
if (source.authorizer !== undefined) {
|
|
6049
|
-
normalized.authorizerConfig =
|
|
6050
|
-
source.authorizer ?? null;
|
|
6097
|
+
this.type = FACTORY_META$5.key;
|
|
6098
|
+
this.isDefault = true;
|
|
6051
6099
|
}
|
|
6052
|
-
|
|
6053
|
-
|
|
6100
|
+
async create(config, dependencies) {
|
|
6101
|
+
const resolvedConfig = normalizeConfig$3(config);
|
|
6102
|
+
const helper = dependencies?.aftHelper ?? null;
|
|
6103
|
+
const securityLevel = normalizeStickinessMode(resolvedConfig.securityLevel ?? DEFAULT_VALUES.securityLevel) ?? DEFAULT_VALUES.securityLevel;
|
|
6104
|
+
const maxTtlSec = typeof resolvedConfig.maxTtlSec === "number" &&
|
|
6105
|
+
Number.isFinite(resolvedConfig.maxTtlSec)
|
|
6106
|
+
? Math.max(0, Math.floor(resolvedConfig.maxTtlSec))
|
|
6107
|
+
: DEFAULT_VALUES.maxTtlSec;
|
|
6108
|
+
return new AFTReplicaStickinessManager({
|
|
6109
|
+
securityLevel,
|
|
6110
|
+
maxTtlSec,
|
|
6111
|
+
aftHelper: helper,
|
|
6112
|
+
});
|
|
6054
6113
|
}
|
|
6055
|
-
return normalized;
|
|
6056
6114
|
}
|
|
6057
6115
|
|
|
6058
|
-
var
|
|
6116
|
+
var aftReplicaStickinessManagerFactory = /*#__PURE__*/Object.freeze({
|
|
6059
6117
|
__proto__: null,
|
|
6060
|
-
|
|
6061
|
-
FACTORY_META: FACTORY_META$
|
|
6062
|
-
default:
|
|
6118
|
+
AFTReplicaStickinessManagerFactory: AFTReplicaStickinessManagerFactory,
|
|
6119
|
+
FACTORY_META: FACTORY_META$5,
|
|
6120
|
+
default: AFTReplicaStickinessManagerFactory
|
|
6063
6121
|
});
|
|
6064
6122
|
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
"./security/cert/default-ca-service-factory.js",
|
|
6073
|
-
"./security/cert/default-certificate-manager-factory.js",
|
|
6074
|
-
"./security/cert/trust-store/browser-trust-store-provider-factory.js",
|
|
6075
|
-
"./security/cert/trust-store/node-trust-store-provider-factory.js",
|
|
6076
|
-
"./security/encryption/channel/channel-encryption-manager-factory.js",
|
|
6077
|
-
"./security/encryption/composite-encryption-manager-factory.js",
|
|
6078
|
-
"./security/encryption/default-secure-channel-manager-factory.js",
|
|
6079
|
-
"./security/encryption/sealed/x25519-encryption-manager-factory.js",
|
|
6080
|
-
"./security/keys/x5c-key-manager-factory.js",
|
|
6081
|
-
"./security/signing/eddsa-envelope-signer-factory.js",
|
|
6082
|
-
"./security/signing/eddsa-envelope-verifier-factory.js",
|
|
6083
|
-
"./stickiness/aft-load-balancer-stickiness-manager-factory.js",
|
|
6084
|
-
"./stickiness/aft-replica-stickiness-manager-factory.js",
|
|
6085
|
-
"./welcome/advanced-welcome-service-factory.js"
|
|
6086
|
-
];
|
|
6087
|
-
const MODULE_LOADERS = {
|
|
6088
|
-
"./security/cert/default-ca-service-factory.js": () => Promise.resolve().then(function () { return defaultCaServiceFactory; }),
|
|
6089
|
-
"./security/cert/default-certificate-manager-factory.js": () => Promise.resolve().then(function () { return defaultCertificateManagerFactory; }),
|
|
6090
|
-
"./security/cert/trust-store/browser-trust-store-provider-factory.js": () => Promise.resolve().then(function () { return browserTrustStoreProviderFactory; }),
|
|
6091
|
-
"./security/cert/trust-store/node-trust-store-provider-factory.js": () => Promise.resolve().then(function () { return nodeTrustStoreProviderFactory; }),
|
|
6092
|
-
"./security/encryption/channel/channel-encryption-manager-factory.js": () => Promise.resolve().then(function () { return channelEncryptionManagerFactory; }),
|
|
6093
|
-
"./security/encryption/composite-encryption-manager-factory.js": () => Promise.resolve().then(function () { return compositeEncryptionManagerFactory; }),
|
|
6094
|
-
"./security/encryption/default-secure-channel-manager-factory.js": () => Promise.resolve().then(function () { return defaultSecureChannelManagerFactory; }),
|
|
6095
|
-
"./security/encryption/sealed/x25519-encryption-manager-factory.js": () => Promise.resolve().then(function () { return x25519EncryptionManagerFactory; }),
|
|
6096
|
-
"./security/keys/x5c-key-manager-factory.js": () => Promise.resolve().then(function () { return x5cKeyManagerFactory; }),
|
|
6097
|
-
"./security/signing/eddsa-envelope-signer-factory.js": () => Promise.resolve().then(function () { return eddsaEnvelopeSignerFactory; }),
|
|
6098
|
-
"./security/signing/eddsa-envelope-verifier-factory.js": () => Promise.resolve().then(function () { return eddsaEnvelopeVerifierFactory; }),
|
|
6099
|
-
"./stickiness/aft-load-balancer-stickiness-manager-factory.js": () => Promise.resolve().then(function () { return aftLoadBalancerStickinessManagerFactory; }),
|
|
6100
|
-
"./stickiness/aft-replica-stickiness-manager-factory.js": () => Promise.resolve().then(function () { return aftReplicaStickinessManagerFactory; }),
|
|
6101
|
-
"./welcome/advanced-welcome-service-factory.js": () => Promise.resolve().then(function () { return advancedWelcomeServiceFactory; }),
|
|
6102
|
-
};
|
|
6103
|
-
|
|
6104
|
-
const SECURITY_PREFIX = "./security/";
|
|
6105
|
-
const SECURITY_MODULES = MODULES.filter((spec) => spec.startsWith(SECURITY_PREFIX));
|
|
6106
|
-
const EXTRA_MODULES = MODULES.filter((spec) => !spec.startsWith(SECURITY_PREFIX));
|
|
6107
|
-
const NODE_ONLY_MODULES = new Set([
|
|
6108
|
-
"./security/cert/default-ca-service-factory.js",
|
|
6109
|
-
"./security/cert/trust-store/node-trust-store-provider-factory.js",
|
|
6110
|
-
]);
|
|
6111
|
-
const FACTORY_MODULE_PREFIX$1 = "@naylence/advanced-security/naylence/fame/";
|
|
6112
|
-
const BROWSER_DIST_SEGMENT = "/dist/browser/";
|
|
6113
|
-
function detectModuleUrl() {
|
|
6114
|
-
if (typeof __filename === "string") {
|
|
6115
|
-
try {
|
|
6116
|
-
return __filename.startsWith("file://")
|
|
6117
|
-
? __filename
|
|
6118
|
-
: `file://${__filename}`;
|
|
6119
|
-
}
|
|
6120
|
-
catch {
|
|
6121
|
-
// fall through to stack parsing
|
|
6122
|
-
}
|
|
6123
|
-
}
|
|
6124
|
-
try {
|
|
6125
|
-
throw new Error();
|
|
6126
|
-
}
|
|
6127
|
-
catch (error) {
|
|
6128
|
-
const stack = typeof error === "object" && error && "stack" in error
|
|
6129
|
-
? String(error.stack ?? "")
|
|
6130
|
-
: "";
|
|
6131
|
-
const lines = stack.split("\n");
|
|
6132
|
-
for (const line of lines) {
|
|
6133
|
-
const match = line.match(/(https?:\/\/[^\s)]+|file:\/\/[^\s)]+\.(?:js|ts)|\/(?:[^\s)]+\.(?:js|ts)))/u);
|
|
6134
|
-
const candidate = match?.[1];
|
|
6135
|
-
if (!candidate) {
|
|
6136
|
-
continue;
|
|
6137
|
-
}
|
|
6138
|
-
if (candidate.startsWith("http://") || candidate.startsWith("https://")) {
|
|
6139
|
-
return candidate;
|
|
6140
|
-
}
|
|
6141
|
-
if (candidate.startsWith("file://")) {
|
|
6142
|
-
return candidate;
|
|
6143
|
-
}
|
|
6144
|
-
return `file://${candidate}`;
|
|
6145
|
-
}
|
|
6146
|
-
}
|
|
6147
|
-
return null;
|
|
6123
|
+
const logger$2 = getLogger("naylence.fame.welcome.advanced_welcome_service");
|
|
6124
|
+
const ENV_VAR_SHOW_ENVELOPES = "FAME_SHOW_ENVELOPES";
|
|
6125
|
+
const DEFAULT_TTL_SEC = 3600;
|
|
6126
|
+
const showEnvelopes = typeof process !== "undefined" &&
|
|
6127
|
+
process.env?.[ENV_VAR_SHOW_ENVELOPES] === "true";
|
|
6128
|
+
function nowUtc() {
|
|
6129
|
+
return new Date();
|
|
6148
6130
|
}
|
|
6149
|
-
function
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
const distMarker = "/dist/";
|
|
6156
|
-
if (sanitized.includes(esmMarker)) {
|
|
6157
|
-
return sanitized.slice(0, sanitized.indexOf(esmMarker) + esmMarker.length);
|
|
6158
|
-
}
|
|
6159
|
-
if (rawUrl.includes(BROWSER_DIST_SEGMENT)) {
|
|
6160
|
-
return new URL("../esm/naylence/fame/", rawUrl).href;
|
|
6131
|
+
function formatTimestampForConsole() {
|
|
6132
|
+
return color(formatTimestamp(), AnsiColor.GRAY);
|
|
6133
|
+
}
|
|
6134
|
+
function prettyModel(value) {
|
|
6135
|
+
try {
|
|
6136
|
+
return jsonDumps(value);
|
|
6161
6137
|
}
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
return `${base.replace(/browser\/?$/u, "")}esm/naylence/fame/`;
|
|
6138
|
+
catch (error) {
|
|
6139
|
+
return String(error);
|
|
6165
6140
|
}
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
return
|
|
6141
|
+
}
|
|
6142
|
+
function coercePlacementMetadataValue(metadata, camelCaseKey, snakeCaseKey) {
|
|
6143
|
+
if (!metadata) {
|
|
6144
|
+
return undefined;
|
|
6170
6145
|
}
|
|
6171
|
-
const
|
|
6172
|
-
if (
|
|
6173
|
-
|
|
6174
|
-
const projectRoot = sanitized.slice(0, index);
|
|
6175
|
-
return `${projectRoot}/dist/esm/naylence/fame/`;
|
|
6146
|
+
const record = metadata;
|
|
6147
|
+
if (record[camelCaseKey] !== undefined) {
|
|
6148
|
+
return record[camelCaseKey];
|
|
6176
6149
|
}
|
|
6177
|
-
if (
|
|
6178
|
-
|
|
6179
|
-
const parsed = new URL(rawUrl);
|
|
6180
|
-
const viteDepsSegment = "/node_modules/.vite/deps/";
|
|
6181
|
-
if (parsed.pathname.includes(viteDepsSegment)) {
|
|
6182
|
-
const baseOrigin = `${parsed.protocol}//${parsed.host}`;
|
|
6183
|
-
return `${baseOrigin}/node_modules/@naylence/advanced-security/dist/esm/naylence/fame/`;
|
|
6184
|
-
}
|
|
6185
|
-
}
|
|
6186
|
-
catch {
|
|
6187
|
-
// ignore
|
|
6188
|
-
}
|
|
6150
|
+
if (record[snakeCaseKey] !== undefined) {
|
|
6151
|
+
return record[snakeCaseKey];
|
|
6189
6152
|
}
|
|
6190
|
-
return
|
|
6153
|
+
return undefined;
|
|
6191
6154
|
}
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6155
|
+
class AdvancedWelcomeService {
|
|
6156
|
+
constructor(options) {
|
|
6157
|
+
this.placementStrategy = options.placementStrategy;
|
|
6158
|
+
this.transportProvisioner = options.transportProvisioner;
|
|
6159
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
6160
|
+
this.authorizer = options.authorizer ?? null;
|
|
6161
|
+
this.caServiceUrl = options.caServiceUrl;
|
|
6162
|
+
this.ttlSec =
|
|
6163
|
+
typeof options.ttlSec === "number" && Number.isFinite(options.ttlSec)
|
|
6164
|
+
? Math.max(0, options.ttlSec)
|
|
6165
|
+
: DEFAULT_TTL_SEC;
|
|
6166
|
+
logger$2.debug("initialized_advanced_welcome_service", {
|
|
6167
|
+
ca_service_url: this.caServiceUrl,
|
|
6168
|
+
ttl_sec: this.ttlSec,
|
|
6169
|
+
});
|
|
6201
6170
|
}
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6171
|
+
async handleHello(hello, metadata) {
|
|
6172
|
+
const fullMetadata = metadata
|
|
6173
|
+
? { ...metadata }
|
|
6174
|
+
: {};
|
|
6175
|
+
const trimmedSystemId = typeof hello.systemId === "string" ? hello.systemId.trim() : "";
|
|
6176
|
+
const systemId = trimmedSystemId.length > 0 ? trimmedSystemId : generateId();
|
|
6177
|
+
const wasAssigned = trimmedSystemId.length === 0;
|
|
6178
|
+
const normalizedHello = {
|
|
6179
|
+
...hello,
|
|
6180
|
+
systemId,
|
|
6181
|
+
};
|
|
6182
|
+
if (showEnvelopes) {
|
|
6183
|
+
// eslint-disable-next-line no-console
|
|
6184
|
+
console.log(`\n${formatTimestampForConsole()} - ${color("Received envelope 📨", AnsiColor.BLUE)}\n${prettyModel(normalizedHello)}`);
|
|
6214
6185
|
}
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6186
|
+
logger$2.debug("starting_hello_frame_processing", {
|
|
6187
|
+
instanceId: normalizedHello.instanceId,
|
|
6188
|
+
systemId,
|
|
6189
|
+
logicals: normalizedHello.logicals,
|
|
6190
|
+
capabilities: normalizedHello.capabilities,
|
|
6191
|
+
ttlSec: this.ttlSec,
|
|
6192
|
+
});
|
|
6193
|
+
const now = nowUtc();
|
|
6194
|
+
const expiry = new Date(now.getTime() + this.ttlSec * 1000);
|
|
6195
|
+
if (normalizedHello.instanceId) {
|
|
6196
|
+
if (fullMetadata.instanceId === undefined) {
|
|
6197
|
+
fullMetadata.instanceId = normalizedHello.instanceId;
|
|
6198
|
+
}
|
|
6199
|
+
if (fullMetadata.instance_id === undefined) {
|
|
6200
|
+
fullMetadata.instance_id = normalizedHello.instanceId;
|
|
6201
|
+
}
|
|
6221
6202
|
}
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6203
|
+
logger$2.debug("system_id_assignment_completed", {
|
|
6204
|
+
systemId,
|
|
6205
|
+
wasAssigned,
|
|
6206
|
+
});
|
|
6207
|
+
if (normalizedHello.logicals?.length) {
|
|
6208
|
+
logger$2.debug("validating_logicals_for_dns_compatibility", {
|
|
6209
|
+
logicals: normalizedHello.logicals,
|
|
6210
|
+
});
|
|
6211
|
+
const [pathsValid, pathError] = validateHostLogicals(normalizedHello.logicals);
|
|
6212
|
+
if (!pathsValid) {
|
|
6213
|
+
logger$2.error("logical_validation_failed", {
|
|
6214
|
+
error: pathError,
|
|
6215
|
+
logicals: normalizedHello.logicals,
|
|
6216
|
+
});
|
|
6217
|
+
throw new Error(`Invalid logical format: ${pathError}`);
|
|
6229
6218
|
}
|
|
6219
|
+
logger$2.debug("logicals_validation_successful");
|
|
6230
6220
|
}
|
|
6231
|
-
|
|
6232
|
-
|
|
6221
|
+
logger$2.debug("requesting_node_placement", { systemId });
|
|
6222
|
+
const placementResult = await this.placementStrategy.place(normalizedHello);
|
|
6223
|
+
if (!placementResult.accept) {
|
|
6224
|
+
logger$2.error("node_placement_rejected", {
|
|
6225
|
+
systemId,
|
|
6226
|
+
reason: placementResult.reason,
|
|
6227
|
+
});
|
|
6228
|
+
throw new Error(placementResult.reason || "Node not accepted");
|
|
6233
6229
|
}
|
|
6230
|
+
const assignedPath = placementResult.assignedPath;
|
|
6231
|
+
logger$2.debug("node_placement_accepted", {
|
|
6232
|
+
systemId,
|
|
6233
|
+
assignedPath,
|
|
6234
|
+
targetPhysicalPath: placementResult.targetPhysicalPath ?? null,
|
|
6235
|
+
targetSystemId: placementResult.targetSystemId ?? null,
|
|
6236
|
+
});
|
|
6237
|
+
const acceptedCapabilities = coercePlacementMetadataValue(placementResult.metadata, "acceptedCapabilities", "accepted_capabilities") ??
|
|
6238
|
+
normalizedHello.capabilities ??
|
|
6239
|
+
null;
|
|
6240
|
+
const acceptedLogicals = coercePlacementMetadataValue(placementResult.metadata, "acceptedLogicals", "accepted_logicals") ??
|
|
6241
|
+
normalizedHello.logicals ??
|
|
6242
|
+
null;
|
|
6243
|
+
logger$2.debug("processing_placement_result_metadata", {
|
|
6244
|
+
acceptedCapabilities,
|
|
6245
|
+
acceptedLogicals,
|
|
6246
|
+
hasPlacementMetadata: placementResult.metadata !== undefined &&
|
|
6247
|
+
placementResult.metadata !== null,
|
|
6248
|
+
});
|
|
6249
|
+
const connectionGrants = [];
|
|
6250
|
+
const metadataInstanceId = (typeof fullMetadata.instanceId === "string" &&
|
|
6251
|
+
fullMetadata.instanceId) ||
|
|
6252
|
+
(typeof fullMetadata.instance_id === "string" &&
|
|
6253
|
+
fullMetadata.instance_id) ||
|
|
6254
|
+
normalizedHello.instanceId ||
|
|
6255
|
+
generateId();
|
|
6256
|
+
if (placementResult.targetSystemId) {
|
|
6257
|
+
logger$2.debug("issuing_node_attach_token", {
|
|
6258
|
+
systemId,
|
|
6259
|
+
assignedPath,
|
|
6260
|
+
});
|
|
6261
|
+
const nodeAttachToken = await this.tokenIssuer.issue({
|
|
6262
|
+
aud: placementResult.targetPhysicalPath,
|
|
6263
|
+
system_id: systemId,
|
|
6264
|
+
parent_path: placementResult.targetPhysicalPath,
|
|
6265
|
+
assigned_path: placementResult.assignedPath,
|
|
6266
|
+
accepted_logicals: acceptedLogicals,
|
|
6267
|
+
instance_id: metadataInstanceId,
|
|
6268
|
+
});
|
|
6269
|
+
logger$2.debug("token_issued_successfully");
|
|
6270
|
+
logger$2.debug("provisioning_transport", { systemId });
|
|
6271
|
+
const transportInfo = await this.transportProvisioner.provision(placementResult, normalizedHello, fullMetadata, nodeAttachToken);
|
|
6272
|
+
logger$2.debug("transport_provisioned_successfully", {
|
|
6273
|
+
systemId,
|
|
6274
|
+
directiveType: transportInfo.connectionGrant &&
|
|
6275
|
+
typeof transportInfo.connectionGrant === "object"
|
|
6276
|
+
? (transportInfo.connectionGrant.type ??
|
|
6277
|
+
"Unknown")
|
|
6278
|
+
: "Unknown",
|
|
6279
|
+
});
|
|
6280
|
+
connectionGrants.push(transportInfo.connectionGrant);
|
|
6281
|
+
}
|
|
6282
|
+
const caSignToken = await this.tokenIssuer.issue({
|
|
6283
|
+
aud: "ca",
|
|
6284
|
+
system_id: systemId,
|
|
6285
|
+
assigned_path: assignedPath,
|
|
6286
|
+
accepted_logicals: acceptedLogicals,
|
|
6287
|
+
instance_id: metadataInstanceId,
|
|
6288
|
+
});
|
|
6289
|
+
const caGrant = {
|
|
6290
|
+
type: HTTP_CONNECTION_GRANT_TYPE,
|
|
6291
|
+
purpose: GRANT_PURPOSE_CA_SIGN,
|
|
6292
|
+
url: this.caServiceUrl,
|
|
6293
|
+
auth: {
|
|
6294
|
+
type: "BearerTokenHeaderAuth",
|
|
6295
|
+
tokenProvider: {
|
|
6296
|
+
type: "StaticTokenProvider",
|
|
6297
|
+
token: caSignToken,
|
|
6298
|
+
},
|
|
6299
|
+
},
|
|
6300
|
+
};
|
|
6301
|
+
connectionGrants.push(caGrant);
|
|
6302
|
+
const welcomeFrame = {
|
|
6303
|
+
type: "NodeWelcome",
|
|
6304
|
+
systemId,
|
|
6305
|
+
targetSystemId: placementResult.targetSystemId ?? undefined,
|
|
6306
|
+
targetPhysicalPath: placementResult.targetPhysicalPath ?? undefined,
|
|
6307
|
+
instanceId: normalizedHello.instanceId,
|
|
6308
|
+
assignedPath,
|
|
6309
|
+
acceptedCapabilities: acceptedCapabilities ?? undefined,
|
|
6310
|
+
acceptedLogicals: acceptedLogicals ?? undefined,
|
|
6311
|
+
rejectedLogicals: undefined,
|
|
6312
|
+
connectionGrants,
|
|
6313
|
+
metadata: Object.keys(fullMetadata).length > 0 ? fullMetadata : undefined,
|
|
6314
|
+
expiresAt: expiry.toISOString(),
|
|
6315
|
+
};
|
|
6316
|
+
logger$2.debug("hello_frame_processing_completed_successfully", {
|
|
6317
|
+
systemId,
|
|
6318
|
+
assignedPath,
|
|
6319
|
+
acceptedLogicals,
|
|
6320
|
+
acceptedCapabilities,
|
|
6321
|
+
expiresAt: welcomeFrame.expiresAt,
|
|
6322
|
+
instanceId: normalizedHello.instanceId,
|
|
6323
|
+
});
|
|
6324
|
+
if (showEnvelopes) {
|
|
6325
|
+
// eslint-disable-next-line no-console
|
|
6326
|
+
console.log(`\n${formatTimestampForConsole()} - ${color("Sent envelope", AnsiColor.BLUE)} 🚀\n${prettyModel(welcomeFrame)}`);
|
|
6327
|
+
}
|
|
6328
|
+
return welcomeFrame;
|
|
6234
6329
|
}
|
|
6235
|
-
const packageCandidate = resolveFactoryModuleSpecifier$1(spec);
|
|
6236
|
-
addCandidate(packageCandidate);
|
|
6237
|
-
if (packageCandidate?.endsWith(".js")) {
|
|
6238
|
-
addCandidate(packageCandidate.replace(/\.js$/u, ".ts"));
|
|
6239
|
-
}
|
|
6240
|
-
const fallback = spec.startsWith("./") ? `../${spec.slice(2)}` : spec;
|
|
6241
|
-
addCandidate(fallback);
|
|
6242
|
-
if (fallback.endsWith(".js")) {
|
|
6243
|
-
addCandidate(fallback.replace(/\.js$/u, ".ts"));
|
|
6244
|
-
}
|
|
6245
|
-
addCandidate(spec);
|
|
6246
|
-
if (spec.endsWith(".js")) {
|
|
6247
|
-
addCandidate(spec.replace(/\.js$/u, ".ts"));
|
|
6248
|
-
}
|
|
6249
|
-
return candidates;
|
|
6250
|
-
}
|
|
6251
|
-
const registeredModules = new Set();
|
|
6252
|
-
const inflightModules = new Map();
|
|
6253
|
-
const browserSkippedModules = new Set();
|
|
6254
|
-
function isNodeEnvironment$5() {
|
|
6255
|
-
return (typeof process !== "undefined" &&
|
|
6256
|
-
typeof process.release !== "undefined" &&
|
|
6257
|
-
process.release?.name === "node");
|
|
6258
|
-
}
|
|
6259
|
-
function shouldSkipModule(spec) {
|
|
6260
|
-
if (isNodeEnvironment$5()) {
|
|
6261
|
-
return false;
|
|
6262
|
-
}
|
|
6263
|
-
if (!NODE_ONLY_MODULES.has(spec)) {
|
|
6264
|
-
return false;
|
|
6265
|
-
}
|
|
6266
|
-
if (!browserSkippedModules.has(spec)) {
|
|
6267
|
-
// console.warn(
|
|
6268
|
-
// "[advanced-security:factory-manifest] skipped browser-incompatible module",
|
|
6269
|
-
// spec,
|
|
6270
|
-
// );
|
|
6271
|
-
browserSkippedModules.add(spec);
|
|
6272
|
-
}
|
|
6273
|
-
return true;
|
|
6274
|
-
}
|
|
6275
|
-
function getDynamicImporter() {
|
|
6276
|
-
if (typeof globalThis === "undefined") {
|
|
6277
|
-
return null;
|
|
6278
|
-
}
|
|
6279
|
-
const candidate = globalThis.__naylenceFactoryDynamicImporter;
|
|
6280
|
-
if (typeof candidate === "function") {
|
|
6281
|
-
return candidate;
|
|
6282
|
-
}
|
|
6283
|
-
return null;
|
|
6284
6330
|
}
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6331
|
+
|
|
6332
|
+
const FACTORY_META$4 = {
|
|
6333
|
+
base: WELCOME_SERVICE_FACTORY_BASE_TYPE,
|
|
6334
|
+
key: "AdvancedWelcomeService",
|
|
6335
|
+
priority: 100,
|
|
6336
|
+
isDefault: true,
|
|
6337
|
+
};
|
|
6338
|
+
class AdvancedWelcomeServiceFactory extends WelcomeServiceFactory {
|
|
6339
|
+
constructor() {
|
|
6340
|
+
super(...arguments);
|
|
6341
|
+
this.type = FACTORY_META$4.key;
|
|
6342
|
+
this.isDefault = FACTORY_META$4.isDefault;
|
|
6343
|
+
this.priority = FACTORY_META$4.priority;
|
|
6298
6344
|
}
|
|
6299
|
-
|
|
6300
|
-
const
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
// console.log("[debug] registering module", { spec, base, key, metadata: extraMetadata });
|
|
6313
|
-
registrar.registerFactory(base, key, Ctor, extraMetadata);
|
|
6314
|
-
return true;
|
|
6315
|
-
};
|
|
6316
|
-
for (const [index, { candidate, load }] of attempts.entries()) {
|
|
6317
|
-
try {
|
|
6318
|
-
const mod = await load();
|
|
6319
|
-
return registerFromModule(mod);
|
|
6345
|
+
async create(config, ...factoryArgs) {
|
|
6346
|
+
const normalized = normalizeConfig$2(config);
|
|
6347
|
+
// Crypto provider should be passed from upstream (node-welcome-server)
|
|
6348
|
+
// Do not create it here - downstream components should use what's passed in factoryArgs
|
|
6349
|
+
const placementStrategy = await NodePlacementStrategyFactory.createNodePlacementStrategy(normalized.placementConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
6350
|
+
const transportProvisioner = await TransportProvisionerFactory.createTransportProvisioner(normalized.transportConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
6351
|
+
const tokenIssuer = await TokenIssuerFactory.createTokenIssuer(normalized.tokenIssuerConfig ?? null, factoryArgs.length > 0 ? { factoryArgs } : undefined);
|
|
6352
|
+
let authorizer = null;
|
|
6353
|
+
if (normalized.authorizerConfig) {
|
|
6354
|
+
authorizer =
|
|
6355
|
+
(await AuthorizerFactory.createAuthorizer(normalized.authorizerConfig, {
|
|
6356
|
+
factoryArgs,
|
|
6357
|
+
})) ?? null;
|
|
6320
6358
|
}
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
if (!moduleNotFound || isLastAttempt) {
|
|
6331
|
-
console.warn("[debug] failed to import candidate", {
|
|
6332
|
-
spec,
|
|
6333
|
-
candidate,
|
|
6334
|
-
message,
|
|
6335
|
-
});
|
|
6336
|
-
console.warn("[advanced-security:factory-manifest] skipped", spec, "-", message);
|
|
6337
|
-
return false;
|
|
6338
|
-
}
|
|
6359
|
+
const options = {
|
|
6360
|
+
placementStrategy,
|
|
6361
|
+
transportProvisioner,
|
|
6362
|
+
tokenIssuer,
|
|
6363
|
+
authorizer,
|
|
6364
|
+
caServiceUrl: normalized.caServiceUrl,
|
|
6365
|
+
};
|
|
6366
|
+
if (normalized.ttlSec !== undefined) {
|
|
6367
|
+
options.ttlSec = normalized.ttlSec;
|
|
6339
6368
|
}
|
|
6369
|
+
return new AdvancedWelcomeService(options);
|
|
6340
6370
|
}
|
|
6341
|
-
return false;
|
|
6342
6371
|
}
|
|
6343
|
-
|
|
6344
|
-
if (
|
|
6345
|
-
|
|
6346
|
-
}
|
|
6347
|
-
const inflight = inflightModules.get(spec);
|
|
6348
|
-
if (inflight) {
|
|
6349
|
-
return inflight;
|
|
6372
|
+
function normalizeConfig$2(config) {
|
|
6373
|
+
if (!config) {
|
|
6374
|
+
throw new Error("AdvancedWelcomeService requires configuration");
|
|
6350
6375
|
}
|
|
6351
|
-
const
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6376
|
+
const source = config;
|
|
6377
|
+
const ttlCandidate = typeof source.ttlSec === "number"
|
|
6378
|
+
? source.ttlSec
|
|
6379
|
+
: typeof source.ttl_sec === "number"
|
|
6380
|
+
? source.ttl_sec
|
|
6381
|
+
: undefined;
|
|
6382
|
+
const caServiceUrlCandidate = typeof source.caServiceUrl === "string" &&
|
|
6383
|
+
source.caServiceUrl.trim().length > 0
|
|
6384
|
+
? source.caServiceUrl.trim()
|
|
6385
|
+
: typeof source.ca_service_url === "string" &&
|
|
6386
|
+
source.ca_service_url.trim().length > 0
|
|
6387
|
+
? source.ca_service_url.trim()
|
|
6388
|
+
: undefined;
|
|
6389
|
+
if (!caServiceUrlCandidate) {
|
|
6390
|
+
throw new Error("AdvancedWelcomeService configuration requires caServiceUrl");
|
|
6361
6391
|
}
|
|
6362
|
-
|
|
6363
|
-
|
|
6392
|
+
const normalized = {
|
|
6393
|
+
caServiceUrl: caServiceUrlCandidate,
|
|
6394
|
+
};
|
|
6395
|
+
if (source.placement !== undefined) {
|
|
6396
|
+
normalized.placementConfig =
|
|
6397
|
+
source.placement ?? null;
|
|
6364
6398
|
}
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
return 0;
|
|
6399
|
+
if (source.transport !== undefined) {
|
|
6400
|
+
normalized.transportConfig =
|
|
6401
|
+
source.transport ?? null;
|
|
6369
6402
|
}
|
|
6370
|
-
const
|
|
6371
|
-
|
|
6372
|
-
|
|
6403
|
+
const tokenIssuerConfig = source.tokenIssuer !== undefined
|
|
6404
|
+
? source.tokenIssuer
|
|
6405
|
+
: source.token_issuer !== undefined
|
|
6406
|
+
? source.token_issuer
|
|
6407
|
+
: undefined;
|
|
6408
|
+
if (tokenIssuerConfig !== undefined) {
|
|
6409
|
+
normalized.tokenIssuerConfig =
|
|
6410
|
+
tokenIssuerConfig ?? null;
|
|
6373
6411
|
}
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
async function registerAdvancedSecurityFactories(registrar = Registry, options) {
|
|
6378
|
-
const newlyRegisteredSecurity = await registerModules(SECURITY_MODULES, registrar);
|
|
6379
|
-
if (newlyRegisteredSecurity > 0) {
|
|
6380
|
-
getEncryptionManagerFactoryRegistry().forceRediscovery();
|
|
6412
|
+
if (source.authorizer !== undefined) {
|
|
6413
|
+
normalized.authorizerConfig =
|
|
6414
|
+
source.authorizer ?? null;
|
|
6381
6415
|
}
|
|
6382
|
-
{
|
|
6383
|
-
|
|
6416
|
+
if (ttlCandidate !== undefined && Number.isFinite(ttlCandidate)) {
|
|
6417
|
+
normalized.ttlSec = ttlCandidate;
|
|
6384
6418
|
}
|
|
6419
|
+
return normalized;
|
|
6385
6420
|
}
|
|
6386
6421
|
|
|
6422
|
+
var advancedWelcomeServiceFactory = /*#__PURE__*/Object.freeze({
|
|
6423
|
+
__proto__: null,
|
|
6424
|
+
AdvancedWelcomeServiceFactory: AdvancedWelcomeServiceFactory,
|
|
6425
|
+
FACTORY_META: FACTORY_META$4,
|
|
6426
|
+
default: AdvancedWelcomeServiceFactory
|
|
6427
|
+
});
|
|
6428
|
+
|
|
6387
6429
|
/**
|
|
6388
6430
|
* Isomorphic entry point for Naylence Advanced Security.
|
|
6389
6431
|
*
|
|
@@ -6458,7 +6500,16 @@ const __advancedSecurityPluginLoader = ensureAdvancedSecurityPluginLoader();
|
|
|
6458
6500
|
* runtimes lacking Node.js built-ins. Node-specific certificate authority
|
|
6459
6501
|
* helpers and Fastify bindings are intentionally excluded.
|
|
6460
6502
|
*/
|
|
6461
|
-
//
|
|
6503
|
+
// Always register the plugin directly. This ensures it is initialized even if
|
|
6504
|
+
// the dynamic import mechanism fails.
|
|
6505
|
+
(async () => {
|
|
6506
|
+
try {
|
|
6507
|
+
await advancedSecurityPlugin.register();
|
|
6508
|
+
}
|
|
6509
|
+
catch (err) {
|
|
6510
|
+
console.error('[naylence:advanced-security] Failed to auto-register plugin:', err);
|
|
6511
|
+
}
|
|
6512
|
+
})();
|
|
6462
6513
|
// Mark as used so bundlers keep the import
|
|
6463
6514
|
if (typeof __advancedSecurityPluginLoader === "undefined") {
|
|
6464
6515
|
throw new Error("Advanced security plugin loader not initialized");
|
|
@@ -9825,46 +9876,4 @@ var nodeTrustStoreProviderFactory = /*#__PURE__*/Object.freeze({
|
|
|
9825
9876
|
default: EnvTrustStoreProviderFactory
|
|
9826
9877
|
});
|
|
9827
9878
|
|
|
9828
|
-
async function registerAdvancedSecurityPluginFactories(registrar = Registry) {
|
|
9829
|
-
await registerAdvancedSecurityFactories(registrar);
|
|
9830
|
-
}
|
|
9831
|
-
let initialized = false;
|
|
9832
|
-
let initializing = null;
|
|
9833
|
-
const advancedSecurityPlugin = {
|
|
9834
|
-
name: "naylence:advanced-security",
|
|
9835
|
-
version: VERSION,
|
|
9836
|
-
async register() {
|
|
9837
|
-
// console.log('[naylence:advanced-security] register() called, initialized=', initialized);
|
|
9838
|
-
if (initialized) {
|
|
9839
|
-
// console.log('[naylence:advanced-security] already initialized, skipping');
|
|
9840
|
-
return;
|
|
9841
|
-
}
|
|
9842
|
-
if (initializing) {
|
|
9843
|
-
console.log("[naylence:advanced-security] already initializing, awaiting...");
|
|
9844
|
-
await initializing;
|
|
9845
|
-
return;
|
|
9846
|
-
}
|
|
9847
|
-
initializing = (async () => {
|
|
9848
|
-
try {
|
|
9849
|
-
// console.log('[naylence:advanced-security] registering advanced security factories...');
|
|
9850
|
-
await registerAdvancedSecurityPluginFactories();
|
|
9851
|
-
// console.log('[naylence:advanced-security] advanced security factories registered');
|
|
9852
|
-
initialized = true;
|
|
9853
|
-
}
|
|
9854
|
-
finally {
|
|
9855
|
-
initializing = null;
|
|
9856
|
-
}
|
|
9857
|
-
})();
|
|
9858
|
-
await initializing;
|
|
9859
|
-
},
|
|
9860
|
-
};
|
|
9861
|
-
const ADVANCED_SECURITY_PLUGIN_SPECIFIER = advancedSecurityPlugin.name;
|
|
9862
|
-
|
|
9863
|
-
var plugin = /*#__PURE__*/Object.freeze({
|
|
9864
|
-
__proto__: null,
|
|
9865
|
-
ADVANCED_SECURITY_PLUGIN_SPECIFIER: ADVANCED_SECURITY_PLUGIN_SPECIFIER,
|
|
9866
|
-
default: advancedSecurityPlugin,
|
|
9867
|
-
registerAdvancedSecurityPluginFactories: registerAdvancedSecurityPluginFactories
|
|
9868
|
-
});
|
|
9869
|
-
|
|
9870
9879
|
export { FACTORY_META$9 as ADVANCED_EDDSA_ENVELOPE_SIGNER_FACTORY_META, FACTORY_META$8 as ADVANCED_EDDSA_ENVELOPE_VERIFIER_FACTORY_META, FACTORY_META$4 as ADVANCED_WELCOME_FACTORY_META, AFTHelper, AFTLoadBalancerStickinessManager, AFTLoadBalancerStickinessManagerFactory, AFTReplicaStickinessManager, AFTReplicaStickinessManagerFactory, FACTORY_META$6 as AFT_LOAD_BALANCER_FACTORY_META, FACTORY_META$5 as AFT_REPLICA_FACTORY_META, AdvancedEdDSAEnvelopeSignerFactory, AdvancedEdDSAEnvelopeVerifierFactory, AdvancedWelcomeService, AdvancedWelcomeServiceFactory, CAServiceClient, CompositeEncryptionManager, CompositeEncryptionManagerFactory, FACTORY_META$b as DEFAULT_SECURE_CHANNEL_MANAGER_FACTORY_META, DEFAULT_STICKINESS_SECURITY_LEVEL, DefaultSecureChannelManager, DefaultSecureChannelManagerFactory, ENV_VAR_FAME_CA_SERVICE_URL, EdDSAEnvelopeVerifier, GRANT_PURPOSE_CA_SIGN, NoAFTSigner, SidOnlyAFTVerifier, SignedAFTSigner, SignedOptionalAFTVerifier, StickinessMode, StrictAFTVerifier, UnsignedAFTSigner, VERSION, X5CKeyManager, X5CKeyManagerFactory, FACTORY_META$7 as X5C_KEY_MANAGER_FACTORY_META, base64UrlDecode, base64UrlEncode, index as channelEncryption, createAftHelper, createAftPayload, createAftReplicaStickinessManager, createAftSigner, createAftVerifier, createEd25519Csr, extractCertificateInfo, formatCertificateInfo, normalizeStickinessMode, publicKeyFromX5c, index$1 as sealedEncryption, serializeAftClaims, serializeAftHeader, utf8Decode, validateJwkX5cCertificate };
|