@naylence/advanced-security 0.4.3 → 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.
Files changed (32) hide show
  1. package/dist/browser/index.cjs +204 -18
  2. package/dist/browser/index.mjs +203 -17
  3. package/dist/cjs/naylence/fame/expr/builtins.js +1 -1
  4. package/dist/cjs/naylence/fame/expr/builtins.js.map +1 -1
  5. package/dist/cjs/naylence/fame/security/auth/policy/advanced-authorization-policy.js +35 -13
  6. package/dist/cjs/naylence/fame/security/auth/policy/advanced-authorization-policy.js.map +1 -1
  7. package/dist/cjs/naylence/fame/security/auth/policy/expr-builtins.js +166 -2
  8. package/dist/cjs/naylence/fame/security/auth/policy/expr-builtins.js.map +1 -1
  9. package/dist/cjs/naylence/fame/security/auth/policy/index.js +1 -1
  10. package/dist/cjs/naylence/fame/security/auth/policy/index.js.map +1 -1
  11. package/dist/cjs/version.js +2 -2
  12. package/dist/esm/naylence/fame/expr/builtins.js +1 -1
  13. package/dist/esm/naylence/fame/expr/builtins.js.map +1 -1
  14. package/dist/esm/naylence/fame/security/auth/policy/advanced-authorization-policy.js +35 -13
  15. package/dist/esm/naylence/fame/security/auth/policy/advanced-authorization-policy.js.map +1 -1
  16. package/dist/esm/naylence/fame/security/auth/policy/expr-builtins.js +166 -2
  17. package/dist/esm/naylence/fame/security/auth/policy/expr-builtins.js.map +1 -1
  18. package/dist/esm/naylence/fame/security/auth/policy/index.js +1 -1
  19. package/dist/esm/naylence/fame/security/auth/policy/index.js.map +1 -1
  20. package/dist/esm/version.js +2 -2
  21. package/dist/node/index.cjs +206 -18
  22. package/dist/node/index.mjs +204 -18
  23. package/dist/node/node.cjs +206 -18
  24. package/dist/node/node.mjs +204 -18
  25. package/dist/types/naylence/fame/security/auth/policy/advanced-authorization-policy.d.ts +1 -1
  26. package/dist/types/naylence/fame/security/auth/policy/advanced-authorization-policy.d.ts.map +1 -1
  27. package/dist/types/naylence/fame/security/auth/policy/expr-builtins.d.ts +71 -1
  28. package/dist/types/naylence/fame/security/auth/policy/expr-builtins.d.ts.map +1 -1
  29. package/dist/types/naylence/fame/security/auth/policy/index.d.ts +1 -1
  30. package/dist/types/naylence/fame/security/auth/policy/index.d.ts.map +1 -1
  31. package/dist/types/version.d.ts +1 -1
  32. package/package.json +1 -1
@@ -8,7 +8,7 @@ var asn1X509 = require('@peculiar/asn1-x509');
8
8
  var ed25519 = require('@noble/ed25519');
9
9
  var sha2_js = require('@noble/hashes/sha2.js');
10
10
  var core = require('@naylence/core');
11
- var sha256 = require('@noble/hashes/sha256');
11
+ var sha2 = require('@noble/hashes/sha2');
12
12
  var chacha_js = require('@noble/ciphers/chacha.js');
13
13
  var ed25519_js = require('@noble/curves/ed25519.js');
14
14
  var hkdf_js = require('@noble/hashes/hkdf.js');
@@ -18,12 +18,12 @@ var sha256_js = require('@noble/hashes/sha256.js');
18
18
  var x509 = require('@peculiar/x509');
19
19
 
20
20
  // This file is auto-generated during build - do not edit manually
21
- // Generated from package.json version: 0.4.3
21
+ // Generated from package.json version: 0.4.5
22
22
  /**
23
23
  * The package version, injected at build time.
24
24
  * @internal
25
25
  */
26
- const VERSION = '0.4.3';
26
+ const VERSION = '0.4.5';
27
27
 
28
28
  /**
29
29
  * Abstract Syntax Tree (AST) node types for the expression language.
@@ -1384,7 +1384,7 @@ const secure_hash = (args) => {
1384
1384
  }
1385
1385
  // Use generateFingerprintSync from @naylence/core
1386
1386
  // This provides SHA-256 hashing, base62 encoding, and profanity filtering
1387
- return core.generateFingerprintSync(input_str, length, sha256.sha256);
1387
+ return core.generateFingerprintSync(input_str, length, sha2.sha256);
1388
1388
  };
1389
1389
  // ============================================================
1390
1390
  // Pattern Helpers (BSL-only)
@@ -1948,8 +1948,86 @@ function evaluateAsBoolean(ast, context) {
1948
1948
  * Null handling semantics:
1949
1949
  * - Scope predicate builtins (has_scope, has_any_scope, has_all_scopes)
1950
1950
  * return `false` when passed `null` for required args.
1951
+ * - Security predicate builtins (is_signed, is_encrypted, is_encrypted_at_least)
1952
+ * return `false` when the envelope lacks the required security posture.
1951
1953
  * - Wrong non-null types still raise BuiltinError to surface real bugs.
1952
1954
  */
1955
+ /**
1956
+ * Valid encryption levels for is_encrypted_at_least comparisons.
1957
+ */
1958
+ const VALID_ENCRYPTION_LEVELS = [
1959
+ "plaintext",
1960
+ "channel",
1961
+ "sealed",
1962
+ ];
1963
+ /**
1964
+ * Encryption level ordering for comparison.
1965
+ * Higher number = stronger encryption.
1966
+ */
1967
+ const ENCRYPTION_LEVEL_ORDER = {
1968
+ plaintext: 0,
1969
+ channel: 1,
1970
+ sealed: 2,
1971
+ };
1972
+ /**
1973
+ * Normalizes an encryption algorithm string to an EncryptionLevel.
1974
+ *
1975
+ * Mapping rules:
1976
+ * - null/undefined => "plaintext" (no encryption present)
1977
+ * - alg contains "-channel" => "channel" (e.g., "chacha20-poly1305-channel")
1978
+ * - alg contains "-sealed" => "sealed" (explicit sealed marker)
1979
+ * - alg matches ECDH-ES pattern with AEAD cipher => "sealed" (e.g., "ECDH-ES+A256GCM")
1980
+ * - otherwise => "unknown"
1981
+ *
1982
+ * Currently supported algorithms:
1983
+ * - Channel: "chacha20-poly1305-channel"
1984
+ * - Sealed: "ECDH-ES+A256GCM"
1985
+ *
1986
+ * This helper is centralized to ensure consistent mapping across TS and Python.
1987
+ */
1988
+ function normalizeEncryptionLevelFromAlg(alg) {
1989
+ if (alg === null || alg === undefined) {
1990
+ return "plaintext";
1991
+ }
1992
+ const algLower = alg.toLowerCase();
1993
+ // Check for channel encryption (e.g., "chacha20-poly1305-channel")
1994
+ // Must check before other patterns since channel suffix is explicit
1995
+ if (algLower.includes("-channel")) {
1996
+ return "channel";
1997
+ }
1998
+ // Check for explicit sealed marker
1999
+ if (algLower.includes("-sealed")) {
2000
+ return "sealed";
2001
+ }
2002
+ // ECDH-ES key agreement with AEAD cipher => sealed encryption
2003
+ // Pattern: "ECDH-ES+A256GCM", "ECDH-ES+A128GCM", etc.
2004
+ if (algLower.startsWith("ecdh-es") && algLower.includes("+a")) {
2005
+ return "sealed";
2006
+ }
2007
+ return "unknown";
2008
+ }
2009
+ /**
2010
+ * Creates security bindings from an envelope's sec header.
2011
+ * Exposes only metadata, never raw values like sig.val or enc.val.
2012
+ */
2013
+ function createSecurityBindings(sec) {
2014
+ const sigPresent = sec?.sig !== undefined;
2015
+ const encPresent = sec?.enc !== undefined;
2016
+ return {
2017
+ sig: {
2018
+ present: sigPresent,
2019
+ kid: sec?.sig?.kid ?? null,
2020
+ },
2021
+ enc: {
2022
+ present: encPresent,
2023
+ alg: sec?.enc?.alg ?? null,
2024
+ kid: sec?.enc?.kid ?? null,
2025
+ level: encPresent
2026
+ ? normalizeEncryptionLevelFromAlg(sec?.enc?.alg ?? null)
2027
+ : "plaintext",
2028
+ },
2029
+ };
2030
+ }
1953
2031
  /**
1954
2032
  * Checks if a value is null.
1955
2033
  */
@@ -1958,9 +2036,21 @@ function isNull(value) {
1958
2036
  }
1959
2037
  /**
1960
2038
  * Creates a function registry with auth helpers installed.
2039
+ *
2040
+ * This registry extends the base builtins with:
2041
+ * - Scope builtins: has_scope, has_any_scope, has_all_scopes
2042
+ * - Security builtins: is_signed, encryption_level, is_encrypted, is_encrypted_at_least
1961
2043
  */
1962
- function createAuthFunctionRegistry(grantedScopes = []) {
1963
- const scopes = grantedScopes ?? [];
2044
+ function createAuthFunctionRegistry(grantedScopesOrOptions = []) {
2045
+ // Handle both old signature (array) and new signature (options object)
2046
+ const options = Array.isArray(grantedScopesOrOptions)
2047
+ ? { grantedScopes: grantedScopesOrOptions }
2048
+ : grantedScopesOrOptions;
2049
+ const scopes = options.grantedScopes ?? [];
2050
+ const secBindings = options.securityBindings ?? {
2051
+ sig: { present: false},
2052
+ enc: { level: "plaintext" },
2053
+ };
1964
2054
  /**
1965
2055
  * Checks if any granted scope matches a pattern (using glob syntax).
1966
2056
  */
@@ -2016,11 +2106,85 @@ function createAuthFunctionRegistry(grantedScopes = []) {
2016
2106
  }
2017
2107
  return values.every((scope) => matchesScope(scope));
2018
2108
  };
2109
+ // ============================================================
2110
+ // Security posture builtins
2111
+ // ============================================================
2112
+ /**
2113
+ * is_signed() -> bool
2114
+ *
2115
+ * Returns true if the envelope has a signature present.
2116
+ * No arguments required.
2117
+ */
2118
+ const is_signed = (args) => {
2119
+ assertArgCount(args, 0, "is_signed");
2120
+ return secBindings.sig.present;
2121
+ };
2122
+ /**
2123
+ * encryption_level() -> string
2124
+ *
2125
+ * Returns the normalized encryption level: "plaintext" | "channel" | "sealed" | "unknown"
2126
+ * No arguments required.
2127
+ */
2128
+ const encryption_level = (args) => {
2129
+ assertArgCount(args, 0, "encryption_level");
2130
+ return secBindings.enc.level;
2131
+ };
2132
+ /**
2133
+ * is_encrypted() -> bool
2134
+ *
2135
+ * Returns true if the encryption level is not "plaintext".
2136
+ * This means the envelope has some form of encryption (channel, sealed, or unknown).
2137
+ * No arguments required.
2138
+ */
2139
+ const is_encrypted = (args) => {
2140
+ assertArgCount(args, 0, "is_encrypted");
2141
+ return secBindings.enc.level !== "plaintext";
2142
+ };
2143
+ /**
2144
+ * is_encrypted_at_least(level: string) -> bool
2145
+ *
2146
+ * Returns true if the envelope's encryption level meets or exceeds the required level.
2147
+ *
2148
+ * Level ordering: plaintext < channel < sealed
2149
+ *
2150
+ * Special handling:
2151
+ * - "unknown" encryption level does NOT satisfy "channel" or "sealed" (conservative)
2152
+ * - "plaintext" is always satisfied (any envelope meets at least plaintext)
2153
+ * - null argument => false (predicate-style)
2154
+ * - invalid level string => BuiltinError
2155
+ */
2156
+ const is_encrypted_at_least = (args) => {
2157
+ assertArgCount(args, 1, "is_encrypted_at_least");
2158
+ const requiredLevel = getArg(args, 0, "is_encrypted_at_least");
2159
+ // Null-tolerant: return false if level is null
2160
+ if (!assertStringOrNull(requiredLevel, "level", "is_encrypted_at_least")) {
2161
+ return false;
2162
+ }
2163
+ // Validate required level
2164
+ if (!VALID_ENCRYPTION_LEVELS.includes(requiredLevel)) {
2165
+ throw new BuiltinError("is_encrypted_at_least", `level must be one of: ${VALID_ENCRYPTION_LEVELS.join(", ")}; got "${requiredLevel}"`);
2166
+ }
2167
+ const currentLevel = secBindings.enc.level;
2168
+ const requiredOrder = ENCRYPTION_LEVEL_ORDER[requiredLevel] ?? 0;
2169
+ const currentOrder = ENCRYPTION_LEVEL_ORDER[currentLevel];
2170
+ // If current level is "unknown", it only satisfies "plaintext"
2171
+ if (currentOrder === undefined) {
2172
+ // "unknown" is treated as NOT meeting channel/sealed requirements
2173
+ return requiredOrder === 0; // Only plaintext is satisfied by unknown
2174
+ }
2175
+ return currentOrder >= requiredOrder;
2176
+ };
2019
2177
  return new Map([
2020
2178
  ...BUILTIN_FUNCTIONS,
2179
+ // Scope builtins
2021
2180
  ["has_scope", has_scope],
2022
2181
  ["has_any_scope", has_any_scope],
2023
2182
  ["has_all_scopes", has_all_scopes],
2183
+ // Security posture builtins
2184
+ ["is_signed", is_signed],
2185
+ ["encryption_level", encryption_level],
2186
+ ["is_encrypted", is_encrypted],
2187
+ ["is_encrypted_at_least", is_encrypted_at_least],
2024
2188
  ]);
2025
2189
  }
2026
2190
  /**
@@ -2099,6 +2263,9 @@ const VALID_FRAME_TYPES = [
2099
2263
  "CreditUpdate",
2100
2264
  "KeyAnnounce",
2101
2265
  "KeyRequest",
2266
+ "SecureOpen",
2267
+ "SecureAccept",
2268
+ "SecureClose",
2102
2269
  ];
2103
2270
  /**
2104
2271
  * Simple console logger implementation.
@@ -2160,19 +2327,33 @@ function extractClaims(context) {
2160
2327
  }
2161
2328
  /**
2162
2329
  * Creates a safe envelope subset for expression bindings.
2330
+ *
2331
+ * Exposes:
2332
+ * - id, sid, traceId, corrId, flowId, to
2333
+ * - frame: { type }
2334
+ * - sec: { sig: { present, kid }, enc: { present, alg, kid, level } }
2335
+ *
2336
+ * IMPORTANT: Does NOT expose raw security values (sig.val, enc.val).
2163
2337
  */
2164
2338
  function createEnvelopeBindings(envelope) {
2165
2339
  const frame = envelope.frame;
2166
2340
  const envelopeRecord = envelope;
2341
+ const sec = envelopeRecord.sec;
2342
+ const securityBindings = createSecurityBindings(sec);
2167
2343
  return {
2168
- id: envelope.id ?? null,
2169
- traceId: envelopeRecord.traceId ?? null,
2170
- corrId: envelopeRecord.corrId ?? null,
2171
- flowId: envelopeRecord.flowId ?? null,
2172
- to: extractAddress(envelope) ?? null,
2173
- frame: frame
2174
- ? { type: frame.type ?? null }
2175
- : { type: null },
2344
+ bindings: {
2345
+ id: envelope.id ?? null,
2346
+ sid: envelopeRecord.sid ?? null,
2347
+ traceId: envelopeRecord.traceId ?? null,
2348
+ corrId: envelopeRecord.corrId ?? null,
2349
+ flowId: envelopeRecord.flowId ?? null,
2350
+ to: extractAddress(envelope) ?? null,
2351
+ frame: frame
2352
+ ? { type: frame.type ?? null }
2353
+ : { type: null },
2354
+ sec: securityBindings,
2355
+ },
2356
+ securityBindings,
2176
2357
  };
2177
2358
  }
2178
2359
  /**
@@ -2326,11 +2507,12 @@ class AdvancedAuthorizationPolicy {
2326
2507
  continue;
2327
2508
  }
2328
2509
  if (rule.whenAst) {
2329
- // Lazy initialization of expression bindings
2510
+ // Lazy initialization of expression bindings and security context
2330
2511
  if (!expressionBindings) {
2512
+ const envelopeResult = createEnvelopeBindings(envelope);
2331
2513
  expressionBindings = {
2332
2514
  claims: extractClaims(context),
2333
- envelope: createEnvelopeBindings(envelope),
2515
+ envelope: envelopeResult.bindings,
2334
2516
  delivery: createDeliveryBindings(context, resolvedAction),
2335
2517
  node: createNodeBindings(node),
2336
2518
  time: {
@@ -2338,9 +2520,13 @@ class AdvancedAuthorizationPolicy {
2338
2520
  now_iso: new Date().toISOString(),
2339
2521
  },
2340
2522
  };
2523
+ // Create function registry with security bindings for security builtins
2524
+ functionRegistry = createAuthFunctionRegistry({
2525
+ grantedScopes,
2526
+ securityBindings: envelopeResult.securityBindings,
2527
+ });
2341
2528
  }
2342
- const functions = functionRegistry ?? createAuthFunctionRegistry(grantedScopes);
2343
- functionRegistry = functions;
2529
+ const functions = functionRegistry;
2344
2530
  const evalContext = {
2345
2531
  bindings: expressionBindings,
2346
2532
  limits: this.expressionLimits,
@@ -12732,12 +12918,14 @@ exports.createAftSigner = createAftSigner;
12732
12918
  exports.createAftVerifier = createAftVerifier;
12733
12919
  exports.createAuthFunctionRegistry = createAuthFunctionRegistry;
12734
12920
  exports.createEd25519Csr = createEd25519Csr;
12921
+ exports.createSecurityBindings = createSecurityBindings;
12735
12922
  exports.evaluate = evaluate;
12736
12923
  exports.evaluateAsBoolean = evaluateAsBoolean;
12737
12924
  exports.extractCertificateInfo = extractCertificateInfo;
12738
12925
  exports.formatCertificateInfo = formatCertificateInfo;
12739
12926
  exports.getTypeName = getTypeName;
12740
12927
  exports.isBuiltinFunction = isBuiltinFunction;
12928
+ exports.normalizeEncryptionLevelFromAlg = normalizeEncryptionLevelFromAlg;
12741
12929
  exports.normalizeJsValue = normalizeJsValue;
12742
12930
  exports.normalizeStickinessMode = normalizeStickinessMode;
12743
12931
  exports.parse = parse;
@@ -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/sha256';
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.3
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.3';
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(grantedScopes = []) {
1961
- const scopes = grantedScopes ?? [];
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
  /**
@@ -2097,6 +2261,9 @@ const VALID_FRAME_TYPES = [
2097
2261
  "CreditUpdate",
2098
2262
  "KeyAnnounce",
2099
2263
  "KeyRequest",
2264
+ "SecureOpen",
2265
+ "SecureAccept",
2266
+ "SecureClose",
2100
2267
  ];
2101
2268
  /**
2102
2269
  * Simple console logger implementation.
@@ -2158,19 +2325,33 @@ function extractClaims(context) {
2158
2325
  }
2159
2326
  /**
2160
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).
2161
2335
  */
2162
2336
  function createEnvelopeBindings(envelope) {
2163
2337
  const frame = envelope.frame;
2164
2338
  const envelopeRecord = envelope;
2339
+ const sec = envelopeRecord.sec;
2340
+ const securityBindings = createSecurityBindings(sec);
2165
2341
  return {
2166
- id: envelope.id ?? null,
2167
- traceId: envelopeRecord.traceId ?? null,
2168
- corrId: envelopeRecord.corrId ?? null,
2169
- flowId: envelopeRecord.flowId ?? null,
2170
- to: extractAddress(envelope) ?? null,
2171
- frame: frame
2172
- ? { type: frame.type ?? null }
2173
- : { type: null },
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,
2174
2355
  };
2175
2356
  }
2176
2357
  /**
@@ -2324,11 +2505,12 @@ class AdvancedAuthorizationPolicy {
2324
2505
  continue;
2325
2506
  }
2326
2507
  if (rule.whenAst) {
2327
- // Lazy initialization of expression bindings
2508
+ // Lazy initialization of expression bindings and security context
2328
2509
  if (!expressionBindings) {
2510
+ const envelopeResult = createEnvelopeBindings(envelope);
2329
2511
  expressionBindings = {
2330
2512
  claims: extractClaims(context),
2331
- envelope: createEnvelopeBindings(envelope),
2513
+ envelope: envelopeResult.bindings,
2332
2514
  delivery: createDeliveryBindings(context, resolvedAction),
2333
2515
  node: createNodeBindings(node),
2334
2516
  time: {
@@ -2336,9 +2518,13 @@ class AdvancedAuthorizationPolicy {
2336
2518
  now_iso: new Date().toISOString(),
2337
2519
  },
2338
2520
  };
2521
+ // Create function registry with security bindings for security builtins
2522
+ functionRegistry = createAuthFunctionRegistry({
2523
+ grantedScopes,
2524
+ securityBindings: envelopeResult.securityBindings,
2525
+ });
2339
2526
  }
2340
- const functions = functionRegistry ?? createAuthFunctionRegistry(grantedScopes);
2341
- functionRegistry = functions;
2527
+ const functions = functionRegistry;
2342
2528
  const evalContext = {
2343
2529
  bindings: expressionBindings,
2344
2530
  limits: this.expressionLimits,
@@ -12659,4 +12845,4 @@ var plugin = /*#__PURE__*/Object.freeze({
12659
12845
  registerAdvancedSecurityPluginFactories: registerAdvancedSecurityPluginFactories
12660
12846
  });
12661
12847
 
12662
- 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 };