Skip to content

Commit 8f47bd1

Browse files
committed
fixes after rebasing
1 parent 3aa469c commit 8f47bd1

17 files changed

Lines changed: 777 additions & 67 deletions
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
hive-router-config: patch
3+
hive-router: patch
4+
hive-router-plan-executor: patch
5+
hive-router-internal: patch
6+
---
7+
8+
# Subgraph Error Masking
9+
10+
Mask subgraph errors before they reach clients, preventing internal details from leaking.
11+
12+
Masking is **enabled by default**: subgraph error messages are replaced with `"Unexpected error"`. It runs last in the pipeline, so metrics, tracing, and logging still see the original error.
13+
14+
Configure it under `error_masking`:
15+
16+
```yaml
17+
error_masking:
18+
redacted_error_message: "Unexpected error"
19+
all:
20+
error_message: true
21+
extensions:
22+
mode: allow # allow | deny
23+
keys:
24+
- code
25+
subgraphs:
26+
products:
27+
error_message: false
28+
```
29+
30+
- `error_message` toggles message redaction; `extensions` redacts extension keys via an `allow`/`deny` list.
31+
- `subgraphs.<name>` overrides `all` per subgraph, inheriting any field it doesn't set.
32+
- Set `DISABLE_SUBGRAPH_ERROR_MASKING=true` to disable message masking without editing the config.
33+
34+
[Documentation](http://the-guild.dev/graphql/hive/docs/router/security/error-masking)
35+
36+
Fixes https://github.com/graphql-hive/router/issues/1194

bin/router/src/pipeline/execution.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use hive_router_internal::telemetry::traces::spans::graphql::{
77
};
88
use hive_router_plan_executor::execution::client_request_details::ClientRequestDetails;
99
use hive_router_plan_executor::execution::demand_control::DemandControlExecutionContext;
10-
use hive_router_plan_executor::execution::error_masking::ErrorMaskingRuntime;
1110
use hive_router_plan_executor::execution::jwt_forward::JwtAuthForwardingPlan;
1211
use hive_router_plan_executor::execution::operation_name::OperationNameFactory;
1312
use hive_router_plan_executor::execution::plan::{
@@ -119,10 +118,6 @@ pub async fn execute_plan<'exec>(
119118
None
120119
};
121120

122-
let error_masking_runtime = Arc::new(ErrorMaskingRuntime::compile_from_config(
123-
&app_state.router_config.error_masking,
124-
));
125-
126121
let operation_name = planned_request.client_request_details.operation.name;
127122
let result = execute_query_plan(QueryPlanExecutionOpts {
128123
query_plan: planned_request.query_plan_payload,
@@ -156,7 +151,7 @@ pub async fn execute_plan<'exec>(
156151
operation_name,
157152
),
158153
response_header_sink,
159-
error_masking_runtime,
154+
error_masking_runtime: app_state.error_masking.clone(),
160155
})
161156
.await?;
162157

bin/router/src/shared_state.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use hive_router_internal::telemetry::metrics::subscription_metrics::Subscription
1212
use hive_router_internal::telemetry::metrics::Metrics;
1313
use hive_router_internal::telemetry::TelemetryContext;
1414
use hive_router_plan_executor::coprocessor::{CoprocessorError, CoprocessorRuntime};
15+
use hive_router_plan_executor::execution::error_masking::ErrorMaskingRuntime;
1516
use hive_router_plan_executor::execution::plan::FailedExecutionResult;
1617
use hive_router_plan_executor::extensions::{
1718
compile::compile_extensions_plan, plan::ExtensionsPlan,
@@ -329,6 +330,8 @@ pub struct RouterSharedState {
329330
pub active_subscriptions: ActiveSubscriptions,
330331
/// The storage manager for the router.
331332
pub storage_manager: Arc<StorageManager>,
333+
/// The error masking configuration for the router.
334+
pub error_masking: Arc<ErrorMaskingRuntime>,
332335
}
333336

334337
impl RouterSharedState {
@@ -358,6 +361,9 @@ impl RouterSharedState {
358361
.map_err(Box::new)
359362
})
360363
.transpose()?;
364+
let error_masking = Arc::new(ErrorMaskingRuntime::compile_from_config(
365+
&router_config.error_masking,
366+
));
361367

362368
Ok(Self {
363369
validation_plan: Arc::new(validation_plan),
@@ -394,6 +400,7 @@ impl RouterSharedState {
394400
long_lived_client_count: Arc::new(AtomicUsize::new(0)),
395401
active_subscriptions,
396402
storage_manager,
403+
error_masking,
397404
})
398405
}
399406
}

docs/README.md

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
|[**cors**](#cors)|`object`|Configuration for CORS (Cross-Origin Resource Sharing).<br/>Default: `{"allow_any_origin":false,"allow_credentials":false,"enabled":false,"policies":[]}`<br/>|yes|
1010
|[**csrf**](#csrf)|`object`|Configuration for CSRF prevention.<br/>Default: `{"enabled":false,"required_headers":[]}`<br/>||
1111
|[**demand\_control**](#demand_control)|`object`, `null`||yes|
12-
|[**error\_masking**](#error_masking)|`object`|Configuration for error masking.<br/>Default: `{"all":{"error_message":true,"extensions":null},"redacted_error_message":"Unexpected error"}`<br/>||
12+
|[**error\_masking**](#error_masking)|`object`|Configuration for error masking.<br/>Default: `{"all":{"error_message":true},"redacted_error_message":"Unexpected error"}`<br/>||
1313
|[**headers**](#headers)|`object`|Configuration for the headers.<br/>Default: `{}`<br/>||
1414
|[**http**](#http)|`object`|Configuration for the HTTP server/listener.<br/>Default: `{"graphql_endpoint":"/graphql","host":"0.0.0.0","port":4000}`<br/>||
1515
|**introspection**||Configuration to enable or disable introspection queries.<br/>||
@@ -61,7 +61,6 @@ csrf:
6161
error_masking:
6262
all:
6363
error_message: true
64-
extensions: null
6564
redacted_error_message: Unexpected error
6665
headers:
6766
all:
@@ -1011,42 +1010,48 @@ Configuration for error masking.
10111010

10121011
|Name|Type|Description|Required|
10131012
|----|----|-----------|--------|
1014-
|[**all**](#error_maskingall)|`object`|Default: `{"error_message":true,"extensions":null}`<br/>||
1015-
|**redacted\_error\_message**|`string`|Default: `"Unexpected error"`<br/>||
1016-
|[**subgraphs**](#error_maskingsubgraphs)|`object`, `null`|||
1013+
|[**all**](#error_maskingall)|`object`|The default error masking configuration for all subgraphs.<br/>Default: `{"error_message":true}`<br/>||
1014+
|**redacted\_error\_message**|`string`|The error message to redact in subgraph errors. The default is "Unexpected error".<br/>Default: `"Unexpected error"`<br/>||
1015+
|[**subgraphs**](#error_maskingsubgraphs)|`object`, `null`|The error masking configuration for individual subgraphs.<br/>||
10171016

10181017
**Additional Properties:** not allowed
10191018
**Example**
10201019

10211020
```yaml
10221021
all:
10231022
error_message: true
1024-
extensions: null
10251023
redacted_error_message: Unexpected error
10261024
10271025
```
10281026

10291027
<a name="error_maskingall"></a>
10301028
### error\_masking\.all: object
10311029

1030+
The default error masking configuration for all subgraphs.
1031+
1032+
10321033
**Properties**
10331034

10341035
|Name|Type|Description|Required|
10351036
|----|----|-----------|--------|
1036-
|**error\_message**|`boolean`, `null`|||
1037-
|**extensions**||||
1037+
|**error\_message**|`boolean`|Whether to redact the error message in subgraph errors. The default is `true`.<br/><br/>This field can be set to `false`, in order to disable error masking, by setting the `DISABLE_SUBGRAPH_ERROR_MASKING=true` environment variable.<br/>Default: `true`<br/>||
1038+
|**extensions**||Whether to redact the `extensions` in errors.<br/><br/>You may pick the execution mode by setting `mode: allow` or `mode: deny`.<br/>Note: only root-level fields are supported.<br/>||
10381039

1040+
**Additional Properties:** not allowed
10391041
**Example**
10401042

10411043
```yaml
10421044
error_message: true
1043-
extensions: null
10441045
10451046
```
10461047

10471048
<a name="error_maskingsubgraphs"></a>
10481049
### error\_masking\.subgraphs: object,null
10491050

1051+
The error masking configuration for individual subgraphs.
1052+
Any configuration field that will be specified here, will override the configuration in `all`.
1053+
1054+
10501055
**Additional Properties**
10511056

10521057
|Name|Type|Description|Required|
@@ -1060,9 +1065,10 @@ extensions: null
10601065

10611066
|Name|Type|Description|Required|
10621067
|----|----|-----------|--------|
1063-
|**error\_message**|`boolean`, `null`|||
1064-
|**extensions**||||
1068+
|**error\_message**|`boolean`, `null`|Whether to redact the `error_message` in errors, for that specific subgraph.<br/><br/>Configuring this will override the global `all.error_message` setting.<br/>||
1069+
|**extensions**||Whether to redact the `extensions` in errors, for that specific subgraph.<br/>Configuring this will override the global `all.extensions` setting.<br/><br/>You may pick the execution mode by setting `mode: allow` or `mode: deny`.<br/>Note: only root-level fields are supported.<br/>||
10651070

1071+
**Additional Properties:** not allowed
10661072
**Example**
10671073

10681074
```yaml

e2e/configs/env_vars.router.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ headers:
1313
- insert:
1414
name: "x-router-env"
1515
expression: env("ROUTER_ENV_HEADER", "default")
16+
error_masking:
17+
all:
18+
error_message: false

e2e/configs/timeout_per_subgraph_dynamic.router.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ traffic_shaping:
1616
} else {
1717
.default
1818
}
19+
error_masking:
20+
all:
21+
error_message: false

e2e/src/circuit_breaker.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ mod circuit_breaker_e2e_tests {
8686
supergraph:
8787
source: file
8888
path: supergraph.graphql
89+
error_masking:
90+
all:
91+
error_message: false
8992
traffic_shaping:
9093
all:
9194
request_timeout: 200ms
@@ -246,6 +249,9 @@ mod circuit_breaker_e2e_tests {
246249
supergraph:
247250
source: file
248251
path: supergraph.graphql
252+
error_masking:
253+
all:
254+
error_message: false
249255
traffic_shaping:
250256
all:
251257
circuit_breaker:
@@ -336,6 +342,9 @@ mod circuit_breaker_e2e_tests {
336342
supergraph:
337343
source: file
338344
path: supergraph.graphql
345+
error_masking:
346+
all:
347+
error_message: false
339348
traffic_shaping:
340349
all:
341350
circuit_breaker:
@@ -443,6 +452,9 @@ mod circuit_breaker_e2e_tests {
443452
supergraph:
444453
source: file
445454
path: supergraph.graphql
455+
error_masking:
456+
all:
457+
error_message: false
446458
traffic_shaping:
447459
all:
448460
circuit_breaker:
@@ -540,6 +552,9 @@ mod circuit_breaker_e2e_tests {
540552
supergraph:
541553
source: file
542554
path: supergraph.graphql
555+
error_masking:
556+
all:
557+
error_message: false
543558
traffic_shaping:
544559
all:
545560
circuit_breaker:
@@ -608,6 +623,9 @@ mod circuit_breaker_e2e_tests {
608623
supergraph:
609624
source: file
610625
path: supergraph.graphql
626+
error_masking:
627+
all:
628+
error_message: false
611629
traffic_shaping:
612630
all:
613631
circuit_breaker:
@@ -671,6 +689,9 @@ mod circuit_breaker_e2e_tests {
671689
supergraph:
672690
source: file
673691
path: supergraph.graphql
692+
error_masking:
693+
all:
694+
error_message: false
674695
traffic_shaping:
675696
all:
676697
circuit_breaker:
@@ -740,6 +761,9 @@ mod circuit_breaker_e2e_tests {
740761
supergraph:
741762
source: file
742763
path: supergraph.graphql
764+
error_masking:
765+
all:
766+
error_message: false
743767
traffic_shaping:
744768
all:
745769
request_timeout: 500ms
@@ -1332,6 +1356,9 @@ mod circuit_breaker_e2e_tests {
13321356
supergraph:
13331357
source: file
13341358
path: supergraph.graphql
1359+
error_masking:
1360+
all:
1361+
error_message: false
13351362
traffic_shaping:
13361363
all:
13371364
circuit_breaker:
@@ -1408,6 +1435,9 @@ mod circuit_breaker_e2e_tests {
14081435
supergraph:
14091436
source: file
14101437
path: supergraph.graphql
1438+
error_masking:
1439+
all:
1440+
error_message: false
14111441
traffic_shaping:
14121442
all:
14131443
circuit_breaker:
@@ -1490,6 +1520,9 @@ mod circuit_breaker_e2e_tests {
14901520
supergraph:
14911521
source: file
14921522
path: supergraph.graphql
1523+
error_masking:
1524+
all:
1525+
error_message: false
14931526
traffic_shaping:
14941527
all:
14951528
circuit_breaker:
@@ -1579,6 +1612,9 @@ mod circuit_breaker_e2e_tests {
15791612
supergraph:
15801613
source: file
15811614
path: supergraph.graphql
1615+
error_masking:
1616+
all:
1617+
error_message: false
15821618
traffic_shaping:
15831619
all:
15841620
circuit_breaker:
@@ -1755,6 +1791,9 @@ mod circuit_breaker_e2e_tests {
17551791
supergraph:
17561792
source: file
17571793
path: supergraph.graphql
1794+
error_masking:
1795+
all:
1796+
error_message: false
17581797
traffic_shaping:
17591798
all:
17601799
circuit_breaker:

e2e/src/demand_control/subgraph_budgets.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ mod subgraph_budgets_tests {
1212
supergraph:
1313
source: file
1414
path: supergraph.graphql
15+
error_masking:
16+
all:
17+
error_message: false
1518
demand_control:
1619
enabled: true
1720
operation_cost:

0 commit comments

Comments
 (0)