Skip to content

Commit 6c48c0d

Browse files
committed
api,test: reject CR/LF/NUL in PathRegex hostname rewrite fields
The substitution of a PathRegex hostname rewrite becomes the value of the upstream Host header, which Envoy's proto constrains via well_known_regex HTTP_HEADER_VALUE (^[^\x00\n\r]*$). Sending NUL/CR/LF characters causes an xDS NACK at config load instead of a clear admission-time error. Add a CRD pattern constraint (^[^\r\n\x00]*$) to the pattern and substitution fields of HostnamePathRegexRewrite so the value is rejected at kubectl apply, mirroring the fix in #8862, and regenerate the CRDs and API reference docs. Add CEL-suite cases covering control characters in both fields. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com>
1 parent 3fd95e2 commit 6c48c0d

5 files changed

Lines changed: 64 additions & 2 deletions

File tree

api/v1alpha1/httproutefilter_types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,22 @@ type ReplaceRegexMatch struct {
147147
type HostnamePathRegexRewrite struct {
148148
// Pattern matches a regular expression against the value of the HTTP Path. The regex string must
149149
// adhere to the syntax documented in https://github.com/google/re2/wiki/Syntax.
150+
//
151+
// The value must not contain the NUL, CR, or LF characters, which are meaningless in a path-matching
152+
// regex and are rejected for consistency with the constraints on the resulting Host header.
150153
// +kubebuilder:validation:MinLength=1
154+
// +kubebuilder:validation:Pattern=`^[^\r\n\x00]*$`
151155
Pattern string `json:"pattern"`
152156
// Substitution is an expression that replaces the matched portion. The expression may include numbered
153157
// capture groups that adhere to syntax documented in https://github.com/google/re2/wiki/Syntax.
154158
//
155159
// The resulting value is used as the upstream Host header and should be constrained to a valid
156160
// DNS hostname by using explicit regex capture groups in Pattern.
161+
//
162+
// The value must not contain the NUL, CR, or LF characters, which are rejected by the Envoy proto
163+
// (well_known_regex HTTP_HEADER_VALUE) for the rewritten Host header.
157164
// +kubebuilder:validation:MinLength=1
165+
// +kubebuilder:validation:Pattern=`^[^\r\n\x00]*$`
158166
Substitution string `json:"substitution"`
159167
}
160168

charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_httproutefilters.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,11 @@ spec:
439439
description: |-
440440
Pattern matches a regular expression against the value of the HTTP Path. The regex string must
441441
adhere to the syntax documented in https://github.com/google/re2/wiki/Syntax.
442+
443+
The value must not contain the NUL, CR, or LF characters, which are meaningless in a path-matching
444+
regex and are rejected for consistency with the constraints on the resulting Host header.
442445
minLength: 1
446+
pattern: ^[^\r\n\x00]*$
443447
type: string
444448
substitution:
445449
description: |-
@@ -448,7 +452,11 @@ spec:
448452
449453
The resulting value is used as the upstream Host header and should be constrained to a valid
450454
DNS hostname by using explicit regex capture groups in Pattern.
455+
456+
The value must not contain the NUL, CR, or LF characters, which are rejected by the Envoy proto
457+
(well_known_regex HTTP_HEADER_VALUE) for the rewritten Host header.
451458
minLength: 1
459+
pattern: ^[^\r\n\x00]*$
452460
type: string
453461
required:
454462
- pattern

charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,11 @@ spec:
438438
description: |-
439439
Pattern matches a regular expression against the value of the HTTP Path. The regex string must
440440
adhere to the syntax documented in https://github.com/google/re2/wiki/Syntax.
441+
442+
The value must not contain the NUL, CR, or LF characters, which are meaningless in a path-matching
443+
regex and are rejected for consistency with the constraints on the resulting Host header.
441444
minLength: 1
445+
pattern: ^[^\r\n\x00]*$
442446
type: string
443447
substitution:
444448
description: |-
@@ -447,7 +451,11 @@ spec:
447451
448452
The resulting value is used as the upstream Host header and should be constrained to a valid
449453
DNS hostname by using explicit regex capture groups in Pattern.
454+
455+
The value must not contain the NUL, CR, or LF characters, which are rejected by the Envoy proto
456+
(well_known_regex HTTP_HEADER_VALUE) for the rewritten Host header.
450457
minLength: 1
458+
pattern: ^[^\r\n\x00]*$
451459
type: string
452460
required:
453461
- pattern

site/content/en/latest/api/extension_types.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3424,8 +3424,8 @@ _Appears in:_
34243424

34253425
| Field | Type | Required | Default | Description |
34263426
| --- | --- | --- | --- | --- |
3427-
| `pattern` | _string_ | true | | Pattern matches a regular expression against the value of the HTTP Path. The regex string must<br />adhere to the syntax documented in https://github.com/google/re2/wiki/Syntax. |
3428-
| `substitution` | _string_ | true | | Substitution is an expression that replaces the matched portion. The expression may include numbered<br />capture groups that adhere to syntax documented in https://github.com/google/re2/wiki/Syntax.<br />The resulting value is used as the upstream Host header and should be constrained to a valid<br />DNS hostname by using explicit regex capture groups in Pattern. |
3427+
| `pattern` | _string_ | true | | Pattern matches a regular expression against the value of the HTTP Path. The regex string must<br />adhere to the syntax documented in https://github.com/google/re2/wiki/Syntax.<br />The value must not contain the NUL, CR, or LF characters, which are meaningless in a path-matching<br />regex and are rejected for consistency with the constraints on the resulting Host header. |
3428+
| `substitution` | _string_ | true | | Substitution is an expression that replaces the matched portion. The expression may include numbered<br />capture groups that adhere to syntax documented in https://github.com/google/re2/wiki/Syntax.<br />The resulting value is used as the upstream Host header and should be constrained to a valid<br />DNS hostname by using explicit regex capture groups in Pattern.<br />The value must not contain the NUL, CR, or LF characters, which are rejected by the Envoy proto<br />(well_known_regex HTTP_HEADER_VALUE) for the rewritten Host header. |
34293429

34303430

34313431
#### IPEndpoint

test/cel-validation/httproutefilter_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,44 @@ func TestHTTPRouteFilter(t *testing.T) {
132132
},
133133
wantErrors: []string{},
134134
},
135+
{
136+
desc: "invalid PathRegex substitution with control characters",
137+
mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) {
138+
httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{
139+
URLRewrite: &egv1a1.HTTPURLRewriteFilter{
140+
Hostname: &egv1a1.HTTPHostnameModifier{
141+
Type: egv1a1.PathRegexHTTPHostnameModifier,
142+
PathRegex: &egv1a1.HostnamePathRegexRewrite{
143+
Pattern: "^/node/([0-9]+)/api.*",
144+
Substitution: "backend-\\1.service\r\n.local",
145+
},
146+
},
147+
},
148+
}
149+
},
150+
wantErrors: []string{
151+
"spec.urlRewrite.hostname.pathRegex.substitution in body should match '^[^\\r\\n\\x00]*$'",
152+
},
153+
},
154+
{
155+
desc: "invalid PathRegex pattern with control characters",
156+
mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) {
157+
httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{
158+
URLRewrite: &egv1a1.HTTPURLRewriteFilter{
159+
Hostname: &egv1a1.HTTPHostnameModifier{
160+
Type: egv1a1.PathRegexHTTPHostnameModifier,
161+
PathRegex: &egv1a1.HostnamePathRegexRewrite{
162+
Pattern: "^/node/\x00([0-9]+)/api.*",
163+
Substitution: "backend-\\1.service.namespace.svc.cluster.local",
164+
},
165+
},
166+
},
167+
}
168+
},
169+
wantErrors: []string{
170+
"spec.urlRewrite.hostname.pathRegex.pattern in body should match '^[^\\r\\n\\x00]*$'",
171+
},
172+
},
135173
{
136174
desc: "Valid appendXForwardedHost false",
137175
mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) {

0 commit comments

Comments
 (0)