@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 +6 -0
- package/PROVIDER_CONFIG.md +15 -7
- package/android/build.gradle +2 -2
- package/build/components/providerAttest/replayRewrite.d.ts.map +1 -1
- package/build/components/providerAttest/replayRewrite.js +4 -1
- package/build/components/providerAttest/replayRewrite.js.map +1 -1
- package/build/index.d.ts +4 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -1
- package/build/index.js.map +1 -1
- package/build/providerRegistry/capture.d.ts +2 -1
- package/build/providerRegistry/capture.d.ts.map +1 -1
- package/build/providerRegistry/capture.js +25 -14
- package/build/providerRegistry/capture.js.map +1 -1
- package/build/providerRegistry/endpoints.js +1 -1
- package/build/providerRegistry/endpoints.js.map +1 -1
- package/build/types/provider.types.d.ts +1 -0
- package/build/types/provider.types.d.ts.map +1 -1
- package/build/types/provider.types.js.map +1 -1
- package/ios/Frameworks/libsatya_ffi-rs.xcframework/ios-arm64/libsatya_ffi.a +0 -0
- package/ios/Frameworks/libsatya_ffi-rs.xcframework/ios-arm64_x86_64-simulator/libsatya_ffi.a +0 -0
- package/ios/SatyaAttest.podspec +1 -1
- package/package.json +1 -1
- package/src/components/providerAttest/replayRewrite.ts +5 -1
- package/src/index.ts +4 -1
- package/src/providerRegistry/capture.ts +32 -13
- package/src/providerRegistry/endpoints.ts +5 -1
- package/src/types/provider.types.ts +1 -0
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.
|
package/PROVIDER_CONFIG.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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`,
|
|
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
|
package/android/build.gradle
CHANGED
|
@@ -4,13 +4,13 @@ plugins {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
group = 'expo.modules.satyaattest'
|
|
7
|
-
version = '1.0
|
|
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
|
|
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,
|
|
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 = {
|
|
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,
|
|
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
|
package/build/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
package/build/index.js.map
CHANGED
|
@@ -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,
|
|
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[],
|
|
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,
|
|
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,
|
|
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
|
|
78
|
-
if (!
|
|
79
|
-
|
|
80
|
-
|
|
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}
|
|
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}
|
|
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}
|
|
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}
|
|
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}
|
|
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":"AACA,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AAC1E,OAAO,EACL,SAAS,EACT,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAClG,OAAO,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElE,MAAM,oBAAoB,GAAG,uCAAuC,CAAC;AACrE,MAAM,wBAAwB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEnE,MAAM,UAAU,yBAAyB,CACvC,KAA8B,EAC9B,OAAoE;IAEpE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpE,MAAM,UAAU,CACd,cAAc,CAAC,yBAAyB,EACxC,GAAG,OAAO,CAAC,MAAM,sCAAsC,CACxD,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;QAC7C,MAAM,SAAS,GAAG,wBAAwB,CAAC,QAAQ,EAAE;YACnD,GAAG,OAAO;YACV,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,cAAc,KAAK,GAAG;SAChD,CAAC,CAAC;QACH,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;YAC3B,MAAM,UAAU,CACd,cAAc,CAAC,yBAAyB,EACxC,GAAG,OAAO,CAAC,UAAU,qCAAqC,SAAS,CAAC,EAAE,EAAE,CACzE,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACvB,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,UAAkB,EAClB,SAAkC,EAClC,UAAmB;IAEnB,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,oBAAoB,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IAC/C,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,oBAAoB,CAAC,CAAC;IACtF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,UAAU,CACd,cAAc,CAAC,yBAAyB,EACxC,2BAA2B,UAAU,KAAK,oBAAoB,EAAE,CACjE,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,wBAAwB,CAC/B,KAAc,EACd,OAAoE;IAEpE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACpD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAClB,MAAM,UAAU,CAAC,cAAc,CAAC,yBAAyB,EAAE,GAAG,MAAM,oBAAoB,CAAC,CAAC;IAE5F,MAAM,EAAE,GAAG,kBAAkB,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;IACzE,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACvC,MAAM,UAAU,CACd,cAAc,CAAC,yBAAyB,EACxC,GAAG,UAAU,cAAc,EAAE,qCAAqC,CACnE,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,GAAG,UAAU,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;IAC1F,IAAI,SAAS,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;QACvC,MAAM,UAAU,CACd,cAAc,CAAC,yBAAyB,EACxC,GAAG,UAAU,cAAc,EAAE,qDAAqD,CACnF,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAC1D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,UAAU,CACd,cAAc,CAAC,yBAAyB,EACxC,GAAG,UAAU,cAAc,EAAE,6BAA6B,CAC3D,CAAC;IACJ,CAAC;IACD,MAAM,YAAY,GAAG,kBAAkB,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;IACrF,IAAI,CAAC,wBAAwB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;QAChD,MAAM,UAAU,CACd,cAAc,CAAC,yBAAyB,EACxC,GAAG,UAAU,cAAc,EAAE,iDAAiD,CAC/E,CAAC;IACJ,CAAC;IACD,MAAM,iBAAiB,GAAG,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,EAAE,MAAM,CAAC,CAAC;IAChF,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,UAAU,CACd,cAAc,CAAC,yBAAyB,EACxC,GAAG,UAAU,cAAc,EAAE,uCAAuC,CACrE,CAAC;IACJ,CAAC;IACD,yBAAyB,CAAC,UAAU,EAAE,iBAAiB,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAE5E,MAAM,WAAW,GAAG,iBAAiB,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;IACpE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,UAAU,CACd,cAAc,CAAC,yBAAyB,EACxC,GAAG,UAAU,cAAc,EAAE,iCAAiC,CAC/D,CAAC;IACJ,CAAC;IACD,qBAAqB,CAAC,UAAU,EAAE,EAAE,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;IACjE,MAAM,sBAAsB,GAAG,kBAAkB,CAAC,KAAK,EAAE,wBAAwB,EAAE,MAAM,CAAC,CAAC;IAC3F,IAAI,sBAAsB,KAAK,SAAS,EAAE,CAAC;QACzC,IAAI,YAAY,KAAK,MAAM,IAAI,YAAY,KAAK,MAAM,EAAE,CAAC;YACvD,MAAM,UAAU,CACd,cAAc,CAAC,yBAAyB,EACxC,GAAG,UAAU,cAAc,EAAE,6DAA6D,CAC3F,CAAC;QACJ,CAAC;QACD,qBAAqB,CACnB,sBAAsB,EACtB,GAAG,UAAU,cAAc,EAAE,0BAA0B,CACxD,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,uBAAuB,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;IAC/D,IAAI,KAAK,CAAC,iBAAiB,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,iBAAiB,KAAK,SAAS,EAAE,CAAC;QAC1F,MAAM,UAAU,CACd,cAAc,CAAC,yBAAyB,EACxC,GAAG,UAAU,cAAc,EAAE,uCAAuC,CACrE,CAAC;IACJ,CAAC;IACD,MAAM,gBAAgB,GAAG,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAExE,MAAM,QAAQ,GAA0B;QACtC,GAAG,SAAS,CAAC,KAA8B,CAAC;QAC5C,EAAE;QACF,GAAG,EAAE,SAAS,CAAC,QAAQ,EAAE;QACzB,MAAM;QACN,OAAO,EAAE,gBAAgB;QACzB,YAAY;QACZ,iBAAiB,EAAE,iBAA8C;QACjE,WAAW,EAAE,WAAwC;KACtD,CAAC;IACF,IAAI,OAAO,CAAC,QAAQ;QAAE,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;;QACrD,OAAO,QAAQ,CAAC,OAAO,CAAC;IAC7B,IAAI,sBAAsB,KAAK,SAAS,EAAE,CAAC;QACzC,QAAQ,CAAC,sBAAsB,GAAG,sBAAsB,CAAC;IAC3D,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC","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":"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"}
|
|
@@ -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"]}
|
|
Binary file
|
package/ios/Frameworks/libsatya_ffi-rs.xcframework/ios-arm64_x86_64-simulator/libsatya_ffi.a
CHANGED
|
Binary file
|
package/ios/SatyaAttest.podspec
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Pod::Spec.new do |s|
|
|
2
2
|
s.name = 'SatyaAttest'
|
|
3
|
-
s.version = '1.0
|
|
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
|
|
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> = {
|
|
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
|
-
|
|
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
|
|
106
|
-
if (!
|
|
107
|
-
|
|
108
|
-
seen.
|
|
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}
|
|
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}
|
|
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}
|
|
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}
|
|
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}
|
|
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(
|
|
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;
|