@opensyber/tokenforge 0.1.2 → 1.0.0

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 (118) hide show
  1. package/LICENSE.md +32 -0
  2. package/README.md +205 -47
  3. package/dist/adapters/astro.d.ts +55 -0
  4. package/dist/adapters/astro.d.ts.map +1 -0
  5. package/dist/adapters/astro.js +91 -0
  6. package/dist/adapters/astro.js.map +1 -0
  7. package/dist/adapters/express.d.ts +15 -0
  8. package/dist/adapters/express.d.ts.map +1 -1
  9. package/dist/adapters/express.js +27 -0
  10. package/dist/adapters/express.js.map +1 -1
  11. package/dist/adapters/fastify.d.ts +17 -0
  12. package/dist/adapters/fastify.d.ts.map +1 -1
  13. package/dist/adapters/fastify.js +27 -0
  14. package/dist/adapters/fastify.js.map +1 -1
  15. package/dist/adapters/hono.d.ts +1 -1
  16. package/dist/adapters/hono.d.ts.map +1 -1
  17. package/dist/adapters/hono.js +1 -1
  18. package/dist/adapters/hono.js.map +1 -1
  19. package/dist/adapters/index.d.ts +5 -3
  20. package/dist/adapters/index.d.ts.map +1 -1
  21. package/dist/adapters/index.js +5 -3
  22. package/dist/adapters/index.js.map +1 -1
  23. package/dist/adapters/nextjs.d.ts +16 -0
  24. package/dist/adapters/nextjs.d.ts.map +1 -1
  25. package/dist/adapters/nextjs.js +26 -0
  26. package/dist/adapters/nextjs.js.map +1 -1
  27. package/dist/adapters/sveltekit.d.ts +57 -0
  28. package/dist/adapters/sveltekit.d.ts.map +1 -0
  29. package/dist/adapters/sveltekit.js +88 -0
  30. package/dist/adapters/sveltekit.js.map +1 -0
  31. package/dist/client/action-signer.d.ts +53 -0
  32. package/dist/client/action-signer.d.ts.map +1 -0
  33. package/dist/client/action-signer.js +48 -0
  34. package/dist/client/action-signer.js.map +1 -0
  35. package/dist/client/binding.d.ts +24 -7
  36. package/dist/client/binding.d.ts.map +1 -1
  37. package/dist/client/binding.js +17 -6
  38. package/dist/client/binding.js.map +1 -1
  39. package/dist/client/index.d.ts +13 -0
  40. package/dist/client/index.d.ts.map +1 -1
  41. package/dist/client/index.js +27 -1
  42. package/dist/client/index.js.map +1 -1
  43. package/dist/client/signer.d.ts +17 -0
  44. package/dist/client/signer.d.ts.map +1 -1
  45. package/dist/client/signer.js +23 -0
  46. package/dist/client/signer.js.map +1 -1
  47. package/dist/client/webauthn.d.ts +45 -0
  48. package/dist/client/webauthn.d.ts.map +1 -0
  49. package/dist/client/webauthn.js +118 -0
  50. package/dist/client/webauthn.js.map +1 -0
  51. package/dist/server/action-verify.d.ts +56 -0
  52. package/dist/server/action-verify.d.ts.map +1 -0
  53. package/dist/server/action-verify.js +47 -0
  54. package/dist/server/action-verify.js.map +1 -0
  55. package/dist/server/aitm-heuristics.d.ts +57 -0
  56. package/dist/server/aitm-heuristics.d.ts.map +1 -0
  57. package/dist/server/aitm-heuristics.js +120 -0
  58. package/dist/server/aitm-heuristics.js.map +1 -0
  59. package/dist/server/bound-cookie.d.ts +39 -0
  60. package/dist/server/bound-cookie.d.ts.map +1 -0
  61. package/dist/server/bound-cookie.js +59 -0
  62. package/dist/server/bound-cookie.js.map +1 -0
  63. package/dist/server/cbor.d.ts +20 -0
  64. package/dist/server/cbor.d.ts.map +1 -0
  65. package/dist/server/cbor.js +110 -0
  66. package/dist/server/cbor.js.map +1 -0
  67. package/dist/server/dbsc-challenge.d.ts +51 -0
  68. package/dist/server/dbsc-challenge.d.ts.map +1 -0
  69. package/dist/server/dbsc-challenge.js +78 -0
  70. package/dist/server/dbsc-challenge.js.map +1 -0
  71. package/dist/server/index.d.ts +2 -0
  72. package/dist/server/index.d.ts.map +1 -1
  73. package/dist/server/index.js +2 -0
  74. package/dist/server/index.js.map +1 -1
  75. package/dist/server/jws-verify.d.ts +46 -0
  76. package/dist/server/jws-verify.d.ts.map +1 -0
  77. package/dist/server/jws-verify.js +64 -0
  78. package/dist/server/jws-verify.js.map +1 -0
  79. package/dist/server/middleware.d.ts +15 -0
  80. package/dist/server/middleware.d.ts.map +1 -1
  81. package/dist/server/middleware.js +27 -0
  82. package/dist/server/middleware.js.map +1 -1
  83. package/dist/server/oidc-verify.d.ts +56 -0
  84. package/dist/server/oidc-verify.d.ts.map +1 -0
  85. package/dist/server/oidc-verify.js +107 -0
  86. package/dist/server/oidc-verify.js.map +1 -0
  87. package/dist/server/policy.d.ts +47 -0
  88. package/dist/server/policy.d.ts.map +1 -0
  89. package/dist/server/policy.js +84 -0
  90. package/dist/server/policy.js.map +1 -0
  91. package/dist/server/step-up-policy.d.ts +36 -0
  92. package/dist/server/step-up-policy.d.ts.map +1 -0
  93. package/dist/server/step-up-policy.js +101 -0
  94. package/dist/server/step-up-policy.js.map +1 -0
  95. package/dist/server/trust-score-fixtures.d.ts +9 -0
  96. package/dist/server/trust-score-fixtures.d.ts.map +1 -0
  97. package/dist/server/trust-score-fixtures.js +23 -0
  98. package/dist/server/trust-score-fixtures.js.map +1 -0
  99. package/dist/server/webauthn-verify.d.ts +41 -0
  100. package/dist/server/webauthn-verify.d.ts.map +1 -0
  101. package/dist/server/webauthn-verify.js +175 -0
  102. package/dist/server/webauthn-verify.js.map +1 -0
  103. package/dist/server/webauthn-verify.test-helpers.d.ts +8 -0
  104. package/dist/server/webauthn-verify.test-helpers.d.ts.map +1 -0
  105. package/dist/server/webauthn-verify.test-helpers.js +39 -0
  106. package/dist/server/webauthn-verify.test-helpers.js.map +1 -0
  107. package/dist/server/webhooks.d.ts +42 -0
  108. package/dist/server/webhooks.d.ts.map +1 -0
  109. package/dist/server/webhooks.js +77 -0
  110. package/dist/server/webhooks.js.map +1 -0
  111. package/dist/shared/action-hash.d.ts +23 -0
  112. package/dist/shared/action-hash.d.ts.map +1 -0
  113. package/dist/shared/action-hash.js +35 -0
  114. package/dist/shared/action-hash.js.map +1 -0
  115. package/dist/shared/types.d.ts +2 -0
  116. package/dist/shared/types.d.ts.map +1 -1
  117. package/dist/shared/types.js.map +1 -1
  118. package/package.json +41 -5
@@ -1,6 +1,7 @@
1
1
  import { bindDevice } from './binding.js';
2
2
  import { clearDeviceKeys } from './storage.js';
3
3
  import { installFetchInterceptor } from './interceptor.js';
4
+ import { signAction as signActionInternal, } from './action-signer.js';
4
5
  /**
5
6
  * TokenForge Client SDK — main entry point.
6
7
  *
@@ -33,7 +34,9 @@ export class TokenForge {
33
34
  try {
34
35
  const result = await bindDevice(this.config.apiBase, sessionId);
35
36
  this.deviceId = result.deviceId;
36
- this.keyPair = result.keyPair;
37
+ // ECDSA path stores the keyPair; WebAuthn path keeps the credential on
38
+ // the authenticator (signed via navigator.credentials.get on demand).
39
+ this.keyPair = result.type === 'ecdsa' ? result.keyPair : null;
37
40
  this.bound = true;
38
41
  this.config.onDeviceBound?.(this.deviceId);
39
42
  if (this.config.autoIntercept !== false) {
@@ -98,6 +101,29 @@ export class TokenForge {
98
101
  await this.clearKeys();
99
102
  await this.init();
100
103
  }
104
+ /**
105
+ * Sign a high-trust action (checkout, password change, admin grant…) with
106
+ * the device-bound private key. Returns a compact JWS the server verifies
107
+ * via `verifyCompactJws` + `actionHash` claim match.
108
+ *
109
+ * @throws when the device is not yet bound (call `init()` first).
110
+ */
111
+ async signAction(payload, options = {}) {
112
+ if (!this.keyPair || !this.bound) {
113
+ throw new Error('TokenForge.signAction: device not bound — call init() first');
114
+ }
115
+ const sessionId = this.config.getSessionId();
116
+ if (!sessionId) {
117
+ throw new Error('TokenForge.signAction: no sessionId from getSessionId()');
118
+ }
119
+ return signActionInternal(payload, {
120
+ privateKey: this.keyPair.privateKey,
121
+ sessionId,
122
+ kid: options.kid,
123
+ ttlSeconds: options.ttlSeconds,
124
+ tlsExporter: options.tlsExporter,
125
+ });
126
+ }
101
127
  }
102
128
  /**
103
129
  * Factory function to create a TokenForge instance.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,11 @@
1
1
  /**
2
2
  * Challenge-response signing protocol.
3
3
  * Signs: `${sessionId}:${nonce}:${timestamp}` with the device-bound private key.
4
+ *
5
+ * Also exports `isNativeDbscAvailable()` — feature-detect for the W3C
6
+ * Device-Bound Session Credentials API (Chrome 146+). When present, the
7
+ * SDK can defer key generation + binding to the platform; otherwise it
8
+ * continues using the Web Crypto fallback path implemented here.
4
9
  */
5
10
  /**
6
11
  * Sign a challenge payload with the device-bound private key.
@@ -12,4 +17,16 @@ export declare function signChallenge(privateKey: CryptoKey, sessionId: string,
12
17
  */
13
18
  export declare function generateNonce(): string;
14
19
  export declare function arrayBufferToBase64Url(buffer: ArrayBuffer): string;
20
+ /**
21
+ * Feature-detect the W3C Device-Bound Session Credentials API.
22
+ *
23
+ * Returns `true` when `navigator.deviceBoundSession` is a real object the
24
+ * SDK could route through. Treats undefined / null / non-object values
25
+ * (functions, primitives) as unavailable so a downgrade attacker who
26
+ * shadows `navigator` with a truthy primitive does not flip the SDK into
27
+ * thinking native DBSC is present.
28
+ *
29
+ * Spec: https://w3c.github.io/webappsec-dbsc/ (Chrome 146+)
30
+ */
31
+ export declare function isNativeDbscAvailable(): boolean;
15
32
  //# sourceMappingURL=signer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../../src/client/signer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,SAAS,EACrB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAWjB;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAItC;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAOlE"}
1
+ {"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../../src/client/signer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,wBAAsB,aAAa,CACjC,UAAU,EAAE,SAAS,EACrB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAWjB;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAItC;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAOlE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAK/C"}
@@ -1,6 +1,11 @@
1
1
  /**
2
2
  * Challenge-response signing protocol.
3
3
  * Signs: `${sessionId}:${nonce}:${timestamp}` with the device-bound private key.
4
+ *
5
+ * Also exports `isNativeDbscAvailable()` — feature-detect for the W3C
6
+ * Device-Bound Session Credentials API (Chrome 146+). When present, the
7
+ * SDK can defer key generation + binding to the platform; otherwise it
8
+ * continues using the Web Crypto fallback path implemented here.
4
9
  */
5
10
  /**
6
11
  * Sign a challenge payload with the device-bound private key.
@@ -28,4 +33,22 @@ export function arrayBufferToBase64Url(buffer) {
28
33
  }
29
34
  return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
30
35
  }
36
+ /**
37
+ * Feature-detect the W3C Device-Bound Session Credentials API.
38
+ *
39
+ * Returns `true` when `navigator.deviceBoundSession` is a real object the
40
+ * SDK could route through. Treats undefined / null / non-object values
41
+ * (functions, primitives) as unavailable so a downgrade attacker who
42
+ * shadows `navigator` with a truthy primitive does not flip the SDK into
43
+ * thinking native DBSC is present.
44
+ *
45
+ * Spec: https://w3c.github.io/webappsec-dbsc/ (Chrome 146+)
46
+ */
47
+ export function isNativeDbscAvailable() {
48
+ if (typeof navigator === 'undefined')
49
+ return false;
50
+ const nav = navigator;
51
+ const candidate = nav.deviceBoundSession;
52
+ return typeof candidate === 'object' && candidate !== null;
53
+ }
31
54
  //# sourceMappingURL=signer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"signer.js","sourceRoot":"","sources":["../../src/client/signer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAAqB,EACrB,SAAiB,EACjB,KAAa,EACb,SAAiB;IAEjB,MAAM,OAAO,GAAG,GAAG,SAAS,IAAI,KAAK,IAAI,SAAS,EAAE,CAAC;IACrD,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAElD,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CACxC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAC5C,UAAU,EACV,OAAO,CACR,CAAC;IAEF,OAAO,sBAAsB,CAAC,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACjC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC9B,OAAO,sBAAsB,CAAC,KAAK,CAAC,MAAqB,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAmB;IACxD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAChF,CAAC"}
1
+ {"version":3,"file":"signer.js","sourceRoot":"","sources":["../../src/client/signer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAAqB,EACrB,SAAiB,EACjB,KAAa,EACb,SAAiB;IAEjB,MAAM,OAAO,GAAG,GAAG,SAAS,IAAI,KAAK,IAAI,SAAS,EAAE,CAAC;IACrD,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAElD,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CACxC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAC5C,UAAU,EACV,OAAO,CACR,CAAC;IAEF,OAAO,sBAAsB,CAAC,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACjC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC9B,OAAO,sBAAsB,CAAC,KAAK,CAAC,MAAqB,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAmB;IACxD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAChF,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB;IACnC,IAAI,OAAO,SAAS,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IACnD,MAAM,GAAG,GAAG,SAAyD,CAAC;IACtE,MAAM,SAAS,GAAG,GAAG,CAAC,kBAAkB,CAAC;IACzC,OAAO,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,IAAI,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * WebAuthn / FIDO2 client flow — alternative to the ECDSA browser key path.
3
+ *
4
+ * Coexists with the ECDSA flow in binding.ts. Caller opts in via
5
+ * `bindDevice(..., { prefer: 'webauthn' })`. The credential private key is
6
+ * never exposed to JS — it lives on the authenticator (YubiKey, Touch ID,
7
+ * Windows Hello, platform passkey provider).
8
+ *
9
+ * NOTE: WebAuthn signatures are over (authenticatorData || sha256(clientDataJSON)),
10
+ * not the raw `${sessionId}:${nonce}:${timestamp}` payload. The server reconstructs
11
+ * the same expected challenge from session/nonce/timestamp and compares it to
12
+ * the challenge embedded inside clientDataJSON.
13
+ */
14
+ export interface WebAuthnBindResult {
15
+ deviceId: string;
16
+ credentialId: string;
17
+ }
18
+ export interface WebAuthnAssertion {
19
+ signature: string;
20
+ authenticatorData: string;
21
+ clientDataJSON: string;
22
+ credentialId: string;
23
+ }
24
+ /** base64url encode a buffer (no padding, URL-safe alphabet). */
25
+ export declare function bufferToBase64Url(buffer: ArrayBuffer | Uint8Array): string;
26
+ /** base64url decode to ArrayBuffer. */
27
+ export declare function base64UrlToBuffer(b64url: string): ArrayBuffer;
28
+ /**
29
+ * Bind a hardware credential to the active session.
30
+ *
31
+ * Calls navigator.credentials.create with ES256 (alg -7) + EdDSA (alg -8)
32
+ * pubKeyCredParams. Sends the attestation to the server, receives the
33
+ * persisted deviceId.
34
+ */
35
+ export declare function bindDeviceWebAuthn(apiBase: string, sessionId: string, userId: string, displayName: string): Promise<WebAuthnBindResult>;
36
+ /**
37
+ * Sign a per-request challenge with the bound hardware credential.
38
+ *
39
+ * The challenge is `${sessionId}:${nonce}:${timestamp}` — same payload shape
40
+ * as the ECDSA path, but the WebAuthn signature is over
41
+ * (authenticatorData || sha256(clientDataJSON)) where clientDataJSON contains
42
+ * the challenge. The server reconstructs and compares.
43
+ */
44
+ export declare function signChallengeWebAuthn(credentialId: string, sessionId: string, nonce: string, timestamp: number): Promise<WebAuthnAssertion>;
45
+ //# sourceMappingURL=webauthn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webauthn.d.ts","sourceRoot":"","sources":["../../src/client/webauthn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,CAK1E;AAED,uCAAuC;AACvC,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAO7D;AAOD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,kBAAkB,CAAC,CAgD7B;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,CAAC,CA0B5B"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * WebAuthn / FIDO2 client flow — alternative to the ECDSA browser key path.
3
+ *
4
+ * Coexists with the ECDSA flow in binding.ts. Caller opts in via
5
+ * `bindDevice(..., { prefer: 'webauthn' })`. The credential private key is
6
+ * never exposed to JS — it lives on the authenticator (YubiKey, Touch ID,
7
+ * Windows Hello, platform passkey provider).
8
+ *
9
+ * NOTE: WebAuthn signatures are over (authenticatorData || sha256(clientDataJSON)),
10
+ * not the raw `${sessionId}:${nonce}:${timestamp}` payload. The server reconstructs
11
+ * the same expected challenge from session/nonce/timestamp and compares it to
12
+ * the challenge embedded inside clientDataJSON.
13
+ */
14
+ /** base64url encode a buffer (no padding, URL-safe alphabet). */
15
+ export function bufferToBase64Url(buffer) {
16
+ const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
17
+ let binary = '';
18
+ for (const byte of bytes)
19
+ binary += String.fromCharCode(byte);
20
+ return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
21
+ }
22
+ /** base64url decode to ArrayBuffer. */
23
+ export function base64UrlToBuffer(b64url) {
24
+ const base64 = b64url.replace(/-/g, '+').replace(/_/g, '/');
25
+ const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);
26
+ const binary = atob(padded);
27
+ const bytes = new Uint8Array(binary.length);
28
+ for (let i = 0; i < binary.length; i++)
29
+ bytes[i] = binary.charCodeAt(i);
30
+ return bytes.buffer;
31
+ }
32
+ /** SHA-256 → bytes for the WebAuthn challenge derivation. */
33
+ async function sha256(input) {
34
+ return crypto.subtle.digest('SHA-256', new TextEncoder().encode(input));
35
+ }
36
+ /**
37
+ * Bind a hardware credential to the active session.
38
+ *
39
+ * Calls navigator.credentials.create with ES256 (alg -7) + EdDSA (alg -8)
40
+ * pubKeyCredParams. Sends the attestation to the server, receives the
41
+ * persisted deviceId.
42
+ */
43
+ export async function bindDeviceWebAuthn(apiBase, sessionId, userId, displayName) {
44
+ if (typeof navigator === 'undefined' || !navigator.credentials) {
45
+ throw new Error('WebAuthn not available in this environment');
46
+ }
47
+ // Server-bound challenge: derived from sessionId so a stolen challenge
48
+ // can't be replayed against another session.
49
+ const challenge = await sha256(`tf-bind:${sessionId}`);
50
+ const userIdBytes = new TextEncoder().encode(userId);
51
+ const publicKey = {
52
+ challenge,
53
+ rp: { name: 'TokenForge' },
54
+ user: { id: userIdBytes, name: userId, displayName },
55
+ pubKeyCredParams: [
56
+ { type: 'public-key', alg: -7 }, // ES256
57
+ { type: 'public-key', alg: -8 }, // EdDSA
58
+ ],
59
+ authenticatorSelection: { userVerification: 'preferred' },
60
+ timeout: 60_000,
61
+ attestation: 'none',
62
+ };
63
+ const credential = (await navigator.credentials.create({ publicKey }));
64
+ if (!credential)
65
+ throw new Error('WebAuthn credential creation cancelled');
66
+ const response = credential.response;
67
+ const body = {
68
+ sessionId,
69
+ userId,
70
+ credentialId: bufferToBase64Url(credential.rawId),
71
+ attestationObject: bufferToBase64Url(response.attestationObject),
72
+ clientDataJSON: bufferToBase64Url(response.clientDataJSON),
73
+ };
74
+ const httpResponse = await fetch(`${apiBase}/api/tf/bind/webauthn`, {
75
+ method: 'POST',
76
+ headers: { 'Content-Type': 'application/json' },
77
+ credentials: 'include',
78
+ body: JSON.stringify(body),
79
+ });
80
+ if (!httpResponse.ok) {
81
+ throw new Error(`TokenForge WebAuthn binding failed: ${httpResponse.status}`);
82
+ }
83
+ const { deviceId } = (await httpResponse.json());
84
+ return { deviceId, credentialId: body.credentialId };
85
+ }
86
+ /**
87
+ * Sign a per-request challenge with the bound hardware credential.
88
+ *
89
+ * The challenge is `${sessionId}:${nonce}:${timestamp}` — same payload shape
90
+ * as the ECDSA path, but the WebAuthn signature is over
91
+ * (authenticatorData || sha256(clientDataJSON)) where clientDataJSON contains
92
+ * the challenge. The server reconstructs and compares.
93
+ */
94
+ export async function signChallengeWebAuthn(credentialId, sessionId, nonce, timestamp) {
95
+ if (typeof navigator === 'undefined' || !navigator.credentials) {
96
+ throw new Error('WebAuthn not available in this environment');
97
+ }
98
+ const challenge = await sha256(`${sessionId}:${nonce}:${timestamp}`);
99
+ const publicKey = {
100
+ challenge,
101
+ allowCredentials: [
102
+ { id: base64UrlToBuffer(credentialId), type: 'public-key' },
103
+ ],
104
+ userVerification: 'preferred',
105
+ timeout: 60_000,
106
+ };
107
+ const assertion = (await navigator.credentials.get({ publicKey }));
108
+ if (!assertion)
109
+ throw new Error('WebAuthn assertion cancelled');
110
+ const response = assertion.response;
111
+ return {
112
+ signature: bufferToBase64Url(response.signature),
113
+ authenticatorData: bufferToBase64Url(response.authenticatorData),
114
+ clientDataJSON: bufferToBase64Url(response.clientDataJSON),
115
+ credentialId: bufferToBase64Url(assertion.rawId),
116
+ };
117
+ }
118
+ //# sourceMappingURL=webauthn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webauthn.js","sourceRoot":"","sources":["../../src/client/webauthn.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Server-side action-signing verifier (Sprint 39).
3
+ *
4
+ * Wraps `verifyCompactJws` with two extra invariants the client SDK
5
+ * promises:
6
+ * 1. The JWS `action` claim equals the `expectedAction` the route
7
+ * handler is enforcing (e.g. POST /checkout asserts "checkout").
8
+ * 2. The JWS `actionHash` claim equals the SHA-256 of the
9
+ * canonicalized request body. This binds the signature to the
10
+ * payload — replaying the same JWS against a different body
11
+ * (different recipient, amount, etc.) fails.
12
+ *
13
+ * Replay prevention against the SAME body is the caller's
14
+ * responsibility: they should pass `claims.nonce` through
15
+ * `consumeChallenge` (server/dbsc-challenge.ts) so a second submission
16
+ * of the same JWS is rejected even within the maxAgeSeconds window.
17
+ */
18
+ import type { JwsClaims } from './jws-verify.js';
19
+ export interface VerifyActionOptions {
20
+ /** Bound device public key (JWK JSON or PEM SPKI). */
21
+ publicKey: string;
22
+ /** Action verb the route asserts (must equal claims.action). */
23
+ expectedAction: string;
24
+ /** Optional request body — when present, must hash to claims.actionHash. */
25
+ body?: Record<string, unknown> | null;
26
+ /** Maximum age for the JWS in seconds. Default 60. */
27
+ maxAgeSeconds?: number;
28
+ /** Override clock for tests. */
29
+ now?: Date;
30
+ /**
31
+ * RFC 9266 TLS exporter material (hex) extracted from the active TLS
32
+ * connection by the runtime. When provided, claims.tlsExporter MUST
33
+ * match it — protects against replay across a different TLS session.
34
+ * Omit on workerd or any runtime that doesn't expose exporter
35
+ * material; the server emits Sec-TF-Channel-Bound: 0 in that case
36
+ * via the route handler.
37
+ */
38
+ expectedTlsExporter?: string;
39
+ /**
40
+ * If true, reject when claims.tlsExporter is absent. Use this on
41
+ * sensitive routes when running on a runtime that *does* expose the
42
+ * exporter — a missing claim then indicates the client signed
43
+ * without binding the TLS channel.
44
+ */
45
+ requireTlsExporter?: boolean;
46
+ }
47
+ export type VerifyActionResult = {
48
+ ok: true;
49
+ claims: JwsClaims;
50
+ protectedHeader: Record<string, unknown>;
51
+ } | {
52
+ ok: false;
53
+ reason: string;
54
+ };
55
+ export declare function verifyAction(jws: string, opts: VerifyActionOptions): Promise<VerifyActionResult>;
56
+ //# sourceMappingURL=action-verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-verify.d.ts","sourceRoot":"","sources":["../../src/server/action-verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjD,MAAM,WAAW,mBAAmB;IAClC,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,cAAc,EAAE,MAAM,CAAC;IACvB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,sDAAsD;IACtD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,GAAG,CAAC,EAAE,IAAI,CAAC;IACX;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACzE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,kBAAkB,CAAC,CA4B7B"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Server-side action-signing verifier (Sprint 39).
3
+ *
4
+ * Wraps `verifyCompactJws` with two extra invariants the client SDK
5
+ * promises:
6
+ * 1. The JWS `action` claim equals the `expectedAction` the route
7
+ * handler is enforcing (e.g. POST /checkout asserts "checkout").
8
+ * 2. The JWS `actionHash` claim equals the SHA-256 of the
9
+ * canonicalized request body. This binds the signature to the
10
+ * payload — replaying the same JWS against a different body
11
+ * (different recipient, amount, etc.) fails.
12
+ *
13
+ * Replay prevention against the SAME body is the caller's
14
+ * responsibility: they should pass `claims.nonce` through
15
+ * `consumeChallenge` (server/dbsc-challenge.ts) so a second submission
16
+ * of the same JWS is rejected even within the maxAgeSeconds window.
17
+ */
18
+ import { verifyCompactJws } from './jws-verify.js';
19
+ import { hashActionPayload } from '../shared/action-hash.js';
20
+ export async function verifyAction(jws, opts) {
21
+ const inner = await verifyCompactJws(jws, {
22
+ publicKey: opts.publicKey,
23
+ maxAgeSeconds: opts.maxAgeSeconds,
24
+ now: opts.now,
25
+ });
26
+ if (!inner.ok)
27
+ return inner;
28
+ if (inner.claims.action !== opts.expectedAction) {
29
+ return { ok: false, reason: 'action_mismatch' };
30
+ }
31
+ if (opts.body !== undefined && opts.body !== null) {
32
+ const expectedHash = await hashActionPayload(opts.body);
33
+ if (inner.claims.actionHash !== expectedHash) {
34
+ return { ok: false, reason: 'action_hash_mismatch' };
35
+ }
36
+ }
37
+ if (opts.expectedTlsExporter !== undefined) {
38
+ if (inner.claims.tlsExporter !== opts.expectedTlsExporter) {
39
+ return { ok: false, reason: 'tls_exporter_mismatch' };
40
+ }
41
+ }
42
+ else if (opts.requireTlsExporter && !inner.claims.tlsExporter) {
43
+ return { ok: false, reason: 'tls_exporter_missing' };
44
+ }
45
+ return inner;
46
+ }
47
+ //# sourceMappingURL=action-verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-verify.js","sourceRoot":"","sources":["../../src/server/action-verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAmC7D,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAW,EACX,IAAyB;IAEzB,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE;QACxC,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,GAAG,EAAE,IAAI,CAAC,GAAG;KACd,CAAC,CAAC;IACH,IAAI,CAAC,KAAK,CAAC,EAAE;QAAE,OAAO,KAAK,CAAC;IAE5B,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,IAAI,CAAC,cAAc,EAAE,CAAC;QAChD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAClD,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAClD,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxD,IAAI,KAAK,CAAC,MAAM,CAAC,UAAU,KAAK,YAAY,EAAE,CAAC;YAC7C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;QACvD,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,mBAAmB,KAAK,SAAS,EAAE,CAAC;QAC3C,IAAI,KAAK,CAAC,MAAM,CAAC,WAAW,KAAK,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC1D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC;QACxD,CAAC;IACH,CAAC;SAAM,IAAI,IAAI,CAAC,kBAAkB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QAChE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;IACvD,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Adversary-in-the-middle (AitM) heuristic engine. Pure module.
3
+ * Compares a bind-time fingerprint vs. current request, emits anomalies.
4
+ * trust-score subtracts the delta; device-telemetry surfaces the list.
5
+ */
6
+ export interface ClientFingerprint {
7
+ userAgent: string;
8
+ language: string;
9
+ timezone: string;
10
+ colorDepth: number;
11
+ screenWidth: number;
12
+ screenHeight: number;
13
+ /** Optional — only present on protected routes that capture them. */
14
+ origin?: string;
15
+ sni?: string;
16
+ host?: string;
17
+ rttMs?: number;
18
+ /** True when TLS exporter binding succeeded for this request. */
19
+ channelBound?: boolean;
20
+ }
21
+ export type AitmAnomalyKind = 'origin_mismatch' | 'latency_floor' | 'ua_drift' | 'tz_drift' | 'color_drift' | 'resolution_swap' | 'locale_drift' | 'channel_unbound';
22
+ export type AitmConfidence = 'low' | 'medium' | 'high';
23
+ export interface AitmAnomaly {
24
+ kind: AitmAnomalyKind;
25
+ confidence: AitmConfidence;
26
+ details?: Record<string, unknown>;
27
+ }
28
+ export interface AitmEvalOptions {
29
+ /**
30
+ * Persistent latency floor delta (ms) above baseline that's considered
31
+ * suspicious. Default 40ms — typical AitM proxy adds at least this much
32
+ * even after EWMA smoothing.
33
+ */
34
+ rttFloorDeltaMs?: number;
35
+ /**
36
+ * When true, missing TLS-exporter binding emits `channel_unbound`.
37
+ * Default true. Disable on routes that don't require channel binding.
38
+ */
39
+ requireChannelBinding?: boolean;
40
+ }
41
+ /**
42
+ * Evaluate the eight AitM heuristics. Pure function.
43
+ *
44
+ * @param baseline - fingerprint captured at device-bind time
45
+ * @param current - fingerprint of the current request
46
+ * @param opts - thresholds (override per route / tenant)
47
+ * @returns list of anomalies; empty when nothing trips
48
+ */
49
+ export declare function evaluateAitm(baseline: ClientFingerprint, current: ClientFingerprint, opts?: AitmEvalOptions): AitmAnomaly[];
50
+ /**
51
+ * Convert anomalies to a trust-score delta. Negative number; used by
52
+ * TrustScoreEngine to drop score on suspicious sessions. Cap at -60 so
53
+ * a single anomalous request can never block a clean baseline outright
54
+ * — that's the trust score's job once it falls below `step_up_threshold`.
55
+ */
56
+ export declare function aitmScoreDelta(anomalies: AitmAnomaly[]): number;
57
+ //# sourceMappingURL=aitm-heuristics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aitm-heuristics.d.ts","sourceRoot":"","sources":["../../src/server/aitm-heuristics.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,eAAe,GACvB,iBAAiB,GACjB,eAAe,GACf,UAAU,GACV,UAAU,GACV,aAAa,GACb,iBAAiB,GACjB,cAAc,GACd,iBAAiB,CAAC;AAEtB,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEvD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,UAAU,EAAE,cAAc,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAID;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,EAAE,iBAAiB,EAC1B,IAAI,GAAE,eAAoB,GACzB,WAAW,EAAE,CA0Ff;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,MAAM,CAQ/D"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Adversary-in-the-middle (AitM) heuristic engine. Pure module.
3
+ * Compares a bind-time fingerprint vs. current request, emits anomalies.
4
+ * trust-score subtracts the delta; device-telemetry surfaces the list.
5
+ */
6
+ const DEFAULT_RTT_FLOOR_DELTA_MS = 40;
7
+ /**
8
+ * Evaluate the eight AitM heuristics. Pure function.
9
+ *
10
+ * @param baseline - fingerprint captured at device-bind time
11
+ * @param current - fingerprint of the current request
12
+ * @param opts - thresholds (override per route / tenant)
13
+ * @returns list of anomalies; empty when nothing trips
14
+ */
15
+ export function evaluateAitm(baseline, current, opts = {}) {
16
+ const out = [];
17
+ const rttFloorDelta = opts.rttFloorDeltaMs ?? DEFAULT_RTT_FLOOR_DELTA_MS;
18
+ const requireChannel = opts.requireChannelBinding ?? true;
19
+ if (current.origin && current.sni && current.host) {
20
+ const originHost = safeHost(current.origin);
21
+ const sni = current.sni.toLowerCase();
22
+ const host = current.host.toLowerCase();
23
+ const all = [originHost, sni, host].filter(Boolean);
24
+ const allMatch = all.every((h) => h === all[0]);
25
+ if (!allMatch) {
26
+ out.push({
27
+ kind: 'origin_mismatch',
28
+ confidence: 'high',
29
+ details: { origin: originHost, sni, host },
30
+ });
31
+ }
32
+ }
33
+ if (baseline.userAgent !== current.userAgent) {
34
+ out.push({
35
+ kind: 'ua_drift',
36
+ confidence: 'high',
37
+ details: { from: baseline.userAgent, to: current.userAgent },
38
+ });
39
+ }
40
+ if (baseline.timezone !== current.timezone) {
41
+ out.push({
42
+ kind: 'tz_drift',
43
+ confidence: 'medium',
44
+ details: { from: baseline.timezone, to: current.timezone },
45
+ });
46
+ }
47
+ if (baseline.language !== current.language) {
48
+ out.push({
49
+ kind: 'locale_drift',
50
+ confidence: 'medium',
51
+ details: { from: baseline.language, to: current.language },
52
+ });
53
+ }
54
+ if (baseline.colorDepth !== current.colorDepth) {
55
+ out.push({
56
+ kind: 'color_drift',
57
+ confidence: 'low',
58
+ details: { from: baseline.colorDepth, to: current.colorDepth },
59
+ });
60
+ }
61
+ const baseDim = `${baseline.screenWidth}x${baseline.screenHeight}`;
62
+ const curDim = `${current.screenWidth}x${current.screenHeight}`;
63
+ const swapped = baseline.screenWidth === current.screenHeight &&
64
+ baseline.screenHeight === current.screenWidth &&
65
+ baseline.screenWidth !== baseline.screenHeight;
66
+ if (swapped) {
67
+ out.push({
68
+ kind: 'resolution_swap',
69
+ confidence: 'medium',
70
+ details: { from: baseDim, to: curDim },
71
+ });
72
+ }
73
+ if (typeof baseline.rttMs === 'number' &&
74
+ typeof current.rttMs === 'number' &&
75
+ current.rttMs - baseline.rttMs >= rttFloorDelta) {
76
+ out.push({
77
+ kind: 'latency_floor',
78
+ confidence: 'medium',
79
+ details: {
80
+ baselineMs: baseline.rttMs,
81
+ currentMs: current.rttMs,
82
+ deltaMs: current.rttMs - baseline.rttMs,
83
+ },
84
+ });
85
+ }
86
+ if (requireChannel && current.channelBound === false) {
87
+ out.push({
88
+ kind: 'channel_unbound',
89
+ confidence: 'high',
90
+ });
91
+ }
92
+ return out;
93
+ }
94
+ /**
95
+ * Convert anomalies to a trust-score delta. Negative number; used by
96
+ * TrustScoreEngine to drop score on suspicious sessions. Cap at -60 so
97
+ * a single anomalous request can never block a clean baseline outright
98
+ * — that's the trust score's job once it falls below `step_up_threshold`.
99
+ */
100
+ export function aitmScoreDelta(anomalies) {
101
+ let delta = 0;
102
+ for (const a of anomalies) {
103
+ if (a.confidence === 'high')
104
+ delta -= 25;
105
+ else if (a.confidence === 'medium')
106
+ delta -= 12;
107
+ else
108
+ delta -= 5;
109
+ }
110
+ return Math.max(delta, -60);
111
+ }
112
+ function safeHost(originUrl) {
113
+ try {
114
+ return new URL(originUrl).host.toLowerCase();
115
+ }
116
+ catch {
117
+ return undefined;
118
+ }
119
+ }
120
+ //# sourceMappingURL=aitm-heuristics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aitm-heuristics.js","sourceRoot":"","sources":["../../src/server/aitm-heuristics.ts"],"names":[],"mappings":"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"}