1
#pragma once
2

            
3
#include <functional>
4

            
5
#include "envoy/extensions/http/header_validators/envoy_default/v3/header_validator.pb.h"
6
#include "envoy/http/header_validator.h"
7

            
8
#include "source/common/http/headers.h"
9
#include "source/extensions/http/header_validators/envoy_default/config_overrides.h"
10
#include "source/extensions/http/header_validators/envoy_default/path_normalizer.h"
11

            
12
#include "absl/container/node_hash_map.h"
13

            
14
namespace Envoy {
15
namespace Extensions {
16
namespace Http {
17
namespace HeaderValidators {
18
namespace EnvoyDefault {
19

            
20
/*
21
 * Base class for all HTTP codec header validations. This class has several methods to validate
22
 * headers that are shared across multiple codec versions where the RFC guidance did not change.
23
 */
24
class HeaderValidator {
25
public:
26
  HeaderValidator(
27
      const envoy::extensions::http::header_validators::envoy_default::v3::HeaderValidatorConfig&
28
          config,
29
      ::Envoy::Http::Protocol protocol, ::Envoy::Http::HeaderValidatorStats& stats,
30
      const ConfigOverrides& config_overrides);
31
570
  virtual ~HeaderValidator() = default;
32

            
33
  using HeaderEntryValidationResult = ::Envoy::Http::HeaderValidator::RejectResult;
34
  using HeaderValueValidationResult = ::Envoy::Http::HeaderValidator::RejectResult;
35
  /*
36
   * Validate the :method pseudo header, honoring the restrict_http_methods configuration option.
37
   */
38
  HeaderValueValidationResult validateMethodHeader(const ::Envoy::Http::HeaderString& value);
39

            
40
  /*
41
   * Validate the :status response pseudo header based on the range of valid response statuses.
42
   */
43
  HeaderValueValidationResult validateStatusHeader(const ::Envoy::Http::HeaderString& value);
44

            
45
  /*
46
   * Validate any request or response header name.
47
   */
48
  virtual HeaderEntryValidationResult
49
  validateGenericHeaderName(const ::Envoy::Http::HeaderString& name);
50

            
51
  /*
52
   * Validate any request or response header value.
53
   */
54
  HeaderValueValidationResult validateGenericHeaderValue(const ::Envoy::Http::HeaderString& value);
55

            
56
  /*
57
   * Validate the Content-Length request and response header as a whole number integer. The RFC
58
   * states that multiple Content-Length values are acceptable if they are all the same value.
59
   * However, UHV does not allow multiple values currently because the comma character will be
60
   * rejected. We can add an option to allow multiple Content-Length values in the future if
61
   * needed.
62
   */
63
  HeaderValueValidationResult validateContentLengthHeader(const ::Envoy::Http::HeaderString& value);
64

            
65
  /*
66
   * Validate the :scheme pseudo header.
67
   */
68
  HeaderValueValidationResult validateSchemeHeader(const ::Envoy::Http::HeaderString& value);
69

            
70
  /*
71
   * Validate the Host header or :authority pseudo header. This method does not allow the
72
   * userinfo component (user:pass@host).
73
   */
74
  HeaderValueValidationResult validateHostHeader(const ::Envoy::Http::HeaderString& value);
75

            
76
  /*
77
   * Validate the :path pseudo header. This method only validates that the :path header only
78
   * contains valid characters and does not validate the syntax or form of the path URI.
79
   */
80
  HeaderValueValidationResult
81
  validatePathHeaderCharacters(const ::Envoy::Http::HeaderString& value);
82

            
83
  /*
84
   * Check if the Transfer-Encoding header contains the "chunked" transfer encoding.
85
   */
86
  bool hasChunkedTransferEncoding(const ::Envoy::Http::HeaderString& value);
87

            
88
protected:
89
  /*
90
   * An internal class that stores the result of validating syntax-specific URI hosts.
91
   */
92
  class HostHeaderValidationResult {
93
  public:
94
    using RejectAction = ::Envoy::Http::HeaderValidator::RejectAction;
95
    HostHeaderValidationResult(RejectAction action, absl::string_view details,
96
                               absl::string_view address, absl::string_view port)
97
4998
        : result_(action, details, address, port) {
98
4998
      ENVOY_BUG(action == RejectAction::Accept || !details.empty(),
99
4998
                "Error details must not be empty in case of an error");
100
4998
    }
101

            
102
12
    static HostHeaderValidationResult reject(absl::string_view details) {
103
12
      return {RejectAction::Reject, details, "", ""};
104
12
    }
105

            
106
4986
    static HostHeaderValidationResult success(absl::string_view address, absl::string_view port) {
107
4986
      return {RejectAction::Accept, "", address, port};
108
4986
    }
109

            
110
4998
    bool ok() const { return action() == RejectAction::Accept; }
111

            
112
4998
    RejectAction action() const { return std::get<0>(result_); }
113

            
114
12
    absl::string_view details() const { return std::get<1>(result_); }
115

            
116
    // The address component of the URI path.
117
    absl::string_view address() const { return std::get<2>(result_); }
118

            
119
    // The port component of the URI path, including the leading ":" delimiter.
120
4986
    absl::string_view portAndDelimiter() const { return std::get<3>(result_); }
121

            
122
  private:
123
    std::tuple<RejectAction, std::string, absl::string_view, absl::string_view> result_;
124
  };
125

            
126
  /*
127
   * Validate an IPv6 host header value. The port specifier, if included in the host string, is
128
   * stored in the return details on success.
129
   */
130
  HostHeaderValidationResult validateHostHeaderIPv6(absl::string_view host);
131

            
132
  /*
133
   * Validate a reg-name host header value. The port specifier, if included in the host string, is
134
   * stored in the return details on success.
135
   */
136
  HostHeaderValidationResult validateHostHeaderRegName(absl::string_view host);
137

            
138
  /*
139
   * Validate a header value. The `protocol_specific_header_validators` map contains validation
140
   * function for protocol specific header keys. If the header key is not found in the
141
   * `protocol_specific_header_validators` the header key is checked by calling the
142
   * `validateGenericHeaderName` method (Note that `validateGenericHeaderName` is virtual and has
143
   * different behavior for H/1 and H/2, H/3 validators) and the header value is checked by calling
144
   * the `validateGenericHeaderValue` method.
145
   */
146
  using HeaderValidatorFunction = std::function<HeaderValidator::HeaderValueValidationResult(
147
      const ::Envoy::Http::HeaderString&)>;
148
  using HeaderValidatorMap = absl::node_hash_map<absl::string_view, HeaderValidatorFunction>;
149
  HeaderEntryValidationResult
150
  validateGenericRequestHeaderEntry(const ::Envoy::Http::HeaderString& key,
151
                                    const ::Envoy::Http::HeaderString& value,
152
                                    const HeaderValidatorMap& protocol_specific_header_validators);
153

            
154
  /*
155
   * Common method for validating request or response trailers.
156
   */
157
  ::Envoy::Http::HeaderValidator::ValidationResult
158
  validateTrailers(const ::Envoy::Http::HeaderMap& trailers);
159

            
160
  /**
161
   * Removes headers with underscores in their names iff the headers_with_underscores_action
162
   * config value is DROP. Noop otherwise.
163
   * The REJECT config option for header names with underscores is handled in the
164
   * validateRequestHeaders or validateRequestTrailers methods.
165
   */
166
  void sanitizeHeadersWithUnderscores(::Envoy::Http::HeaderMap& header_map);
167

            
168
  /*
169
   * Validate the :path pseudo header using specific allowed character set.
170
   */
171
  HeaderValueValidationResult
172
  validatePathHeaderCharacterSet(const ::Envoy::Http::HeaderString& value,
173
                                 const std::array<uint32_t, 8>& allowed_path_chracters,
174
                                 const std::array<uint32_t, 8>& allowed_query_fragment_characters);
175

            
176
  // URL-encode additional characters in URL path. This method is called iff
177
  // `envoy.uhv.allow_non_compliant_characters_in_path` is true.
178
  // Encoded characters:
179
  //
180
  // " < > ^ ` { } | TAB space extended-ASCII
181
  // This method is provided for backward compatibility with Envoy's pre header validator
182
  // behavior. See comments in the HeaderValidatorConfigOverrides declaration above for more
183
  // information.
184
  void encodeAdditionalCharactersInPath(::Envoy::Http::RequestHeaderMap& header_map);
185
  /**
186
   * Check if the :path header contains a fragment. If the fragment is found it is stripped from
187
   * the :path.
188
   */
189
  void sanitizePathWithFragment(::Envoy::Http::RequestHeaderMap& header_map);
190

            
191
  /**
192
   * Decode percent-encoded slash characters based on configuration.
193
   */
194
  PathNormalizer::PathNormalizationResult
195
  sanitizeEncodedSlashes(::Envoy::Http::RequestHeaderMap& header_map);
196

            
197
  /**
198
   * Transform URL path according to configuration (i.e. apply path normalization).
199
   */
200
  PathNormalizer::PathNormalizationResult
201
  transformUrlPath(::Envoy::Http::RequestHeaderMap& header_map);
202

            
203
  /**
204
   * Check for presence of %00 sequence based on configuration.
205
   * Reject request if %00 sequence was found.
206
   */
207
  HeaderValueValidationResult
208
  checkForPercent00InUrlPath(const ::Envoy::Http::RequestHeaderMap& header_map);
209

            
210
  const envoy::extensions::http::header_validators::envoy_default::v3::HeaderValidatorConfig
211
      config_;
212
  ::Envoy::Http::Protocol protocol_;
213
  const ConfigOverrides config_overrides_;
214
  const ::Envoy::Http::HeaderValues& header_values_;
215
  ::Envoy::Http::HeaderValidatorStats& stats_;
216
  const PathNormalizer path_normalizer_;
217
};
218

            
219
} // namespace EnvoyDefault
220
} // namespace HeaderValidators
221
} // namespace Http
222
} // namespace Extensions
223
} // namespace Envoy