@pulse-compute/jwt 0.0.0 → 1.0.0-beta.2
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/README.md +59 -3
- package/as/index.as.ts +388 -0
- package/conformance/native-contract.json +53 -0
- package/dist/bearer.d.ts +4 -0
- package/dist/bearer.d.ts.map +1 -0
- package/dist/bearer.js +33 -0
- package/dist/bearer.js.map +1 -0
- package/dist/crypto-verifier.d.ts +19 -0
- package/dist/crypto-verifier.d.ts.map +1 -0
- package/dist/crypto-verifier.js +225 -0
- package/dist/crypto-verifier.js.map +1 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +65 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/clock.d.ts +7 -0
- package/dist/internal/clock.d.ts.map +1 -0
- package/dist/internal/clock.js +34 -0
- package/dist/internal/clock.js.map +1 -0
- package/dist/internal/data.d.ts +12 -0
- package/dist/internal/data.d.ts.map +1 -0
- package/dist/internal/data.js +86 -0
- package/dist/internal/data.js.map +1 -0
- package/dist/internal/package-runtime.d.ts +9 -0
- package/dist/internal/package-runtime.d.ts.map +1 -0
- package/dist/internal/package-runtime.js +14 -0
- package/dist/internal/package-runtime.js.map +1 -0
- package/dist/internal/registered-claims.d.ts +3 -0
- package/dist/internal/registered-claims.d.ts.map +1 -0
- package/dist/internal/registered-claims.js +84 -0
- package/dist/internal/registered-claims.js.map +1 -0
- package/dist/internal/verifier-authority.d.ts +24 -0
- package/dist/internal/verifier-authority.d.ts.map +1 -0
- package/dist/internal/verifier-authority.js +101 -0
- package/dist/internal/verifier-authority.js.map +1 -0
- package/dist/options.d.ts +113 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +382 -0
- package/dist/options.js.map +1 -0
- package/dist/provider.d.ts +7 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +7 -0
- package/dist/provider.js.map +1 -0
- package/dist/result.d.ts +17 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +96 -0
- package/dist/result.js.map +1 -0
- package/dist/token.d.ts +20 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +323 -0
- package/dist/token.js.map +1 -0
- package/package.json +64 -6
- package/pulse.package.json +75 -0
- package/pulsewasm.compiler.cjs +1258 -0
- package/pulsewasm.manifest.cjs +117 -0
- package/pulsewasm.native.cjs +60 -0
package/README.md
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@pulse-compute/jwt`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<!-- pulse-package-status:start -->
|
|
4
|
+
> **Support tier:** Supported provider/extension surface<br>
|
|
5
|
+
> **Audience:** Applications verifying bounded JWT bearer credentials through Pulse providers.<br>
|
|
6
|
+
> **Install directly:** Yes, when an application verifies JWTs.<br>
|
|
7
|
+
> **Supported entry points:** `@pulse-compute/jwt`<br>
|
|
8
|
+
> **Stability:** The package root is the supported provider-neutral verification contract; provider and compiler integration subpaths are not application APIs.<br>
|
|
9
|
+
> **npm:** [`@pulse-compute/jwt`](https://www.npmjs.com/package/@pulse-compute/jwt)<br>
|
|
10
|
+
> **Canonical documentation:** [Package guide](https://pulsecompute.io/v1.0.0-beta.2/packages/jwt/)
|
|
11
|
+
>
|
|
12
|
+
> This release-status block is generated from the synchronized `Pulse 1.0.0-beta.2` package policy.
|
|
13
|
+
<!-- pulse-package-status:end -->
|
|
4
14
|
|
|
5
|
-
|
|
15
|
+
Bounded, provider-neutral JWT verification for Pulse handlers.
|
|
16
|
+
|
|
17
|
+
This package and `@pulse-compute/crypto` are members of the synchronized
|
|
18
|
+
`1.0.0-beta.2` release catalog.
|
|
19
|
+
|
|
20
|
+
Verification is implemented for HS256 in all four sealed target cells. G3 also
|
|
21
|
+
activates ES256 through the crypto-owned signature seam. H4 aligns Node and
|
|
22
|
+
Fastly JavaScript on the runtime-builtin realization and both Native providers
|
|
23
|
+
on the exact guest-linked composition. No target probes for a realization and
|
|
24
|
+
no failure falls back to a different key, algorithm, backend, target, or
|
|
25
|
+
provider.
|
|
26
|
+
|
|
27
|
+
Applications import the package root:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { jwt } from '@pulse-compute/jwt'
|
|
31
|
+
|
|
32
|
+
const verified = await jwt.verify(
|
|
33
|
+
ctx,
|
|
34
|
+
jwt.bearer(ctx.req),
|
|
35
|
+
{
|
|
36
|
+
algorithms: ['HS256'],
|
|
37
|
+
key: { type: 'secret', binding: 'JWT_SECRET' },
|
|
38
|
+
issuer: 'https://issuer.example',
|
|
39
|
+
audience: 'pulse-api',
|
|
40
|
+
},
|
|
41
|
+
)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
ES256 accepts only inline public P-256 JWKs or static JWKS values with at most
|
|
45
|
+
16 entries. Coordinates are canonical unpadded base64url encodings of exactly
|
|
46
|
+
32 bytes. Private and certificate members are rejected; `kid` selection is
|
|
47
|
+
deterministic; duplicate, missing, ambiguous, and unknown selections fail
|
|
48
|
+
closed. There is no remote discovery, fetch, cache, or refresh path.
|
|
49
|
+
|
|
50
|
+
The provider resolves the request-owned secret and passes only bounded bytes to
|
|
51
|
+
`@pulse-compute/crypto`. JWT authenticates the compact JWS before exposing
|
|
52
|
+
claims, captures one wall-clock instant only after authenticity, evaluates
|
|
53
|
+
registered claims before an optional runtime schema, and returns a detached,
|
|
54
|
+
deeply frozen result. It does not authorize application or host behavior.
|
|
55
|
+
|
|
56
|
+
RS256, EdDSA, signing, remote key authority, custom crypto providers, and
|
|
57
|
+
automatic fallback remain unavailable. The shared G3 ES256 corpus and
|
|
58
|
+
package-to-crypto composition are recorded in
|
|
59
|
+
`wasm/.test-results/jwt-g3/jwt-g3-evidence.json`; the six-cell target matrix is
|
|
60
|
+
recorded in `wasm/.test-results/boundary-h4/es256-six-cell-matrix.json`. The
|
|
61
|
+
prior four-cell HS256 seal remains historical regression evidence.
|
package/as/index.as.ts
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Pulse package-owned Native JWT verifier.
|
|
3
|
+
*
|
|
4
|
+
* This source is concatenated into a provider's one final AssemblyScript
|
|
5
|
+
* module. It owns compact-JWS parsing, protected-header policy, registered
|
|
6
|
+
* claim validation, result shaping, and the ordered composition boundary to
|
|
7
|
+
* @pulse-compute/crypto. Provider callbacks retain request-owned secret,
|
|
8
|
+
* wall-clock, schema, redaction, and error-transport authority.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const PULSE_JWT_SIDECAR_ABI_VERSION: i32 = 2
|
|
12
|
+
export const PULSE_JWT_VERIFY_CRYPTO_COMPOSITION_REQUIRED: i32 = -2
|
|
13
|
+
|
|
14
|
+
const __PULSE_JWT_TOKEN_BYTES_MAX: i32 = 16 * 1024
|
|
15
|
+
const __PULSE_JWT_HEADER_BYTES_MAX: i32 = 4 * 1024
|
|
16
|
+
const __PULSE_JWT_CLAIMS_BYTES_MAX: i32 = 16 * 1024
|
|
17
|
+
const __PULSE_JWT_HS256_SIGNATURE_BYTES: i32 = 32
|
|
18
|
+
const __PULSE_JWT_ES256_SIGNATURE_BYTES: i32 = 64
|
|
19
|
+
const __PULSE_JWT_KEY_BYTES_MIN: i32 = 32
|
|
20
|
+
const __PULSE_JWT_KEY_BYTES_MAX: i32 = 4096
|
|
21
|
+
const __PULSE_JWT_ES256_PUBLIC_KEY_BYTES: i32 = 64
|
|
22
|
+
|
|
23
|
+
const __PULSE_JWT_ERROR_TOKEN_REQUIRED: i32 = 1
|
|
24
|
+
const __PULSE_JWT_ERROR_BEARER_INVALID: i32 = 2
|
|
25
|
+
const __PULSE_JWT_ERROR_MALFORMED: i32 = 3
|
|
26
|
+
const __PULSE_JWT_ERROR_LIMIT_EXCEEDED: i32 = 4
|
|
27
|
+
const __PULSE_JWT_ERROR_ALGORITHM_NOT_ALLOWED: i32 = 5
|
|
28
|
+
const __PULSE_JWT_ERROR_KEY_INVALID: i32 = 6
|
|
29
|
+
const __PULSE_JWT_ERROR_SIGNATURE_INVALID: i32 = 7
|
|
30
|
+
const __PULSE_JWT_ERROR_CLOCK_INVALID: i32 = 8
|
|
31
|
+
const __PULSE_JWT_ERROR_CLAIMS_INVALID: i32 = 9
|
|
32
|
+
const __PULSE_JWT_ERROR_CLAIMS_SCHEMA_INVALID: i32 = 10
|
|
33
|
+
const __PULSE_JWT_ERROR_OPERATION_FAILED: i32 = 11
|
|
34
|
+
|
|
35
|
+
function __pulse_jwt_fail(effectIndex: i32, code: i32, stage: i32): i32 {
|
|
36
|
+
return __pulse_fastly_jwt_fail_code(effectIndex, code, stage)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function __pulse_jwt_base64url_value(code: i32): i32 {
|
|
40
|
+
if (code >= 65 && code <= 90) return code - 65
|
|
41
|
+
if (code >= 97 && code <= 122) return code - 71
|
|
42
|
+
if (code >= 48 && code <= 57) return code + 4
|
|
43
|
+
if (code == 45) return 62
|
|
44
|
+
if (code == 95) return 63
|
|
45
|
+
return -1
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function __pulse_jwt_base64url_decode(segment: string): Uint8Array | null {
|
|
49
|
+
if (segment.length == 0 || segment.length % 4 == 1) return null
|
|
50
|
+
const remainder = segment.length % 4
|
|
51
|
+
const last = __pulse_jwt_base64url_value(segment.charCodeAt(segment.length - 1))
|
|
52
|
+
if (last < 0 || (remainder == 2 && (last & 15) != 0) || (remainder == 3 && (last & 3) != 0)) return null
|
|
53
|
+
const output = new Uint8Array((segment.length * 6) >> 3)
|
|
54
|
+
let accumulator: u32 = 0
|
|
55
|
+
let bits: i32 = 0
|
|
56
|
+
let outputIndex: i32 = 0
|
|
57
|
+
for (let index: i32 = 0; index < segment.length; index += 1) {
|
|
58
|
+
const value = __pulse_jwt_base64url_value(segment.charCodeAt(index))
|
|
59
|
+
if (value < 0) return null
|
|
60
|
+
accumulator = (accumulator << 6) | <u32>value
|
|
61
|
+
bits += 6
|
|
62
|
+
if (bits >= 8) {
|
|
63
|
+
bits -= 8
|
|
64
|
+
unchecked(output[outputIndex] = <u8>(accumulator >> bits))
|
|
65
|
+
outputIndex += 1
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return output
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function __pulse_jwt_decode_text(bytes: Uint8Array): string {
|
|
72
|
+
return String.UTF8.decodeUnsafe(bytes.dataStart, bytes.byteLength, false)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function __pulse_jwt_wipe(bytes: Uint8Array): void {
|
|
76
|
+
for (let index: i32 = 0; index < bytes.byteLength; index += 1) unchecked(bytes[index] = 0)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function __pulse_jwt_object_field(object: __PulseFastlyValue, name: string): i32 {
|
|
80
|
+
const index = __pulse_fastly_find(object, name)
|
|
81
|
+
return index < 0 ? host_value_undefined() : unchecked(object.values[index])
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function __pulse_jwt_has_field(object: __PulseFastlyValue, name: string): bool {
|
|
85
|
+
return __pulse_fastly_find(object, name) >= 0
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function __pulse_jwt_string_member(object: __PulseFastlyValue, name: string): string | null {
|
|
89
|
+
const index = __pulse_fastly_find(object, name)
|
|
90
|
+
if (index < 0) return null
|
|
91
|
+
const value = __pulse_fastly_value(unchecked(object.values[index]))
|
|
92
|
+
return value.kind == PULSE_VALUE_STRING ? value.text : null
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function __pulse_jwt_array_contains(array: __PulseFastlyValue, expected: string): bool {
|
|
96
|
+
if (array.kind != PULSE_VALUE_ARRAY) return false
|
|
97
|
+
for (let index: i32 = 0; index < array.values.length; index += 1) {
|
|
98
|
+
const value = __pulse_fastly_value(unchecked(array.values[index]))
|
|
99
|
+
if (value.kind == PULSE_VALUE_STRING && value.text == expected) return true
|
|
100
|
+
}
|
|
101
|
+
return false
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function __pulse_jwt_config_accepts(handle: i32, candidate: string): bool {
|
|
105
|
+
const configured = __pulse_fastly_value(handle)
|
|
106
|
+
if (configured.kind == PULSE_VALUE_STRING) return configured.text == candidate
|
|
107
|
+
return __pulse_jwt_array_contains(configured, candidate)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function __pulse_jwt_audience_valid(claims: __PulseFastlyValue, policy: __PulseFastlyValue): bool {
|
|
111
|
+
const policyIndex = __pulse_fastly_find(policy, "audience")
|
|
112
|
+
if (policyIndex < 0) return true
|
|
113
|
+
const audienceIndex = __pulse_fastly_find(claims, "aud")
|
|
114
|
+
if (audienceIndex < 0) return false
|
|
115
|
+
const audience = __pulse_fastly_value(unchecked(claims.values[audienceIndex]))
|
|
116
|
+
if (audience.kind == PULSE_VALUE_STRING) {
|
|
117
|
+
return __pulse_jwt_config_accepts(unchecked(policy.values[policyIndex]), audience.text)
|
|
118
|
+
}
|
|
119
|
+
if (audience.kind != PULSE_VALUE_ARRAY) return false
|
|
120
|
+
for (let index: i32 = 0; index < audience.values.length; index += 1) {
|
|
121
|
+
const value = __pulse_fastly_value(unchecked(audience.values[index]))
|
|
122
|
+
if (value.kind != PULSE_VALUE_STRING) return false
|
|
123
|
+
if (__pulse_jwt_config_accepts(unchecked(policy.values[policyIndex]), value.text)) return true
|
|
124
|
+
}
|
|
125
|
+
return false
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function __pulse_jwt_numeric_claim(claims: __PulseFastlyValue, name: string): f64 {
|
|
129
|
+
const index = __pulse_fastly_find(claims, name)
|
|
130
|
+
if (index < 0) return NaN
|
|
131
|
+
const value = __pulse_fastly_value(unchecked(claims.values[index]))
|
|
132
|
+
return value.kind == PULSE_VALUE_NUMBER ? value.number : NaN
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function __pulse_jwt_validate_registered(
|
|
136
|
+
claims: __PulseFastlyValue,
|
|
137
|
+
policy: __PulseFastlyValue,
|
|
138
|
+
now: f64,
|
|
139
|
+
): bool {
|
|
140
|
+
const requiredIndex = __pulse_fastly_find(policy, "requiredClaims")
|
|
141
|
+
if (requiredIndex >= 0) {
|
|
142
|
+
const required = __pulse_fastly_value(unchecked(policy.values[requiredIndex]))
|
|
143
|
+
if (required.kind != PULSE_VALUE_ARRAY) return false
|
|
144
|
+
for (let index: i32 = 0; index < required.values.length; index += 1) {
|
|
145
|
+
const name = __pulse_fastly_value(unchecked(required.values[index]))
|
|
146
|
+
if (name.kind != PULSE_VALUE_STRING) return false
|
|
147
|
+
const claimIndex = __pulse_fastly_find(claims, name.text)
|
|
148
|
+
if (claimIndex < 0 || __pulse_fastly_value(unchecked(claims.values[claimIndex])).kind == PULSE_VALUE_UNDEFINED) return false
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const issuerIndex = __pulse_fastly_find(policy, "issuer")
|
|
153
|
+
if (issuerIndex >= 0) {
|
|
154
|
+
const issuer = __pulse_jwt_string_member(claims, "iss")
|
|
155
|
+
if (issuer === null || !__pulse_jwt_config_accepts(unchecked(policy.values[issuerIndex]), issuer)) return false
|
|
156
|
+
}
|
|
157
|
+
if (!__pulse_jwt_audience_valid(claims, policy)) return false
|
|
158
|
+
|
|
159
|
+
const subjectIndex = __pulse_fastly_find(policy, "subject")
|
|
160
|
+
if (subjectIndex >= 0) {
|
|
161
|
+
const subject = __pulse_jwt_string_member(claims, "sub")
|
|
162
|
+
const expected = __pulse_fastly_value(unchecked(policy.values[subjectIndex]))
|
|
163
|
+
if (subject === null || expected.kind != PULSE_VALUE_STRING || subject != expected.text) return false
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const toleranceIndex = __pulse_fastly_find(policy, "clockToleranceSeconds")
|
|
167
|
+
const tolerance = toleranceIndex < 0 ? 0.0 : __pulse_fastly_number(unchecked(policy.values[toleranceIndex]))
|
|
168
|
+
const expIndex = __pulse_fastly_find(claims, "exp")
|
|
169
|
+
if (expIndex >= 0) {
|
|
170
|
+
const exp = __pulse_jwt_numeric_claim(claims, "exp")
|
|
171
|
+
if (isNaN(exp) || now - tolerance >= exp) return false
|
|
172
|
+
}
|
|
173
|
+
const nbfIndex = __pulse_fastly_find(claims, "nbf")
|
|
174
|
+
if (nbfIndex >= 0) {
|
|
175
|
+
const nbf = __pulse_jwt_numeric_claim(claims, "nbf")
|
|
176
|
+
if (isNaN(nbf) || now + tolerance < nbf) return false
|
|
177
|
+
}
|
|
178
|
+
const iatIndex = __pulse_fastly_find(claims, "iat")
|
|
179
|
+
if (iatIndex >= 0 && isNaN(__pulse_jwt_numeric_claim(claims, "iat"))) return false
|
|
180
|
+
const maxAgeIndex = __pulse_fastly_find(policy, "maxTokenAgeSeconds")
|
|
181
|
+
if (maxAgeIndex >= 0) {
|
|
182
|
+
const iat = __pulse_jwt_numeric_claim(claims, "iat")
|
|
183
|
+
const maxAge = __pulse_fastly_number(unchecked(policy.values[maxAgeIndex]))
|
|
184
|
+
if (isNaN(iat) || iat > now + tolerance || now - iat - tolerance > maxAge) return false
|
|
185
|
+
}
|
|
186
|
+
return true
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function __pulse_jwt_bearer(authorization: string): string | null {
|
|
190
|
+
if (authorization.length < 8 || authorization.indexOf("\r") >= 0 || authorization.indexOf("\n") >= 0 || authorization.indexOf(",") >= 0) return null
|
|
191
|
+
if (authorization.substring(0, 7).toLowerCase() != "bearer ") return null
|
|
192
|
+
const token = authorization.substring(7)
|
|
193
|
+
return token.length == 0 ? null : token
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function __pulse_jwt_hex(bytes: Uint8Array): string {
|
|
197
|
+
const alphabet = "0123456789abcdef"
|
|
198
|
+
let output = ""
|
|
199
|
+
for (let index: i32 = 0; index < bytes.byteLength; index += 1) {
|
|
200
|
+
const value = unchecked(bytes[index])
|
|
201
|
+
output += alphabet.charAt(value >> 4) + alphabet.charAt(value & 15)
|
|
202
|
+
}
|
|
203
|
+
return output
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function __pulse_jwt_es256_scalar_valid(
|
|
207
|
+
signature: Uint8Array,
|
|
208
|
+
offset: i32,
|
|
209
|
+
): bool {
|
|
210
|
+
let nonzero = false
|
|
211
|
+
let lessThanOrder = false
|
|
212
|
+
let greaterThanOrder = false
|
|
213
|
+
for (let index: i32 = 0; index < 32; index += 1) {
|
|
214
|
+
const value: i32 = unchecked(signature[offset + index])
|
|
215
|
+
if (value != 0) nonzero = true
|
|
216
|
+
const order = unchecked(__PULSE_JWT_P256_ORDER[index])
|
|
217
|
+
if (!lessThanOrder && !greaterThanOrder) {
|
|
218
|
+
if (value < order) lessThanOrder = true
|
|
219
|
+
else if (value > order) greaterThanOrder = true
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return nonzero && lessThanOrder && !greaterThanOrder
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
@lazy const __PULSE_JWT_P256_ORDER = [
|
|
226
|
+
255, 255, 255, 255, 0, 0, 0, 0,
|
|
227
|
+
255, 255, 255, 255, 255, 255, 255, 255,
|
|
228
|
+
188, 230, 250, 173, 167, 23, 158, 132,
|
|
229
|
+
243, 185, 202, 194, 252, 99, 37, 81,
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Provider-neutral package effect sentinel retained for canonical lowering.
|
|
234
|
+
*/
|
|
235
|
+
export function pulse_jwt_verify(
|
|
236
|
+
operationRef: i32,
|
|
237
|
+
invocationRef: i32,
|
|
238
|
+
resultRef: i32,
|
|
239
|
+
): i32 {
|
|
240
|
+
void operationRef
|
|
241
|
+
void invocationRef
|
|
242
|
+
void resultRef
|
|
243
|
+
return PULSE_JWT_VERIFY_CRYPTO_COMPOSITION_REQUIRED
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Package-owned Fastly integration entry used only after canonical lowering.
|
|
248
|
+
* The provider supplies authority callbacks; this function supplies semantics.
|
|
249
|
+
*/
|
|
250
|
+
export function pulse_jwt_fastly_verify(effectIndex: i32, outerHandle: i32): i32 {
|
|
251
|
+
const outer = __pulse_fastly_value(outerHandle)
|
|
252
|
+
if (outer.kind != PULSE_VALUE_OBJECT) return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_OPERATION_FAILED, 200)
|
|
253
|
+
const policy = __pulse_fastly_value(__pulse_jwt_object_field(outer, "payload"))
|
|
254
|
+
const invocation = __pulse_fastly_value(__pulse_jwt_object_field(outer, "invocation"))
|
|
255
|
+
if (policy.kind != PULSE_VALUE_OBJECT || invocation.kind != PULSE_VALUE_OBJECT) {
|
|
256
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_OPERATION_FAILED, 201)
|
|
257
|
+
}
|
|
258
|
+
const authorizationValue = __pulse_fastly_value(__pulse_jwt_object_field(invocation, "token"))
|
|
259
|
+
if (authorizationValue.kind == PULSE_VALUE_UNDEFINED) {
|
|
260
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_TOKEN_REQUIRED, 202)
|
|
261
|
+
}
|
|
262
|
+
if (authorizationValue.kind != PULSE_VALUE_STRING) {
|
|
263
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_BEARER_INVALID, 203)
|
|
264
|
+
}
|
|
265
|
+
const bearer = __pulse_jwt_bearer(authorizationValue.text)
|
|
266
|
+
if (bearer === null) return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_BEARER_INVALID, 204)
|
|
267
|
+
const token = bearer
|
|
268
|
+
if (String.UTF8.encode(token, false).byteLength > __PULSE_JWT_TOKEN_BYTES_MAX) {
|
|
269
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_LIMIT_EXCEEDED, 205)
|
|
270
|
+
}
|
|
271
|
+
const segments = token.split(".")
|
|
272
|
+
if (segments.length != 3) return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_MALFORMED, 206)
|
|
273
|
+
const protectedBytes = __pulse_jwt_base64url_decode(unchecked(segments[0]))
|
|
274
|
+
const claimsBytes = __pulse_jwt_base64url_decode(unchecked(segments[1]))
|
|
275
|
+
const signature = __pulse_jwt_base64url_decode(unchecked(segments[2]))
|
|
276
|
+
if (protectedBytes === null || claimsBytes === null || signature === null) {
|
|
277
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_MALFORMED, 207)
|
|
278
|
+
}
|
|
279
|
+
if (protectedBytes.byteLength > __PULSE_JWT_HEADER_BYTES_MAX || claimsBytes.byteLength > __PULSE_JWT_CLAIMS_BYTES_MAX) {
|
|
280
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_LIMIT_EXCEEDED, 208)
|
|
281
|
+
}
|
|
282
|
+
const protectedParser = new __PulseJsonParser(__pulse_jwt_decode_text(protectedBytes))
|
|
283
|
+
const protectedHandle = protectedParser.parse()
|
|
284
|
+
const protectedHeader = __pulse_fastly_value(protectedHandle)
|
|
285
|
+
if (protectedParser.failed || protectedHandle <= 0 || protectedHeader.kind != PULSE_VALUE_OBJECT) {
|
|
286
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_MALFORMED, 210)
|
|
287
|
+
}
|
|
288
|
+
if (__pulse_jwt_has_field(protectedHeader, "crit") || __pulse_jwt_has_field(protectedHeader, "b64")) {
|
|
289
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_MALFORMED, 211)
|
|
290
|
+
}
|
|
291
|
+
const algorithm = __pulse_jwt_string_member(protectedHeader, "alg")
|
|
292
|
+
const algorithmPolicy = __pulse_fastly_value(__pulse_jwt_object_field(policy, "algorithms"))
|
|
293
|
+
if (
|
|
294
|
+
algorithm === null ||
|
|
295
|
+
(algorithm != "HS256" && algorithm != "ES256") ||
|
|
296
|
+
!__pulse_jwt_array_contains(algorithmPolicy, algorithm)
|
|
297
|
+
) {
|
|
298
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_ALGORITHM_NOT_ALLOWED, 212)
|
|
299
|
+
}
|
|
300
|
+
if (
|
|
301
|
+
(algorithm == "HS256" && signature.byteLength != __PULSE_JWT_HS256_SIGNATURE_BYTES) ||
|
|
302
|
+
(
|
|
303
|
+
algorithm == "ES256" &&
|
|
304
|
+
(
|
|
305
|
+
signature.byteLength != __PULSE_JWT_ES256_SIGNATURE_BYTES ||
|
|
306
|
+
!__pulse_jwt_es256_scalar_valid(signature, 0) ||
|
|
307
|
+
!__pulse_jwt_es256_scalar_valid(signature, 32)
|
|
308
|
+
)
|
|
309
|
+
)
|
|
310
|
+
) {
|
|
311
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_MALFORMED, 209)
|
|
312
|
+
}
|
|
313
|
+
const typPolicy = __pulse_jwt_string_member(policy, "typ")
|
|
314
|
+
if (typPolicy !== null) {
|
|
315
|
+
const typ = __pulse_jwt_string_member(protectedHeader, "typ")
|
|
316
|
+
if (typ === null || typ != typPolicy) return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_CLAIMS_INVALID, 213)
|
|
317
|
+
}
|
|
318
|
+
const kidIndex = __pulse_fastly_find(protectedHeader, "kid")
|
|
319
|
+
if (kidIndex >= 0 && __pulse_fastly_value(unchecked(protectedHeader.values[kidIndex])).kind != PULSE_VALUE_STRING) {
|
|
320
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_MALFORMED, 214)
|
|
321
|
+
}
|
|
322
|
+
if (!__pulse_fastly_jwt_key_descriptor_valid(effectIndex)) {
|
|
323
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_KEY_INVALID, 215)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
let key: Uint8Array | null
|
|
327
|
+
if (algorithm == "HS256") {
|
|
328
|
+
key = __pulse_fastly_jwt_resolve_secret(effectIndex)
|
|
329
|
+
if (key.byteLength < __PULSE_JWT_KEY_BYTES_MIN || key.byteLength > __PULSE_JWT_KEY_BYTES_MAX) {
|
|
330
|
+
__pulse_jwt_wipe(key)
|
|
331
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_KEY_INVALID, 216)
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
const kid = __pulse_jwt_string_member(protectedHeader, "kid")
|
|
335
|
+
key = __pulse_fastly_jwt_resolve_es256_key(effectIndex, kid)
|
|
336
|
+
if (key === null || key.byteLength != __PULSE_JWT_ES256_PUBLIC_KEY_BYTES) {
|
|
337
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_KEY_INVALID, 216)
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const signingInput = unchecked(segments[0]) + "." + unchecked(segments[1])
|
|
341
|
+
const signingBytes = String.UTF8.encode(signingInput, false)
|
|
342
|
+
const signingView = Uint8Array.wrap(signingBytes)
|
|
343
|
+
__pulse_fastly_jwt_capture_signing_input(signingView)
|
|
344
|
+
const cryptoStatus = __pulse_fastly_jwt_force_crypto_failure(effectIndex)
|
|
345
|
+
? -3
|
|
346
|
+
: __pulse_fastly_jwt_crypto_verify(
|
|
347
|
+
algorithm,
|
|
348
|
+
key,
|
|
349
|
+
signingView,
|
|
350
|
+
signature,
|
|
351
|
+
)
|
|
352
|
+
__pulse_fastly_jwt_record_crypto_attempt()
|
|
353
|
+
__pulse_jwt_wipe(key)
|
|
354
|
+
if (cryptoStatus == 0) return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_SIGNATURE_INVALID, 217)
|
|
355
|
+
if (cryptoStatus == -1) return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_KEY_INVALID, 218)
|
|
356
|
+
if (cryptoStatus != 1) return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_OPERATION_FAILED, 219)
|
|
357
|
+
|
|
358
|
+
const claimsParser = new __PulseJsonParser(__pulse_jwt_decode_text(claimsBytes))
|
|
359
|
+
const claimsHandle = claimsParser.parse()
|
|
360
|
+
const claims = __pulse_fastly_value(claimsHandle)
|
|
361
|
+
if (claimsParser.failed || claimsHandle <= 0 || claims.kind != PULSE_VALUE_OBJECT) {
|
|
362
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_MALFORMED, 220)
|
|
363
|
+
}
|
|
364
|
+
__pulse_fastly_jwt_record_claims_parsed()
|
|
365
|
+
const now = __pulse_fastly_jwt_capture_clock(effectIndex)
|
|
366
|
+
if (isNaN(now) || !isFinite(now)) return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_CLOCK_INVALID, 221)
|
|
367
|
+
if (!__pulse_jwt_validate_registered(claims, policy, now)) {
|
|
368
|
+
return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_CLAIMS_INVALID, 222)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
let normalizedClaims = claimsHandle
|
|
372
|
+
const schema = __pulse_jwt_string_member(policy, "claimsSchema")
|
|
373
|
+
if (schema !== null) {
|
|
374
|
+
normalizedClaims = __pulse_fastly_jwt_validate_schema(effectIndex, schema, claimsHandle)
|
|
375
|
+
if (normalizedClaims <= 0) return __pulse_jwt_fail(effectIndex, __PULSE_JWT_ERROR_CLAIMS_SCHEMA_INVALID, 223)
|
|
376
|
+
}
|
|
377
|
+
const outputHeader = host_value_object()
|
|
378
|
+
host_value_object_set(outputHeader, __pulse_fastly_string_value("alg"), __pulse_fastly_string_value(algorithm))
|
|
379
|
+
if (kidIndex >= 0) host_value_object_set(outputHeader, __pulse_fastly_string_value("kid"), unchecked(protectedHeader.values[kidIndex]))
|
|
380
|
+
const typIndex = __pulse_fastly_find(protectedHeader, "typ")
|
|
381
|
+
if (typIndex >= 0) host_value_object_set(outputHeader, __pulse_fastly_string_value("typ"), unchecked(protectedHeader.values[typIndex]))
|
|
382
|
+
const output = host_value_object()
|
|
383
|
+
host_value_object_set(output, __pulse_fastly_string_value("claims"), normalizedClaims)
|
|
384
|
+
host_value_object_set(output, __pulse_fastly_string_value("protectedHeader"), outputHeader)
|
|
385
|
+
__pulse_fastly_deep_freeze(output)
|
|
386
|
+
__pulse_fastly_jwt_record_verified()
|
|
387
|
+
return output
|
|
388
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "pulse.jwt-native-contract-conformance.v1",
|
|
3
|
+
"status": "crypto-composed-hs256",
|
|
4
|
+
"operation": "jwt.verify",
|
|
5
|
+
"cases": [
|
|
6
|
+
"bearer-request-header",
|
|
7
|
+
"literal-verification-policy",
|
|
8
|
+
"runtime-secret-binding",
|
|
9
|
+
"claims-schema-reference",
|
|
10
|
+
"target-capability-selection",
|
|
11
|
+
"guest-source-pulse-hmac-as",
|
|
12
|
+
"exact-signing-and-tag-bytes",
|
|
13
|
+
"signature-before-claims",
|
|
14
|
+
"critical-header-rejection",
|
|
15
|
+
"non-finite-json-rejection",
|
|
16
|
+
"exact-time-boundary-parity",
|
|
17
|
+
"hostile-accessor-rejection",
|
|
18
|
+
"prototype-safe-result-detachment",
|
|
19
|
+
"no-key-material-artifact",
|
|
20
|
+
"no-guest-unit-for-hs256",
|
|
21
|
+
"dynamic-bearer-invocation",
|
|
22
|
+
"migrated-handler-javascript-native-parity",
|
|
23
|
+
"direct-and-parallel-parity",
|
|
24
|
+
"audience-and-time-boundary-parity",
|
|
25
|
+
"schema-and-resource-limit-parity",
|
|
26
|
+
"fastly-native-planning-rejection",
|
|
27
|
+
"redacted-rejection"
|
|
28
|
+
],
|
|
29
|
+
"realizations": {
|
|
30
|
+
"crypto-composed": {
|
|
31
|
+
"status": "implemented",
|
|
32
|
+
"provider": "node",
|
|
33
|
+
"realization": "guest-source:pulse-hmac-as",
|
|
34
|
+
"implementation": "pulse-hmac-as.v1",
|
|
35
|
+
"algorithms": [
|
|
36
|
+
"HS256"
|
|
37
|
+
],
|
|
38
|
+
"keyTypes": [
|
|
39
|
+
"secret"
|
|
40
|
+
],
|
|
41
|
+
"guestUnitRequired": false,
|
|
42
|
+
"automaticFallback": false
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"policy": {
|
|
46
|
+
"automaticFallback": false,
|
|
47
|
+
"nativeCryptoImplemented": true,
|
|
48
|
+
"secretMaterialExcludedFromCanonicalPlan": true,
|
|
49
|
+
"tokenExcludedFromStaticPlan": true,
|
|
50
|
+
"targetBindingRequired": true,
|
|
51
|
+
"cryptoSemanticOwner": "@pulse-compute/crypto"
|
|
52
|
+
}
|
|
53
|
+
}
|
package/dist/bearer.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PulseRequest } from '@pulse-compute/runtime';
|
|
2
|
+
export declare function bearerFromAuthorizationValue(value: string | undefined): string | undefined;
|
|
3
|
+
export declare function bearer(request: PulseRequest | Request): string | undefined;
|
|
4
|
+
//# sourceMappingURL=bearer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bearer.d.ts","sourceRoot":"","sources":["../src/bearer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAsB3D,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAS1F;AAED,wBAAgB,MAAM,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAE1E"}
|
package/dist/bearer.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jwtError } from './errors.js';
|
|
2
|
+
import { jwtOwnDataProperty } from './internal/data.js';
|
|
3
|
+
function authorizationValue(request) {
|
|
4
|
+
if (!request || typeof request !== 'object') {
|
|
5
|
+
throw jwtError('PULSE_JWT_BEARER_INVALID', { category: 'request' });
|
|
6
|
+
}
|
|
7
|
+
if (typeof Request !== 'undefined' && request instanceof Request) {
|
|
8
|
+
return request.headers.get('authorization') ?? undefined;
|
|
9
|
+
}
|
|
10
|
+
const header = jwtOwnDataProperty(request, 'header');
|
|
11
|
+
if (header.valid && typeof header.value === 'function') {
|
|
12
|
+
const value = header.value('authorization');
|
|
13
|
+
if (value === undefined || typeof value === 'string')
|
|
14
|
+
return value;
|
|
15
|
+
throw jwtError('PULSE_JWT_BEARER_INVALID', { category: 'authorization' });
|
|
16
|
+
}
|
|
17
|
+
throw jwtError('PULSE_JWT_BEARER_INVALID', { category: 'request' });
|
|
18
|
+
}
|
|
19
|
+
export function bearerFromAuthorizationValue(value) {
|
|
20
|
+
if (value === undefined)
|
|
21
|
+
return undefined;
|
|
22
|
+
if (value.length === 0 || /[\r\n,]/.test(value)) {
|
|
23
|
+
throw jwtError('PULSE_JWT_BEARER_INVALID', { category: 'authorization' });
|
|
24
|
+
}
|
|
25
|
+
const match = /^[Bb][Ee][Aa][Rr][Ee][Rr] ([A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)$/.exec(value);
|
|
26
|
+
if (!match)
|
|
27
|
+
throw jwtError('PULSE_JWT_BEARER_INVALID', { category: 'authorization' });
|
|
28
|
+
return match[1];
|
|
29
|
+
}
|
|
30
|
+
export function bearer(request) {
|
|
31
|
+
return bearerFromAuthorizationValue(authorizationValue(request));
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=bearer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bearer.js","sourceRoot":"","sources":["../src/bearer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,SAAS,kBAAkB,CAAC,OAA+B;IACzD,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,MAAM,QAAQ,CAAC,0BAA0B,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,YAAY,OAAO,EAAE,CAAC;QACjE,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,SAAS,CAAC;IAC3D,CAAC;IAED,MAAM,MAAM,GAAG,kBAAkB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACrD,IAAI,MAAM,CAAC,KAAK,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QACvD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAC5C,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QACnE,MAAM,QAAQ,CAAC,0BAA0B,EAAE,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,QAAQ,CAAC,0BAA0B,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,KAAyB;IACpE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,MAAM,QAAQ,CAAC,0BAA0B,EAAE,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,KAAK,GAAG,6EAA6E,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxG,IAAI,CAAC,KAAK;QAAE,MAAM,QAAQ,CAAC,0BAA0B,EAAE,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC;IACtF,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,OAA+B;IACpD,OAAO,4BAA4B,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type CryptoVerificationResult, type MacVerifyRequest, type SignatureVerifyRequest } from '@pulse-compute/crypto';
|
|
2
|
+
import { type JwtClaims } from './options.js';
|
|
3
|
+
import { type JwtVerification } from './result.js';
|
|
4
|
+
import { type JwtVerifierHost, type JwtVerifierInput } from './internal/verifier-authority.js';
|
|
5
|
+
export type JwtCryptoVerifierInput = JwtVerifierInput;
|
|
6
|
+
export interface JwtCryptoVerifierHost extends JwtVerifierHost {
|
|
7
|
+
}
|
|
8
|
+
export type JwtCryptoMacVerify = (request: MacVerifyRequest) => CryptoVerificationResult | PromiseLike<CryptoVerificationResult>;
|
|
9
|
+
export type JwtCryptoSignatureVerify = (request: SignatureVerifyRequest) => CryptoVerificationResult | PromiseLike<CryptoVerificationResult>;
|
|
10
|
+
export interface JwtCryptoVerifier {
|
|
11
|
+
readonly mac: Readonly<{
|
|
12
|
+
readonly verify: JwtCryptoMacVerify;
|
|
13
|
+
}>;
|
|
14
|
+
readonly signature?: Readonly<{
|
|
15
|
+
readonly verify: JwtCryptoSignatureVerify;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
export declare function verifyJwtWithCrypto<Claims = JwtClaims>(input: JwtCryptoVerifierInput, host: JwtCryptoVerifierHost, crypto: JwtCryptoVerifier): Promise<JwtVerification<Claims>>;
|
|
19
|
+
//# sourceMappingURL=crypto-verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto-verifier.d.ts","sourceRoot":"","sources":["../src/crypto-verifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,wBAAwB,EAE7B,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC5B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAIL,KAAK,SAAS,EAEf,MAAM,cAAc,CAAC;AACtB,OAAO,EAA4B,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAiB7E,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,kCAAkC,CAAC;AAE1C,MAAM,MAAM,sBAAsB,GAAG,gBAAgB,CAAC;AAEtD,MAAM,WAAW,qBAAsB,SAAQ,eAAe;CAAG;AAEjE,MAAM,MAAM,kBAAkB,GAAG,CAC/B,OAAO,EAAE,gBAAgB,KACtB,wBAAwB,GAAG,WAAW,CAAC,wBAAwB,CAAC,CAAC;AAEtE,MAAM,MAAM,wBAAwB,GAAG,CACrC,OAAO,EAAE,sBAAsB,KAC5B,wBAAwB,GAAG,WAAW,CAAC,wBAAwB,CAAC,CAAC;AAEtE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;QACrB,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;KACrC,CAAC,CAAC;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC;QAC5B,QAAQ,CAAC,MAAM,EAAE,wBAAwB,CAAC;KAC3C,CAAC,CAAC;CACJ;AA8HD,wBAAsB,mBAAmB,CAAC,MAAM,GAAG,SAAS,EAC1D,KAAK,EAAE,sBAAsB,EAC7B,IAAI,EAAE,qBAAqB,EAC3B,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CA2GlC"}
|