@naylence/advanced-security 0.4.4 → 0.4.5
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 +201 -18
- package/dist/browser/index.mjs +200 -17
- package/dist/cjs/naylence/fame/expr/builtins.js +1 -1
- package/dist/cjs/naylence/fame/expr/builtins.js.map +1 -1
- package/dist/cjs/naylence/fame/security/auth/policy/advanced-authorization-policy.js +32 -13
- package/dist/cjs/naylence/fame/security/auth/policy/advanced-authorization-policy.js.map +1 -1
- package/dist/cjs/naylence/fame/security/auth/policy/expr-builtins.js +166 -2
- package/dist/cjs/naylence/fame/security/auth/policy/expr-builtins.js.map +1 -1
- package/dist/cjs/naylence/fame/security/auth/policy/index.js +1 -1
- package/dist/cjs/naylence/fame/security/auth/policy/index.js.map +1 -1
- package/dist/cjs/version.js +2 -2
- package/dist/esm/naylence/fame/expr/builtins.js +1 -1
- package/dist/esm/naylence/fame/expr/builtins.js.map +1 -1
- package/dist/esm/naylence/fame/security/auth/policy/advanced-authorization-policy.js +32 -13
- package/dist/esm/naylence/fame/security/auth/policy/advanced-authorization-policy.js.map +1 -1
- package/dist/esm/naylence/fame/security/auth/policy/expr-builtins.js +166 -2
- package/dist/esm/naylence/fame/security/auth/policy/expr-builtins.js.map +1 -1
- package/dist/esm/naylence/fame/security/auth/policy/index.js +1 -1
- package/dist/esm/naylence/fame/security/auth/policy/index.js.map +1 -1
- package/dist/esm/version.js +2 -2
- package/dist/node/index.cjs +203 -18
- package/dist/node/index.mjs +201 -18
- package/dist/node/node.cjs +203 -18
- package/dist/node/node.mjs +201 -18
- package/dist/types/naylence/fame/security/auth/policy/advanced-authorization-policy.d.ts.map +1 -1
- package/dist/types/naylence/fame/security/auth/policy/expr-builtins.d.ts +71 -1
- package/dist/types/naylence/fame/security/auth/policy/expr-builtins.d.ts.map +1 -1
- package/dist/types/naylence/fame/security/auth/policy/index.d.ts +1 -1
- package/dist/types/naylence/fame/security/auth/policy/index.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/node/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { Certificate, SubjectAlternativeName, NameConstraints, id_ce_subjectAltN
|
|
|
6
6
|
import { verify, etc } from '@noble/ed25519';
|
|
7
7
|
import { sha256 as sha256$1, sha512 } from '@noble/hashes/sha2.js';
|
|
8
8
|
import { generateFingerprintSync, localDeliveryContext, createFameEnvelope, generateId, formatAddress, FameAddress, SigningMaterial, DeliveryOriginType as DeliveryOriginType$1 } from '@naylence/core';
|
|
9
|
-
import { sha256 } from '@noble/hashes/
|
|
9
|
+
import { sha256 } from '@noble/hashes/sha2';
|
|
10
10
|
import { chacha20poly1305 } from '@noble/ciphers/chacha.js';
|
|
11
11
|
import { x25519 } from '@noble/curves/ed25519.js';
|
|
12
12
|
import { hkdf } from '@noble/hashes/hkdf.js';
|
|
@@ -16,12 +16,12 @@ import { sha256 as sha256$2 } from '@noble/hashes/sha256.js';
|
|
|
16
16
|
import { X509Certificate } from '@peculiar/x509';
|
|
17
17
|
|
|
18
18
|
// This file is auto-generated during build - do not edit manually
|
|
19
|
-
// Generated from package.json version: 0.4.
|
|
19
|
+
// Generated from package.json version: 0.4.5
|
|
20
20
|
/**
|
|
21
21
|
* The package version, injected at build time.
|
|
22
22
|
* @internal
|
|
23
23
|
*/
|
|
24
|
-
const VERSION = '0.4.
|
|
24
|
+
const VERSION = '0.4.5';
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Abstract Syntax Tree (AST) node types for the expression language.
|
|
@@ -1946,8 +1946,86 @@ function evaluateAsBoolean(ast, context) {
|
|
|
1946
1946
|
* Null handling semantics:
|
|
1947
1947
|
* - Scope predicate builtins (has_scope, has_any_scope, has_all_scopes)
|
|
1948
1948
|
* return `false` when passed `null` for required args.
|
|
1949
|
+
* - Security predicate builtins (is_signed, is_encrypted, is_encrypted_at_least)
|
|
1950
|
+
* return `false` when the envelope lacks the required security posture.
|
|
1949
1951
|
* - Wrong non-null types still raise BuiltinError to surface real bugs.
|
|
1950
1952
|
*/
|
|
1953
|
+
/**
|
|
1954
|
+
* Valid encryption levels for is_encrypted_at_least comparisons.
|
|
1955
|
+
*/
|
|
1956
|
+
const VALID_ENCRYPTION_LEVELS = [
|
|
1957
|
+
"plaintext",
|
|
1958
|
+
"channel",
|
|
1959
|
+
"sealed",
|
|
1960
|
+
];
|
|
1961
|
+
/**
|
|
1962
|
+
* Encryption level ordering for comparison.
|
|
1963
|
+
* Higher number = stronger encryption.
|
|
1964
|
+
*/
|
|
1965
|
+
const ENCRYPTION_LEVEL_ORDER = {
|
|
1966
|
+
plaintext: 0,
|
|
1967
|
+
channel: 1,
|
|
1968
|
+
sealed: 2,
|
|
1969
|
+
};
|
|
1970
|
+
/**
|
|
1971
|
+
* Normalizes an encryption algorithm string to an EncryptionLevel.
|
|
1972
|
+
*
|
|
1973
|
+
* Mapping rules:
|
|
1974
|
+
* - null/undefined => "plaintext" (no encryption present)
|
|
1975
|
+
* - alg contains "-channel" => "channel" (e.g., "chacha20-poly1305-channel")
|
|
1976
|
+
* - alg contains "-sealed" => "sealed" (explicit sealed marker)
|
|
1977
|
+
* - alg matches ECDH-ES pattern with AEAD cipher => "sealed" (e.g., "ECDH-ES+A256GCM")
|
|
1978
|
+
* - otherwise => "unknown"
|
|
1979
|
+
*
|
|
1980
|
+
* Currently supported algorithms:
|
|
1981
|
+
* - Channel: "chacha20-poly1305-channel"
|
|
1982
|
+
* - Sealed: "ECDH-ES+A256GCM"
|
|
1983
|
+
*
|
|
1984
|
+
* This helper is centralized to ensure consistent mapping across TS and Python.
|
|
1985
|
+
*/
|
|
1986
|
+
function normalizeEncryptionLevelFromAlg(alg) {
|
|
1987
|
+
if (alg === null || alg === undefined) {
|
|
1988
|
+
return "plaintext";
|
|
1989
|
+
}
|
|
1990
|
+
const algLower = alg.toLowerCase();
|
|
1991
|
+
// Check for channel encryption (e.g., "chacha20-poly1305-channel")
|
|
1992
|
+
// Must check before other patterns since channel suffix is explicit
|
|
1993
|
+
if (algLower.includes("-channel")) {
|
|
1994
|
+
return "channel";
|
|
1995
|
+
}
|
|
1996
|
+
// Check for explicit sealed marker
|
|
1997
|
+
if (algLower.includes("-sealed")) {
|
|
1998
|
+
return "sealed";
|
|
1999
|
+
}
|
|
2000
|
+
// ECDH-ES key agreement with AEAD cipher => sealed encryption
|
|
2001
|
+
// Pattern: "ECDH-ES+A256GCM", "ECDH-ES+A128GCM", etc.
|
|
2002
|
+
if (algLower.startsWith("ecdh-es") && algLower.includes("+a")) {
|
|
2003
|
+
return "sealed";
|
|
2004
|
+
}
|
|
2005
|
+
return "unknown";
|
|
2006
|
+
}
|
|
2007
|
+
/**
|
|
2008
|
+
* Creates security bindings from an envelope's sec header.
|
|
2009
|
+
* Exposes only metadata, never raw values like sig.val or enc.val.
|
|
2010
|
+
*/
|
|
2011
|
+
function createSecurityBindings(sec) {
|
|
2012
|
+
const sigPresent = sec?.sig !== undefined;
|
|
2013
|
+
const encPresent = sec?.enc !== undefined;
|
|
2014
|
+
return {
|
|
2015
|
+
sig: {
|
|
2016
|
+
present: sigPresent,
|
|
2017
|
+
kid: sec?.sig?.kid ?? null,
|
|
2018
|
+
},
|
|
2019
|
+
enc: {
|
|
2020
|
+
present: encPresent,
|
|
2021
|
+
alg: sec?.enc?.alg ?? null,
|
|
2022
|
+
kid: sec?.enc?.kid ?? null,
|
|
2023
|
+
level: encPresent
|
|
2024
|
+
? normalizeEncryptionLevelFromAlg(sec?.enc?.alg ?? null)
|
|
2025
|
+
: "plaintext",
|
|
2026
|
+
},
|
|
2027
|
+
};
|
|
2028
|
+
}
|
|
1951
2029
|
/**
|
|
1952
2030
|
* Checks if a value is null.
|
|
1953
2031
|
*/
|
|
@@ -1956,9 +2034,21 @@ function isNull(value) {
|
|
|
1956
2034
|
}
|
|
1957
2035
|
/**
|
|
1958
2036
|
* Creates a function registry with auth helpers installed.
|
|
2037
|
+
*
|
|
2038
|
+
* This registry extends the base builtins with:
|
|
2039
|
+
* - Scope builtins: has_scope, has_any_scope, has_all_scopes
|
|
2040
|
+
* - Security builtins: is_signed, encryption_level, is_encrypted, is_encrypted_at_least
|
|
1959
2041
|
*/
|
|
1960
|
-
function createAuthFunctionRegistry(
|
|
1961
|
-
|
|
2042
|
+
function createAuthFunctionRegistry(grantedScopesOrOptions = []) {
|
|
2043
|
+
// Handle both old signature (array) and new signature (options object)
|
|
2044
|
+
const options = Array.isArray(grantedScopesOrOptions)
|
|
2045
|
+
? { grantedScopes: grantedScopesOrOptions }
|
|
2046
|
+
: grantedScopesOrOptions;
|
|
2047
|
+
const scopes = options.grantedScopes ?? [];
|
|
2048
|
+
const secBindings = options.securityBindings ?? {
|
|
2049
|
+
sig: { present: false},
|
|
2050
|
+
enc: { level: "plaintext" },
|
|
2051
|
+
};
|
|
1962
2052
|
/**
|
|
1963
2053
|
* Checks if any granted scope matches a pattern (using glob syntax).
|
|
1964
2054
|
*/
|
|
@@ -2014,11 +2104,85 @@ function createAuthFunctionRegistry(grantedScopes = []) {
|
|
|
2014
2104
|
}
|
|
2015
2105
|
return values.every((scope) => matchesScope(scope));
|
|
2016
2106
|
};
|
|
2107
|
+
// ============================================================
|
|
2108
|
+
// Security posture builtins
|
|
2109
|
+
// ============================================================
|
|
2110
|
+
/**
|
|
2111
|
+
* is_signed() -> bool
|
|
2112
|
+
*
|
|
2113
|
+
* Returns true if the envelope has a signature present.
|
|
2114
|
+
* No arguments required.
|
|
2115
|
+
*/
|
|
2116
|
+
const is_signed = (args) => {
|
|
2117
|
+
assertArgCount(args, 0, "is_signed");
|
|
2118
|
+
return secBindings.sig.present;
|
|
2119
|
+
};
|
|
2120
|
+
/**
|
|
2121
|
+
* encryption_level() -> string
|
|
2122
|
+
*
|
|
2123
|
+
* Returns the normalized encryption level: "plaintext" | "channel" | "sealed" | "unknown"
|
|
2124
|
+
* No arguments required.
|
|
2125
|
+
*/
|
|
2126
|
+
const encryption_level = (args) => {
|
|
2127
|
+
assertArgCount(args, 0, "encryption_level");
|
|
2128
|
+
return secBindings.enc.level;
|
|
2129
|
+
};
|
|
2130
|
+
/**
|
|
2131
|
+
* is_encrypted() -> bool
|
|
2132
|
+
*
|
|
2133
|
+
* Returns true if the encryption level is not "plaintext".
|
|
2134
|
+
* This means the envelope has some form of encryption (channel, sealed, or unknown).
|
|
2135
|
+
* No arguments required.
|
|
2136
|
+
*/
|
|
2137
|
+
const is_encrypted = (args) => {
|
|
2138
|
+
assertArgCount(args, 0, "is_encrypted");
|
|
2139
|
+
return secBindings.enc.level !== "plaintext";
|
|
2140
|
+
};
|
|
2141
|
+
/**
|
|
2142
|
+
* is_encrypted_at_least(level: string) -> bool
|
|
2143
|
+
*
|
|
2144
|
+
* Returns true if the envelope's encryption level meets or exceeds the required level.
|
|
2145
|
+
*
|
|
2146
|
+
* Level ordering: plaintext < channel < sealed
|
|
2147
|
+
*
|
|
2148
|
+
* Special handling:
|
|
2149
|
+
* - "unknown" encryption level does NOT satisfy "channel" or "sealed" (conservative)
|
|
2150
|
+
* - "plaintext" is always satisfied (any envelope meets at least plaintext)
|
|
2151
|
+
* - null argument => false (predicate-style)
|
|
2152
|
+
* - invalid level string => BuiltinError
|
|
2153
|
+
*/
|
|
2154
|
+
const is_encrypted_at_least = (args) => {
|
|
2155
|
+
assertArgCount(args, 1, "is_encrypted_at_least");
|
|
2156
|
+
const requiredLevel = getArg(args, 0, "is_encrypted_at_least");
|
|
2157
|
+
// Null-tolerant: return false if level is null
|
|
2158
|
+
if (!assertStringOrNull(requiredLevel, "level", "is_encrypted_at_least")) {
|
|
2159
|
+
return false;
|
|
2160
|
+
}
|
|
2161
|
+
// Validate required level
|
|
2162
|
+
if (!VALID_ENCRYPTION_LEVELS.includes(requiredLevel)) {
|
|
2163
|
+
throw new BuiltinError("is_encrypted_at_least", `level must be one of: ${VALID_ENCRYPTION_LEVELS.join(", ")}; got "${requiredLevel}"`);
|
|
2164
|
+
}
|
|
2165
|
+
const currentLevel = secBindings.enc.level;
|
|
2166
|
+
const requiredOrder = ENCRYPTION_LEVEL_ORDER[requiredLevel] ?? 0;
|
|
2167
|
+
const currentOrder = ENCRYPTION_LEVEL_ORDER[currentLevel];
|
|
2168
|
+
// If current level is "unknown", it only satisfies "plaintext"
|
|
2169
|
+
if (currentOrder === undefined) {
|
|
2170
|
+
// "unknown" is treated as NOT meeting channel/sealed requirements
|
|
2171
|
+
return requiredOrder === 0; // Only plaintext is satisfied by unknown
|
|
2172
|
+
}
|
|
2173
|
+
return currentOrder >= requiredOrder;
|
|
2174
|
+
};
|
|
2017
2175
|
return new Map([
|
|
2018
2176
|
...BUILTIN_FUNCTIONS,
|
|
2177
|
+
// Scope builtins
|
|
2019
2178
|
["has_scope", has_scope],
|
|
2020
2179
|
["has_any_scope", has_any_scope],
|
|
2021
2180
|
["has_all_scopes", has_all_scopes],
|
|
2181
|
+
// Security posture builtins
|
|
2182
|
+
["is_signed", is_signed],
|
|
2183
|
+
["encryption_level", encryption_level],
|
|
2184
|
+
["is_encrypted", is_encrypted],
|
|
2185
|
+
["is_encrypted_at_least", is_encrypted_at_least],
|
|
2022
2186
|
]);
|
|
2023
2187
|
}
|
|
2024
2188
|
/**
|
|
@@ -2161,19 +2325,33 @@ function extractClaims(context) {
|
|
|
2161
2325
|
}
|
|
2162
2326
|
/**
|
|
2163
2327
|
* Creates a safe envelope subset for expression bindings.
|
|
2328
|
+
*
|
|
2329
|
+
* Exposes:
|
|
2330
|
+
* - id, sid, traceId, corrId, flowId, to
|
|
2331
|
+
* - frame: { type }
|
|
2332
|
+
* - sec: { sig: { present, kid }, enc: { present, alg, kid, level } }
|
|
2333
|
+
*
|
|
2334
|
+
* IMPORTANT: Does NOT expose raw security values (sig.val, enc.val).
|
|
2164
2335
|
*/
|
|
2165
2336
|
function createEnvelopeBindings(envelope) {
|
|
2166
2337
|
const frame = envelope.frame;
|
|
2167
2338
|
const envelopeRecord = envelope;
|
|
2339
|
+
const sec = envelopeRecord.sec;
|
|
2340
|
+
const securityBindings = createSecurityBindings(sec);
|
|
2168
2341
|
return {
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
:
|
|
2342
|
+
bindings: {
|
|
2343
|
+
id: envelope.id ?? null,
|
|
2344
|
+
sid: envelopeRecord.sid ?? null,
|
|
2345
|
+
traceId: envelopeRecord.traceId ?? null,
|
|
2346
|
+
corrId: envelopeRecord.corrId ?? null,
|
|
2347
|
+
flowId: envelopeRecord.flowId ?? null,
|
|
2348
|
+
to: extractAddress(envelope) ?? null,
|
|
2349
|
+
frame: frame
|
|
2350
|
+
? { type: frame.type ?? null }
|
|
2351
|
+
: { type: null },
|
|
2352
|
+
sec: securityBindings,
|
|
2353
|
+
},
|
|
2354
|
+
securityBindings,
|
|
2177
2355
|
};
|
|
2178
2356
|
}
|
|
2179
2357
|
/**
|
|
@@ -2327,11 +2505,12 @@ class AdvancedAuthorizationPolicy {
|
|
|
2327
2505
|
continue;
|
|
2328
2506
|
}
|
|
2329
2507
|
if (rule.whenAst) {
|
|
2330
|
-
// Lazy initialization of expression bindings
|
|
2508
|
+
// Lazy initialization of expression bindings and security context
|
|
2331
2509
|
if (!expressionBindings) {
|
|
2510
|
+
const envelopeResult = createEnvelopeBindings(envelope);
|
|
2332
2511
|
expressionBindings = {
|
|
2333
2512
|
claims: extractClaims(context),
|
|
2334
|
-
envelope:
|
|
2513
|
+
envelope: envelopeResult.bindings,
|
|
2335
2514
|
delivery: createDeliveryBindings(context, resolvedAction),
|
|
2336
2515
|
node: createNodeBindings(node),
|
|
2337
2516
|
time: {
|
|
@@ -2339,9 +2518,13 @@ class AdvancedAuthorizationPolicy {
|
|
|
2339
2518
|
now_iso: new Date().toISOString(),
|
|
2340
2519
|
},
|
|
2341
2520
|
};
|
|
2521
|
+
// Create function registry with security bindings for security builtins
|
|
2522
|
+
functionRegistry = createAuthFunctionRegistry({
|
|
2523
|
+
grantedScopes,
|
|
2524
|
+
securityBindings: envelopeResult.securityBindings,
|
|
2525
|
+
});
|
|
2342
2526
|
}
|
|
2343
|
-
const functions = functionRegistry
|
|
2344
|
-
functionRegistry = functions;
|
|
2527
|
+
const functions = functionRegistry;
|
|
2345
2528
|
const evalContext = {
|
|
2346
2529
|
bindings: expressionBindings,
|
|
2347
2530
|
limits: this.expressionLimits,
|
|
@@ -12662,4 +12845,4 @@ var plugin = /*#__PURE__*/Object.freeze({
|
|
|
12662
12845
|
registerAdvancedSecurityPluginFactories: registerAdvancedSecurityPluginFactories
|
|
12663
12846
|
});
|
|
12664
12847
|
|
|
12665
|
-
export { FACTORY_META$e as ADVANCED_AUTHORIZATION_POLICY_FACTORY_META, 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, AdvancedAuthorizationPolicy, AdvancedAuthorizationPolicyFactory, AdvancedEdDSAEnvelopeSignerFactory, AdvancedEdDSAEnvelopeVerifierFactory, AdvancedWelcomeService, AdvancedWelcomeServiceFactory, BUILTIN_FUNCTIONS, BuiltinError, CAServiceClient, CompositeEncryptionManager, CompositeEncryptionManagerFactory, DEFAULT_EXPRESSION_LIMITS, FACTORY_META$b as DEFAULT_SECURE_CHANNEL_MANAGER_FACTORY_META, DEFAULT_STICKINESS_SECURITY_LEVEL, DefaultSecureChannelManager, DefaultSecureChannelManagerFactory, ENV_VAR_FAME_CA_SERVICE_URL, EdDSAEnvelopeVerifier, EvaluationError, Evaluator, ExpressionError, GRANT_PURPOSE_CA_SIGN, LimitExceededError, NoAFTSigner, ParseError, Parser, SidOnlyAFTVerifier, SignedAFTSigner, SignedOptionalAFTVerifier, StickinessMode, StrictAFTVerifier, Tokenizer, TokenizerError, TypeError, UnsignedAFTSigner, VERSION, X5CKeyManager, X5CKeyManagerFactory, FACTORY_META$7 as X5C_KEY_MANAGER_FACTORY_META, __advancedSecurityPluginLoader, astToString, base64UrlDecode, base64UrlEncode, calculateAstDepth, callBuiltin, index as channelEncryption, checkArrayLength, checkAstDepth, checkAstNodeCount, checkExpressionLength, checkFunctionArgCount, checkGlobPatternLength, checkRegexPatternLength, countAstNodes, createAftHelper, createAftPayload, createAftReplicaStickinessManager, createAftSigner, createAftVerifier, createAuthFunctionRegistry, createEd25519Csr, evaluate, evaluateAsBoolean, extractCertificateInfo, formatCertificateInfo, getTypeName, isBuiltinFunction, normalizeJsValue, normalizeStickinessMode, parse, publicKeyFromX5c, registerAdvancedSecurityFactories, index$1 as sealedEncryption, serializeAftClaims, serializeAftHeader, tokenize, utf8Decode, validateJwkX5cCertificate };
|
|
12848
|
+
export { FACTORY_META$e as ADVANCED_AUTHORIZATION_POLICY_FACTORY_META, 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, AdvancedAuthorizationPolicy, AdvancedAuthorizationPolicyFactory, AdvancedEdDSAEnvelopeSignerFactory, AdvancedEdDSAEnvelopeVerifierFactory, AdvancedWelcomeService, AdvancedWelcomeServiceFactory, BUILTIN_FUNCTIONS, BuiltinError, CAServiceClient, CompositeEncryptionManager, CompositeEncryptionManagerFactory, DEFAULT_EXPRESSION_LIMITS, FACTORY_META$b as DEFAULT_SECURE_CHANNEL_MANAGER_FACTORY_META, DEFAULT_STICKINESS_SECURITY_LEVEL, DefaultSecureChannelManager, DefaultSecureChannelManagerFactory, ENV_VAR_FAME_CA_SERVICE_URL, EdDSAEnvelopeVerifier, EvaluationError, Evaluator, ExpressionError, GRANT_PURPOSE_CA_SIGN, LimitExceededError, NoAFTSigner, ParseError, Parser, SidOnlyAFTVerifier, SignedAFTSigner, SignedOptionalAFTVerifier, StickinessMode, StrictAFTVerifier, Tokenizer, TokenizerError, TypeError, UnsignedAFTSigner, VERSION, X5CKeyManager, X5CKeyManagerFactory, FACTORY_META$7 as X5C_KEY_MANAGER_FACTORY_META, __advancedSecurityPluginLoader, astToString, base64UrlDecode, base64UrlEncode, calculateAstDepth, callBuiltin, index as channelEncryption, checkArrayLength, checkAstDepth, checkAstNodeCount, checkExpressionLength, checkFunctionArgCount, checkGlobPatternLength, checkRegexPatternLength, countAstNodes, createAftHelper, createAftPayload, createAftReplicaStickinessManager, createAftSigner, createAftVerifier, createAuthFunctionRegistry, createEd25519Csr, createSecurityBindings, evaluate, evaluateAsBoolean, extractCertificateInfo, formatCertificateInfo, getTypeName, isBuiltinFunction, normalizeEncryptionLevelFromAlg, normalizeJsValue, normalizeStickinessMode, parse, publicKeyFromX5c, registerAdvancedSecurityFactories, index$1 as sealedEncryption, serializeAftClaims, serializeAftHeader, tokenize, utf8Decode, validateJwkX5cCertificate };
|
package/dist/node/node.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var factory = require('@naylence/factory');
|
|
4
4
|
var runtime = require('@naylence/runtime');
|
|
5
|
-
var
|
|
5
|
+
var sha2 = require('@noble/hashes/sha2');
|
|
6
6
|
var core = require('@naylence/core');
|
|
7
7
|
var asn1Schema = require('@peculiar/asn1-schema');
|
|
8
8
|
var asn1X509 = require('@peculiar/asn1-x509');
|
|
@@ -575,12 +575,12 @@ async function registerAdvancedSecurityFactories(registrar = factory.Registry, o
|
|
|
575
575
|
}
|
|
576
576
|
|
|
577
577
|
// This file is auto-generated during build - do not edit manually
|
|
578
|
-
// Generated from package.json version: 0.4.
|
|
578
|
+
// Generated from package.json version: 0.4.5
|
|
579
579
|
/**
|
|
580
580
|
* The package version, injected at build time.
|
|
581
581
|
* @internal
|
|
582
582
|
*/
|
|
583
|
-
const VERSION = '0.4.
|
|
583
|
+
const VERSION = '0.4.5';
|
|
584
584
|
|
|
585
585
|
async function registerAdvancedSecurityPluginFactories(registrar = factory.Registry) {
|
|
586
586
|
await registerAdvancedSecurityFactories(registrar, { includeExtras: true });
|
|
@@ -1985,7 +1985,7 @@ const secure_hash = (args) => {
|
|
|
1985
1985
|
}
|
|
1986
1986
|
// Use generateFingerprintSync from @naylence/core
|
|
1987
1987
|
// This provides SHA-256 hashing, base62 encoding, and profanity filtering
|
|
1988
|
-
return core.generateFingerprintSync(input_str, length,
|
|
1988
|
+
return core.generateFingerprintSync(input_str, length, sha2.sha256);
|
|
1989
1989
|
};
|
|
1990
1990
|
// ============================================================
|
|
1991
1991
|
// Pattern Helpers (BSL-only)
|
|
@@ -2559,8 +2559,86 @@ function evaluateAsBoolean(ast, context) {
|
|
|
2559
2559
|
* Null handling semantics:
|
|
2560
2560
|
* - Scope predicate builtins (has_scope, has_any_scope, has_all_scopes)
|
|
2561
2561
|
* return `false` when passed `null` for required args.
|
|
2562
|
+
* - Security predicate builtins (is_signed, is_encrypted, is_encrypted_at_least)
|
|
2563
|
+
* return `false` when the envelope lacks the required security posture.
|
|
2562
2564
|
* - Wrong non-null types still raise BuiltinError to surface real bugs.
|
|
2563
2565
|
*/
|
|
2566
|
+
/**
|
|
2567
|
+
* Valid encryption levels for is_encrypted_at_least comparisons.
|
|
2568
|
+
*/
|
|
2569
|
+
const VALID_ENCRYPTION_LEVELS = [
|
|
2570
|
+
"plaintext",
|
|
2571
|
+
"channel",
|
|
2572
|
+
"sealed",
|
|
2573
|
+
];
|
|
2574
|
+
/**
|
|
2575
|
+
* Encryption level ordering for comparison.
|
|
2576
|
+
* Higher number = stronger encryption.
|
|
2577
|
+
*/
|
|
2578
|
+
const ENCRYPTION_LEVEL_ORDER = {
|
|
2579
|
+
plaintext: 0,
|
|
2580
|
+
channel: 1,
|
|
2581
|
+
sealed: 2,
|
|
2582
|
+
};
|
|
2583
|
+
/**
|
|
2584
|
+
* Normalizes an encryption algorithm string to an EncryptionLevel.
|
|
2585
|
+
*
|
|
2586
|
+
* Mapping rules:
|
|
2587
|
+
* - null/undefined => "plaintext" (no encryption present)
|
|
2588
|
+
* - alg contains "-channel" => "channel" (e.g., "chacha20-poly1305-channel")
|
|
2589
|
+
* - alg contains "-sealed" => "sealed" (explicit sealed marker)
|
|
2590
|
+
* - alg matches ECDH-ES pattern with AEAD cipher => "sealed" (e.g., "ECDH-ES+A256GCM")
|
|
2591
|
+
* - otherwise => "unknown"
|
|
2592
|
+
*
|
|
2593
|
+
* Currently supported algorithms:
|
|
2594
|
+
* - Channel: "chacha20-poly1305-channel"
|
|
2595
|
+
* - Sealed: "ECDH-ES+A256GCM"
|
|
2596
|
+
*
|
|
2597
|
+
* This helper is centralized to ensure consistent mapping across TS and Python.
|
|
2598
|
+
*/
|
|
2599
|
+
function normalizeEncryptionLevelFromAlg(alg) {
|
|
2600
|
+
if (alg === null || alg === undefined) {
|
|
2601
|
+
return "plaintext";
|
|
2602
|
+
}
|
|
2603
|
+
const algLower = alg.toLowerCase();
|
|
2604
|
+
// Check for channel encryption (e.g., "chacha20-poly1305-channel")
|
|
2605
|
+
// Must check before other patterns since channel suffix is explicit
|
|
2606
|
+
if (algLower.includes("-channel")) {
|
|
2607
|
+
return "channel";
|
|
2608
|
+
}
|
|
2609
|
+
// Check for explicit sealed marker
|
|
2610
|
+
if (algLower.includes("-sealed")) {
|
|
2611
|
+
return "sealed";
|
|
2612
|
+
}
|
|
2613
|
+
// ECDH-ES key agreement with AEAD cipher => sealed encryption
|
|
2614
|
+
// Pattern: "ECDH-ES+A256GCM", "ECDH-ES+A128GCM", etc.
|
|
2615
|
+
if (algLower.startsWith("ecdh-es") && algLower.includes("+a")) {
|
|
2616
|
+
return "sealed";
|
|
2617
|
+
}
|
|
2618
|
+
return "unknown";
|
|
2619
|
+
}
|
|
2620
|
+
/**
|
|
2621
|
+
* Creates security bindings from an envelope's sec header.
|
|
2622
|
+
* Exposes only metadata, never raw values like sig.val or enc.val.
|
|
2623
|
+
*/
|
|
2624
|
+
function createSecurityBindings(sec) {
|
|
2625
|
+
const sigPresent = sec?.sig !== undefined;
|
|
2626
|
+
const encPresent = sec?.enc !== undefined;
|
|
2627
|
+
return {
|
|
2628
|
+
sig: {
|
|
2629
|
+
present: sigPresent,
|
|
2630
|
+
kid: sec?.sig?.kid ?? null,
|
|
2631
|
+
},
|
|
2632
|
+
enc: {
|
|
2633
|
+
present: encPresent,
|
|
2634
|
+
alg: sec?.enc?.alg ?? null,
|
|
2635
|
+
kid: sec?.enc?.kid ?? null,
|
|
2636
|
+
level: encPresent
|
|
2637
|
+
? normalizeEncryptionLevelFromAlg(sec?.enc?.alg ?? null)
|
|
2638
|
+
: "plaintext",
|
|
2639
|
+
},
|
|
2640
|
+
};
|
|
2641
|
+
}
|
|
2564
2642
|
/**
|
|
2565
2643
|
* Checks if a value is null.
|
|
2566
2644
|
*/
|
|
@@ -2569,9 +2647,21 @@ function isNull(value) {
|
|
|
2569
2647
|
}
|
|
2570
2648
|
/**
|
|
2571
2649
|
* Creates a function registry with auth helpers installed.
|
|
2650
|
+
*
|
|
2651
|
+
* This registry extends the base builtins with:
|
|
2652
|
+
* - Scope builtins: has_scope, has_any_scope, has_all_scopes
|
|
2653
|
+
* - Security builtins: is_signed, encryption_level, is_encrypted, is_encrypted_at_least
|
|
2572
2654
|
*/
|
|
2573
|
-
function createAuthFunctionRegistry(
|
|
2574
|
-
|
|
2655
|
+
function createAuthFunctionRegistry(grantedScopesOrOptions = []) {
|
|
2656
|
+
// Handle both old signature (array) and new signature (options object)
|
|
2657
|
+
const options = Array.isArray(grantedScopesOrOptions)
|
|
2658
|
+
? { grantedScopes: grantedScopesOrOptions }
|
|
2659
|
+
: grantedScopesOrOptions;
|
|
2660
|
+
const scopes = options.grantedScopes ?? [];
|
|
2661
|
+
const secBindings = options.securityBindings ?? {
|
|
2662
|
+
sig: { present: false, kid: null },
|
|
2663
|
+
enc: { present: false, alg: null, kid: null, level: "plaintext" },
|
|
2664
|
+
};
|
|
2575
2665
|
/**
|
|
2576
2666
|
* Checks if any granted scope matches a pattern (using glob syntax).
|
|
2577
2667
|
*/
|
|
@@ -2627,11 +2717,85 @@ function createAuthFunctionRegistry(grantedScopes = []) {
|
|
|
2627
2717
|
}
|
|
2628
2718
|
return values.every((scope) => matchesScope(scope));
|
|
2629
2719
|
};
|
|
2720
|
+
// ============================================================
|
|
2721
|
+
// Security posture builtins
|
|
2722
|
+
// ============================================================
|
|
2723
|
+
/**
|
|
2724
|
+
* is_signed() -> bool
|
|
2725
|
+
*
|
|
2726
|
+
* Returns true if the envelope has a signature present.
|
|
2727
|
+
* No arguments required.
|
|
2728
|
+
*/
|
|
2729
|
+
const is_signed = (args) => {
|
|
2730
|
+
assertArgCount(args, 0, "is_signed");
|
|
2731
|
+
return secBindings.sig.present;
|
|
2732
|
+
};
|
|
2733
|
+
/**
|
|
2734
|
+
* encryption_level() -> string
|
|
2735
|
+
*
|
|
2736
|
+
* Returns the normalized encryption level: "plaintext" | "channel" | "sealed" | "unknown"
|
|
2737
|
+
* No arguments required.
|
|
2738
|
+
*/
|
|
2739
|
+
const encryption_level = (args) => {
|
|
2740
|
+
assertArgCount(args, 0, "encryption_level");
|
|
2741
|
+
return secBindings.enc.level;
|
|
2742
|
+
};
|
|
2743
|
+
/**
|
|
2744
|
+
* is_encrypted() -> bool
|
|
2745
|
+
*
|
|
2746
|
+
* Returns true if the encryption level is not "plaintext".
|
|
2747
|
+
* This means the envelope has some form of encryption (channel, sealed, or unknown).
|
|
2748
|
+
* No arguments required.
|
|
2749
|
+
*/
|
|
2750
|
+
const is_encrypted = (args) => {
|
|
2751
|
+
assertArgCount(args, 0, "is_encrypted");
|
|
2752
|
+
return secBindings.enc.level !== "plaintext";
|
|
2753
|
+
};
|
|
2754
|
+
/**
|
|
2755
|
+
* is_encrypted_at_least(level: string) -> bool
|
|
2756
|
+
*
|
|
2757
|
+
* Returns true if the envelope's encryption level meets or exceeds the required level.
|
|
2758
|
+
*
|
|
2759
|
+
* Level ordering: plaintext < channel < sealed
|
|
2760
|
+
*
|
|
2761
|
+
* Special handling:
|
|
2762
|
+
* - "unknown" encryption level does NOT satisfy "channel" or "sealed" (conservative)
|
|
2763
|
+
* - "plaintext" is always satisfied (any envelope meets at least plaintext)
|
|
2764
|
+
* - null argument => false (predicate-style)
|
|
2765
|
+
* - invalid level string => BuiltinError
|
|
2766
|
+
*/
|
|
2767
|
+
const is_encrypted_at_least = (args) => {
|
|
2768
|
+
assertArgCount(args, 1, "is_encrypted_at_least");
|
|
2769
|
+
const requiredLevel = getArg(args, 0, "is_encrypted_at_least");
|
|
2770
|
+
// Null-tolerant: return false if level is null
|
|
2771
|
+
if (!assertStringOrNull(requiredLevel, "level", "is_encrypted_at_least")) {
|
|
2772
|
+
return false;
|
|
2773
|
+
}
|
|
2774
|
+
// Validate required level
|
|
2775
|
+
if (!VALID_ENCRYPTION_LEVELS.includes(requiredLevel)) {
|
|
2776
|
+
throw new BuiltinError("is_encrypted_at_least", `level must be one of: ${VALID_ENCRYPTION_LEVELS.join(", ")}; got "${requiredLevel}"`);
|
|
2777
|
+
}
|
|
2778
|
+
const currentLevel = secBindings.enc.level;
|
|
2779
|
+
const requiredOrder = ENCRYPTION_LEVEL_ORDER[requiredLevel] ?? 0;
|
|
2780
|
+
const currentOrder = ENCRYPTION_LEVEL_ORDER[currentLevel];
|
|
2781
|
+
// If current level is "unknown", it only satisfies "plaintext"
|
|
2782
|
+
if (currentOrder === undefined) {
|
|
2783
|
+
// "unknown" is treated as NOT meeting channel/sealed requirements
|
|
2784
|
+
return requiredOrder === 0; // Only plaintext is satisfied by unknown
|
|
2785
|
+
}
|
|
2786
|
+
return currentOrder >= requiredOrder;
|
|
2787
|
+
};
|
|
2630
2788
|
return new Map([
|
|
2631
2789
|
...BUILTIN_FUNCTIONS,
|
|
2790
|
+
// Scope builtins
|
|
2632
2791
|
["has_scope", has_scope],
|
|
2633
2792
|
["has_any_scope", has_any_scope],
|
|
2634
2793
|
["has_all_scopes", has_all_scopes],
|
|
2794
|
+
// Security posture builtins
|
|
2795
|
+
["is_signed", is_signed],
|
|
2796
|
+
["encryption_level", encryption_level],
|
|
2797
|
+
["is_encrypted", is_encrypted],
|
|
2798
|
+
["is_encrypted_at_least", is_encrypted_at_least],
|
|
2635
2799
|
]);
|
|
2636
2800
|
}
|
|
2637
2801
|
/**
|
|
@@ -2774,19 +2938,33 @@ function extractClaims(context) {
|
|
|
2774
2938
|
}
|
|
2775
2939
|
/**
|
|
2776
2940
|
* Creates a safe envelope subset for expression bindings.
|
|
2941
|
+
*
|
|
2942
|
+
* Exposes:
|
|
2943
|
+
* - id, sid, traceId, corrId, flowId, to
|
|
2944
|
+
* - frame: { type }
|
|
2945
|
+
* - sec: { sig: { present, kid }, enc: { present, alg, kid, level } }
|
|
2946
|
+
*
|
|
2947
|
+
* IMPORTANT: Does NOT expose raw security values (sig.val, enc.val).
|
|
2777
2948
|
*/
|
|
2778
2949
|
function createEnvelopeBindings(envelope) {
|
|
2779
2950
|
const frame = envelope.frame;
|
|
2780
2951
|
const envelopeRecord = envelope;
|
|
2952
|
+
const sec = envelopeRecord.sec;
|
|
2953
|
+
const securityBindings = createSecurityBindings(sec);
|
|
2781
2954
|
return {
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
:
|
|
2955
|
+
bindings: {
|
|
2956
|
+
id: envelope.id ?? null,
|
|
2957
|
+
sid: envelopeRecord.sid ?? null,
|
|
2958
|
+
traceId: envelopeRecord.traceId ?? null,
|
|
2959
|
+
corrId: envelopeRecord.corrId ?? null,
|
|
2960
|
+
flowId: envelopeRecord.flowId ?? null,
|
|
2961
|
+
to: extractAddress(envelope) ?? null,
|
|
2962
|
+
frame: frame
|
|
2963
|
+
? { type: frame.type ?? null }
|
|
2964
|
+
: { type: null },
|
|
2965
|
+
sec: securityBindings,
|
|
2966
|
+
},
|
|
2967
|
+
securityBindings,
|
|
2790
2968
|
};
|
|
2791
2969
|
}
|
|
2792
2970
|
/**
|
|
@@ -2940,11 +3118,12 @@ class AdvancedAuthorizationPolicy {
|
|
|
2940
3118
|
continue;
|
|
2941
3119
|
}
|
|
2942
3120
|
if (rule.whenAst) {
|
|
2943
|
-
// Lazy initialization of expression bindings
|
|
3121
|
+
// Lazy initialization of expression bindings and security context
|
|
2944
3122
|
if (!expressionBindings) {
|
|
3123
|
+
const envelopeResult = createEnvelopeBindings(envelope);
|
|
2945
3124
|
expressionBindings = {
|
|
2946
3125
|
claims: extractClaims(context),
|
|
2947
|
-
envelope:
|
|
3126
|
+
envelope: envelopeResult.bindings,
|
|
2948
3127
|
delivery: createDeliveryBindings(context, resolvedAction),
|
|
2949
3128
|
node: createNodeBindings(node),
|
|
2950
3129
|
time: {
|
|
@@ -2952,9 +3131,13 @@ class AdvancedAuthorizationPolicy {
|
|
|
2952
3131
|
now_iso: new Date().toISOString(),
|
|
2953
3132
|
},
|
|
2954
3133
|
};
|
|
3134
|
+
// Create function registry with security bindings for security builtins
|
|
3135
|
+
functionRegistry = createAuthFunctionRegistry({
|
|
3136
|
+
grantedScopes,
|
|
3137
|
+
securityBindings: envelopeResult.securityBindings,
|
|
3138
|
+
});
|
|
2955
3139
|
}
|
|
2956
|
-
const functions = functionRegistry
|
|
2957
|
-
functionRegistry = functions;
|
|
3140
|
+
const functions = functionRegistry;
|
|
2958
3141
|
const evalContext = {
|
|
2959
3142
|
bindings: expressionBindings,
|
|
2960
3143
|
limits: this.expressionLimits,
|
|
@@ -13104,6 +13287,7 @@ exports.createAftVerifier = createAftVerifier;
|
|
|
13104
13287
|
exports.createAuthFunctionRegistry = createAuthFunctionRegistry;
|
|
13105
13288
|
exports.createEd25519Csr = createEd25519Csr;
|
|
13106
13289
|
exports.createEd25519CsrFromPem = createEd25519CsrFromPem;
|
|
13290
|
+
exports.createSecurityBindings = createSecurityBindings;
|
|
13107
13291
|
exports.createTestCA = createTestCA;
|
|
13108
13292
|
exports.evaluate = evaluate;
|
|
13109
13293
|
exports.evaluateAsBoolean = evaluateAsBoolean;
|
|
@@ -13116,6 +13300,7 @@ exports.extractSpiffeIdFromCert = extractSpiffeIdFromCert;
|
|
|
13116
13300
|
exports.formatCertificateInfo = formatCertificateInfo;
|
|
13117
13301
|
exports.getTypeName = getTypeName;
|
|
13118
13302
|
exports.isBuiltinFunction = isBuiltinFunction;
|
|
13303
|
+
exports.normalizeEncryptionLevelFromAlg = normalizeEncryptionLevelFromAlg;
|
|
13119
13304
|
exports.normalizeJsValue = normalizeJsValue;
|
|
13120
13305
|
exports.normalizeStickinessMode = normalizeStickinessMode;
|
|
13121
13306
|
exports.parse = parse;
|