Re-adds some minimal validation to NoopHeaderValidator.

The minimal set of checks necessary for Envoy fuzz tests to succeed are:
* header names and values may not contain \0, \r or \n
* requests and responses must contain the minimal required set of pseudo-headers

PiperOrigin-RevId: 587014516
diff --git a/quiche/http2/adapter/noop_header_validator.cc b/quiche/http2/adapter/noop_header_validator.cc
index f39342d..b687df5 100644
--- a/quiche/http2/adapter/noop_header_validator.cc
+++ b/quiche/http2/adapter/noop_header_validator.cc
@@ -1,20 +1,56 @@
 #include "quiche/http2/adapter/noop_header_validator.h"
 
 #include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "quiche/http2/adapter/header_validator_base.h"
 #include "quiche/common/platform/api/quiche_logging.h"
 
 namespace http2 {
 namespace adapter {
 
+namespace {
+
+constexpr absl::string_view kInvalidChars("\0\r\n", 3);
+
+}  // namespace
+
+void NoopHeaderValidator::StartHeaderBlock() {
+  HeaderValidatorBase::StartHeaderBlock();
+  has_method_ = false;
+  has_scheme_ = false;
+  has_path_ = false;
+}
+
 HeaderValidatorBase::HeaderStatus NoopHeaderValidator::ValidateSingleHeader(
     absl::string_view key, absl::string_view value) {
+  if (key.empty()) {
+    return HEADER_FIELD_INVALID;
+  }
+  if (key.find_first_of(kInvalidChars) != absl::string_view::npos ||
+      value.find_first_of(kInvalidChars) != absl::string_view::npos) {
+    return HEADER_FIELD_INVALID;
+  }
+  if (key[0] != ':') {
+    return HEADER_OK;
+  }
   if (key == ":status") {
     status_ = std::string(value);
+  } else if (key == ":method") {
+    has_method_ = true;
+  } else if (key == ":scheme") {
+    has_scheme_ = true;
+  } else if (key == ":path") {
+    has_path_ = true;
   }
   return HEADER_OK;
 }
 
-bool NoopHeaderValidator::FinishHeaderBlock(HeaderType /* type */) {
+bool NoopHeaderValidator::FinishHeaderBlock(HeaderType type) {
+  if (type == HeaderType::REQUEST) {
+    return has_method_ && has_scheme_ && has_path_;
+  } else if (type == HeaderType::RESPONSE || type == HeaderType::RESPONSE_100) {
+    return !status_.empty();
+  }
   return true;
 }
 
diff --git a/quiche/http2/adapter/noop_header_validator.h b/quiche/http2/adapter/noop_header_validator.h
index f6b95e9..ef78027 100644
--- a/quiche/http2/adapter/noop_header_validator.h
+++ b/quiche/http2/adapter/noop_header_validator.h
@@ -13,10 +13,16 @@
  public:
   NoopHeaderValidator() = default;
 
+  void StartHeaderBlock() override;
   HeaderStatus ValidateSingleHeader(absl::string_view key,
                                     absl::string_view value) override;
 
   bool FinishHeaderBlock(HeaderType type) override;
+
+ private:
+  bool has_method_ = false;
+  bool has_scheme_ = false;
+  bool has_path_ = false;
 };
 
 }  // namespace adapter
diff --git a/quiche/http2/adapter/noop_header_validator_test.cc b/quiche/http2/adapter/noop_header_validator_test.cc
index 65cc763..a6dc041 100644
--- a/quiche/http2/adapter/noop_header_validator_test.cc
+++ b/quiche/http2/adapter/noop_header_validator_test.cc
@@ -5,6 +5,7 @@
 #include <vector>
 
 #include "absl/strings/str_cat.h"
+#include "quiche/http2/adapter/header_validator_base.h"
 #include "quiche/common/platform/api/quiche_test.h"
 
 namespace http2 {
@@ -19,11 +20,20 @@
                                                   {":path", "/foo"},
                                                   {":scheme", "https"}};
 
+TEST(NoopHeaderValidatorTest, EmptyHeaderBlock) {
+  NoopHeaderValidator v;
+  v.StartHeaderBlock();
+  EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST));
+
+  v.StartHeaderBlock();
+  EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::RESPONSE));
+}
+
 TEST(NoopHeaderValidatorTest, HeaderNameEmpty) {
   NoopHeaderValidator v;
   NoopHeaderValidator::HeaderStatus status =
       v.ValidateSingleHeader("", "value");
-  EXPECT_EQ(NoopHeaderValidator::HEADER_OK, status);
+  EXPECT_EQ(NoopHeaderValidator::HEADER_FIELD_INVALID, status);
 }
 
 TEST(NoopHeaderValidatorTest, HeaderValueEmpty) {
@@ -44,27 +54,29 @@
   EXPECT_EQ(NoopHeaderValidator::HEADER_OK, status);
 }
 
-TEST(NoopHeaderValidatorTest, AnyNameCharIsValid) {
+TEST(NoopHeaderValidatorTest, FewInvalidNameChars) {
   NoopHeaderValidator v;
   char pseudo_name[] = ":met hod";
   char name[] = "na me";
   for (int i = std::numeric_limits<char>::min();
        i < std::numeric_limits<char>::max(); ++i) {
     char c = static_cast<char>(i);
+    const HeaderValidatorBase::HeaderStatus expected_status =
+        (c == '\0' || c == '\r' || c == '\n')
+            ? HeaderValidatorBase::HEADER_FIELD_INVALID
+            : HeaderValidatorBase::HEADER_OK;
     // Test a pseudo-header name with this char.
     pseudo_name[3] = c;
     auto sv = absl::string_view(pseudo_name, 8);
-    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
-              v.ValidateSingleHeader(sv, "value"));
+    EXPECT_EQ(expected_status, v.ValidateSingleHeader(sv, "value"));
     // Test a regular header name with this char.
     name[2] = c;
     sv = absl::string_view(name, 5);
-    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
-              v.ValidateSingleHeader(sv, "value"));
+    EXPECT_EQ(expected_status, v.ValidateSingleHeader(sv, "value"));
   }
 }
 
-TEST(NoopHeaderValidatorTest, AnyValueCharIsValid) {
+TEST(NoopHeaderValidatorTest, FewInvalidValueChars) {
   NoopHeaderValidator v;
   char value[] = "val ue";
   for (int i = std::numeric_limits<char>::min();
@@ -72,8 +84,11 @@
     char c = static_cast<char>(i);
     value[3] = c;
     auto sv = absl::string_view(value, 6);
-    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
-              v.ValidateSingleHeader("name", sv));
+    const HeaderValidatorBase::HeaderStatus expected_status =
+        (c == '\0' || c == '\r' || c == '\n')
+            ? HeaderValidatorBase::HEADER_FIELD_INVALID
+            : HeaderValidatorBase::HEADER_OK;
+    EXPECT_EQ(expected_status, v.ValidateSingleHeader("name", sv));
   }
 }
 
@@ -103,18 +118,21 @@
   }
 }
 
-TEST(NoopHeaderValidatorTest, AnyAuthorityCharIsValid) {
+TEST(NoopHeaderValidatorTest, FewInvalidAuthorityChars) {
   char value[] = "ho st.example.com";
   for (int i = std::numeric_limits<char>::min();
        i < std::numeric_limits<char>::max(); ++i) {
     char c = static_cast<char>(i);
     value[2] = c;
     auto sv = absl::string_view(value, 17);
+    const HeaderValidatorBase::HeaderStatus expected_status =
+        (c == '\0' || c == '\r' || c == '\n')
+            ? HeaderValidatorBase::HEADER_FIELD_INVALID
+            : HeaderValidatorBase::HEADER_OK;
     for (absl::string_view key : {":authority", "host"}) {
       NoopHeaderValidator v;
       v.StartHeaderBlock();
-      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
-                v.ValidateSingleHeader(key, sv));
+      EXPECT_EQ(expected_status, v.ValidateSingleHeader(key, sv));
     }
   }
 }
@@ -152,8 +170,13 @@
                   v.ValidateSingleHeader(to_add.first, to_add.second));
       }
     }
-    // Even if a pseudo-header is missing, final validation will succeed.
-    EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST));
+    // If the missing pseudo-header is :authority, final validation will
+    // succeed. Otherwise, it will fail.
+    if (to_skip.first == ":authority") {
+      EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST));
+    } else {
+      EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST));
+    }
   }
 
   // When all pseudo-headers are present, final validation will succeed.
@@ -297,11 +320,11 @@
   NoopHeaderValidator v;
 
   for (HeaderType type : {HeaderType::RESPONSE, HeaderType::RESPONSE_100}) {
-    // When `:status` is missing, validation succeeds.
+    // When `:status` is missing, validation fails.
     v.StartHeaderBlock();
     EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
               v.ValidateSingleHeader("foo", "bar"));
-    EXPECT_TRUE(v.FinishHeaderBlock(type));
+    EXPECT_FALSE(v.FinishHeaderBlock(type));
 
     // When all pseudo-headers are present, final validation succeeds.
     v.StartHeaderBlock();