zuplo 6.70.70 → 6.71.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/getting-started.mdx +2 -1
- package/docs/ai-gateway/integrations/ai-sdk.mdx +17 -0
- package/docs/ai-gateway/introduction.mdx +5 -5
- package/docs/ai-gateway/providers.mdx +2 -0
- package/docs/analytics/access-and-entitlements.md +71 -0
- package/docs/analytics/overview.md +67 -0
- package/docs/analytics/reference/metrics-glossary.md +105 -0
- package/docs/analytics/reference/url-parameters.md +66 -0
- package/docs/analytics/shared-controls.md +122 -0
- package/docs/analytics/tabs/agents.md +88 -0
- package/docs/analytics/tabs/consumers.md +73 -0
- package/docs/analytics/tabs/graphql.md +78 -0
- package/docs/analytics/tabs/mcp.md +80 -0
- package/docs/analytics/tabs/origins.md +83 -0
- package/docs/analytics/tabs/requests.md +97 -0
- package/docs/articles/accounts/enterprise-sso.mdx +8 -6
- package/docs/articles/api-key-administration.mdx +4 -0
- package/docs/articles/bypass-policy-for-testing.mdx +4 -0
- package/docs/articles/ci-cd-github/basic-deployment.mdx +10 -1
- package/docs/articles/ci-cd-github/deploy-and-test.mdx +14 -1
- package/docs/articles/ci-cd-github/local-testing.mdx +3 -1
- package/docs/articles/ci-cd-github/pr-preview-environments.mdx +36 -4
- package/docs/articles/custom-ci-cd-github.mdx +11 -2
- package/docs/articles/environment-variables.mdx +5 -1
- package/docs/articles/environments.mdx +2 -2
- package/docs/articles/graphql.mdx +23 -39
- package/docs/articles/mcp-quickstart-local.mdx +2 -1
- package/docs/articles/mcp-quickstart.mdx +6 -1
- package/docs/articles/monetization/api-access.mdx +184 -0
- package/docs/articles/monetization/meters.mdx +4 -4
- package/docs/articles/monetization/monetization-policy.md +4 -1
- package/docs/articles/monetization/private-plans.md +3 -4
- package/docs/articles/monetization/stripe-integration.md +9 -0
- package/docs/articles/monetization/subscription-lifecycle.md +12 -11
- package/docs/articles/monorepo-deployment.mdx +20 -2
- package/docs/articles/multiple-auth-policies.mdx +2 -2
- package/docs/articles/openapi.mdx +6 -1
- package/docs/articles/rename-or-move-project.mdx +4 -0
- package/docs/articles/securing-your-backend.mdx +11 -3
- package/docs/articles/source-control-setup-github.mdx +4 -0
- package/docs/articles/troubleshooting-slow-responses.mdx +2 -2
- package/docs/articles/troubleshooting.md +3 -3
- package/docs/cli/deploy.mdx +32 -0
- package/docs/cli/deploy.partial.mdx +32 -0
- package/docs/concepts/api-keys.md +2 -2
- package/docs/dedicated/akamai/architecture.mdx +9 -9
- package/docs/dev-portal/zudoku/components/browser-window.mdx +94 -0
- package/docs/dev-portal/zudoku/components/callout.mdx +11 -18
- package/docs/dev-portal/zudoku/components/landing-page.mdx +283 -0
- package/docs/dev-portal/zudoku/configuration/search.md +36 -0
- package/docs/dev-portal/zudoku/configuration/site.md +38 -0
- package/docs/dev-portal/zudoku/customization/colors-theme.mdx +51 -40
- package/docs/errors/rate-limit-exceeded.mdx +30 -3
- package/docs/handlers/system-handlers.mdx +2 -1
- package/docs/mcp-gateway/how-to/connect-upstream-api-key.mdx +2 -2
- package/docs/mcp-gateway/observability/analytics.mdx +17 -13
- package/docs/mcp-gateway/quickstart.mdx +4 -3
- package/docs/mcp-gateway/troubleshooting.mdx +4 -4
- package/docs/policies/_index.md +3 -0
- package/docs/policies/data-loss-prevention-inbound/doc.md +115 -0
- package/docs/policies/data-loss-prevention-inbound/intro.md +15 -0
- package/docs/policies/data-loss-prevention-inbound/schema.json +220 -0
- package/docs/policies/data-loss-prevention-outbound/doc.md +115 -0
- package/docs/policies/data-loss-prevention-outbound/intro.md +18 -0
- package/docs/policies/data-loss-prevention-outbound/schema.json +220 -0
- package/docs/policies/graphql-analytics-outbound/doc.md +93 -0
- package/docs/policies/graphql-analytics-outbound/intro.md +12 -0
- package/docs/policies/graphql-analytics-outbound/schema.json +93 -0
- package/docs/programmable-api/background-dispatcher.mdx +6 -8
- package/docs/programmable-api/zone-cache.mdx +1 -1
- package/docs/programmable-api/zuplo-context.mdx +3 -2
- package/docs/rate-limiting/combining-policies.mdx +293 -0
- package/docs/rate-limiting/dynamic-rate-limiting.mdx +240 -0
- package/docs/rate-limiting/getting-started.mdx +339 -0
- package/docs/rate-limiting/how-it-works.md +225 -0
- package/docs/rate-limiting/monitoring-and-troubleshooting.mdx +243 -0
- package/docs/{articles → rate-limiting}/per-user-rate-limits-using-db.mdx +39 -27
- package/package.json +4 -4
- package/docs/concepts/rate-limiting.md +0 -246
|
@@ -164,29 +164,55 @@ const config = {
|
|
|
164
164
|
};
|
|
165
165
|
```
|
|
166
166
|
|
|
167
|
-
Alternatively,
|
|
167
|
+
Alternatively, paste the copied CSS into a stylesheet and import it from your config:
|
|
168
|
+
|
|
169
|
+
```css title=styles.css
|
|
170
|
+
/* Copied CSS code */
|
|
171
|
+
```
|
|
168
172
|
|
|
169
173
|
```ts title=zudoku.config.ts
|
|
170
|
-
|
|
171
|
-
theme: {
|
|
172
|
-
customCss: `
|
|
173
|
-
/* Copied CSS code */
|
|
174
|
-
`,
|
|
175
|
-
},
|
|
176
|
-
};
|
|
174
|
+
import "./styles.css";
|
|
177
175
|
```
|
|
178
176
|
|
|
179
177
|
## Custom CSS
|
|
180
178
|
|
|
181
|
-
|
|
179
|
+
The recommended way to add custom styles is to write a `.css` file alongside your config and import
|
|
180
|
+
it. This gives you HMR during development, syntax highlighting, autocompletion, and lets you split
|
|
181
|
+
styles across files as your site grows.
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
```css title=styles.css
|
|
184
|
+
.custom {
|
|
185
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
184
188
|
|
|
185
|
-
|
|
189
|
+
```ts title=zudoku.config.ts
|
|
190
|
+
import "./styles.css";
|
|
186
191
|
|
|
187
|
-
|
|
192
|
+
const config = {
|
|
193
|
+
// ...
|
|
194
|
+
};
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
If TypeScript reports `Cannot find module './styles.css'`, add `zudoku/client` to your tsconfig
|
|
198
|
+
types so CSS side-effect imports are recognized:
|
|
199
|
+
|
|
200
|
+
```json title=tsconfig.json
|
|
201
|
+
{
|
|
202
|
+
"compilerOptions": {
|
|
203
|
+
"types": ["zudoku/client"]
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Projects created with `create-zudoku` include this by default.
|
|
209
|
+
|
|
210
|
+
### Inline alternatives (deprecated)
|
|
188
211
|
|
|
189
|
-
|
|
212
|
+
The `theme.customCss` option is deprecated and will be removed in a future release. It still accepts
|
|
213
|
+
a CSS string or object for backwards compatibility, but every change requires restarting the dev
|
|
214
|
+
server and you lose syntax highlighting, autocompletion, and HMR. Migrate to an imported `.css`
|
|
215
|
+
file.
|
|
190
216
|
|
|
191
217
|
```ts title=zudoku.config.ts
|
|
192
218
|
const config = {
|
|
@@ -200,37 +226,22 @@ const config = {
|
|
|
200
226
|
};
|
|
201
227
|
```
|
|
202
228
|
|
|
203
|
-
### CSS Object
|
|
204
|
-
|
|
205
|
-
```ts title=zudoku.config.ts
|
|
206
|
-
const config = {
|
|
207
|
-
theme: {
|
|
208
|
-
customCss: {
|
|
209
|
-
".custom": {
|
|
210
|
-
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
|
|
211
|
-
},
|
|
212
|
-
},
|
|
213
|
-
},
|
|
214
|
-
};
|
|
215
|
-
```
|
|
216
|
-
|
|
217
229
|
### Enabling Code Ligatures
|
|
218
230
|
|
|
219
231
|
Dev Portal disables ligatures in code blocks by default to avoid unwanted glyph joining in fonts like
|
|
220
232
|
Geist Mono (e.g. `---`, `###`, `|--|`). If you're using a coding font designed around ligatures
|
|
221
|
-
(Fira Code, JetBrains Mono, etc.), re-enable them
|
|
222
|
-
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
};
|
|
233
|
+
(Fira Code, JetBrains Mono, etc.), re-enable them in your CSS file:
|
|
234
|
+
|
|
235
|
+
```css title=styles.css
|
|
236
|
+
code,
|
|
237
|
+
pre,
|
|
238
|
+
kbd,
|
|
239
|
+
samp,
|
|
240
|
+
.shiki,
|
|
241
|
+
.shiki span {
|
|
242
|
+
font-variant-ligatures: normal;
|
|
243
|
+
font-feature-settings: normal;
|
|
244
|
+
}
|
|
234
245
|
```
|
|
235
246
|
|
|
236
247
|
## Default Theme
|
|
@@ -4,6 +4,29 @@ title: Rate Limit Exceeded (RATE_LIMIT_EXCEEDED)
|
|
|
4
4
|
|
|
5
5
|
The request was rejected because the client exceeded the configured rate limit.
|
|
6
6
|
|
|
7
|
+
## Response format
|
|
8
|
+
|
|
9
|
+
The 429 response uses the
|
|
10
|
+
[Problem Details](../programmable-api/http-problems.mdx) format:
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"type": "https://httpproblems.com/http-status/429",
|
|
15
|
+
"title": "Too Many Requests",
|
|
16
|
+
"status": 429,
|
|
17
|
+
"detail": "Rate limit exceeded",
|
|
18
|
+
"instance": "/your-route",
|
|
19
|
+
"trace": {
|
|
20
|
+
"requestId": "4d54e4ee-c003-4d75-aba9-e09a6d707b08",
|
|
21
|
+
"timestamp": "2026-04-14T12:00:00.000Z",
|
|
22
|
+
"buildId": "ec44e831-3a02-467e-a26c-7e401e4473bf"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
If `headerMode` is set to `"retry-after"` (the default), the response includes a
|
|
28
|
+
`Retry-After` header with the number of seconds to wait before retrying.
|
|
29
|
+
|
|
7
30
|
## Common Causes
|
|
8
31
|
|
|
9
32
|
- **Too many requests** — The client sent more requests than the rate limit
|
|
@@ -24,8 +47,12 @@ The request was rejected because the client exceeded the configured rate limit.
|
|
|
24
47
|
|
|
25
48
|
## For API Operators
|
|
26
49
|
|
|
27
|
-
- Review the rate limiting policy configuration in the route settings.
|
|
50
|
+
- Review the rate limiting policy configuration in the route settings. Check the
|
|
51
|
+
`requestsAllowed` and `timeWindowMinutes` values and verify that the
|
|
52
|
+
`rateLimitBy` identifier is resolving correctly.
|
|
28
53
|
- Consider using
|
|
29
|
-
[dynamic rate limiting](../
|
|
54
|
+
[dynamic rate limiting](../rate-limiting/dynamic-rate-limiting.mdx) to set
|
|
30
55
|
different limits per customer tier.
|
|
31
|
-
-
|
|
56
|
+
- Use your [logging integration](../articles/logging.mdx) to filter for 429
|
|
57
|
+
responses and identify which consumers are being throttled. Break down by user
|
|
58
|
+
or IP to spot noisy neighbors.
|
|
@@ -5,7 +5,8 @@ sidebar_label: Internal Route Handlers
|
|
|
5
5
|
|
|
6
6
|
The Zuplo Runtime automatically registers certain routes on your gateway to
|
|
7
7
|
provide enhanced functionality. Requests to these routes may appear in your
|
|
8
|
-
|
|
8
|
+
project's analytics under the **Observability** tab. Below is a list of reserved
|
|
9
|
+
routes:
|
|
9
10
|
|
|
10
11
|
| Name | Method | Path | Description |
|
|
11
12
|
| ----------------------- | ------- | --------------------------------- | ----------------------------------------------------------- |
|
|
@@ -48,7 +48,7 @@ Virtual Server**, with these choices:
|
|
|
48
48
|
|
|
49
49
|
<ModalScreenshot size="md">
|
|
50
50
|
|
|
51
|
-

|
|
52
52
|
|
|
53
53
|
</ModalScreenshot>
|
|
54
54
|
|
|
@@ -85,7 +85,7 @@ set.
|
|
|
85
85
|
|
|
86
86
|
<ModalScreenshot size="md">
|
|
87
87
|
|
|
88
|
-

|
|
89
89
|
|
|
90
90
|
</ModalScreenshot>
|
|
91
91
|
|
|
@@ -7,10 +7,11 @@ description:
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
Every authenticated MCP request the Zuplo MCP Gateway handles produces a set of
|
|
10
|
-
structured analytics events. The events power the MCP
|
|
11
|
-
**Analytics**
|
|
12
|
-
pipelines. This page explains why each event exists,
|
|
13
|
-
the data, and the operational questions the dashboard
|
|
10
|
+
structured analytics events. The events power the MCP section of the Zuplo
|
|
11
|
+
Portal's **Observability → Analytics** view and feed the same data into Zuplo's
|
|
12
|
+
standard log and metrics pipelines. This page explains why each event exists,
|
|
13
|
+
the dimensions that scope the data, and the operational questions the dashboard
|
|
14
|
+
exists to answer.
|
|
14
15
|
|
|
15
16
|
## What the analytics are for
|
|
16
17
|
|
|
@@ -125,15 +126,18 @@ A few panels carry detail worth calling out:
|
|
|
125
126
|
|
|
126
127
|
## Where to find it
|
|
127
128
|
|
|
128
|
-
The MCP analytics dashboard is a
|
|
129
|
-
At the account scope,
|
|
130
|
-
[Analytics → MCP](https://portal.zuplo.com/+/account/analytics)
|
|
131
|
-
across every project on the account that has MCP routes. At the
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
129
|
+
The MCP analytics dashboard is a section of the Analytics view inside the Zuplo
|
|
130
|
+
Portal's **Observability** tab. At the account scope,
|
|
131
|
+
[**Observability → Analytics → MCP**](https://portal.zuplo.com/+/account/observability/analytics/mcp)
|
|
132
|
+
aggregates across every project on the account that has MCP routes. At the
|
|
133
|
+
project scope, open your project, click **Observability**, then select
|
|
134
|
+
**Analytics → MCP** to see
|
|
135
|
+
[that project's events](https://portal.zuplo.com/+/account/project/analytics)
|
|
136
|
+
only.
|
|
137
|
+
|
|
138
|
+
The MCP section appears automatically once any MCP request has been recorded for
|
|
139
|
+
the project. New projects show the empty state until the first MCP request
|
|
140
|
+
lands.
|
|
137
141
|
|
|
138
142
|
## Reference: event types
|
|
139
143
|
|
|
@@ -251,9 +251,10 @@ result on your machine.
|
|
|
251
251
|
tool, then returns results proxied through the gateway.
|
|
252
252
|
|
|
253
253
|
Open the project's
|
|
254
|
-
[Analytics
|
|
255
|
-
and
|
|
256
|
-
the success rate, the top capabilities table, and the user
|
|
254
|
+
[**Observability → Analytics**](https://portal.zuplo.com/+/account/project/analytics)
|
|
255
|
+
view and select the **MCP** section to see the call appear in the events
|
|
256
|
+
timeline, the success rate, the top capabilities table, and the user
|
|
257
|
+
breakdown.
|
|
257
258
|
|
|
258
259
|
</Stepper>
|
|
259
260
|
|
|
@@ -216,10 +216,10 @@ window where a stolen cookie is useful.
|
|
|
216
216
|
|
|
217
217
|
## Where to look when none of the above match
|
|
218
218
|
|
|
219
|
-
- Open the project's **Logs** in the Portal and filter on
|
|
220
|
-
`zuplo-request-id`. Gateway log entries include the relevant
|
|
221
|
-
`subjectId`.
|
|
222
|
-
- Open
|
|
219
|
+
- Open the project's **Observability → Logs** view in the Portal and filter on
|
|
220
|
+
the request's `zuplo-request-id`. Gateway log entries include the relevant
|
|
221
|
+
`operationId` and `subjectId`.
|
|
222
|
+
- Open **Observability → Analytics** and select the **MCP** section. The "Top
|
|
223
223
|
Reason Codes" and "Failure Origins" panels surface the highest-cardinality
|
|
224
224
|
failure modes for the current time window.
|
|
225
225
|
- For OAuth-specific issues, the
|
package/docs/policies/_index.md
CHANGED
|
@@ -37,10 +37,13 @@
|
|
|
37
37
|
| curity-phantom-token-inbound | Curity Phantom Token Auth | Authenticate users using the Curity Phantom Token Pattern. | api-gateway |
|
|
38
38
|
| custom-code-inbound | Custom Code Inbound | Enables a custom code policy written in TypeScript. Change YOUR_MODULE to the name of your module (without .ts extension) | api-gateway |
|
|
39
39
|
| custom-code-outbound | Custom Code Outbound | A custom outbound response policy. | api-gateway |
|
|
40
|
+
| data-loss-prevention-outbound | Data Loss Prevention | Scans the upstream response body for sensitive data — PII, secrets, and financial identifiers — using an extensible catalog of built-in recognizers plus any custom patterns, and takes a configurable action when a match is found. The action is one of `mask` (redact matches before returning the response), `block` (replace the response with a `422` listing the detected entity names only), or `log` (record a warning and return unchanged). Only text content types are inspected; binary bodies pass through untouched, and the body is read from a clone so the client still receives the original stream. | api-gateway |
|
|
41
|
+
| data-loss-prevention-inbound | Data Loss Prevention | Scans the incoming request body for sensitive data — PII, secrets, and financial identifiers — using an extensible catalog of built-in recognizers plus any custom patterns, and takes a configurable action when a match is found. The action is one of `mask` (redact matches before forwarding the request), `block` (reject with a `422` listing the detected entity names only), or `log` (record a warning and forward unchanged). Only text content types are inspected; binary bodies pass through untouched, and the body is read from a clone so the upstream still receives the original stream. | api-gateway |
|
|
40
42
|
| firebase-jwt-inbound | Firebase JWT Auth | Authenticate users using Firebase issued JWT tokens. | api-gateway |
|
|
41
43
|
| formdata-to-json-inbound | Form Data to JSON | Converts form data in the incoming request to JSON. | api-gateway |
|
|
42
44
|
| galileo-tracing-inbound | Galileo Tracing | Galileo Tracing Inbound Policy | ai-gateway |
|
|
43
45
|
| geo-filter-inbound | Geo-location filtering | Block requests based on geo-location parameters: country, region code, and ASN | api-gateway |
|
|
46
|
+
| graphql-analytics-outbound | GraphQL Analytics | Reports GraphQL errors returned in response bodies to Zuplo's GraphQL analytics. GraphQL servers following the standard Apollo / graphql-yoga pattern return `200 OK` with an `errors[]` array in the body when an operation fails, which HTTP-level analytics alone report as a success — add this policy to a GraphQL route and failed operations show up as failures on the GraphQL dashboard, classified by error type. Each error in `errors[]` is classified from its `extensions.code` following the Apollo Server conventions (`GRAPHQL_PARSE_FAILED` → `syntax`, `GRAPHQL_VALIDATION_FAILED` → `validation`, `UNAUTHENTICATED` / `FORBIDDEN` → `auth`, timeout codes → `timeout`); custom codes can be mapped with `errorCodeClassification`, and anything unrecognized falls back to `defaultErrorClass` (`resolver`). Optionally set `logErrors` to also write a structured warning per errored response. Bodies larger than `maxResponseBytes` (default 5 MiB) are not inspected. The response always passes through unchanged — the body is read from a clone, and any internal failure is swallowed so reporting can never break the request. The route must be marked `x-graphql: true` in `routes.oas.json` (which enables GraphQL analytics for the route); without the marker the policy logs a warning and does nothing. | api-gateway |
|
|
44
47
|
| graphql-complexity-limit-inbound | GraphQL Complexity Limit | Policy that limits the complexity and depth of GraphQL queries to prevent abuse. Protects your GraphQL API from expensive queries that could cause performance issues or denial of service attacks. | api-gateway |
|
|
45
48
|
| graphql-disable-introspection-inbound | GraphQL Disable Introspection | Policy that disables GraphQL introspection queries in production. Introspection allows clients to discover the schema, which can be a security risk as it exposes your entire API structure. | api-gateway |
|
|
46
49
|
| graphql-introspection-filter-outbound | GraphQL Introspection Filter | Filters GraphQL introspection responses to exclude specific types and fields. This policy intercepts GraphQL introspection query responses and removes configured types and fields from the schema. Useful for hiding internal types or sensitive fields from the public schema. | api-gateway |
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
This policy inspects the body of each incoming request for sensitive data and
|
|
2
|
+
applies a configurable action. It is the inbound counterpart to the
|
|
3
|
+
[Data Loss Prevention - Outbound](/docs/policies/data-loss-prevention-outbound)
|
|
4
|
+
policy, which inspects upstream responses.
|
|
5
|
+
|
|
6
|
+
Detection happens entirely inside the gateway isolate — request bodies are never
|
|
7
|
+
sent to a third-party service.
|
|
8
|
+
|
|
9
|
+
## Actions
|
|
10
|
+
|
|
11
|
+
- **`mask`** (default) — every detected value is replaced with the `mask` string
|
|
12
|
+
and the modified body is forwarded upstream. Overlapping matches are merged
|
|
13
|
+
and masked once.
|
|
14
|
+
- **`block`** — the request is rejected with a `422 Unprocessable Content`. The
|
|
15
|
+
problem detail lists only the names of the detected entities, never the
|
|
16
|
+
matched values, so the policy never leaks the data it caught.
|
|
17
|
+
- **`log`** — a structured warning is written (entity ids and counts only) and
|
|
18
|
+
the request is forwarded unchanged.
|
|
19
|
+
|
|
20
|
+
## Built-in recognizers
|
|
21
|
+
|
|
22
|
+
Enable entities individually or by **group selector** in the `entities` option,
|
|
23
|
+
or omit it to use the full catalog. Entity ids follow a
|
|
24
|
+
`{category}-{scope}-{name}` taxonomy, and any dash-aligned prefix of an id is a
|
|
25
|
+
valid selector: `secret` enables every secret, `id-au` enables Australia's
|
|
26
|
+
identifiers, `secret-aws` enables both AWS entities. Two named groups (`pii`,
|
|
27
|
+
`region-eu`) bundle entities across categories.
|
|
28
|
+
|
|
29
|
+
| Group | Entities |
|
|
30
|
+
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
+
| `secret` | `secret-private-key`, `secret-jwt`, `secret-aws-access-key`, `secret-aws-bedrock`, `secret-github`, `secret-gitlab`, `secret-zuplo`, `secret-openai`, `secret-anthropic`, `secret-google-api-key`, `secret-stripe`, `secret-slack`, `secret-discord-webhook`, `secret-npm`, `secret-pypi`, `secret-sendgrid`, `secret-twilio`, `secret-hugging-face`, `secret-databricks`, `secret-shopify`, `secret-square`, `secret-mailchimp`, `secret-mailgun`, `secret-postman`, `secret-terraform`, `secret-sentry`, `secret-digitalocean`, `secret-heroku`, `secret-perplexity`, `secret-azure-client`, `secret-telegram-bot` |
|
|
32
|
+
| `finance` | `finance-credit-card` (Luhn), `finance-iban` (per-country length + mod-97), `finance-crypto-wallet`, `finance-us-aba-routing` (checksum), `finance-swift-bic`, `finance-us-bank-account`, `finance-cvv` |
|
|
33
|
+
| `id` | `id-us-ssn`, `id-us-itin`, `id-us-passport`, `id-uk-nino`, `id-uk-nhs` (mod-11), `id-ca-sin` (Luhn), `id-au-abn`, `id-au-acn`, `id-au-tfn`, `id-au-medicare` (all checksummed), `id-in-aadhaar` (Verhoeff), `id-in-pan`, `id-sg-nric` (checksum), `id-es-nif` (checksum), `id-it-fiscal-code` (checksum), `id-pl-pesel` (checksum), `id-nl-bsn` (11-proef), `id-br-cpf` (checksum), `id-fr-nir` (mod-97) |
|
|
34
|
+
| `contact` | `contact-email`, `contact-phone` |
|
|
35
|
+
| `network` | `network-ipv4`, `network-ipv6`, `network-mac` |
|
|
36
|
+
| `pii` | `contact` + `id` |
|
|
37
|
+
| Prefixes | `id-us`, `id-uk`, `id-au`, `id-ca`, `id-in`, `id-sg`, `id-es`, `id-it`, `id-pl`, `id-nl`, `id-br`, `id-fr`, `finance-us`, `secret-aws` — everything whose id starts with that prefix |
|
|
38
|
+
| `region-eu` | `id-es-nif`, `id-it-fiscal-code`, `id-pl-pesel`, `id-nl-bsn`, `id-fr-nir`, `finance-iban` |
|
|
39
|
+
|
|
40
|
+
## Context-word scoring
|
|
41
|
+
|
|
42
|
+
Every match gets a confidence score. Recognizers whose raw pattern is just "a
|
|
43
|
+
run of digits" (bank accounts, routing numbers, NHS numbers, …) carry a low base
|
|
44
|
+
confidence and a list of **context words**; when one of those words appears near
|
|
45
|
+
the match — in prose, or in a JSON key, form field, or header-like label
|
|
46
|
+
(`nhsNumber`, `routing_number`, `cvv:`) — the confidence is boosted above the
|
|
47
|
+
detection threshold.
|
|
48
|
+
|
|
49
|
+
For example, with the `id-uk-nhs` entity enabled, `{"nhsNumber": "9434765919"}`
|
|
50
|
+
is masked while the same digits in `{"orderId": "9434765919"}` pass through
|
|
51
|
+
untouched.
|
|
52
|
+
|
|
53
|
+
The threshold is configurable via `minConfidence` (default `0.5`): lower it to
|
|
54
|
+
detect context-dependent entities everywhere, raise it to keep only prefix- and
|
|
55
|
+
checksum-validated matches.
|
|
56
|
+
|
|
57
|
+
## Custom patterns
|
|
58
|
+
|
|
59
|
+
Add your own recognizers with `customPatterns`. Each entry has a `name`, a
|
|
60
|
+
JavaScript regular expression `pattern`, and optionally a `confidence` and
|
|
61
|
+
`context` words to participate in context scoring. Invalid patterns are logged
|
|
62
|
+
and skipped rather than failing the request. Remember to escape backslashes for
|
|
63
|
+
JSON (for example `\\d` to match a digit).
|
|
64
|
+
|
|
65
|
+
## Content types
|
|
66
|
+
|
|
67
|
+
Only text-based bodies (JSON, XML, form-encoded, and `text/*`) are scanned;
|
|
68
|
+
binary bodies pass through untouched. Override the allow-list with the
|
|
69
|
+
`contentTypes` option if you need to scan a different set of content types.
|
|
70
|
+
|
|
71
|
+
## Configuration
|
|
72
|
+
|
|
73
|
+
- `engine`: The detection engine. Only `builtin` is available today.
|
|
74
|
+
**Default:** `builtin`
|
|
75
|
+
- `entities`: Recognizer ids and/or group selectors (prefixes, `pii`,
|
|
76
|
+
`region-eu`) to enable. **Default:** all recognizers
|
|
77
|
+
- `customPatterns`: Additional `{ name, pattern, confidence?, context? }` regex
|
|
78
|
+
recognizers
|
|
79
|
+
- `action`: `mask`, `block`, or `log`. **Default:** `mask`
|
|
80
|
+
- `mask`: Replacement string used when `action` is `mask`. **Default:**
|
|
81
|
+
`[REDACTED]`
|
|
82
|
+
- `minConfidence`: Detection threshold (0-1). **Default:** `0.5`
|
|
83
|
+
- `contentTypes`: Override the scannable content-type allow-list
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
Apply this policy to inbound requests in your route configuration:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"policies": [
|
|
92
|
+
{
|
|
93
|
+
"name": "data-loss-prevention-inbound",
|
|
94
|
+
"policyType": "data-loss-prevention-inbound",
|
|
95
|
+
"handler": {
|
|
96
|
+
"export": "DataLossPreventionInboundPolicy",
|
|
97
|
+
"module": "$import(@zuplo/runtime)",
|
|
98
|
+
"options": {
|
|
99
|
+
"action": "mask",
|
|
100
|
+
"entities": ["secret", "finance", "id-us", "contact-email"],
|
|
101
|
+
"mask": "[REDACTED]",
|
|
102
|
+
"customPatterns": [
|
|
103
|
+
{
|
|
104
|
+
"name": "employee-id",
|
|
105
|
+
"pattern": "EMP-\\d{6}",
|
|
106
|
+
"confidence": 0.3,
|
|
107
|
+
"context": ["employee"]
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
The Data Loss Prevention (DLP) policy scans incoming request bodies for
|
|
2
|
+
sensitive data — personally identifiable information (PII), secrets and API keys
|
|
3
|
+
for dozens of vendors, payment and bank identifiers, and national IDs for many
|
|
4
|
+
countries — using a catalog of 60+ built-in recognizers plus any custom patterns
|
|
5
|
+
you add. When a match is found it takes a configurable action: mask the matches,
|
|
6
|
+
block the request, or log a warning and let it through.
|
|
7
|
+
|
|
8
|
+
Recognizers are selected individually or via entity groups (`secret`, `finance`,
|
|
9
|
+
`pii`, `id-us`, `id-uk`, `region-eu`, …). Detection runs entirely in the gateway
|
|
10
|
+
isolate using regular expressions, checksums (Luhn, mod-97, Verhoeff, and
|
|
11
|
+
friends), and context-word scoring — no request data leaves the gateway.
|
|
12
|
+
|
|
13
|
+
Pair with the
|
|
14
|
+
[Data Loss Prevention - Outbound](/docs/policies/data-loss-prevention-outbound)
|
|
15
|
+
policy to also scan upstream responses before they're returned to the client.
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft-07/schema",
|
|
3
|
+
"$id": "https://cdn.zuplo.com/policies/runtime/schemas/data-loss-prevention-inbound.json",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"title": "Data Loss Prevention",
|
|
6
|
+
"isDeprecated": false,
|
|
7
|
+
"isPaidAddOn": false,
|
|
8
|
+
"isEnterprise": false,
|
|
9
|
+
"isInternal": false,
|
|
10
|
+
"isBeta": false,
|
|
11
|
+
"isHidden": false,
|
|
12
|
+
"requiresAI": false,
|
|
13
|
+
"products": ["api-gateway"],
|
|
14
|
+
"description": "Scans the incoming request body for sensitive data — PII, secrets, and financial identifiers — using an extensible catalog of built-in recognizers plus any custom patterns, and takes a configurable action when a match is found.\n\nThe action is one of `mask` (redact matches before forwarding the request), `block` (reject with a `422` listing the detected entity names only), or `log` (record a warning and forward unchanged). Only text content types are inspected; binary bodies pass through untouched, and the body is read from a clone so the upstream still receives the original stream.",
|
|
15
|
+
"deprecatedMessage": "",
|
|
16
|
+
"required": ["handler"],
|
|
17
|
+
"properties": {
|
|
18
|
+
"handler": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"default": {},
|
|
21
|
+
"required": ["export", "module", "options"],
|
|
22
|
+
"properties": {
|
|
23
|
+
"export": {
|
|
24
|
+
"const": "DataLossPreventionInboundPolicy",
|
|
25
|
+
"description": "The name of the exported type"
|
|
26
|
+
},
|
|
27
|
+
"module": {
|
|
28
|
+
"const": "$import(@zuplo/runtime)",
|
|
29
|
+
"description": "The module containing the policy"
|
|
30
|
+
},
|
|
31
|
+
"options": {
|
|
32
|
+
"title": "DataLossPreventionInboundPolicyOptions",
|
|
33
|
+
"type": "object",
|
|
34
|
+
"description": "The options for the Data Loss Prevention inbound policy. Scans the incoming request body for sensitive data and applies the configured action (mask, block, or log).",
|
|
35
|
+
"additionalProperties": false,
|
|
36
|
+
"required": [],
|
|
37
|
+
"examples": [
|
|
38
|
+
{
|
|
39
|
+
"action": "mask",
|
|
40
|
+
"entities": ["secret", "finance", "contact-email", "id-us-ssn"],
|
|
41
|
+
"mask": "[REDACTED]"
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"properties": {
|
|
45
|
+
"engine": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"enum": ["builtin"],
|
|
48
|
+
"default": "builtin",
|
|
49
|
+
"description": "The detection engine. Only `builtin` (in-isolate regex + checksum detection with context-word scoring) is available today. This is the extension point for a future hosted `presidio-service` mode; declaring it now keeps adding that mode an additive, non-breaking change."
|
|
50
|
+
},
|
|
51
|
+
"entities": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"description": "Built-in recognizer ids and/or group selectors to enable. Entity ids follow a {category}-{scope}-{name} taxonomy, and any dash-aligned id prefix acts as a selector (for example `secret` is every secret, `id-au` is Australia's identifiers, `secret-aws` is both AWS entities), plus the named groups `pii` and `region-eu`. Available selectors: `contact`, `finance`, `finance-us`, `id`, `id-au`, `id-br`, `id-ca`, `id-es`, `id-fr`, `id-in`, `id-it`, `id-nl`, `id-pl`, `id-sg`, `id-uk`, `id-us`, `network`, `pii`, `region-eu`, `secret`, `secret-aws`. When omitted, the full built-in catalog is used.",
|
|
54
|
+
"items": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"enum": [
|
|
57
|
+
"contact",
|
|
58
|
+
"finance",
|
|
59
|
+
"finance-us",
|
|
60
|
+
"id",
|
|
61
|
+
"id-au",
|
|
62
|
+
"id-br",
|
|
63
|
+
"id-ca",
|
|
64
|
+
"id-es",
|
|
65
|
+
"id-fr",
|
|
66
|
+
"id-in",
|
|
67
|
+
"id-it",
|
|
68
|
+
"id-nl",
|
|
69
|
+
"id-pl",
|
|
70
|
+
"id-sg",
|
|
71
|
+
"id-uk",
|
|
72
|
+
"id-us",
|
|
73
|
+
"network",
|
|
74
|
+
"pii",
|
|
75
|
+
"region-eu",
|
|
76
|
+
"secret",
|
|
77
|
+
"secret-aws",
|
|
78
|
+
"contact-email",
|
|
79
|
+
"contact-phone",
|
|
80
|
+
"finance-credit-card",
|
|
81
|
+
"finance-crypto-wallet",
|
|
82
|
+
"finance-cvv",
|
|
83
|
+
"finance-iban",
|
|
84
|
+
"finance-swift-bic",
|
|
85
|
+
"finance-us-aba-routing",
|
|
86
|
+
"finance-us-bank-account",
|
|
87
|
+
"id-au-abn",
|
|
88
|
+
"id-au-acn",
|
|
89
|
+
"id-au-medicare",
|
|
90
|
+
"id-au-tfn",
|
|
91
|
+
"id-br-cpf",
|
|
92
|
+
"id-ca-sin",
|
|
93
|
+
"id-es-nif",
|
|
94
|
+
"id-fr-nir",
|
|
95
|
+
"id-in-aadhaar",
|
|
96
|
+
"id-in-pan",
|
|
97
|
+
"id-it-fiscal-code",
|
|
98
|
+
"id-nl-bsn",
|
|
99
|
+
"id-pl-pesel",
|
|
100
|
+
"id-sg-nric",
|
|
101
|
+
"id-uk-nhs",
|
|
102
|
+
"id-uk-nino",
|
|
103
|
+
"id-us-itin",
|
|
104
|
+
"id-us-passport",
|
|
105
|
+
"id-us-ssn",
|
|
106
|
+
"network-ipv4",
|
|
107
|
+
"network-ipv6",
|
|
108
|
+
"network-mac",
|
|
109
|
+
"secret-anthropic",
|
|
110
|
+
"secret-aws-access-key",
|
|
111
|
+
"secret-aws-bedrock",
|
|
112
|
+
"secret-azure-client",
|
|
113
|
+
"secret-databricks",
|
|
114
|
+
"secret-digitalocean",
|
|
115
|
+
"secret-discord-webhook",
|
|
116
|
+
"secret-github",
|
|
117
|
+
"secret-gitlab",
|
|
118
|
+
"secret-google-api-key",
|
|
119
|
+
"secret-heroku",
|
|
120
|
+
"secret-hugging-face",
|
|
121
|
+
"secret-jwt",
|
|
122
|
+
"secret-mailchimp",
|
|
123
|
+
"secret-mailgun",
|
|
124
|
+
"secret-npm",
|
|
125
|
+
"secret-openai",
|
|
126
|
+
"secret-perplexity",
|
|
127
|
+
"secret-postman",
|
|
128
|
+
"secret-private-key",
|
|
129
|
+
"secret-pypi",
|
|
130
|
+
"secret-sendgrid",
|
|
131
|
+
"secret-sentry",
|
|
132
|
+
"secret-shopify",
|
|
133
|
+
"secret-slack",
|
|
134
|
+
"secret-square",
|
|
135
|
+
"secret-stripe",
|
|
136
|
+
"secret-telegram-bot",
|
|
137
|
+
"secret-terraform",
|
|
138
|
+
"secret-twilio",
|
|
139
|
+
"secret-zuplo"
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"customPatterns": {
|
|
144
|
+
"type": "array",
|
|
145
|
+
"description": "Additional customer-defined regex recognizers. Invalid patterns are logged and skipped rather than failing the request.",
|
|
146
|
+
"items": {
|
|
147
|
+
"type": "object",
|
|
148
|
+
"title": "DlpCustomPattern",
|
|
149
|
+
"additionalProperties": false,
|
|
150
|
+
"required": ["name", "pattern"],
|
|
151
|
+
"properties": {
|
|
152
|
+
"name": {
|
|
153
|
+
"type": "string",
|
|
154
|
+
"description": "Identifier reported in findings and block details for this pattern."
|
|
155
|
+
},
|
|
156
|
+
"pattern": {
|
|
157
|
+
"type": "string",
|
|
158
|
+
"description": "A JavaScript regular expression source string. Remember to escape backslashes for JSON (for example `\\\\d` for a digit)."
|
|
159
|
+
},
|
|
160
|
+
"confidence": {
|
|
161
|
+
"type": "number",
|
|
162
|
+
"minimum": 0,
|
|
163
|
+
"maximum": 1,
|
|
164
|
+
"default": 0.85,
|
|
165
|
+
"description": "Base confidence (0-1) for matches of this pattern. The default of 0.85 is above the default detection threshold; combine a low value with `context` words for patterns that are only sensitive in context."
|
|
166
|
+
},
|
|
167
|
+
"context": {
|
|
168
|
+
"type": "array",
|
|
169
|
+
"items": {
|
|
170
|
+
"type": "string"
|
|
171
|
+
},
|
|
172
|
+
"description": "Context words that boost a match's confidence by 0.45 when one appears near the match (in the surrounding field, label, or key)."
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
"action": {
|
|
178
|
+
"type": "string",
|
|
179
|
+
"enum": ["mask", "block", "log"],
|
|
180
|
+
"default": "mask",
|
|
181
|
+
"description": "What to do when sensitive data is detected. `mask` redacts matches before forwarding the request, `block` rejects with a 422 listing only the detected entity names, and `log` records a warning and forwards the request unchanged."
|
|
182
|
+
},
|
|
183
|
+
"mask": {
|
|
184
|
+
"type": "string",
|
|
185
|
+
"default": "[REDACTED]",
|
|
186
|
+
"examples": ["[REDACTED]"],
|
|
187
|
+
"description": "The string that replaces detected values when `action` is `mask`."
|
|
188
|
+
},
|
|
189
|
+
"minConfidence": {
|
|
190
|
+
"type": "number",
|
|
191
|
+
"minimum": 0,
|
|
192
|
+
"maximum": 1,
|
|
193
|
+
"default": 0.5,
|
|
194
|
+
"x-show-example": false,
|
|
195
|
+
"description": "Minimum confidence (0-1) a match must reach to count as a finding. Context-dependent recognizers (for example `finance-us-bank-account` or `finance-us-aba-routing`) sit below the default threshold of 0.5 until a context word near the match boosts them above it. Lower the threshold to surface them everywhere; raise it to keep only prefix- or checksum-validated matches."
|
|
196
|
+
},
|
|
197
|
+
"contentTypes": {
|
|
198
|
+
"type": "array",
|
|
199
|
+
"description": "Override the set of scannable content-type prefixes. When omitted, the built-in text content-type allow-list (JSON, XML, form-encoded, text/\\*) is used.",
|
|
200
|
+
"items": {
|
|
201
|
+
"type": "string"
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
"examples": [
|
|
208
|
+
{
|
|
209
|
+
"export": "DataLossPreventionInboundPolicy",
|
|
210
|
+
"module": "$import(@zuplo/runtime)",
|
|
211
|
+
"options": {
|
|
212
|
+
"action": "mask",
|
|
213
|
+
"entities": ["secret", "finance", "contact-email", "id-us-ssn"],
|
|
214
|
+
"mask": "[REDACTED]"
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|