[api] [fuzz] fix many header related config fuzz bugs (#10093)

This change includes validations on header names and values that appear in config fields. This prevents setting header keys/values with invalid characters in config fields, which pops up as ASSERT failures when converting to HeaderString values.

For reference, the well_known_regex for the header names and values do the following:
- HTTP_HEADER_NAME: whitelists alphanums and the whitelisted tokens (`!#$%&'*+-.^_|~``) in https://tools.ietf.org/html/rfc7230#section-3.2
- HTTP_HEADER_VALUE: blacklists control characters except SPC and TAB. Purposely meant to be permissive, and blacklist problems like nulls

Changes in `base.proto`
* Invalid headers to match (from [`HeaderValue`](88d3556981/api/envoy/api/v2/core/base.proto (L234)) proto)
  - `clusterfuzz-testcase-minimized-route_fuzz_test-5635252339343360`

Changes in `route_components.proto`:
* [domains](88d3556981/api/envoy/api/v2/route/route_components.proto (L75)) with invalid control characters in `VirtualHost` config
  - `clusterfuzz-testcase-minimized-route_fuzz_test-5699465522970624`
* Invalid [header_name](88d3556981/api/envoy/api/v2/route/route_components.proto (L604)) in hash policy
  - `clusterfuzz-testcase-minimized-route_fuzz_test-5634743613259776`
* Invalid control characters in [upgrade_type](88d3556981/api/envoy/api/v2/route/route_components.proto (L698)) header
  - `clusterfuzz-testcase-minimized-route_fuzz_test-5750746072481792`
* `RateLimit` message has a [header_name](88d3556981/api/envoy/api/v2/route/route_components.proto (L1273)) field
  - `clusterfuzz-testcase-minimized-route_fuzz_test-5206842068697088`
* Invalid [response_headers_to_add](88d3556981/api/envoy/api/v2/route/route_components.proto (L113)) in
  - `clusterfuzz-testcase-minimized-route_fuzz_test-4592245302362112`
* The [cluster_header](88d3556981/api/envoy/api/v2/route/route_components.proto (L723)) change came up in a fuzz bug, but after that was fixed, the code ran in to another deeper issue to be fixed.
* The [name](bbdc33e537/api/envoy/config/route/v3/route_components.proto (L1381)) in `HeaderMatcher` message
  - `clusterfuzz-testcase-minimized-route_fuzz_test-5635252339343360`

Changes in `custom_tag.proto`
* `CustomTag` has a [name](88d3556981/api/envoy/type/tracing/v3/custom_tag.proto (L51))
  - `clusterfuzz-testcase-minimized-route_fuzz_test-5661762636742656`

Changes in fuzz tests:
* `conn_manager_impl_fuzz_test` that fails on invalid characters in an authority header. this is not a config related change, just handled by replaced the invalid character in the fuzz test.
   - `clusterfuzz-testcase-minimized-conn_manager_impl_fuzz_test-5714279517126656`
* `route_fuzz_test` was cleaned up now that a lot of the processing was moved to config.

Fixes:
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=19923
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=16143 (most of)

Signed-off-by: Asra Ali <asraa@google.com>

Mirrored from https://github.com/envoyproxy/envoy @ 855b2a359a7133fd559ddbd757be4c0963cd8a73
master-ci-test
data-plane-api(CircleCI) 5 years ago
parent 35d8df254d
commit bb2dc33477
  1. 4
      bazel/repository_locations.bzl
  2. 7
      envoy/api/v2/core/base.proto
  3. 9
      envoy/api/v2/route.proto
  4. 20
      envoy/api/v2/route/route_components.proto
  5. 7
      envoy/config/core/v3/base.proto
  6. 9
      envoy/config/route/v3/route.proto
  7. 20
      envoy/config/route/v3/route_components.proto
  8. 2
      envoy/type/tracing/v2/custom_tag.proto
  9. 2
      envoy/type/tracing/v3/custom_tag.proto

@ -4,8 +4,8 @@ BAZEL_SKYLIB_SHA256 = "1dde365491125a3db70731e25658dfdd3bc5dbdfd11b840b3e987ecf0
OPENCENSUS_PROTO_GIT_SHA = "be218fb6bd674af7519b1850cdf8410d8cbd48e8" # Dec 20, 2019
OPENCENSUS_PROTO_SHA256 = "e3bbdc94375e86c0edfb2fc5851507e08a3f26ee725ffff7c5c0e73264bdfcde"
PGV_GIT_SHA = "973ea075fe989fd7878619845d1e532e5bfe7115" # Dec 26, 2019
PGV_SHA256 = "d1cd20dd2cae953d6ccd09db6926fe67d0d2231412666c9b4ac5f159011f14a6"
PGV_GIT_SHA = "61843aea0c3ca81fe7a558caf75fa36789a6d16e" # Feb 14, 2020
PGV_SHA256 = "0cdadf1bf786fcd05944831bd23bfcdb15c7c8940405c476696c9560fb039e26"
GOOGLEAPIS_GIT_SHA = "82944da21578a53b74e547774cf62ed31a05b841" # Dec 2, 2019
GOOGLEAPIS_SHA = "a45019af4d3290f02eaeb1ce10990166978c807cb33a9692141a076ba46d1405"

@ -233,14 +233,17 @@ message RuntimeFeatureFlag {
// Header name/value pair.
message HeaderValue {
// Header name.
string key = 1 [(validate.rules).string = {min_bytes: 1 max_bytes: 16384}];
string key = 1 [
(validate.rules).string = {min_bytes: 1 max_bytes: 16384 well_known_regex: HTTP_HEADER_NAME}
];
// Header value.
//
// The same :ref:`format specifier <config_access_log_format>` as used for
// :ref:`HTTP access logging <config_access_log>` applies here, however
// unknown header values are replaced with the empty string instead of `-`.
string value = 2 [(validate.rules).string = {max_bytes: 16384}];
string value = 2
[(validate.rules).string = {max_bytes: 16384 well_known_regex: HTTP_HEADER_VALUE}];
}
// Header name/value pair plus option to control append behavior.

@ -43,7 +43,8 @@ message RouteConfiguration {
// will consider to be internal only. If they are found on external requests they will be cleaned
// prior to filter invocation. See :ref:`config_http_conn_man_headers_x-envoy-internal` for more
// information.
repeated string internal_only_headers = 3;
repeated string internal_only_headers = 3
[(validate.rules).repeated = {items {string {well_known_regex: HTTP_HEADER_NAME}}}];
// Specifies a list of HTTP headers that should be added to each response that
// the connection manager encodes. Headers specified at this level are applied
@ -56,7 +57,8 @@ message RouteConfiguration {
// Specifies a list of HTTP headers that should be removed from each response
// that the connection manager encodes.
repeated string response_headers_to_remove = 5;
repeated string response_headers_to_remove = 5
[(validate.rules).repeated = {items {string {well_known_regex: HTTP_HEADER_NAME}}}];
// Specifies a list of HTTP headers that should be added to each request
// routed by the HTTP connection manager. Headers specified at this level are
@ -69,7 +71,8 @@ message RouteConfiguration {
// Specifies a list of HTTP headers that should be removed from each request
// routed by the HTTP connection manager.
repeated string request_headers_to_remove = 8;
repeated string request_headers_to_remove = 8
[(validate.rules).repeated = {items {string {well_known_regex: HTTP_HEADER_NAME}}}];
// By default, headers that should be added/removed are evaluated from most to least specific:
//

@ -69,7 +69,12 @@ message VirtualHost {
// The longest wildcards match first.
// Only a single virtual host in the entire route configuration can match on ``*``. A domain
// must be unique across all virtual hosts or the config will fail to load.
repeated string domains = 2 [(validate.rules).repeated = {min_items: 1}];
//
// Domains cannot contain control characters. This is validated by the well_known_regex HTTP_HEADER_VALUE.
repeated string domains = 2 [(validate.rules).repeated = {
min_items: 1
items {string {well_known_regex: HTTP_HEADER_VALUE}}
}];
// The list of routes that will be matched, in order, for incoming requests.
// The first route that matches will be used.
@ -597,7 +602,8 @@ message RouteAction {
message Header {
// The name of the request header that will be used to obtain the hash
// key. If the request header is not present, no hash will be produced.
string header_name = 1 [(validate.rules).string = {min_bytes: 1}];
string header_name = 1
[(validate.rules).string = {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME}];
}
// Envoy supports two types of cookie affinity:
@ -690,7 +696,7 @@ message RouteAction {
// The case-insensitive name of this upgrade, e.g. "websocket".
// For each upgrade type present in upgrade_configs, requests with
// Upgrade: [upgrade_type] will be proxied upstream.
string upgrade_type = 1;
string upgrade_type = 1 [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE}];
// Determines if upgrades are available on this route. Defaults to true.
google.protobuf.BoolValue enabled = 2;
@ -714,7 +720,8 @@ message RouteAction {
//
// Internally, Envoy always uses the HTTP/2 *:authority* header to represent the HTTP/1
// *Host* header. Thus, if attempting to match on *Host*, match on *:authority* instead.
string cluster_header = 2 [(validate.rules).string = {min_bytes: 1}];
string cluster_header = 2
[(validate.rules).string = {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME}];
// Multiple upstream clusters can be specified for a given route. The
// request is routed to one of the upstream clusters based on weights
@ -1263,7 +1270,8 @@ message RateLimit {
// The header name to be queried from the request headers. The headers
// value is used to populate the value of the descriptor entry for the
// descriptor_key.
string header_name = 1 [(validate.rules).string = {min_bytes: 1}];
string header_name = 1
[(validate.rules).string = {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME}];
// The key to use in the descriptor entry.
string descriptor_key = 2 [(validate.rules).string = {min_bytes: 1}];
@ -1384,7 +1392,7 @@ message HeaderMatcher {
reserved 2, 3;
// Specifies the name of the header in the request.
string name = 1 [(validate.rules).string = {min_bytes: 1}];
string name = 1 [(validate.rules).string = {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME}];
// Specifies how the header match will be performed to route the request.
oneof header_match_specifier {

@ -248,14 +248,17 @@ message HeaderValue {
option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.core.HeaderValue";
// Header name.
string key = 1 [(validate.rules).string = {min_bytes: 1 max_bytes: 16384}];
string key = 1 [
(validate.rules).string = {min_bytes: 1 max_bytes: 16384 well_known_regex: HTTP_HEADER_NAME}
];
// Header value.
//
// The same :ref:`format specifier <config_access_log_format>` as used for
// :ref:`HTTP access logging <config_access_log>` applies here, however
// unknown header values are replaced with the empty string instead of `-`.
string value = 2 [(validate.rules).string = {max_bytes: 16384}];
string value = 2
[(validate.rules).string = {max_bytes: 16384 well_known_regex: HTTP_HEADER_VALUE}];
}
// Header name/value pair plus option to control append behavior.

@ -45,7 +45,8 @@ message RouteConfiguration {
// will consider to be internal only. If they are found on external requests they will be cleaned
// prior to filter invocation. See :ref:`config_http_conn_man_headers_x-envoy-internal` for more
// information.
repeated string internal_only_headers = 3;
repeated string internal_only_headers = 3
[(validate.rules).repeated = {items {string {well_known_regex: HTTP_HEADER_NAME}}}];
// Specifies a list of HTTP headers that should be added to each response that
// the connection manager encodes. Headers specified at this level are applied
@ -58,7 +59,8 @@ message RouteConfiguration {
// Specifies a list of HTTP headers that should be removed from each response
// that the connection manager encodes.
repeated string response_headers_to_remove = 5;
repeated string response_headers_to_remove = 5
[(validate.rules).repeated = {items {string {well_known_regex: HTTP_HEADER_NAME}}}];
// Specifies a list of HTTP headers that should be added to each request
// routed by the HTTP connection manager. Headers specified at this level are
@ -71,7 +73,8 @@ message RouteConfiguration {
// Specifies a list of HTTP headers that should be removed from each request
// routed by the HTTP connection manager.
repeated string request_headers_to_remove = 8;
repeated string request_headers_to_remove = 8
[(validate.rules).repeated = {items {string {well_known_regex: HTTP_HEADER_NAME}}}];
// By default, headers that should be added/removed are evaluated from most to least specific:
//

@ -73,7 +73,12 @@ message VirtualHost {
// The longest wildcards match first.
// Only a single virtual host in the entire route configuration can match on ``*``. A domain
// must be unique across all virtual hosts or the config will fail to load.
repeated string domains = 2 [(validate.rules).repeated = {min_items: 1}];
//
// Domains cannot contain control characters. This is validated by the well_known_regex HTTP_HEADER_VALUE.
repeated string domains = 2 [(validate.rules).repeated = {
min_items: 1
items {string {well_known_regex: HTTP_HEADER_VALUE}}
}];
// The list of routes that will be matched, in order, for incoming requests.
// The first route that matches will be used.
@ -556,7 +561,8 @@ message RouteAction {
// The name of the request header that will be used to obtain the hash
// key. If the request header is not present, no hash will be produced.
string header_name = 1 [(validate.rules).string = {min_bytes: 1}];
string header_name = 1
[(validate.rules).string = {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME}];
}
// Envoy supports two types of cookie affinity:
@ -661,7 +667,7 @@ message RouteAction {
// The case-insensitive name of this upgrade, e.g. "websocket".
// For each upgrade type present in upgrade_configs, requests with
// Upgrade: [upgrade_type] will be proxied upstream.
string upgrade_type = 1;
string upgrade_type = 1 [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE}];
// Determines if upgrades are available on this route. Defaults to true.
google.protobuf.BoolValue enabled = 2;
@ -687,7 +693,8 @@ message RouteAction {
//
// Internally, Envoy always uses the HTTP/2 *:authority* header to represent the HTTP/1
// *Host* header. Thus, if attempting to match on *Host*, match on *:authority* instead.
string cluster_header = 2 [(validate.rules).string = {min_bytes: 1}];
string cluster_header = 2
[(validate.rules).string = {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME}];
// Multiple upstream clusters can be specified for a given route. The
// request is routed to one of the upstream clusters based on weights
@ -1245,7 +1252,8 @@ message RateLimit {
// The header name to be queried from the request headers. The headers
// value is used to populate the value of the descriptor entry for the
// descriptor_key.
string header_name = 1 [(validate.rules).string = {min_bytes: 1}];
string header_name = 1
[(validate.rules).string = {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME}];
// The key to use in the descriptor entry.
string descriptor_key = 2 [(validate.rules).string = {min_bytes: 1}];
@ -1378,7 +1386,7 @@ message HeaderMatcher {
reserved "regex_match";
// Specifies the name of the header in the request.
string name = 1 [(validate.rules).string = {min_bytes: 1}];
string name = 1 [(validate.rules).string = {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME}];
// Specifies how the header match will be performed to route the request.
oneof header_match_specifier {

@ -35,7 +35,7 @@ message CustomTag {
// Header type custom tag with header name and default value.
message Header {
// Header name to obtain the value to populate the tag value.
string name = 1 [(validate.rules).string = {min_bytes: 1}];
string name = 1 [(validate.rules).string = {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME}];
// When the header does not exist,
// the tag value will be populated with this default value if specified,

@ -48,7 +48,7 @@ message CustomTag {
"envoy.type.tracing.v2.CustomTag.Header";
// Header name to obtain the value to populate the tag value.
string name = 1 [(validate.rules).string = {min_bytes: 1}];
string name = 1 [(validate.rules).string = {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME}];
// When the header does not exist,
// the tag value will be populated with this default value if specified,

Loading…
Cancel
Save