zuplo 7.7.17 → 7.8.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/docs/ai-gateway/bedrock-runtime.mdx +17 -15
- package/docs/policies/ai-gateway-configuration-executor-v2-inbound/doc.md +26 -0
- package/docs/policies/mcp-auth0-oauth-inbound/schema.json +2 -2
- package/docs/policies/mcp-oauth-inbound/schema.json +1 -1
- package/docs/policies/mcp-token-exchange-inbound/doc.md +48 -2
- package/docs/policies/mcp-token-exchange-inbound/schema.json +8 -0
- package/package.json +5 -5
|
@@ -243,23 +243,25 @@ into it.
|
|
|
243
243
|
|
|
244
244
|
## Policies on Bedrock Runtime routes
|
|
245
245
|
|
|
246
|
-
These routes run your app's [policy chain](./policy-chains.mdx) like any other
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
|
253
|
-
|
|
|
254
|
-
| [
|
|
255
|
-
| [
|
|
256
|
-
|
|
|
246
|
+
These routes run your app's [policy chain](./policy-chains.mdx) like any other.
|
|
247
|
+
What changes is that the request body is Bedrock's shape rather than one the
|
|
248
|
+
gateway parses, so a policy that reads the body applies its `onUnknownShape`
|
|
249
|
+
setting instead. Each policy's default follows from its job: a guardrail fails
|
|
250
|
+
closed, while an observer or an optimization fails open.
|
|
251
|
+
|
|
252
|
+
| Policy | On Bedrock Runtime routes |
|
|
253
|
+
| ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
254
|
+
| [Authentication](../policies/ai-gateway-auth-v2-inbound.mdx) | Works. Reads the app key from `Authorization: Bearer` |
|
|
255
|
+
| [Model Filtering](../policies/ai-gateway-model-filtering-v2-inbound.mdx) | Works. Refuses a model outside the allow list before any AWS call |
|
|
256
|
+
| [Metering and budgets](../policies/ai-gateway-metering-v2-inbound.mdx) | Works. Meters tokens and cost on all four operations, streamed or not |
|
|
257
|
+
| [DLP](../policies/ai-gateway-dlp-inbound.mdx) and content guardrails | **Blocks by default.** The gateway can't read these bodies, so the policy denies the request. Set `onUnknownShape` to `skip` to forward it uninspected |
|
|
258
|
+
| [Galileo](../policies/galileo-tracing-v2-inbound.mdx) and [Comet Opik](../policies/comet-opik-tracing-v2-inbound.mdx) tracing | **Skipped by default.** The request goes through untraced. Set `onUnknownShape` to `deny` to make a trace a hard requirement |
|
|
259
|
+
| Semantic Cache | Skipped |
|
|
257
260
|
|
|
258
261
|
The table lists every policy the gateway supports on these routes. It audits the
|
|
259
|
-
chain and refuses any other policy, such as a custom policy, Smart Router,
|
|
260
|
-
[Fallback Model](./fallback.mdx),
|
|
261
|
-
|
|
262
|
-
that app.
|
|
262
|
+
chain and refuses any other policy, such as a custom policy, Smart Router, or
|
|
263
|
+
[Fallback Model](./fallback.mdx), with a `400 ValidationException` that names
|
|
264
|
+
the policy. Remove it from the chain to use Bedrock Runtime through that app.
|
|
263
265
|
|
|
264
266
|
If you need content inspection on Bedrock traffic, use
|
|
265
267
|
[Bedrock's own guardrails](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html)
|
|
@@ -406,6 +406,32 @@ The corresponding expression is `request.headers.get("x-budget-customer")`.
|
|
|
406
406
|
Configuration code should create that ordinary string and pass the enclosing
|
|
407
407
|
object to `JSON.stringify`; the serializer adds the JSON transport escaping.
|
|
408
408
|
|
|
409
|
+
Headers you set or remove in the inbound chain control what reaches your AI
|
|
410
|
+
provider. For example, you can add a session header or remove an internal
|
|
411
|
+
customer header before sending the request. Gateway-owned headers are excluded,
|
|
412
|
+
including credentials, `Content-Type`, `Accept`, and headers starting with `zp-`
|
|
413
|
+
or `cf-`; setting a credential header does not override your configured provider
|
|
414
|
+
credential.
|
|
415
|
+
|
|
416
|
+
That includes headers you add only for the gateway's own use. The
|
|
417
|
+
`x-budget-customer` header above carries the caller's user id, and unless you
|
|
418
|
+
remove it after the metering policy has read it, the provider receives it on
|
|
419
|
+
every request. Add a `remove-headers-inbound` entry after the metering entry:
|
|
420
|
+
|
|
421
|
+
```json
|
|
422
|
+
[
|
|
423
|
+
{ "name": "set-budget-dimension" },
|
|
424
|
+
{ "name": "ai-gateway-metering-v2-inbound" },
|
|
425
|
+
{
|
|
426
|
+
"name": "remove-headers-inbound",
|
|
427
|
+
"options": { "headers": ["x-budget-customer"] }
|
|
428
|
+
}
|
|
429
|
+
]
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Declare `remove-headers-inbound` in `config/policies.json` so the chain can
|
|
433
|
+
select it.
|
|
434
|
+
|
|
409
435
|
Invalid budget rules and unsupported expressions are logged and skipped. Other
|
|
410
436
|
valid rules continue to apply.
|
|
411
437
|
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"clientSecret": {
|
|
54
54
|
"type": "string",
|
|
55
|
-
"examples": ["$env(
|
|
55
|
+
"examples": ["$env(AUTH0_CLIENT_SECRET)"],
|
|
56
56
|
"description": "The Auth0 client\\_secret. Use $env(...) to source from a secret environment variable."
|
|
57
57
|
},
|
|
58
58
|
"scope": {
|
|
@@ -209,7 +209,7 @@
|
|
|
209
209
|
"sessionTtlSeconds": 28800,
|
|
210
210
|
"stateTtlSeconds": 900
|
|
211
211
|
},
|
|
212
|
-
"clientSecret": "$env(
|
|
212
|
+
"clientSecret": "$env(AUTH0_CLIENT_SECRET)",
|
|
213
213
|
"gateway": {
|
|
214
214
|
"accessTokenTtlSeconds": 900,
|
|
215
215
|
"cimdEnabled": true,
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"type": "string",
|
|
74
74
|
"format": "uri",
|
|
75
75
|
"examples": ["https://my-tenant.us.auth0.com/authorize"],
|
|
76
|
-
"description": "The IdP /authorize endpoint to redirect the user to. For local development on loopback, use http://127.0.0.1:9000/oauth/dev-login."
|
|
76
|
+
"description": "The IdP /authorize endpoint to redirect the user to. For local development on loopback, use http://127.0.0.1:9000/\\_\\_zuplo/oauth/dev-login (the gateway's default basePath is /\\_\\_zuplo; adjust if the plugin is configured with a different one)."
|
|
77
77
|
},
|
|
78
78
|
"tokenUrl": {
|
|
79
79
|
"type": "string",
|
|
@@ -108,6 +108,52 @@ as Entra tenant admin consent. If connections start requiring frequent
|
|
|
108
108
|
reauthorization after setting `prompt: false`, remove the option so the consent
|
|
109
109
|
grant is requested again.
|
|
110
110
|
|
|
111
|
+
## Authorization Server Issuer Mismatch
|
|
112
|
+
|
|
113
|
+
Some providers publish inconsistent OAuth metadata: the protected-resource
|
|
114
|
+
document advertises a path-scoped authorization server, but that server's own
|
|
115
|
+
metadata reports a different (usually root-origin) `issuer`. ServiceNow is one —
|
|
116
|
+
its protected-resource metadata names
|
|
117
|
+
`https://<instance>.service-now.com/sncapps/...` while the metadata served there
|
|
118
|
+
reports `https://<instance>.service-now.com`. Atlassian and Okta
|
|
119
|
+
org-versus-custom authorization servers publish the same shape.
|
|
120
|
+
|
|
121
|
+
RFC 8414 section 3.3 requires a client to reject a metadata document whose
|
|
122
|
+
`issuer` differs from the identifier the well-known URL was built from, so the
|
|
123
|
+
gateway hard-fails discovery against these providers. Set
|
|
124
|
+
`skipIssuerMetadataValidation` to `true` on that connection to proceed anyway:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"name": "mcp-token-exchange-servicenow",
|
|
129
|
+
"policyType": "mcp-token-exchange-inbound",
|
|
130
|
+
"handler": {
|
|
131
|
+
"module": "$import(@zuplo/runtime/mcp-gateway)",
|
|
132
|
+
"export": "McpTokenExchangeInboundPolicy",
|
|
133
|
+
"options": {
|
|
134
|
+
"displayName": "ServiceNow",
|
|
135
|
+
"authMode": "user-oauth",
|
|
136
|
+
"skipIssuerMetadataValidation": true
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Warning**: this puts the connection out of spec. The issuer check is an
|
|
143
|
+
unqualified MUST in both RFC 8414 section 3.3 and the MCP authorization spec,
|
|
144
|
+
whose own example is the attack it prevents — a document served from
|
|
145
|
+
`https://attacker.example/.well-known/oauth-authorization-server` claiming
|
|
146
|
+
`"issuer": "https://honest.example"` must be rejected. The check binds the
|
|
147
|
+
metadata to the TLS-authenticated host it came from, and with it off the gateway
|
|
148
|
+
trusts the authorization, token and JWKS endpoints from a document that never
|
|
149
|
+
proved it speaks for that issuer.
|
|
150
|
+
|
|
151
|
+
Only enable it for a specific upstream known to publish a mismatched issuer, and
|
|
152
|
+
only when the metadata URL's host is itself trusted. Never enable it as a
|
|
153
|
+
default. It relaxes the discovery-time check only: the RFC 9207
|
|
154
|
+
authorization-response check and the callback-leg authorization-server binding
|
|
155
|
+
still apply.
|
|
156
|
+
|
|
111
157
|
## ID-JAG Token Exchange (Cross-App Access)
|
|
112
158
|
|
|
113
159
|
Set `authMode` to `id-jag` when the upstream is protected by an Identity
|
|
@@ -128,8 +174,8 @@ Use this mode in enterprise deployments where the gateway and the upstream trust
|
|
|
128
174
|
the same identity provider (for example, Okta Cross-App Access), so upstream
|
|
129
175
|
access follows the caller's identity without a per-user upstream consent flow.
|
|
130
176
|
The `user-oauth`/`shared-oauth` options — `scopes`, `scopeDelimiter`, `prompt`,
|
|
131
|
-
and `clientRegistration` — do not apply; the
|
|
132
|
-
entirely through the `idJag` option.
|
|
177
|
+
`skipIssuerMetadataValidation`, and `clientRegistration` — do not apply; the
|
|
178
|
+
`id-jag` mode is configured entirely through the `idJag` option.
|
|
133
179
|
|
|
134
180
|
```json
|
|
135
181
|
{
|
|
@@ -56,6 +56,9 @@
|
|
|
56
56
|
{
|
|
57
57
|
"required": ["prompt"]
|
|
58
58
|
},
|
|
59
|
+
{
|
|
60
|
+
"required": ["skipIssuerMetadataValidation"]
|
|
61
|
+
},
|
|
59
62
|
{
|
|
60
63
|
"required": ["clientRegistration"]
|
|
61
64
|
}
|
|
@@ -120,6 +123,11 @@
|
|
|
120
123
|
}
|
|
121
124
|
]
|
|
122
125
|
},
|
|
126
|
+
"skipIssuerMetadataValidation": {
|
|
127
|
+
"type": "boolean",
|
|
128
|
+
"x-advanced": true,
|
|
129
|
+
"description": "Skips the RFC 8414 section 3.3 issuer-echo check when discovering the upstream authorization server. That check requires the `issuer` in the authorization server's metadata to equal the `authorization_servers` entry the protected-resource metadata advertised. Some providers publish a path-scoped authorization server whose metadata reports the root origin instead — for example ServiceNow advertises `https://<instance>.service-now.com/sncapps/...` but its metadata reports `https://<instance>.service-now.com` — so discovery hard-fails without this option. Warning: enabling this puts the connection out of spec. The check is an unqualified MUST in both RFC 8414 section 3.3 and the MCP authorization spec, which require rejecting a metadata document whose `issuer` differs from the identifier the well-known URL was built from; the spec's own example is a document served from `https://attacker.example/.well-known/oauth-authorization-server` claiming `\"issuer\": \"https://honest.example\"`. The echo check binds the metadata to the TLS-authenticated host it came from, and with it off the gateway trusts the authorization, token and JWKS endpoints from a document that never proved it speaks for that issuer. Only enable it for a specific upstream known to publish a mismatched issuer, and only when the metadata URL's host is itself trusted. It relaxes the discovery-time check only — the RFC 9207 authorization-response check and the callback-leg authorization-server binding still apply."
|
|
130
|
+
},
|
|
123
131
|
"clientRegistration": {
|
|
124
132
|
"x-advanced": true,
|
|
125
133
|
"description": "OAuth client registration mode. Defaults to `auto`, which uses Client ID Metadata Documents when the upstream advertises support and falls back to Dynamic Client Registration otherwise.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zuplo",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The official Zuplo CLI for local development and platform management",
|
|
6
6
|
"homepage": "https://zuplo.com/docs/cli/overview",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"zuplo": "zuplo.js"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@zuplo/cli": "7.
|
|
36
|
-
"@zuplo/core": "7.
|
|
37
|
-
"@zuplo/runtime": "7.
|
|
38
|
-
"@zuplo/test": "7.
|
|
35
|
+
"@zuplo/cli": "7.8.0",
|
|
36
|
+
"@zuplo/core": "7.8.0",
|
|
37
|
+
"@zuplo/runtime": "7.8.0",
|
|
38
|
+
"@zuplo/test": "7.8.0"
|
|
39
39
|
}
|
|
40
40
|
}
|