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
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
This policy inspects the body of each upstream response for sensitive data and
|
|
2
|
+
applies a configurable action. It is the outbound counterpart to the
|
|
3
|
+
[Data Loss Prevention - Inbound](/docs/policies/data-loss-prevention-inbound)
|
|
4
|
+
policy, which inspects incoming requests.
|
|
5
|
+
|
|
6
|
+
Detection happens entirely inside the gateway isolate — response bodies are
|
|
7
|
+
never 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 response is returned to the client. Overlapping matches are
|
|
13
|
+
merged and masked once.
|
|
14
|
+
- **`block`** — the response is replaced 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 response is returned 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 response. 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 outbound responses in your route configuration:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"policies": [
|
|
92
|
+
{
|
|
93
|
+
"name": "data-loss-prevention-outbound",
|
|
94
|
+
"policyType": "data-loss-prevention-outbound",
|
|
95
|
+
"handler": {
|
|
96
|
+
"export": "DataLossPreventionOutboundPolicy",
|
|
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,18 @@
|
|
|
1
|
+
The Data Loss Prevention (DLP) policy scans upstream response 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 response, 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 response data leaves the gateway. This
|
|
12
|
+
is especially useful in front of APIs that interface with user-generated
|
|
13
|
+
content, MCP servers, and AI consumers, where a response might otherwise leak
|
|
14
|
+
data the client should never see.
|
|
15
|
+
|
|
16
|
+
Pair with the
|
|
17
|
+
[Data Loss Prevention - Inbound](/docs/policies/data-loss-prevention-inbound)
|
|
18
|
+
policy to also scan incoming requests before they reach your handler.
|
|
@@ -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-outbound.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 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.\n\nThe 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.",
|
|
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": "DataLossPreventionOutboundPolicy",
|
|
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": "DataLossPreventionOutboundPolicyOptions",
|
|
33
|
+
"type": "object",
|
|
34
|
+
"description": "The options for the Data Loss Prevention outbound policy. Scans the upstream response 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 response.",
|
|
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 returning the response, `block` replaces the response with a 422 listing only the detected entity names, and `log` records a warning and returns the response 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": "DataLossPreventionOutboundPolicy",
|
|
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
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
This policy reads GraphQL `errors[]` from response bodies and reports them to
|
|
2
|
+
Zuplo's GraphQL analytics, so operations that fail with the standard "`200 OK`
|
|
3
|
+
with errors" pattern (Apollo Server, graphql-yoga, and most other GraphQL
|
|
4
|
+
servers) show up as failures on the GraphQL dashboard instead of successes.
|
|
5
|
+
|
|
6
|
+
The response always passes through to the client unchanged — the body is read
|
|
7
|
+
from a clone, and any internal failure inside the policy is swallowed so error
|
|
8
|
+
reporting can never break a request.
|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
The route must be marked with `"x-graphql": true` in `routes.oas.json`. That
|
|
13
|
+
marker is what enables GraphQL analytics for the route (one `graphql_operation`
|
|
14
|
+
event per request); this policy enriches that event with the errors it finds in
|
|
15
|
+
the response body. On a route without the marker the policy logs a warning and
|
|
16
|
+
does nothing.
|
|
17
|
+
|
|
18
|
+
## Error classification
|
|
19
|
+
|
|
20
|
+
Each entry in the response's `errors[]` array counts as one error toward the
|
|
21
|
+
operation's `errorCount`, and is classified into one of five classes from its
|
|
22
|
+
`extensions.code`, following the Apollo Server conventions:
|
|
23
|
+
|
|
24
|
+
| `extensions.code` | Class |
|
|
25
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
|
|
26
|
+
| `GRAPHQL_PARSE_FAILED` | `syntax` |
|
|
27
|
+
| `GRAPHQL_VALIDATION_FAILED`, `BAD_USER_INPUT`, `PERSISTED_QUERY_NOT_FOUND`, `PERSISTED_QUERY_NOT_SUPPORTED`, `OPERATION_RESOLUTION_FAILURE` | `validation` |
|
|
28
|
+
| `UNAUTHENTICATED`, `FORBIDDEN` | `auth` |
|
|
29
|
+
| `REQUEST_TIMEOUT`, `TIMEOUT`, `GATEWAY_TIMEOUT` | `timeout` |
|
|
30
|
+
| `INTERNAL_SERVER_ERROR`, `DOWNSTREAM_SERVICE_ERROR` | `resolver` |
|
|
31
|
+
|
|
32
|
+
An error whose code is missing or unrecognized is classified as
|
|
33
|
+
`defaultErrorClass` (`resolver` unless configured otherwise). Servers that emit
|
|
34
|
+
their own codes can extend or override the table with `errorCodeClassification`;
|
|
35
|
+
those entries are matched case-sensitively and win against the built-ins, which
|
|
36
|
+
are matched case-insensitively.
|
|
37
|
+
|
|
38
|
+
Batched (array) responses are supported — errors are collected across every
|
|
39
|
+
result in the batch.
|
|
40
|
+
|
|
41
|
+
## What is inspected
|
|
42
|
+
|
|
43
|
+
Only responses with a JSON content type (`application/json` or any `+json` type
|
|
44
|
+
such as `application/graphql-response+json`) are read, and bodies larger than
|
|
45
|
+
`maxResponseBytes` (5 MiB by default) are skipped — by `Content-Length` when the
|
|
46
|
+
header is present, or measured while reading when it is absent. Everything else
|
|
47
|
+
passes through without the body being touched.
|
|
48
|
+
|
|
49
|
+
## Logging
|
|
50
|
+
|
|
51
|
+
Set `logErrors` to `true` to also write a structured warning to the request log
|
|
52
|
+
whenever a response contains GraphQL errors. The entry carries the message,
|
|
53
|
+
`extensions.code`, and path of each error (capped at the first 10) under a
|
|
54
|
+
consistent `"GraphQL response contained errors"` message, so you can search or
|
|
55
|
+
alert on it.
|
|
56
|
+
|
|
57
|
+
## Configuration
|
|
58
|
+
|
|
59
|
+
- `errorCodeClassification`: Additional `extensions.code` → class mappings,
|
|
60
|
+
merged over the built-in table. **Default:** none
|
|
61
|
+
- `defaultErrorClass`: Class for errors with a missing or unrecognized code —
|
|
62
|
+
one of `syntax`, `validation`, `auth`, `timeout`, `resolver`. **Default:**
|
|
63
|
+
`resolver`
|
|
64
|
+
- `logErrors`: Also log a structured warning per errored response. **Default:**
|
|
65
|
+
`false`
|
|
66
|
+
- `maxResponseBytes`: Maximum response body size in bytes to inspect.
|
|
67
|
+
**Default:** `5242880` (5 MiB)
|
|
68
|
+
|
|
69
|
+
## Usage
|
|
70
|
+
|
|
71
|
+
Apply this policy to outbound responses on your GraphQL route:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"policies": [
|
|
76
|
+
{
|
|
77
|
+
"name": "graphql-analytics",
|
|
78
|
+
"policyType": "graphql-analytics-outbound",
|
|
79
|
+
"handler": {
|
|
80
|
+
"export": "GraphqlAnalyticsOutboundPolicy",
|
|
81
|
+
"module": "$import(@zuplo/runtime)",
|
|
82
|
+
"options": {
|
|
83
|
+
"errorCodeClassification": {
|
|
84
|
+
"RATE_LIMITED": "resolver",
|
|
85
|
+
"NOT_LOGGED_IN": "auth"
|
|
86
|
+
},
|
|
87
|
+
"logErrors": true
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
The GraphQL Analytics policy makes failed GraphQL operations visible on Zuplo's
|
|
2
|
+
GraphQL analytics dashboard. GraphQL servers following the standard Apollo /
|
|
3
|
+
graphql-yoga pattern return `200 OK` with an `errors[]` array in the response
|
|
4
|
+
body when an operation fails — invisible to HTTP-level analytics, which would
|
|
5
|
+
report every such operation as a success.
|
|
6
|
+
|
|
7
|
+
Add this policy to a GraphQL route (marked `x-graphql: true`) and the gateway
|
|
8
|
+
reads the response body, counts the GraphQL errors, and classifies each one by
|
|
9
|
+
its `extensions.code` (syntax, validation, auth, timeout, or resolver) — no
|
|
10
|
+
changes to your GraphQL server or client code required. The classification map
|
|
11
|
+
is configurable for servers that emit custom error codes, and errors can
|
|
12
|
+
optionally be written to the request log.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft-07/schema",
|
|
3
|
+
"$id": "https://cdn.zuplo.com/policies/runtime/schemas/graphql-analytics-outbound.json",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"title": "GraphQL Analytics",
|
|
6
|
+
"isDeprecated": false,
|
|
7
|
+
"isPaidAddOn": false,
|
|
8
|
+
"isEnterprise": false,
|
|
9
|
+
"isInternal": false,
|
|
10
|
+
"isBeta": true,
|
|
11
|
+
"isHidden": false,
|
|
12
|
+
"requiresAI": false,
|
|
13
|
+
"products": ["api-gateway"],
|
|
14
|
+
"description": "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.\n\nEach 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.\n\nThe 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.",
|
|
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": "GraphqlAnalyticsOutboundPolicy",
|
|
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": "GraphqlAnalyticsOutboundPolicyOptions",
|
|
33
|
+
"type": "object",
|
|
34
|
+
"description": "The options for the GraphQL Analytics outbound policy. Reads GraphQL `errors[]` from the response body and reports them to Zuplo's GraphQL analytics.",
|
|
35
|
+
"additionalProperties": false,
|
|
36
|
+
"required": [],
|
|
37
|
+
"examples": [
|
|
38
|
+
{
|
|
39
|
+
"errorCodeClassification": {
|
|
40
|
+
"RATE_LIMITED": "resolver",
|
|
41
|
+
"NOT_LOGGED_IN": "auth"
|
|
42
|
+
},
|
|
43
|
+
"defaultErrorClass": "resolver",
|
|
44
|
+
"logErrors": false
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"properties": {
|
|
48
|
+
"errorCodeClassification": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"description": "Additional `extensions.code` → error-class mappings for codes your GraphQL server emits. Entries are merged over (and win against) the built-in Apollo-convention map (`GRAPHQL_PARSE_FAILED` → `syntax`, `GRAPHQL_VALIDATION_FAILED` / `BAD_USER_INPUT` → `validation`, `UNAUTHENTICATED` / `FORBIDDEN` → `auth`, timeout codes → `timeout`, `INTERNAL_SERVER_ERROR` → `resolver`). Keys are matched case-sensitively; built-in codes are matched case-insensitively.",
|
|
51
|
+
"additionalProperties": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"enum": ["syntax", "validation", "auth", "timeout", "resolver"]
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"defaultErrorClass": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"enum": ["syntax", "validation", "auth", "timeout", "resolver"],
|
|
59
|
+
"default": "resolver",
|
|
60
|
+
"description": "The error class reported for a GraphQL error whose `extensions.code` is missing or not in the classification map."
|
|
61
|
+
},
|
|
62
|
+
"logErrors": {
|
|
63
|
+
"type": "boolean",
|
|
64
|
+
"default": false,
|
|
65
|
+
"description": "When `true`, also write a structured warning to the request log (message, `extensions.code`, and path of each error — capped at the first 10) whenever a response contains GraphQL errors."
|
|
66
|
+
},
|
|
67
|
+
"maxResponseBytes": {
|
|
68
|
+
"type": "integer",
|
|
69
|
+
"minimum": 1,
|
|
70
|
+
"default": 5242880,
|
|
71
|
+
"x-show-example": false,
|
|
72
|
+
"description": "Maximum response body size in bytes the policy will inspect. Larger bodies — by `Content-Length`, or measured while reading when the header is absent — pass through without being scanned, so their GraphQL errors (if any) go unreported. The default is 5 MiB."
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"examples": [
|
|
78
|
+
{
|
|
79
|
+
"export": "GraphqlAnalyticsOutboundPolicy",
|
|
80
|
+
"module": "$import(@zuplo/runtime)",
|
|
81
|
+
"options": {
|
|
82
|
+
"errorCodeClassification": {
|
|
83
|
+
"RATE_LIMITED": "resolver",
|
|
84
|
+
"NOT_LOGGED_IN": "auth"
|
|
85
|
+
},
|
|
86
|
+
"defaultErrorClass": "resolver",
|
|
87
|
+
"logErrors": false
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -13,7 +13,7 @@ groups entries and invokes your callback with batches at regular intervals.
|
|
|
13
13
|
```ts
|
|
14
14
|
new BackgroundDispatcher<T>(
|
|
15
15
|
dispatchFunction: (entries: T[]) => Promise<void>,
|
|
16
|
-
options: { msDelay: number }
|
|
16
|
+
options: { msDelay: number; name?: string }
|
|
17
17
|
)
|
|
18
18
|
```
|
|
19
19
|
|
|
@@ -21,6 +21,7 @@ Creates a new background dispatcher instance.
|
|
|
21
21
|
|
|
22
22
|
- `dispatchFunction` - Asynchronous function called with batched entries
|
|
23
23
|
- `options.msDelay` - Milliseconds between dispatch calls (required, non-zero)
|
|
24
|
+
- `options.name` - Optional name that identifies the dispatcher in error logs
|
|
24
25
|
- `T` - The type of entries being batched
|
|
25
26
|
|
|
26
27
|
## Methods
|
|
@@ -30,7 +31,7 @@ Creates a new background dispatcher instance.
|
|
|
30
31
|
Adds an entry to the batch queue for later dispatch.
|
|
31
32
|
|
|
32
33
|
```ts
|
|
33
|
-
enqueue(entry: T
|
|
34
|
+
enqueue(entry: T): void
|
|
34
35
|
```
|
|
35
36
|
|
|
36
37
|
## Example
|
|
@@ -74,12 +75,9 @@ const backgroundDispatcher = new BackgroundDispatcher<ExampleEntry>(
|
|
|
74
75
|
// This is an example Request Handler that used the component, a simple
|
|
75
76
|
// "Hello World" handler.
|
|
76
77
|
export default async function (request: ZuploRequest, context: ZuploContext) {
|
|
77
|
-
backgroundDispatcher.enqueue(
|
|
78
|
-
{
|
|
79
|
-
|
|
80
|
-
},
|
|
81
|
-
context,
|
|
82
|
-
);
|
|
78
|
+
backgroundDispatcher.enqueue({
|
|
79
|
+
message: `new request on '${request.url}' with id ${context.requestId}`,
|
|
80
|
+
});
|
|
83
81
|
|
|
84
82
|
return "Hello World!";
|
|
85
83
|
}
|
|
@@ -18,7 +18,7 @@ time-to-live (TTL) after which it expires and is removed from the cache. Each
|
|
|
18
18
|
cached object can be up to 512 MB in size.
|
|
19
19
|
|
|
20
20
|
There's an demonstration of ZoneCache use in the
|
|
21
|
-
[Per User Rate Limits Using a Database](../
|
|
21
|
+
[Per User Rate Limits Using a Database](../rate-limiting/per-user-rate-limits-using-db.mdx)
|
|
22
22
|
example.
|
|
23
23
|
|
|
24
24
|
## Constructor
|
|
@@ -88,8 +88,9 @@ context.log.info({
|
|
|
88
88
|
|
|
89
89
|
### `log`
|
|
90
90
|
|
|
91
|
-
A logger instance for debugging and monitoring. Logs appear in
|
|
92
|
-
|
|
91
|
+
A logger instance for debugging and monitoring. Logs appear in the
|
|
92
|
+
**Observability → Logs** view of your project in the
|
|
93
|
+
[Zuplo Portal](https://portal.zuplo.com/+/account/project/) and in your
|
|
93
94
|
integrated log solution (for example Datadog). Pre-production environments are
|
|
94
95
|
typically set to **Info** log level, while production is set to **Error**.
|
|
95
96
|
|