@burnt-labs/expo-satya-attest 1.0.2 → 1.1.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.0 - 2026-06-24
4
+
5
+ - Added `capture.nativeReplayHeaders` for literal native replay headers that WebView JavaScript cannot capture, such as `sec-fetch-site`.
6
+ - Native replay header names are now added to the runtime request-header allowlist during provider validation.
7
+ - Updated provider config docs, provider validation tests, and the example app diagnostics for native replay headers.
8
+
3
9
  ## 1.0.2 - 2026-06-23
4
10
 
5
11
  - Added structured `SatyaError.diagnostics` payloads with stable stages, per-occurrence error ids, native rejection metadata, and redacted context for app telemetry.
@@ -28,7 +28,7 @@ Registry { schemaVersion?, providers[] }
28
28
  ├─ endpoints[] one replayable request each
29
29
  │ ├─ requestMatchRules[] which WebView request to capture
30
30
  │ ├─ revealRules[] what the artifact may disclose
31
- │ └─ capture? active-fetch / login-readiness (optional)
31
+ │ └─ capture? active-fetch / native-replay / login-readiness (optional)
32
32
  ├─ tlsIdentityPolicy pinned host + SPKI pins
33
33
  ├─ templateGovernance template/parser identity for the verifier
34
34
  └─ injection? trusted WebView JavaScript (optional)
@@ -159,7 +159,7 @@ Each endpoint describes one replayable request and its disclosure policy.
159
159
  | `id` | `string` | Starts with a letter; letters/digits/`_`/`-` only; unique per provider. Participates in `policyHash`. |
160
160
  | `url` | `string` | HTTPS replay URL. Host must equal `tlsIdentityPolicy.expectedSni`. For dynamic endpoints (`replayCapturedUrl`) this is a stable prefix used for host pinning + match rules. |
161
161
  | `method` | `string` | One of `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`. Must agree with the `methodEquals` rule. |
162
- | `headers` | `string[]` | Non-empty request-header allowlist for native replay. Only these captured headers are forwarded. Include `cookie` for cookie-auth endpoints, `user-agent` when behavior depends on it. `capture.headers` names are auto-added here. |
162
+ | `headers` | `string[]` | Non-empty request-header allowlist for native replay. Only allowlisted captured headers are forwarded. Include `cookie` for cookie-auth endpoints and `user-agent` when behavior depends on it. `capture.headers` and `capture.nativeReplayHeaders` names are auto-added to the runtime allowlist. |
163
163
  | `responseType` | `string` | `json`, `html`, or `text`. `json` allows `json`/`jsonRegex`/`regex` reveals; `html`/`text` allow `regex` only. |
164
164
  | `requestMatchRules` | `Rule[]` | Non-empty. Must bind both method and URL. See §4. |
165
165
  | `revealRules` | `Reveal[]` | Non-empty. Must include ≥1 `RECV BODY` reveal. See §5. |
@@ -168,7 +168,7 @@ Each endpoint describes one replayable request and its disclosure policy.
168
168
 
169
169
  | Field | Type | Description |
170
170
  |---|---|---|
171
- | `capture` | `object` | Active-fetch / login-readiness config. See §6. |
171
+ | `capture` | `object` | Active-fetch, native replay header, and login-readiness config. See §6. |
172
172
  | `replayCapturedUrl` | `boolean` | When `true`, replay the URL the WebView actually fetched (for dynamic path/query). Still constrained to the pinned host + match rules. See §6.2. |
173
173
  | `htmlCaptureBodyPattern` | `string` | For `html`/`text` pages loaded by top-level navigation: wait until the document text matches this regex before re-issuing the page as a credentialed fetch. See §6.1. |
174
174
 
@@ -262,7 +262,8 @@ How it works:
262
262
  | Field | Req? | Type | Description |
263
263
  |---|---|---|---|
264
264
  | `trigger` | no | `boolean` | Enable the active fetch for this endpoint. |
265
- | `headers` | no | `{ name: value }` | Extra fetch headers (≤20). Values may be literals or `{{cookie:NAME}}` templates resolved from `document.cookie` at fire time (non-httpOnly CSRF / app-id headers). Names are auto-added to the endpoint header allowlist. |
265
+ | `headers` | no | `{ name: value }` | Extra WebView active-fetch headers (≤20). Values may be literals or `{{cookie:NAME}}` templates resolved from `document.cookie` at fire time (non-httpOnly CSRF / app-id headers). Names are auto-added to the runtime replay allowlist. |
266
+ | `nativeReplayHeaders` | no | `{ name: value }` | Static native replay headers (≤20), for provider/WAF headers that WebView JS cannot capture, such as `sec-fetch-site: same-origin`. Values must be literal strings. Names are auto-added to the runtime replay allowlist. |
266
267
  | `requestBody` | no | `string` | Body for POST/PUT/PATCH (e.g. a GraphQL query). ≤32 KiB. |
267
268
  | `landingUrl` | no | `string` | HTTPS page to navigate to if the fetch is stuck on a **sibling subdomain** of the proof host (to become same-origin). Defaults to the proof origin root. Set a stable signed-in page when the root redirects (e.g. Spotify → `https://www.spotify.com/account/`). Never hops across registrable domains (won't break third-party SSO). |
268
269
  | `pollMs` | no | `int` | Poll interval ms. Default `2000`, range `500`–`15000`. |
@@ -288,13 +289,20 @@ Examples:
288
289
  "headers": { "content-type": "application/json" },
289
290
  "requestBody": "{\"operationName\":\"consumer\",\"variables\":{},\"query\":\"query consumer { consumer { id isGuest } }\"}"
290
291
  }
292
+
293
+ // Garmin — static browser-context header for native replay
294
+ "capture": {
295
+ "nativeReplayHeaders": {
296
+ "sec-fetch-site": "same-origin"
297
+ }
298
+ }
291
299
  ```
292
300
 
293
301
  Notes: use `trigger` only for APIs a page doesn't call (it's redundant for naturally-fired
294
302
  requests); cross-origin APIs work only if the provider's own app uses credentialed CORS
295
303
  from that origin; `httpOnly` cookies can't be read by `{{cookie:NAME}}` (they're sent
296
304
  automatically by `credentials:'include'`). `capture` fields are **not** part of the parser
297
- hash.
305
+ hash, but they are included in `policyHash`.
298
306
 
299
307
  ### 6.1 HTML / text capture (`htmlCaptureBodyPattern`)
300
308
 
@@ -488,8 +496,8 @@ console.log(body.match(/octolytics-actor-login" content="(.*?)"/)?.[1]);
488
496
  - ≥1 `RECV BODY` reveal; `regex`/`jsonRegex` need a compilable (≤2048 char) `pattern` + an
489
497
  `alias`; `html`/`text` accept `regex` only,
490
498
  - `capture` (when present) is a non-empty object whose only keys are `trigger`, `headers`,
491
- `requestBody`, `landingUrl`, `pollMs`, `maxAttempts`, `requireLoginSignal`, with the
492
- bounds in §6,
499
+ `nativeReplayHeaders`, `requestBody`, `landingUrl`, `pollMs`, `maxAttempts`,
500
+ `requireLoginSignal`, with the bounds in §6,
493
501
  - `injection` is a non-empty string / non-empty string array when present; `login.injection`
494
502
  and endpoint `injection` are rejected,
495
503
  - raw `allowedRequestHeaders`, `allowedResponseBodyJsonPaths`, and `privacyPolicy` are
@@ -4,13 +4,13 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'expo.modules.satyaattest'
7
- version = '1.0.2'
7
+ version = '1.1.0'
8
8
 
9
9
  android {
10
10
  namespace "expo.modules.satyaattest"
11
11
  defaultConfig {
12
12
  versionCode 1
13
- versionName "1.0.2"
13
+ versionName "1.1.0"
14
14
  // StrongBox key attestation needs API 28+; basic Keystore attestation API 24+.
15
15
  minSdkVersion 24
16
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"replayRewrite.d.ts","sourceRoot":"","sources":["../../../src/components/providerAttest/replayRewrite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAElF,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,uBAAuB,EAChC,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,uBAAuB,CAwBzB"}
1
+ {"version":3,"file":"replayRewrite.d.ts","sourceRoot":"","sources":["../../../src/components/providerAttest/replayRewrite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAElF,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,uBAAuB,EAChC,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,GACpB,uBAAuB,CA4BzB"}
@@ -2,7 +2,10 @@ export function rewriteForNativeReplay(request, template, bearer) {
2
2
  const proof = template.proofTarget;
3
3
  const rw = proof?.nativeReplayRewrite;
4
4
  let url = request.url;
5
- const headers = { ...request.headers };
5
+ const headers = {
6
+ ...(proof?.capture?.nativeReplayHeaders ?? {}),
7
+ ...request.headers,
8
+ };
6
9
  if (rw?.fromHost && rw?.toHost) {
7
10
  const fromBase = `https://${rw.fromHost}`;
8
11
  if (url.startsWith(fromBase)) {
@@ -1 +1 @@
1
- {"version":3,"file":"replayRewrite.js","sourceRoot":"","sources":["../../../src/components/providerAttest/replayRewrite.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,sBAAsB,CACpC,OAAgC,EAChC,QAA+B,EAC/B,MAAqB;IAErB,MAAM,KAAK,GAAG,QAAQ,CAAC,WAKV,CAAC;IACd,MAAM,EAAE,GAAG,KAAK,EAAE,mBAAmB,CAAC;IACtC,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACtB,MAAM,OAAO,GAA2B,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAC/D,IAAI,EAAE,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC;QAC1C,IAAI,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,IAAI,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACtC,IAAI,EAAE,CAAC,eAAe,IAAI,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;gBAC9D,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;YAC/C,CAAC;YACD,GAAG,GAAG,WAAW,EAAE,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;QACtC,CAAC;IACH,CAAC;IACD,IAAI,KAAK,EAAE,wBAAwB,IAAI,MAAM,EAAE,CAAC;QAC9C,OAAO,CAAC,aAAa,GAAG,UAAU,MAAM,EAAE,CAAC;IAC7C,CAAC;IACD,OAAO,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;AACtC,CAAC","sourcesContent":["import type { CapturedProviderRequest, SatyaProviderTemplate } from '../../types';\n\nexport function rewriteForNativeReplay(\n request: CapturedProviderRequest,\n template: SatyaProviderTemplate,\n bearer: string | null\n): CapturedProviderRequest {\n const proof = template.proofTarget as\n | {\n nativeReplayRewrite?: { fromHost?: string; toHost?: string; stripPathPrefix?: string };\n nativeReplayInjectBearer?: boolean;\n }\n | undefined;\n const rw = proof?.nativeReplayRewrite;\n let url = request.url;\n const headers: Record<string, string> = { ...request.headers };\n if (rw?.fromHost && rw?.toHost) {\n const fromBase = `https://${rw.fromHost}`;\n if (url.startsWith(fromBase)) {\n let rest = url.slice(fromBase.length);\n if (rw.stripPathPrefix && rest.startsWith(rw.stripPathPrefix)) {\n rest = rest.slice(rw.stripPathPrefix.length);\n }\n url = `https://${rw.toHost}${rest}`;\n }\n }\n if (proof?.nativeReplayInjectBearer && bearer) {\n headers.authorization = `Bearer ${bearer}`;\n }\n return { ...request, url, headers };\n}\n"]}
1
+ {"version":3,"file":"replayRewrite.js","sourceRoot":"","sources":["../../../src/components/providerAttest/replayRewrite.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,sBAAsB,CACpC,OAAgC,EAChC,QAA+B,EAC/B,MAAqB;IAErB,MAAM,KAAK,GAAG,QAAQ,CAAC,WAMV,CAAC;IACd,MAAM,EAAE,GAAG,KAAK,EAAE,mBAAmB,CAAC;IACtC,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACtB,MAAM,OAAO,GAA2B;QACtC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,mBAAmB,IAAI,EAAE,CAAC;QAC9C,GAAG,OAAO,CAAC,OAAO;KACnB,CAAC;IACF,IAAI,EAAE,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC;QAC1C,IAAI,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,IAAI,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACtC,IAAI,EAAE,CAAC,eAAe,IAAI,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;gBAC9D,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;YAC/C,CAAC;YACD,GAAG,GAAG,WAAW,EAAE,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;QACtC,CAAC;IACH,CAAC;IACD,IAAI,KAAK,EAAE,wBAAwB,IAAI,MAAM,EAAE,CAAC;QAC9C,OAAO,CAAC,aAAa,GAAG,UAAU,MAAM,EAAE,CAAC;IAC7C,CAAC;IACD,OAAO,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;AACtC,CAAC","sourcesContent":["import type { CapturedProviderRequest, SatyaProviderTemplate } from '../../types';\n\nexport function rewriteForNativeReplay(\n request: CapturedProviderRequest,\n template: SatyaProviderTemplate,\n bearer: string | null\n): CapturedProviderRequest {\n const proof = template.proofTarget as\n | {\n capture?: { nativeReplayHeaders?: Record<string, string> };\n nativeReplayRewrite?: { fromHost?: string; toHost?: string; stripPathPrefix?: string };\n nativeReplayInjectBearer?: boolean;\n }\n | undefined;\n const rw = proof?.nativeReplayRewrite;\n let url = request.url;\n const headers: Record<string, string> = {\n ...(proof?.capture?.nativeReplayHeaders ?? {}),\n ...request.headers,\n };\n if (rw?.fromHost && rw?.toHost) {\n const fromBase = `https://${rw.fromHost}`;\n if (url.startsWith(fromBase)) {\n let rest = url.slice(fromBase.length);\n if (rw.stripPathPrefix && rest.startsWith(rw.stripPathPrefix)) {\n rest = rest.slice(rw.stripPathPrefix.length);\n }\n url = `https://${rw.toHost}${rest}`;\n }\n }\n if (proof?.nativeReplayInjectBearer && bearer) {\n headers.authorization = `Bearer ${bearer}`;\n }\n return { ...request, url, headers };\n}\n"]}
package/build/index.d.ts CHANGED
@@ -13,5 +13,8 @@ export type { SatyaAttestInitOptions, SatyaRuntimeConfig } from './satyaConfig';
13
13
  export { loadProviderTemplate, requestMatchesProviderTemplate, validateProviderTemplate, withProviderSpkiPins, } from './providerRegistry';
14
14
  export { fetchVerifierProviderPolicy, validateProviderPolicyForTemplate, verifierProviderPolicyUrl, } from './providerPolicy';
15
15
  export type { SatyaProviderPolicyFetchOptions, SatyaProviderPolicyRefreshMode, } from './providerPolicy';
16
- export * from './types';
16
+ export * from './types/artifact.types';
17
+ export * from './types/error.types';
18
+ export * from './types/mobile.types';
19
+ export * from './types/provider.types';
17
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,qBAAqB,CAAC;AAE9C,eAAe,WAAW,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,YAAY,EACV,wBAAwB,EACxB,8BAA8B,EAC9B,8BAA8B,EAC9B,wBAAwB,EACxB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,4BAA4B,EAC5B,yBAAyB,EACzB,0BAA0B,EAC1B,mCAAmC,EACnC,2BAA2B,EAC3B,KAAK,sBAAsB,GAC5B,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,QAAQ,EACR,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,6BAA6B,EAC7B,mCAAmC,EACnC,oCAAoC,EACpC,qBAAqB,EACrB,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EACL,oBAAoB,EACpB,8BAA8B,EAC9B,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,kBAAkB,CAAC;AAC1B,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,qBAAqB,CAAC;AAE9C,eAAe,WAAW,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,YAAY,EACV,wBAAwB,EACxB,8BAA8B,EAC9B,8BAA8B,EAC9B,wBAAwB,EACxB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,4BAA4B,EAC5B,yBAAyB,EACzB,0BAA0B,EAC1B,mCAAmC,EACnC,2BAA2B,EAC3B,KAAK,sBAAsB,GAC5B,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,QAAQ,EACR,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,6BAA6B,EAC7B,mCAAmC,EACnC,oCAAoC,EACpC,qBAAqB,EACrB,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EACL,oBAAoB,EACpB,8BAA8B,EAC9B,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,kBAAkB,CAAC;AAC1B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC"}
package/build/index.js CHANGED
@@ -8,5 +8,8 @@ export { isSatyaDebugEnabled, configureSatyaLogger, subscribeSatyaLog, satyaLog,
8
8
  export { DEFAULT_PROVIDER_REGISTRY_API, DEFAULT_PROVIDER_REGISTRY_MAX_BYTES, DEFAULT_PROVIDER_REGISTRY_TIMEOUT_MS, getSatyaRuntimeConfig, initSatyaAttest, } from './satyaConfig';
9
9
  export { loadProviderTemplate, requestMatchesProviderTemplate, validateProviderTemplate, withProviderSpkiPins, } from './providerRegistry';
10
10
  export { fetchVerifierProviderPolicy, validateProviderPolicyForTemplate, verifierProviderPolicyUrl, } from './providerPolicy';
11
- export * from './types';
11
+ export * from './types/artifact.types';
12
+ export * from './types/error.types';
13
+ export * from './types/mobile.types';
14
+ export * from './types/provider.types';
12
15
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,qBAAqB,CAAC;AAE9C,eAAe,WAAW,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AASxE,OAAO,EACL,4BAA4B,EAC5B,yBAAyB,EACzB,0BAA0B,EAC1B,mCAAmC,EACnC,2BAA2B,GAE5B,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,QAAQ,EACR,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAQpB,OAAO,EACL,6BAA6B,EAC7B,mCAAmC,EACnC,oCAAoC,EACpC,qBAAqB,EACrB,eAAe,GAChB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,oBAAoB,EACpB,8BAA8B,EAC9B,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAK1B,cAAc,SAAS,CAAC","sourcesContent":["import SatyaAttest from './SatyaAttestModule';\n\nexport default SatyaAttest;\nexport { SatyaAttest };\nexport { SatyaMobileClient } from './SatyaMobileClient';\nexport type { AttestOutcome, NativeReplayAttestArgs } from './SatyaMobileClient';\nexport { SatyaProviderAttestButton } from './SatyaProviderAttestButton';\nexport type {\n SatyaBackendVerifyResult,\n SatyaProviderAttestButtonProps,\n SatyaProviderAttestationResult,\n SatyaProviderAttestTheme,\n SatyaProviderFlowPhase,\n SatyaProviderFlowProgress,\n} from './SatyaProviderAttestButton';\nexport {\n buildProviderInjectionScript,\n buildCookieSnapshotScript,\n buildProviderWebViewScript,\n PROVIDER_REQUEST_INTERCEPTOR_SCRIPT,\n WEBVIEW_DEBUG_LOGGER_SCRIPT,\n type ProviderCaptureMessage,\n} from './webview';\nexport {\n isSatyaDebugEnabled,\n configureSatyaLogger,\n subscribeSatyaLog,\n satyaLog,\n satyaDebug,\n redactHeaders,\n summarizeRequest,\n summarizeResponse,\n} from './debugLog';\nexport type {\n SatyaLogEntry,\n SatyaLoggerOptions,\n SatyaLogLevel,\n SatyaLogLevelSetting,\n SatyaLogSink,\n} from './debugLog';\nexport {\n DEFAULT_PROVIDER_REGISTRY_API,\n DEFAULT_PROVIDER_REGISTRY_MAX_BYTES,\n DEFAULT_PROVIDER_REGISTRY_TIMEOUT_MS,\n getSatyaRuntimeConfig,\n initSatyaAttest,\n} from './satyaConfig';\nexport type { SatyaAttestInitOptions, SatyaRuntimeConfig } from './satyaConfig';\nexport {\n loadProviderTemplate,\n requestMatchesProviderTemplate,\n validateProviderTemplate,\n withProviderSpkiPins,\n} from './providerRegistry';\nexport {\n fetchVerifierProviderPolicy,\n validateProviderPolicyForTemplate,\n verifierProviderPolicyUrl,\n} from './providerPolicy';\nexport type {\n SatyaProviderPolicyFetchOptions,\n SatyaProviderPolicyRefreshMode,\n} from './providerPolicy';\nexport * from './types';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,qBAAqB,CAAC;AAE9C,eAAe,WAAW,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AASxE,OAAO,EACL,4BAA4B,EAC5B,yBAAyB,EACzB,0BAA0B,EAC1B,mCAAmC,EACnC,2BAA2B,GAE5B,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,QAAQ,EACR,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAQpB,OAAO,EACL,6BAA6B,EAC7B,mCAAmC,EACnC,oCAAoC,EACpC,qBAAqB,EACrB,eAAe,GAChB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,oBAAoB,EACpB,8BAA8B,EAC9B,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAK1B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC","sourcesContent":["import SatyaAttest from './SatyaAttestModule';\n\nexport default SatyaAttest;\nexport { SatyaAttest };\nexport { SatyaMobileClient } from './SatyaMobileClient';\nexport type { AttestOutcome, NativeReplayAttestArgs } from './SatyaMobileClient';\nexport { SatyaProviderAttestButton } from './SatyaProviderAttestButton';\nexport type {\n SatyaBackendVerifyResult,\n SatyaProviderAttestButtonProps,\n SatyaProviderAttestationResult,\n SatyaProviderAttestTheme,\n SatyaProviderFlowPhase,\n SatyaProviderFlowProgress,\n} from './SatyaProviderAttestButton';\nexport {\n buildProviderInjectionScript,\n buildCookieSnapshotScript,\n buildProviderWebViewScript,\n PROVIDER_REQUEST_INTERCEPTOR_SCRIPT,\n WEBVIEW_DEBUG_LOGGER_SCRIPT,\n type ProviderCaptureMessage,\n} from './webview';\nexport {\n isSatyaDebugEnabled,\n configureSatyaLogger,\n subscribeSatyaLog,\n satyaLog,\n satyaDebug,\n redactHeaders,\n summarizeRequest,\n summarizeResponse,\n} from './debugLog';\nexport type {\n SatyaLogEntry,\n SatyaLoggerOptions,\n SatyaLogLevel,\n SatyaLogLevelSetting,\n SatyaLogSink,\n} from './debugLog';\nexport {\n DEFAULT_PROVIDER_REGISTRY_API,\n DEFAULT_PROVIDER_REGISTRY_MAX_BYTES,\n DEFAULT_PROVIDER_REGISTRY_TIMEOUT_MS,\n getSatyaRuntimeConfig,\n initSatyaAttest,\n} from './satyaConfig';\nexport type { SatyaAttestInitOptions, SatyaRuntimeConfig } from './satyaConfig';\nexport {\n loadProviderTemplate,\n requestMatchesProviderTemplate,\n validateProviderTemplate,\n withProviderSpkiPins,\n} from './providerRegistry';\nexport {\n fetchVerifierProviderPolicy,\n validateProviderPolicyForTemplate,\n verifierProviderPolicyUrl,\n} from './providerPolicy';\nexport type {\n SatyaProviderPolicyFetchOptions,\n SatyaProviderPolicyRefreshMode,\n} from './providerPolicy';\nexport * from './types/artifact.types';\nexport * from './types/error.types';\nexport * from './types/mobile.types';\nexport * from './types/provider.types';\n"]}
@@ -2,6 +2,7 @@ import type { SatyaProviderEndpointCapture } from '../types';
2
2
  export interface ResolvedEndpointCapture {
3
3
  trigger: boolean;
4
4
  headers?: Record<string, string>;
5
+ nativeReplayHeaders?: Record<string, string>;
5
6
  requestBody?: string;
6
7
  landingUrl?: string;
7
8
  pollMs?: number;
@@ -10,5 +11,5 @@ export interface ResolvedEndpointCapture {
10
11
  template?: SatyaProviderEndpointCapture;
11
12
  }
12
13
  export declare function validateEndpointCapture(value: Record<string, unknown>, providerId: string, id: string): ResolvedEndpointCapture;
13
- export declare function mergeHeaderAllowlist(headers: string[], endpointCaptureHeaders?: Record<string, string>): string[];
14
+ export declare function mergeHeaderAllowlist(headers: string[], ...headerMaps: (Record<string, string> | undefined)[]): string[];
14
15
  //# sourceMappingURL=capture.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../../src/providerRegistry/capture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAC;AAO7D,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,EAAE,4BAA4B,CAAC;CACzC;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,MAAM,GACT,uBAAuB,CAyEzB;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EAAE,EACjB,sBAAsB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9C,MAAM,EAAE,CAWV"}
1
+ {"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../../src/providerRegistry/capture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAC;AAO7D,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,EAAE,4BAA4B,CAAC;CACzC;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,MAAM,GACT,uBAAuB,CAkFzB;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EAAE,EACjB,GAAG,UAAU,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,EAAE,GACpD,MAAM,EAAE,CAaV"}
@@ -18,7 +18,10 @@ export function validateEndpointCapture(value, providerId, id) {
18
18
  throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label}.trigger must be a boolean`);
19
19
  }
20
20
  const trigger = rawFlag === true;
21
- const headers = validateEndpointCaptureHeaders(rawCapture.headers, label);
21
+ const headers = validateEndpointCaptureHeaders(rawCapture.headers, `${label}.headers`, {
22
+ allowCookieTemplates: true,
23
+ });
24
+ const nativeReplayHeaders = validateEndpointCaptureHeaders(rawCapture.nativeReplayHeaders, `${label}.nativeReplayHeaders`, { allowCookieTemplates: false });
22
25
  let requestBody;
23
26
  if (rawCapture.requestBody !== undefined) {
24
27
  if (typeof rawCapture.requestBody !== 'string') {
@@ -45,6 +48,8 @@ export function validateEndpointCapture(value, providerId, id) {
45
48
  template.trigger = trigger;
46
49
  if (headers)
47
50
  template.headers = headers;
51
+ if (nativeReplayHeaders)
52
+ template.nativeReplayHeaders = nativeReplayHeaders;
48
53
  if (requestBody !== undefined)
49
54
  template.requestBody = requestBody;
50
55
  if (landingUrl !== undefined)
@@ -61,6 +66,7 @@ export function validateEndpointCapture(value, providerId, id) {
61
66
  return {
62
67
  trigger,
63
68
  headers,
69
+ nativeReplayHeaders,
64
70
  requestBody,
65
71
  landingUrl,
66
72
  pollMs,
@@ -69,40 +75,45 @@ export function validateEndpointCapture(value, providerId, id) {
69
75
  template,
70
76
  };
71
77
  }
72
- export function mergeHeaderAllowlist(headers, endpointCaptureHeaders) {
73
- if (!endpointCaptureHeaders)
74
- return headers;
78
+ export function mergeHeaderAllowlist(headers, ...headerMaps) {
75
79
  const seen = new Set(headers.map((header) => header.toLowerCase()));
76
80
  const merged = [...headers];
77
- for (const name of Object.keys(endpointCaptureHeaders)) {
78
- if (!seen.has(name)) {
79
- merged.push(name);
80
- seen.add(name);
81
+ for (const headerMap of headerMaps) {
82
+ if (!headerMap)
83
+ continue;
84
+ for (const name of Object.keys(headerMap)) {
85
+ if (!seen.has(name)) {
86
+ merged.push(name);
87
+ seen.add(name);
88
+ }
81
89
  }
82
90
  }
83
91
  return merged;
84
92
  }
85
- function validateEndpointCaptureHeaders(rawHeaders, label) {
93
+ function validateEndpointCaptureHeaders(rawHeaders, label, options) {
86
94
  if (rawHeaders === undefined)
87
95
  return undefined;
88
96
  if (!isRecord(rawHeaders)) {
89
- throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label}.headers must be an object of header name -> value`);
97
+ throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label} must be an object of header name -> value`);
90
98
  }
91
99
  const entries = Object.entries(rawHeaders);
92
100
  if (entries.length === 0) {
93
- throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label}.headers must not be empty`);
101
+ throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label} must not be empty`);
94
102
  }
95
103
  const normalized = {};
96
104
  for (const [name, headerValue] of entries) {
97
105
  const headerName = name.trim().toLowerCase();
98
106
  if (!headerName || !HEADER_NAME_RE.test(headerName)) {
99
- throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label}.headers contains an invalid header name: ${name}`);
107
+ throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label} contains an invalid header name: ${name}`);
100
108
  }
101
109
  if (typeof headerValue !== 'string') {
102
- throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label}.headers.${name} must be a string`);
110
+ throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label}.${name} must be a string`);
103
111
  }
104
112
  if (/[\r\n]/.test(headerValue)) {
105
- throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label}.headers.${name} value must not contain newlines`);
113
+ throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label}.${name} value must not contain newlines`);
114
+ }
115
+ if (!options.allowCookieTemplates && /\{\{cookie:[^}]+\}\}/i.test(headerValue)) {
116
+ throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label}.${name} must be a literal value`);
106
117
  }
107
118
  normalized[headerName] = headerValue;
108
119
  }
@@ -1 +1 @@
1
- {"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/providerRegistry/capture.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SatyaProviderEndpointCapture } from '../types';\nimport { isRecord, optionalIntAtLeast, readOptionalString } from './readers';\nimport { validateHttpsUrl } from './url';\nimport { SatyaErrorCode, satyaError } from '../types/error.types';\n\nconst HEADER_NAME_RE = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;\n\nexport interface ResolvedEndpointCapture {\n trigger: boolean;\n headers?: Record<string, string>;\n requestBody?: string;\n landingUrl?: string;\n pollMs?: number;\n maxAttempts?: number;\n requireLoginSignal?: boolean;\n template?: SatyaProviderEndpointCapture;\n}\n\nexport function validateEndpointCapture(\n value: Record<string, unknown>,\n providerId: string,\n id: string\n): ResolvedEndpointCapture {\n const rawCapture = value.capture;\n if (rawCapture === undefined) return { trigger: false };\n\n const label = `${providerId}.endpoints[${id}].capture`;\n if (!isRecord(rawCapture)) {\n throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label} must be an object`);\n }\n if (Object.keys(rawCapture).length === 0) {\n throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label} must not be empty`);\n }\n\n const rawFlag = rawCapture.trigger;\n if (rawFlag !== undefined && typeof rawFlag !== 'boolean') {\n throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label}.trigger must be a boolean`);\n }\n const trigger = rawFlag === true;\n const headers = validateEndpointCaptureHeaders(rawCapture.headers, label);\n\n let requestBody: string | undefined;\n if (rawCapture.requestBody !== undefined) {\n if (typeof rawCapture.requestBody !== 'string') {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label}.requestBody must be a string`\n );\n }\n requestBody = rawCapture.requestBody;\n }\n\n let landingUrl: string | undefined;\n if (rawCapture.landingUrl !== undefined) {\n const landing = readOptionalString(rawCapture, 'landingUrl', label);\n if (landing) {\n validateHttpsUrl(landing, { source: `${label}.landingUrl` });\n landingUrl = landing;\n }\n }\n\n const rawRequireLoginSignal = rawCapture.requireLoginSignal;\n if (rawRequireLoginSignal !== undefined && typeof rawRequireLoginSignal !== 'boolean') {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label}.requireLoginSignal must be a boolean`\n );\n }\n const pollMs = optionalIntAtLeast(rawCapture, 'pollMs', 1, label);\n const maxAttempts = optionalIntAtLeast(rawCapture, 'maxAttempts', 1, label);\n const template: SatyaProviderEndpointCapture = {};\n if (rawFlag !== undefined) template.trigger = trigger;\n if (headers) template.headers = headers;\n if (requestBody !== undefined) template.requestBody = requestBody;\n if (landingUrl !== undefined) template.landingUrl = landingUrl;\n if (pollMs !== undefined) template.pollMs = pollMs;\n if (maxAttempts !== undefined) template.maxAttempts = maxAttempts;\n if (rawRequireLoginSignal !== undefined) template.requireLoginSignal = rawRequireLoginSignal;\n if (Object.keys(template).length === 0) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label} must include at least one setting`\n );\n }\n\n return {\n trigger,\n headers,\n requestBody,\n landingUrl,\n pollMs,\n maxAttempts,\n requireLoginSignal: rawRequireLoginSignal,\n template,\n };\n}\n\nexport function mergeHeaderAllowlist(\n headers: string[],\n endpointCaptureHeaders?: Record<string, string>\n): string[] {\n if (!endpointCaptureHeaders) return headers;\n const seen = new Set(headers.map((header) => header.toLowerCase()));\n const merged = [...headers];\n for (const name of Object.keys(endpointCaptureHeaders)) {\n if (!seen.has(name)) {\n merged.push(name);\n seen.add(name);\n }\n }\n return merged;\n}\n\nfunction validateEndpointCaptureHeaders(\n rawHeaders: unknown,\n label: string\n): Record<string, string> | undefined {\n if (rawHeaders === undefined) return undefined;\n if (!isRecord(rawHeaders)) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label}.headers must be an object of header name -> value`\n );\n }\n const entries = Object.entries(rawHeaders);\n if (entries.length === 0) {\n throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label}.headers must not be empty`);\n }\n const normalized: Record<string, string> = {};\n for (const [name, headerValue] of entries) {\n const headerName = name.trim().toLowerCase();\n if (!headerName || !HEADER_NAME_RE.test(headerName)) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label}.headers contains an invalid header name: ${name}`\n );\n }\n if (typeof headerValue !== 'string') {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label}.headers.${name} must be a string`\n );\n }\n if (/[\\r\\n]/.test(headerValue)) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label}.headers.${name} value must not contain newlines`\n );\n }\n normalized[headerName] = headerValue;\n }\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n"]}
1
+ {"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/providerRegistry/capture.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SatyaProviderEndpointCapture } from '../types';\nimport { isRecord, optionalIntAtLeast, readOptionalString } from './readers';\nimport { validateHttpsUrl } from './url';\nimport { SatyaErrorCode, satyaError } from '../types/error.types';\n\nconst HEADER_NAME_RE = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;\n\nexport interface ResolvedEndpointCapture {\n trigger: boolean;\n headers?: Record<string, string>;\n nativeReplayHeaders?: Record<string, string>;\n requestBody?: string;\n landingUrl?: string;\n pollMs?: number;\n maxAttempts?: number;\n requireLoginSignal?: boolean;\n template?: SatyaProviderEndpointCapture;\n}\n\nexport function validateEndpointCapture(\n value: Record<string, unknown>,\n providerId: string,\n id: string\n): ResolvedEndpointCapture {\n const rawCapture = value.capture;\n if (rawCapture === undefined) return { trigger: false };\n\n const label = `${providerId}.endpoints[${id}].capture`;\n if (!isRecord(rawCapture)) {\n throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label} must be an object`);\n }\n if (Object.keys(rawCapture).length === 0) {\n throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label} must not be empty`);\n }\n\n const rawFlag = rawCapture.trigger;\n if (rawFlag !== undefined && typeof rawFlag !== 'boolean') {\n throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label}.trigger must be a boolean`);\n }\n const trigger = rawFlag === true;\n const headers = validateEndpointCaptureHeaders(rawCapture.headers, `${label}.headers`, {\n allowCookieTemplates: true,\n });\n const nativeReplayHeaders = validateEndpointCaptureHeaders(\n rawCapture.nativeReplayHeaders,\n `${label}.nativeReplayHeaders`,\n { allowCookieTemplates: false }\n );\n\n let requestBody: string | undefined;\n if (rawCapture.requestBody !== undefined) {\n if (typeof rawCapture.requestBody !== 'string') {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label}.requestBody must be a string`\n );\n }\n requestBody = rawCapture.requestBody;\n }\n\n let landingUrl: string | undefined;\n if (rawCapture.landingUrl !== undefined) {\n const landing = readOptionalString(rawCapture, 'landingUrl', label);\n if (landing) {\n validateHttpsUrl(landing, { source: `${label}.landingUrl` });\n landingUrl = landing;\n }\n }\n\n const rawRequireLoginSignal = rawCapture.requireLoginSignal;\n if (rawRequireLoginSignal !== undefined && typeof rawRequireLoginSignal !== 'boolean') {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label}.requireLoginSignal must be a boolean`\n );\n }\n const pollMs = optionalIntAtLeast(rawCapture, 'pollMs', 1, label);\n const maxAttempts = optionalIntAtLeast(rawCapture, 'maxAttempts', 1, label);\n const template: SatyaProviderEndpointCapture = {};\n if (rawFlag !== undefined) template.trigger = trigger;\n if (headers) template.headers = headers;\n if (nativeReplayHeaders) template.nativeReplayHeaders = nativeReplayHeaders;\n if (requestBody !== undefined) template.requestBody = requestBody;\n if (landingUrl !== undefined) template.landingUrl = landingUrl;\n if (pollMs !== undefined) template.pollMs = pollMs;\n if (maxAttempts !== undefined) template.maxAttempts = maxAttempts;\n if (rawRequireLoginSignal !== undefined) template.requireLoginSignal = rawRequireLoginSignal;\n if (Object.keys(template).length === 0) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label} must include at least one setting`\n );\n }\n\n return {\n trigger,\n headers,\n nativeReplayHeaders,\n requestBody,\n landingUrl,\n pollMs,\n maxAttempts,\n requireLoginSignal: rawRequireLoginSignal,\n template,\n };\n}\n\nexport function mergeHeaderAllowlist(\n headers: string[],\n ...headerMaps: (Record<string, string> | undefined)[]\n): string[] {\n const seen = new Set(headers.map((header) => header.toLowerCase()));\n const merged = [...headers];\n for (const headerMap of headerMaps) {\n if (!headerMap) continue;\n for (const name of Object.keys(headerMap)) {\n if (!seen.has(name)) {\n merged.push(name);\n seen.add(name);\n }\n }\n }\n return merged;\n}\n\nfunction validateEndpointCaptureHeaders(\n rawHeaders: unknown,\n label: string,\n options: { allowCookieTemplates: boolean }\n): Record<string, string> | undefined {\n if (rawHeaders === undefined) return undefined;\n if (!isRecord(rawHeaders)) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label} must be an object of header name -> value`\n );\n }\n const entries = Object.entries(rawHeaders);\n if (entries.length === 0) {\n throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label} must not be empty`);\n }\n const normalized: Record<string, string> = {};\n for (const [name, headerValue] of entries) {\n const headerName = name.trim().toLowerCase();\n if (!headerName || !HEADER_NAME_RE.test(headerName)) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label} contains an invalid header name: ${name}`\n );\n }\n if (typeof headerValue !== 'string') {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label}.${name} must be a string`\n );\n }\n if (/[\\r\\n]/.test(headerValue)) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label}.${name} value must not contain newlines`\n );\n }\n if (!options.allowCookieTemplates && /\\{\\{cookie:[^}]+\\}\\}/i.test(headerValue)) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_CAPTURE_INVALID,\n `${label}.${name} must be a literal value`\n );\n }\n normalized[headerName] = headerValue;\n }\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n}\n"]}
@@ -75,7 +75,7 @@ function validateProviderEndpoint(value, options) {
75
75
  if (value.replayCapturedUrl !== undefined && typeof value.replayCapturedUrl !== 'boolean') {
76
76
  throw satyaError(SatyaErrorCode.PROVIDER_ENDPOINT_INVALID, `${providerId}.endpoints[${id}].replayCapturedUrl must be a boolean`);
77
77
  }
78
- const effectiveHeaders = mergeHeaderAllowlist(headers, capture.headers);
78
+ const effectiveHeaders = mergeHeaderAllowlist(headers, capture.headers, capture.nativeReplayHeaders);
79
79
  const endpoint = {
80
80
  ...cloneJson(value),
81
81
  id,
@@ -1 +1 @@
1
- {"version":3,"file":"endpoints.js","sourceRoot":"","sources":["../../src/providerRegistry/endpoints.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SatyaProviderEndpoint } from '../types';\nimport { mergeHeaderAllowlist, validateEndpointCapture } from './capture';\nimport {\n cloneJson,\n isRecord,\n readOptionalArray,\n readOptionalString,\n readRequiredString,\n readStringArray,\n} from './readers';\nimport { deriveResponseReveals, validateRequestMatchRules, validateRevealPattern } from './rules';\nimport { validateHttpsUrl } from './url';\nimport { SatyaErrorCode, satyaError } from '../types/error.types';\n\nconst HTTP_METHOD_TOKEN_RE = /^[A-Za-z][A-Za-z0-9!#$%&'*+.^_`|~-]*$/;\nconst SUPPORTED_RESPONSE_TYPES = new Set(['json', 'html', 'text']);\n\nexport function validateProviderEndpoints(\n value: Record<string, unknown>,\n options: { providerId: string; expectedSni: string; source: string }\n): SatyaProviderEndpoint[] {\n if (!Array.isArray(value.endpoints) || value.endpoints.length === 0) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${options.source}.endpoints must be a non-empty array`\n );\n }\n const seen = new Set<string>();\n return value.endpoints.map((endpoint, index) => {\n const validated = validateProviderEndpoint(endpoint, {\n ...options,\n source: `${options.source}.endpoints[${index}]`,\n });\n if (seen.has(validated.id)) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${options.providerId}.endpoints contains duplicate id: ${validated.id}`\n );\n }\n seen.add(validated.id);\n return validated;\n });\n}\n\nexport function selectProviderEndpoint(\n providerId: string,\n endpoints: SatyaProviderEndpoint[],\n endpointId?: string\n): SatyaProviderEndpoint {\n if (!endpointId) return endpoints[0];\n const normalizedEndpointId = endpointId.trim();\n const endpoint = endpoints.find((candidate) => candidate.id === normalizedEndpointId);\n if (!endpoint) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `Unknown endpoint id for ${providerId}: ${normalizedEndpointId}`\n );\n }\n return endpoint;\n}\n\nfunction validateProviderEndpoint(\n value: unknown,\n options: { providerId: string; expectedSni: string; source: string }\n): SatyaProviderEndpoint {\n const { providerId, expectedSni, source } = options;\n if (!isRecord(value))\n throw satyaError(SatyaErrorCode.PROVIDER_ENDPOINT_INVALID, `${source} must be an object`);\n\n const id = readRequiredString(value, 'id', source);\n const method = readRequiredString(value, 'method', source).toUpperCase();\n if (!HTTP_METHOD_TOKEN_RE.test(method)) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].method must be a valid HTTP token`\n );\n }\n const url = readRequiredString(value, 'url', source);\n const parsedUrl = validateHttpsUrl(url, { source: `${providerId}.endpoints[${id}].url` });\n if (parsedUrl.hostname !== expectedSni) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].url host must match tlsIdentityPolicy.expectedSni`\n );\n }\n\n const headers = readStringArray(value, 'headers', source);\n if (headers.length === 0) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].headers must not be empty`\n );\n }\n const responseType = readRequiredString(value, 'responseType', source).toLowerCase();\n if (!SUPPORTED_RESPONSE_TYPES.has(responseType)) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].responseType must be one of: json, html, text`\n );\n }\n const requestMatchRules = readOptionalArray(value, 'requestMatchRules', source);\n if (requestMatchRules.length === 0) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].requestMatchRules must not be empty`\n );\n }\n validateRequestMatchRules(providerId, requestMatchRules, method, parsedUrl);\n\n const revealRules = readOptionalArray(value, 'revealRules', source);\n if (revealRules.length === 0) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].revealRules must not be empty`\n );\n }\n deriveResponseReveals(providerId, id, revealRules, responseType);\n const htmlCaptureBodyPattern = readOptionalString(value, 'htmlCaptureBodyPattern', source);\n if (htmlCaptureBodyPattern !== undefined) {\n if (responseType !== 'html' && responseType !== 'text') {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].htmlCaptureBodyPattern requires responseType html or text`\n );\n }\n validateRevealPattern(\n htmlCaptureBodyPattern,\n `${providerId}.endpoints[${id}].htmlCaptureBodyPattern`\n );\n }\n\n const capture = validateEndpointCapture(value, providerId, id);\n if (value.replayCapturedUrl !== undefined && typeof value.replayCapturedUrl !== 'boolean') {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].replayCapturedUrl must be a boolean`\n );\n }\n const effectiveHeaders = mergeHeaderAllowlist(headers, capture.headers);\n\n const endpoint: SatyaProviderEndpoint = {\n ...cloneJson(value as SatyaProviderEndpoint),\n id,\n url: parsedUrl.toString(),\n method,\n headers: effectiveHeaders,\n responseType,\n requestMatchRules: requestMatchRules as Record<string, unknown>[],\n revealRules: revealRules as Record<string, unknown>[],\n };\n if (capture.template) endpoint.capture = capture.template;\n else delete endpoint.capture;\n if (htmlCaptureBodyPattern !== undefined) {\n endpoint.htmlCaptureBodyPattern = htmlCaptureBodyPattern;\n }\n return endpoint;\n}\n"]}
1
+ {"version":3,"file":"endpoints.js","sourceRoot":"","sources":["../../src/providerRegistry/endpoints.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SatyaProviderEndpoint } from '../types';\nimport { mergeHeaderAllowlist, validateEndpointCapture } from './capture';\nimport {\n cloneJson,\n isRecord,\n readOptionalArray,\n readOptionalString,\n readRequiredString,\n readStringArray,\n} from './readers';\nimport { deriveResponseReveals, validateRequestMatchRules, validateRevealPattern } from './rules';\nimport { validateHttpsUrl } from './url';\nimport { SatyaErrorCode, satyaError } from '../types/error.types';\n\nconst HTTP_METHOD_TOKEN_RE = /^[A-Za-z][A-Za-z0-9!#$%&'*+.^_`|~-]*$/;\nconst SUPPORTED_RESPONSE_TYPES = new Set(['json', 'html', 'text']);\n\nexport function validateProviderEndpoints(\n value: Record<string, unknown>,\n options: { providerId: string; expectedSni: string; source: string }\n): SatyaProviderEndpoint[] {\n if (!Array.isArray(value.endpoints) || value.endpoints.length === 0) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${options.source}.endpoints must be a non-empty array`\n );\n }\n const seen = new Set<string>();\n return value.endpoints.map((endpoint, index) => {\n const validated = validateProviderEndpoint(endpoint, {\n ...options,\n source: `${options.source}.endpoints[${index}]`,\n });\n if (seen.has(validated.id)) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${options.providerId}.endpoints contains duplicate id: ${validated.id}`\n );\n }\n seen.add(validated.id);\n return validated;\n });\n}\n\nexport function selectProviderEndpoint(\n providerId: string,\n endpoints: SatyaProviderEndpoint[],\n endpointId?: string\n): SatyaProviderEndpoint {\n if (!endpointId) return endpoints[0];\n const normalizedEndpointId = endpointId.trim();\n const endpoint = endpoints.find((candidate) => candidate.id === normalizedEndpointId);\n if (!endpoint) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `Unknown endpoint id for ${providerId}: ${normalizedEndpointId}`\n );\n }\n return endpoint;\n}\n\nfunction validateProviderEndpoint(\n value: unknown,\n options: { providerId: string; expectedSni: string; source: string }\n): SatyaProviderEndpoint {\n const { providerId, expectedSni, source } = options;\n if (!isRecord(value))\n throw satyaError(SatyaErrorCode.PROVIDER_ENDPOINT_INVALID, `${source} must be an object`);\n\n const id = readRequiredString(value, 'id', source);\n const method = readRequiredString(value, 'method', source).toUpperCase();\n if (!HTTP_METHOD_TOKEN_RE.test(method)) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].method must be a valid HTTP token`\n );\n }\n const url = readRequiredString(value, 'url', source);\n const parsedUrl = validateHttpsUrl(url, { source: `${providerId}.endpoints[${id}].url` });\n if (parsedUrl.hostname !== expectedSni) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].url host must match tlsIdentityPolicy.expectedSni`\n );\n }\n\n const headers = readStringArray(value, 'headers', source);\n if (headers.length === 0) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].headers must not be empty`\n );\n }\n const responseType = readRequiredString(value, 'responseType', source).toLowerCase();\n if (!SUPPORTED_RESPONSE_TYPES.has(responseType)) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].responseType must be one of: json, html, text`\n );\n }\n const requestMatchRules = readOptionalArray(value, 'requestMatchRules', source);\n if (requestMatchRules.length === 0) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].requestMatchRules must not be empty`\n );\n }\n validateRequestMatchRules(providerId, requestMatchRules, method, parsedUrl);\n\n const revealRules = readOptionalArray(value, 'revealRules', source);\n if (revealRules.length === 0) {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].revealRules must not be empty`\n );\n }\n deriveResponseReveals(providerId, id, revealRules, responseType);\n const htmlCaptureBodyPattern = readOptionalString(value, 'htmlCaptureBodyPattern', source);\n if (htmlCaptureBodyPattern !== undefined) {\n if (responseType !== 'html' && responseType !== 'text') {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].htmlCaptureBodyPattern requires responseType html or text`\n );\n }\n validateRevealPattern(\n htmlCaptureBodyPattern,\n `${providerId}.endpoints[${id}].htmlCaptureBodyPattern`\n );\n }\n\n const capture = validateEndpointCapture(value, providerId, id);\n if (value.replayCapturedUrl !== undefined && typeof value.replayCapturedUrl !== 'boolean') {\n throw satyaError(\n SatyaErrorCode.PROVIDER_ENDPOINT_INVALID,\n `${providerId}.endpoints[${id}].replayCapturedUrl must be a boolean`\n );\n }\n const effectiveHeaders = mergeHeaderAllowlist(\n headers,\n capture.headers,\n capture.nativeReplayHeaders\n );\n\n const endpoint: SatyaProviderEndpoint = {\n ...cloneJson(value as SatyaProviderEndpoint),\n id,\n url: parsedUrl.toString(),\n method,\n headers: effectiveHeaders,\n responseType,\n requestMatchRules: requestMatchRules as Record<string, unknown>[],\n revealRules: revealRules as Record<string, unknown>[],\n };\n if (capture.template) endpoint.capture = capture.template;\n else delete endpoint.capture;\n if (htmlCaptureBodyPattern !== undefined) {\n endpoint.htmlCaptureBodyPattern = htmlCaptureBodyPattern;\n }\n return endpoint;\n}\n"]}
@@ -26,6 +26,7 @@ export interface SatyaProviderLogin {
26
26
  export interface SatyaProviderEndpointCapture {
27
27
  trigger?: boolean;
28
28
  headers?: Record<string, string>;
29
+ nativeReplayHeaders?: Record<string, string>;
29
30
  requestBody?: string;
30
31
  landingUrl?: string;
31
32
  pollMs?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"provider.types.d.ts","sourceRoot":"","sources":["../../src/types/provider.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"provider.types.d.ts","sourceRoot":"","sources":["../../src/types/provider.types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,wBAAwB,EAAE,CAAC;CACxC;AAED,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC;AAEvD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,4BAA4B;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC7C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACvC,OAAO,CAAC,EAAE,4BAA4B,CAAC;IACvC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,SAAS,EAAE,qBAAqB,EAAE,CAAC;IACnC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE;QACP,MAAM,EAAE,OAAO,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,QAAQ,CAAC,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC;IACF,SAAS,CAAC,EAAE;QACV,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACzB,GAAG,IAAI,CAAC;IACT,SAAS,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;QAC7C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;QACvC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,aAAa,CAAC,EAAE;QACd,qBAAqB,EAAE,MAAM,EAAE,CAAC;QAChC,4BAA4B,EAAE,MAAM,EAAE,CAAC;QACvC,sBAAsB,CAAC,EAAE,kBAAkB,EAAE,CAAC;KAC/C,CAAC;IACF,SAAS,CAAC,EAAE;QACV,YAAY,EAAE,OAAO,CAAC;QACtB,YAAY,EAAE,OAAO,CAAC;QACtB,eAAe,EAAE,OAAO,CAAC;QACzB,cAAc,EAAE,OAAO,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,iBAAiB,EAAE;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC7B,mBAAmB,CAAC,EAAE,gCAAgC,CAAC;QACvD,gBAAgB,CAAC,EAAE,KAAK,CAAC;QACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,kBAAkB,EAAE;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,uBAAuB,CAAC,EAAE,MAAM,CAAC;KAClC,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB;IACtC,wBAAwB,EAAE,MAAM,CAAC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,gCAAgC,CAAC;IACtD,gBAAgB,EAAE,KAAK,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,WAAW,EAAE,uBAAuB,CAAC;CACtC"}
@@ -1 +1 @@
1
- {"version":3,"file":"provider.types.js","sourceRoot":"","sources":["../../src/types/provider.types.ts"],"names":[],"mappings":"","sourcesContent":["export interface SatyaProviderEndpointRef {\n id: string;\n method: string;\n url: string;\n responseType?: string;\n}\n\nexport interface SatyaProviderRef {\n label: string;\n description: string;\n providerId: string;\n iconUrl?: string;\n endpoints?: SatyaProviderEndpointRef[];\n}\n\nexport type SatyaProviderInjection = string | string[];\n\nexport interface ResponseRevealRule {\n alias: string;\n type: 'json' | 'regex' | 'jsonRegex';\n path?: string;\n pattern?: string;\n group?: number | string;\n}\n\nexport interface SatyaProviderLogin {\n url: string;\n [key: string]: unknown;\n}\n\nexport interface SatyaProviderEndpointCapture {\n trigger?: boolean;\n headers?: Record<string, string>;\n requestBody?: string;\n landingUrl?: string;\n pollMs?: number;\n maxAttempts?: number;\n requireLoginSignal?: boolean;\n [key: string]: unknown;\n}\n\nexport interface SatyaProviderEndpoint {\n id: string;\n url: string;\n method: string;\n headers: string[];\n responseType: string;\n requestMatchRules: Record<string, unknown>[];\n revealRules: Record<string, unknown>[];\n capture?: SatyaProviderEndpointCapture;\n replayCapturedUrl?: boolean;\n htmlCaptureBodyPattern?: string;\n [key: string]: unknown;\n}\n\nexport interface SatyaProviderTemplate {\n schemaVersion: string;\n providerId: string;\n name: string;\n iconUrl?: string;\n login: SatyaProviderLogin;\n auth?: Record<string, unknown>;\n endpoints: SatyaProviderEndpoint[];\n selectedEndpointId?: string;\n label?: string;\n description?: string;\n displayName?: string;\n version?: string;\n target?: {\n scheme: 'https';\n host: string;\n port: number;\n };\n webLogin?: {\n startUrl: string;\n callbackUrl?: string;\n oauthHosts?: string[];\n };\n userAgent?: {\n ios?: string | null;\n android?: string | null;\n } | null;\n injection?: SatyaProviderInjection | null;\n proofTarget?: {\n method: string;\n url: string;\n requestMatchRules: Record<string, unknown>[];\n revealRules: Record<string, unknown>[];\n [key: string]: unknown;\n };\n privacyPolicy?: {\n allowedRequestHeaders: string[];\n allowedResponseBodyJsonPaths: string[];\n allowedResponseReveals?: ResponseRevealRule[];\n };\n tlsPolicy?: {\n requireTls13: boolean;\n allowZeroRtt: boolean;\n allowResumption: boolean;\n allowKeyUpdate: boolean;\n preferredCipher: string;\n maxSentBytes: number;\n maxRecvBytes: number;\n };\n tlsIdentityPolicy: {\n expectedSni: string;\n allowedAlpn?: string[];\n spkiSha256Hex?: string | null;\n spkiSha256B64Pins?: string[];\n tlsTrustStorePolicy?: 'web-pki-os-roots-no-user-roots';\n userRootsAllowed?: false;\n redirectPolicyId?: string;\n };\n templateGovernance: {\n policyHash: string;\n fixtureSetHash?: string;\n governancePolicyVersion?: string;\n };\n [key: string]: unknown;\n}\n\nexport interface SatyaProviderPolicy {\n providerId: string;\n host: string;\n policyHash: string;\n spkiSha256B64Pins: string[];\n}\n\nexport interface CapturedProviderRequest {\n url: string;\n method: string;\n headers: Record<string, string>;\n body?: string | null;\n}\n\nexport interface CapturedProviderResponse {\n url: string;\n status: number;\n headers: Record<string, string>;\n body?: string | null;\n}\n\nexport interface NativeReplayTlsEvidence {\n serverCertChainSha256Hex: string;\n sni: string;\n alpn: string;\n tlsVersion: string;\n tlsTrustStorePolicy: 'web-pki-os-roots-no-user-roots';\n userRootsAllowed: false;\n matchedSpkiSha256B64?: string | null;\n expectedSpkiPinsB64: string[];\n}\n\nexport interface NativeReplayClaim {\n name: string;\n sourcePath: string;\n value: unknown;\n}\n\nexport interface NativeReplayObservation {\n providerId: string;\n policyHash: string;\n host: string;\n method: string;\n pathAndQuery: string;\n statusCode: number;\n finalUrl: string;\n contentType?: string | null;\n contentEncoding?: string | null;\n responseSha256Hex: string;\n claims: NativeReplayClaim[];\n tlsEvidence: NativeReplayTlsEvidence;\n}\n"]}
1
+ {"version":3,"file":"provider.types.js","sourceRoot":"","sources":["../../src/types/provider.types.ts"],"names":[],"mappings":"","sourcesContent":["export interface SatyaProviderEndpointRef {\n id: string;\n method: string;\n url: string;\n responseType?: string;\n}\n\nexport interface SatyaProviderRef {\n label: string;\n description: string;\n providerId: string;\n iconUrl?: string;\n endpoints?: SatyaProviderEndpointRef[];\n}\n\nexport type SatyaProviderInjection = string | string[];\n\nexport interface ResponseRevealRule {\n alias: string;\n type: 'json' | 'regex' | 'jsonRegex';\n path?: string;\n pattern?: string;\n group?: number | string;\n}\n\nexport interface SatyaProviderLogin {\n url: string;\n [key: string]: unknown;\n}\n\nexport interface SatyaProviderEndpointCapture {\n trigger?: boolean;\n headers?: Record<string, string>;\n nativeReplayHeaders?: Record<string, string>;\n requestBody?: string;\n landingUrl?: string;\n pollMs?: number;\n maxAttempts?: number;\n requireLoginSignal?: boolean;\n [key: string]: unknown;\n}\n\nexport interface SatyaProviderEndpoint {\n id: string;\n url: string;\n method: string;\n headers: string[];\n responseType: string;\n requestMatchRules: Record<string, unknown>[];\n revealRules: Record<string, unknown>[];\n capture?: SatyaProviderEndpointCapture;\n replayCapturedUrl?: boolean;\n htmlCaptureBodyPattern?: string;\n [key: string]: unknown;\n}\n\nexport interface SatyaProviderTemplate {\n schemaVersion: string;\n providerId: string;\n name: string;\n iconUrl?: string;\n login: SatyaProviderLogin;\n auth?: Record<string, unknown>;\n endpoints: SatyaProviderEndpoint[];\n selectedEndpointId?: string;\n label?: string;\n description?: string;\n displayName?: string;\n version?: string;\n target?: {\n scheme: 'https';\n host: string;\n port: number;\n };\n webLogin?: {\n startUrl: string;\n callbackUrl?: string;\n oauthHosts?: string[];\n };\n userAgent?: {\n ios?: string | null;\n android?: string | null;\n } | null;\n injection?: SatyaProviderInjection | null;\n proofTarget?: {\n method: string;\n url: string;\n requestMatchRules: Record<string, unknown>[];\n revealRules: Record<string, unknown>[];\n [key: string]: unknown;\n };\n privacyPolicy?: {\n allowedRequestHeaders: string[];\n allowedResponseBodyJsonPaths: string[];\n allowedResponseReveals?: ResponseRevealRule[];\n };\n tlsPolicy?: {\n requireTls13: boolean;\n allowZeroRtt: boolean;\n allowResumption: boolean;\n allowKeyUpdate: boolean;\n preferredCipher: string;\n maxSentBytes: number;\n maxRecvBytes: number;\n };\n tlsIdentityPolicy: {\n expectedSni: string;\n allowedAlpn?: string[];\n spkiSha256Hex?: string | null;\n spkiSha256B64Pins?: string[];\n tlsTrustStorePolicy?: 'web-pki-os-roots-no-user-roots';\n userRootsAllowed?: false;\n redirectPolicyId?: string;\n };\n templateGovernance: {\n policyHash: string;\n fixtureSetHash?: string;\n governancePolicyVersion?: string;\n };\n [key: string]: unknown;\n}\n\nexport interface SatyaProviderPolicy {\n providerId: string;\n host: string;\n policyHash: string;\n spkiSha256B64Pins: string[];\n}\n\nexport interface CapturedProviderRequest {\n url: string;\n method: string;\n headers: Record<string, string>;\n body?: string | null;\n}\n\nexport interface CapturedProviderResponse {\n url: string;\n status: number;\n headers: Record<string, string>;\n body?: string | null;\n}\n\nexport interface NativeReplayTlsEvidence {\n serverCertChainSha256Hex: string;\n sni: string;\n alpn: string;\n tlsVersion: string;\n tlsTrustStorePolicy: 'web-pki-os-roots-no-user-roots';\n userRootsAllowed: false;\n matchedSpkiSha256B64?: string | null;\n expectedSpkiPinsB64: string[];\n}\n\nexport interface NativeReplayClaim {\n name: string;\n sourcePath: string;\n value: unknown;\n}\n\nexport interface NativeReplayObservation {\n providerId: string;\n policyHash: string;\n host: string;\n method: string;\n pathAndQuery: string;\n statusCode: number;\n finalUrl: string;\n contentType?: string | null;\n contentEncoding?: string | null;\n responseSha256Hex: string;\n claims: NativeReplayClaim[];\n tlsEvidence: NativeReplayTlsEvidence;\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'SatyaAttest'
3
- s.version = '1.0.2'
3
+ s.version = '1.1.0'
4
4
  s.summary = 'SATYA-W mobile TLS app-attestation (App Attest + Secure Enclave) backed by the Rust satya-sdk core'
5
5
  s.description = 'Expo native module wrapping the Rust satya-ffi XCFramework and layering iOS App Attest + Secure Enclave platform attestation.'
6
6
  s.author = 'Justin'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burnt-labs/expo-satya-attest",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "SATYA-W mobile TLS app-attestation (App Attest + Secure Enclave / Play Integrity) backed by the Rust satya-sdk core",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -7,13 +7,17 @@ export function rewriteForNativeReplay(
7
7
  ): CapturedProviderRequest {
8
8
  const proof = template.proofTarget as
9
9
  | {
10
+ capture?: { nativeReplayHeaders?: Record<string, string> };
10
11
  nativeReplayRewrite?: { fromHost?: string; toHost?: string; stripPathPrefix?: string };
11
12
  nativeReplayInjectBearer?: boolean;
12
13
  }
13
14
  | undefined;
14
15
  const rw = proof?.nativeReplayRewrite;
15
16
  let url = request.url;
16
- const headers: Record<string, string> = { ...request.headers };
17
+ const headers: Record<string, string> = {
18
+ ...(proof?.capture?.nativeReplayHeaders ?? {}),
19
+ ...request.headers,
20
+ };
17
21
  if (rw?.fromHost && rw?.toHost) {
18
22
  const fromBase = `https://${rw.fromHost}`;
19
23
  if (url.startsWith(fromBase)) {
package/src/index.ts CHANGED
@@ -61,4 +61,7 @@ export type {
61
61
  SatyaProviderPolicyFetchOptions,
62
62
  SatyaProviderPolicyRefreshMode,
63
63
  } from './providerPolicy';
64
- export * from './types';
64
+ export * from './types/artifact.types';
65
+ export * from './types/error.types';
66
+ export * from './types/mobile.types';
67
+ export * from './types/provider.types';
@@ -8,6 +8,7 @@ const HEADER_NAME_RE = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;
8
8
  export interface ResolvedEndpointCapture {
9
9
  trigger: boolean;
10
10
  headers?: Record<string, string>;
11
+ nativeReplayHeaders?: Record<string, string>;
11
12
  requestBody?: string;
12
13
  landingUrl?: string;
13
14
  pollMs?: number;
@@ -37,7 +38,14 @@ export function validateEndpointCapture(
37
38
  throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label}.trigger must be a boolean`);
38
39
  }
39
40
  const trigger = rawFlag === true;
40
- const headers = validateEndpointCaptureHeaders(rawCapture.headers, label);
41
+ const headers = validateEndpointCaptureHeaders(rawCapture.headers, `${label}.headers`, {
42
+ allowCookieTemplates: true,
43
+ });
44
+ const nativeReplayHeaders = validateEndpointCaptureHeaders(
45
+ rawCapture.nativeReplayHeaders,
46
+ `${label}.nativeReplayHeaders`,
47
+ { allowCookieTemplates: false }
48
+ );
41
49
 
42
50
  let requestBody: string | undefined;
43
51
  if (rawCapture.requestBody !== undefined) {
@@ -71,6 +79,7 @@ export function validateEndpointCapture(
71
79
  const template: SatyaProviderEndpointCapture = {};
72
80
  if (rawFlag !== undefined) template.trigger = trigger;
73
81
  if (headers) template.headers = headers;
82
+ if (nativeReplayHeaders) template.nativeReplayHeaders = nativeReplayHeaders;
74
83
  if (requestBody !== undefined) template.requestBody = requestBody;
75
84
  if (landingUrl !== undefined) template.landingUrl = landingUrl;
76
85
  if (pollMs !== undefined) template.pollMs = pollMs;
@@ -86,6 +95,7 @@ export function validateEndpointCapture(
86
95
  return {
87
96
  trigger,
88
97
  headers,
98
+ nativeReplayHeaders,
89
99
  requestBody,
90
100
  landingUrl,
91
101
  pollMs,
@@ -97,15 +107,17 @@ export function validateEndpointCapture(
97
107
 
98
108
  export function mergeHeaderAllowlist(
99
109
  headers: string[],
100
- endpointCaptureHeaders?: Record<string, string>
110
+ ...headerMaps: (Record<string, string> | undefined)[]
101
111
  ): string[] {
102
- if (!endpointCaptureHeaders) return headers;
103
112
  const seen = new Set(headers.map((header) => header.toLowerCase()));
104
113
  const merged = [...headers];
105
- for (const name of Object.keys(endpointCaptureHeaders)) {
106
- if (!seen.has(name)) {
107
- merged.push(name);
108
- seen.add(name);
114
+ for (const headerMap of headerMaps) {
115
+ if (!headerMap) continue;
116
+ for (const name of Object.keys(headerMap)) {
117
+ if (!seen.has(name)) {
118
+ merged.push(name);
119
+ seen.add(name);
120
+ }
109
121
  }
110
122
  }
111
123
  return merged;
@@ -113,18 +125,19 @@ export function mergeHeaderAllowlist(
113
125
 
114
126
  function validateEndpointCaptureHeaders(
115
127
  rawHeaders: unknown,
116
- label: string
128
+ label: string,
129
+ options: { allowCookieTemplates: boolean }
117
130
  ): Record<string, string> | undefined {
118
131
  if (rawHeaders === undefined) return undefined;
119
132
  if (!isRecord(rawHeaders)) {
120
133
  throw satyaError(
121
134
  SatyaErrorCode.PROVIDER_CAPTURE_INVALID,
122
- `${label}.headers must be an object of header name -> value`
135
+ `${label} must be an object of header name -> value`
123
136
  );
124
137
  }
125
138
  const entries = Object.entries(rawHeaders);
126
139
  if (entries.length === 0) {
127
- throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label}.headers must not be empty`);
140
+ throw satyaError(SatyaErrorCode.PROVIDER_CAPTURE_INVALID, `${label} must not be empty`);
128
141
  }
129
142
  const normalized: Record<string, string> = {};
130
143
  for (const [name, headerValue] of entries) {
@@ -132,19 +145,25 @@ function validateEndpointCaptureHeaders(
132
145
  if (!headerName || !HEADER_NAME_RE.test(headerName)) {
133
146
  throw satyaError(
134
147
  SatyaErrorCode.PROVIDER_CAPTURE_INVALID,
135
- `${label}.headers contains an invalid header name: ${name}`
148
+ `${label} contains an invalid header name: ${name}`
136
149
  );
137
150
  }
138
151
  if (typeof headerValue !== 'string') {
139
152
  throw satyaError(
140
153
  SatyaErrorCode.PROVIDER_CAPTURE_INVALID,
141
- `${label}.headers.${name} must be a string`
154
+ `${label}.${name} must be a string`
142
155
  );
143
156
  }
144
157
  if (/[\r\n]/.test(headerValue)) {
145
158
  throw satyaError(
146
159
  SatyaErrorCode.PROVIDER_CAPTURE_INVALID,
147
- `${label}.headers.${name} value must not contain newlines`
160
+ `${label}.${name} value must not contain newlines`
161
+ );
162
+ }
163
+ if (!options.allowCookieTemplates && /\{\{cookie:[^}]+\}\}/i.test(headerValue)) {
164
+ throw satyaError(
165
+ SatyaErrorCode.PROVIDER_CAPTURE_INVALID,
166
+ `${label}.${name} must be a literal value`
148
167
  );
149
168
  }
150
169
  normalized[headerName] = headerValue;
@@ -136,7 +136,11 @@ function validateProviderEndpoint(
136
136
  `${providerId}.endpoints[${id}].replayCapturedUrl must be a boolean`
137
137
  );
138
138
  }
139
- const effectiveHeaders = mergeHeaderAllowlist(headers, capture.headers);
139
+ const effectiveHeaders = mergeHeaderAllowlist(
140
+ headers,
141
+ capture.headers,
142
+ capture.nativeReplayHeaders
143
+ );
140
144
 
141
145
  const endpoint: SatyaProviderEndpoint = {
142
146
  ...cloneJson(value as SatyaProviderEndpoint),
@@ -31,6 +31,7 @@ export interface SatyaProviderLogin {
31
31
  export interface SatyaProviderEndpointCapture {
32
32
  trigger?: boolean;
33
33
  headers?: Record<string, string>;
34
+ nativeReplayHeaders?: Record<string, string>;
34
35
  requestBody?: string;
35
36
  landingUrl?: string;
36
37
  pollMs?: number;