]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #1658 in SNORT/snort3 from ~THOPETER/snort3:nhttp122 to master
authorMike Stepanek (mstepane) <mstepane@cisco.com>
Mon, 15 Jul 2019 15:23:09 +0000 (11:23 -0400)
committerMike Stepanek (mstepane) <mstepane@cisco.com>
Mon, 15 Jul 2019 15:23:09 +0000 (11:23 -0400)
Squashed commit of the following:

commit db33060f5d83ad0b2a625abd8287df6073469f84
Author: Tom Peters <thopeter@cisco.com>
Date:   Thu Jul 11 13:35:16 2019 -0400

    http_inspect: perf improvements

commit 37f170ddc1320c6d3bb3eff11a80cd2c21bff1c0
Author: Tom Peters <thopeter@cisco.com>
Date:   Fri Jun 7 10:22:43 2019 -0400

    http_inspect: send headers to detection separately

14 files changed:
doc/http_inspect.txt
src/service_inspectors/http_inspect/dev_notes.txt
src/service_inspectors/http_inspect/http_enum.h
src/service_inspectors/http_inspect/http_inspect.cc
src/service_inspectors/http_inspect/http_inspect.h
src/service_inspectors/http_inspect/http_msg_body.cc
src/service_inspectors/http_inspect/http_msg_body.h
src/service_inspectors/http_inspect/http_msg_header.cc
src/service_inspectors/http_inspect/http_msg_header.h
src/service_inspectors/http_inspect/http_msg_section.cc
src/service_inspectors/http_inspect/http_msg_section.h
src/service_inspectors/http_inspect/http_msg_start.h
src/service_inspectors/http_inspect/http_msg_trailer.h
src/service_inspectors/http_inspect/ips_http.cc

index ea995f710e859bf9589d72766954d7ef1b81d664..740934f8fc3edacf4983e0a6852900860cf55b9f 100644 (file)
@@ -493,40 +493,16 @@ Whenever a new URI is available this rule will be evaluated. Nothing
 complicated about that, but suppose we use more than one rule option:
 
     alert tcp any any -> any any ( msg:"combined example"; flow:established,
-    to_server; http_uri; content:"chocolate"; file_data;
-    content:"sinister POST data"; sid:5; rev:1; )
-
-This rule requires both the URI and the request message body. That sounds
-simple until one considers that the message body may be millions of bytes
-long. The headers with the URI may be long gone by that time.
-
-Is this rule going to work or do we need to do something different?
-
-It is helpful to understand when things happen. All the message headers and
-the first few thousand bytes of the body go through detection at the same
-time. Commonly this is about 16K bytes but there are several exceptions and
-there is no guaranteed minimum amount.
-
-That may be all you need. In many cases that will be the entire message. Or
-it may be more than your request_depth/response_depth. Or this rule may
-simply not care what happens after that in a very long message body.
-
-Beyond that the message body will continue to be subdivided into roughly
-16K-byte sections and inspected. But the previous rule will not be able to
-see the URI and hence will not work unless we rewrite it:
-
-    alert tcp any any -> any any ( msg:"URI with_body"; flow:established,
     to_server; http_uri: with_body; content:"chocolate"; file_data;
-    content:"sinister POST data"; sid:5; rev:2; )
+    content:"sinister POST data"; sid:5; rev:1; )
 
 The with_body option to http_uri causes the URI to be made available with
-every body section, not just the first one. These extra inspections have a
-performance cost which is why they are not done automatically. with_body is
-an option to be used when you actually need it.
+the message body. Use with_body for header-related rule options in rules
+that also examine the message body.
 
 The with_trailer option is analogous and causes an earlier message element
-to be made available at the end of the message when the trailers following a
-chunked body arrive.
+to be made available at the end of the message when the trailers following
+chunked body arrive.
 
     alert tcp any any -> any any ( msg:"double content-language";
     flow:established, to_client; http_header: with_trailer, field
@@ -581,33 +557,29 @@ http_header to be searched is the request header.
 Let's put all of this together. There are six opportunities to do
 detection:
 
-1. When the first part of the request message body arrives. The request
-line, all of the headers, and the first part of the body all go through
-detection at the same time. Of course most requests don't have a body. In
-that case the request line and the headers are the whole message and get
-done at the same time.
+1. When the the request headers arrive. The request line and all of the
+headers go through detection at the same time.
 
-2. When subsequent sections of the request message body arrive. If you want
-to combine this with something from the request line or headers you must
-use the with_body option.
+2. When sections of the request message body arrive. If you want to combine
+this with something from the request line or headers you must use the
+with_body option.
 
 3. When the request trailers arrive. If you want to combine this with
 something from the request line or headers you must use the with_trailer
 option.
 
-4. When the first part of the response message body arrives. The status
-line, all of the headers, and the first part of the body all go through
-detection at the same time. These may be combined with elements from the
-request line, request headers, or request trailers. Where ambiguity arises
-use the request option.
+4. When the response headers arrive. The status line and all of the headers
+go through detection at the same time. These may be combined with elements
+from the request line, request headers, or request trailers. Where
+ambiguity arises use the request option.
 
-5. When subsequent sections of the response message body arrive. These may
-be combined with the status line, response headers, request line, request
-headers, or request trailers as described above.
+5. When sections of the response message body arrive. These may be combined
+with the status line, response headers, request line, request headers, or
+request trailers as described above.
 
 6. When the response trailers arrive. Again these may be combined as
 described above.
 
-Message body data can only go through detection at the time it is received.
-Headers may be combined with later items but the body cannot.
+Message body sections can only go through detection at the time they are
+received. Headers may be combined with later items but the body cannot.
 
index 3443b90a16d890cd1147cdfafc034f9d80925df6..c51fdd6f12ecb06e86f3f49bec91f9e49d95eebc 100644 (file)
@@ -105,7 +105,7 @@ URI normalization is performed during HttpUri construction in four steps.
 
 Step 1: Identify the type of URI.
 
-NHI recognizes four types of URI:
+HI recognizes four types of URI:
 
 1. Asterisk: a lone ‘*’ character signifying that the request does not refer to any resource in
 particular. Often used with the OPTIONS method. This is not normalized.
@@ -144,7 +144,7 @@ This allows rules to be written against a normalized whole URI as is done in 2.X
 The procedures for normalizing the individual pieces are mostly identical to 2.X. Some points
 warrant mention:
 
-1. NHI considers it to be normal for reserved characters to be percent encoded and does not
+1. HI considers it to be normal for reserved characters to be percent encoded and does not
 generate an alert. The 119/1 alert is used only for unreserved characters that are found to be
 percent encoded. The ignore_unreserved configuration option allows the user to specify a list of
 unreserved characters that are exempt from this alert.
@@ -214,7 +214,7 @@ line).
 
 This test tool does not implement the feature of being hardened against bad input. If you write a
 badly formatted or improper test case the program may assert or crash. The responsibility is on the
-developer to get it right. Currently that is the best use of resources.
+developer to get it right.
 
 Test input is currently designed for single-threaded operation only.
 
index 94763c1a642ead864ebf04f848a593a8a9ca4c19..eab6e0307d03e293c7c42267b5d3e4c60e84457b 100644 (file)
@@ -106,7 +106,7 @@ enum UriType { URI__NOT_COMPUTE=-14, URI__PROBLEMATIC=-12, URI_ASTERISK = 2, URI
 enum CompressId { CMP_NONE=2, CMP_GZIP, CMP_DEFLATE };
 
 // Message section in which an IPS option provides the buffer
-enum InspectSection { IS_NONE, IS_DETECTION, IS_BODY, IS_TRAILER };
+enum InspectSection { IS_NONE, IS_HEADER, IS_FLEX_HEADER, IS_FIRST_BODY, IS_BODY, IS_TRAILER };
 
 // Part of the URI to be provided
 enum UriComponent { UC_SCHEME = 1, UC_HOST, UC_PORT, UC_PATH, UC_QUERY, UC_FRAGMENT };
index 0d1006ec2b76bf86d8776124f47ee1a753030a86..14738a977cbd8ace95327dc37fcb8cb152377dc7 100644 (file)
@@ -87,6 +87,11 @@ InspectSection HttpInspect::get_latest_is(const Packet* p)
     if (current_section == nullptr)
         return HttpEnums::IS_NONE;
 
+    // FIXIT-L revisit why we need this check. We should not be getting a current section back
+    // for a raw packet but one of the test cases did exactly that.
+    if (!(p->packet_flags & PKT_PSEUDO))
+        return HttpEnums::IS_NONE;
+
     return current_section->get_inspection_section();
 }
 
@@ -143,19 +148,26 @@ bool HttpInspect::http_get_buf(unsigned id, uint64_t sub_id, uint64_t form, Pack
 
 bool HttpInspect::get_fp_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBuffer& b)
 {
+    if (get_latest_is(p) == IS_NONE)
+        return false;
+
     // Fast pattern buffers only supplied at specific times
     switch (ibt)
     {
     case InspectionBuffer::IBT_KEY:
-        if ((get_latest_is(p) != IS_DETECTION) || (get_latest_src(p) != SRC_CLIENT))
+        // Many rules targeting POST feature http_uri fast pattern with http_client_body. We
+        // accept the performance hit of rerunning http_uri fast pattern with request body message
+        // sections
+        if (get_latest_src(p) != SRC_CLIENT)
             return false;
         break;
     case InspectionBuffer::IBT_HEADER:
-        if ((get_latest_is(p) != IS_DETECTION) && (get_latest_is(p) != IS_TRAILER))
+        // http_header fast patterns for response bodies limited to first section
+        if ((get_latest_src(p) == SRC_SERVER) && (get_latest_is(p) == IS_BODY))
             return false;
         break;
     case InspectionBuffer::IBT_BODY:
-        if ((get_latest_is(p) != IS_DETECTION) && (get_latest_is(p) != IS_BODY))
+        if ((get_latest_is(p) != IS_FIRST_BODY) && (get_latest_is(p) != IS_BODY))
             return false;
         break;
     default:
@@ -266,6 +278,14 @@ void HttpInspect::eval(Packet* p)
     if (session_data->section_type[source_id] == SEC__NOT_COMPUTE)
         return;
 
+    // Don't make pkt_data for headers available to detection
+    // FIXIT-M One byte to avoid potential problems with zero
+    if ((session_data->section_type[source_id] == SEC_HEADER) ||
+        (session_data->section_type[source_id] == SEC_TRAILER))
+    {
+        p->set_detect_limit(1);
+    }
+
     // Limit alt_dsize of message body sections to request/response depth
     if ((session_data->detect_depth_remaining[source_id] > 0) &&
         (session_data->detect_depth_remaining[source_id] < p->dsize))
index 343201d44f86e8f30d2bb99cc7c6eaa5786b0b0c..4cea87b7a44f5c3c490cd3a7f3da04648eae4de3 100644 (file)
@@ -54,6 +54,7 @@ public:
         return new HttpStreamSplitter(is_client_to_server, this);
     }
     static HttpEnums::InspectSection get_latest_is(const snort::Packet* p);
+    static HttpEnums::SourceId get_latest_src(const snort::Packet* p);
 
     // Callbacks that provide "extra data"
     static int get_xtra_trueip(snort::Flow*, uint8_t**, uint32_t*, uint32_t*);
@@ -67,7 +68,6 @@ private:
 
     bool process(const uint8_t* data, const uint16_t dsize, snort::Flow* const flow,
         HttpEnums::SourceId source_id_, bool buf_owner) const;
-    static HttpEnums::SourceId get_latest_src(const snort::Packet* p);
 
     const HttpParaList* const params;
 
index f54582461ed112a3e6fd906bcd9c79ffc5a2258c..3ce6126c1ac5d1eef51db32a38f71f643d3a786f 100644 (file)
@@ -37,7 +37,7 @@ HttpMsgBody::HttpMsgBody(const uint8_t* buffer, const uint16_t buf_size,
     const HttpParaList* params_) :
     HttpMsgSection(buffer, buf_size, session_data_, source_id_, buf_owner, flow_, params_),
     body_octets(session_data->body_octets[source_id]),
-    detection_section((body_octets == 0) && (session_data->detect_depth_remaining[source_id] > 0))
+    first_body(session_data->body_octets[source_id] == 0)
 {
     transaction->set_body(this);
     get_related_sections();
@@ -70,11 +70,6 @@ void HttpMsgBody::analyze()
     body_octets += msg_text.length();
 }
 
-bool HttpMsgBody::detection_required() const
-{
-    return (detect_data.length() > 0) || (get_inspection_section() == IS_DETECTION);
-}
-
 void HttpMsgBody::do_utf_decoding(const Field& input, Field& output)
 {
     if ((source_id == SRC_CLIENT) || (session_data->utf_state == nullptr) || (input.length() == 0))
index d49beb3ecccde8e521a2e93596c16151151773c6..abcdcd0b58b2f7f6fcaf56e4fa9f801163b75d12 100644 (file)
@@ -32,8 +32,8 @@ class HttpMsgBody : public HttpMsgSection
 public:
     void analyze() override;
     HttpEnums::InspectSection get_inspection_section() const override
-        { return detection_section ? HttpEnums::IS_DETECTION : HttpEnums::IS_BODY; }
-    bool detection_required() const override;
+        { return first_body ? HttpEnums::IS_FIRST_BODY : HttpEnums::IS_BODY; }
+    bool detection_required() const override { return (detect_data.length() > 0); }
     HttpMsgBody* get_body() override { return this; }
     const Field& get_classic_client_body();
     const Field& get_detect_data() { return detect_data; }
@@ -45,6 +45,7 @@ protected:
         const HttpParaList* params_);
 
     int64_t body_octets;
+    bool first_body;
 
 #ifdef REG_TEST
     void print_body_section(FILE* output);
@@ -61,7 +62,6 @@ private:
     Field decoded_body;
     Field decompressed_file_body;
     Field js_norm_body;
-    const bool detection_section;
 };
 
 #endif
index 150357cadd939ac028679fceb840341a48c4fbb1..5b696b10606115dd2e70c9e6365bc5ba6ee3f7ef 100644 (file)
@@ -293,12 +293,6 @@ void HttpMsgHeader::prepare_body()
     const int64_t& depth = (source_id == SRC_CLIENT) ? params->request_depth :
         params->response_depth;
     session_data->detect_depth_remaining[source_id] = (depth != -1) ? depth : INT64_MAX;
-    if (session_data->detect_depth_remaining[source_id] > 0)
-    {
-        // Depth must be positive because first body section must actually go to detection in order
-        // to be the detection section
-        detection_section = false;
-    }
     setup_file_processing();
     setup_encoding_decompression();
     setup_utf_decoding();
index 2c00c1ab44d574baceec206416b51823d133700a..64aed16f9299f45ea0f231a1b2f5de0aad08946e 100644 (file)
@@ -37,7 +37,8 @@ public:
         HttpEnums::SourceId source_id_, bool buf_owner, snort::Flow* flow_,
         const HttpParaList* params_);
     HttpEnums::InspectSection get_inspection_section() const override
-        { return detection_section ? HttpEnums::IS_DETECTION : HttpEnums::IS_NONE; }
+        { return HttpEnums::IS_HEADER; }
+    bool detection_required() const override { return true; }
     void update_flow() override;
     void gen_events() override;
     void publish() override;
@@ -58,8 +59,6 @@ private:
     Field true_ip;
     Field true_ip_addr;
 
-    bool detection_section = true;
-
 #ifdef REG_TEST
     void print_section(FILE* output) override;
 #endif
index 0a50d193324cd47a0603c57312c75478777214a5..18701ee823d2f96d1fd9c45d479833f1b7c9ba3d 100644 (file)
@@ -54,12 +54,6 @@ HttpMsgSection::HttpMsgSection(const uint8_t* buffer, const uint16_t buf_size,
     HttpContextData::save_snapshot(this);
 }
 
-bool HttpMsgSection::detection_required() const
-{
-    return ((msg_text.length() > 0) && (get_inspection_section() != IS_NONE)) ||
-           (get_inspection_section() == IS_DETECTION);
-}
-
 void HttpMsgSection::add_infraction(int infraction)
 {
     *transaction->get_infractions(source_id) += infraction;
index 19d32c10522cf670c5ee7581233c6c36abad5712..9ed8e8e96b91af5e774453bd04b7893c8ed4e2c6 100644 (file)
@@ -37,7 +37,7 @@ public:
     virtual ~HttpMsgSection() = default;
     virtual HttpEnums::InspectSection get_inspection_section() const
         { return HttpEnums::IS_NONE; }
-    virtual bool detection_required() const;
+    virtual bool detection_required() const = 0;
     HttpEnums::SourceId get_source_id() const { return source_id; }
     HttpTransaction* get_transaction() const { return transaction; }
     const HttpParaList* get_params() const { return params; }
index ec4843fe52ba46767acee9a4657bd1e261f8df3e..a32decf6b6e77e5577b0c789963550070d81839e 100644 (file)
@@ -31,6 +31,7 @@ class HttpMsgStart : public HttpMsgSection
 {
 public:
     void analyze() override;
+    bool detection_required() const override { return false; }
     const Field& get_version() const { return version; }
 
 protected:
index 849605d03288af81ac26362a800ff2c8f2b929de..4b0815246857ad5527f60a4f74377ce8103c31d5 100644 (file)
@@ -34,6 +34,7 @@ public:
         const HttpParaList* params_);
     HttpEnums::InspectSection get_inspection_section() const override
         { return HttpEnums::IS_TRAILER; }
+    bool detection_required() const override { return (msg_text.length() > 0); }
     void gen_events() override;
     void update_flow() override;
 
index 883d067572a9332a3ee9a42e008ca0f074bdbfda..80203ab44e8c6407bfdfe6be8789bef018f896f0 100644 (file)
@@ -44,20 +44,22 @@ bool HttpCursorModule::begin(const char*, int, SnortConfig*)
     form = 0;
     switch (buffer_index)
     {
+    case HTTP_BUFFER_RAW_STATUS:
+    case HTTP_BUFFER_STAT_CODE:
+    case HTTP_BUFFER_STAT_MSG:
+        inspect_section = IS_HEADER;
+        break;
     case HTTP_BUFFER_COOKIE:
     case HTTP_BUFFER_HEADER:
     case HTTP_BUFFER_METHOD:
     case HTTP_BUFFER_RAW_COOKIE:
     case HTTP_BUFFER_RAW_HEADER:
     case HTTP_BUFFER_RAW_REQUEST:
-    case HTTP_BUFFER_RAW_STATUS:
     case HTTP_BUFFER_RAW_URI:
-    case HTTP_BUFFER_STAT_CODE:
-    case HTTP_BUFFER_STAT_MSG:
     case HTTP_BUFFER_TRUE_IP:
     case HTTP_BUFFER_URI:
     case HTTP_BUFFER_VERSION:
-        inspect_section = IS_DETECTION;
+        inspect_section = IS_FLEX_HEADER;
         break;
     case HTTP_BUFFER_CLIENT_BODY:
     case HTTP_BUFFER_RAW_BODY:
@@ -100,7 +102,7 @@ bool HttpCursorModule::set(const char*, Value& v, SnortConfig*)
     else if (v.is("with_header"))
     {
         para_list.with_header = true;
-        inspect_section = IS_DETECTION;
+        inspect_section = IS_HEADER;
     }
     else if (v.is("with_body"))
     {
@@ -207,16 +209,16 @@ IpsOption::EvalStatus HttpIpsOption::eval(Cursor& c, Packet* p)
 {
     RuleProfile profile(HttpCursorModule::http_ps[psi]);
 
-    if (!p->flow || !p->flow->gadget)
+    if (!p->flow || !p->flow->gadget || (HttpInspect::get_latest_is(p) == IS_NONE))
         return NO_MATCH;
 
-    if (HttpInspect::get_latest_is(p) != inspect_section)
-    {
-        // It is OK to provide a body buffer during the detection section. If there actually is
-        // a body buffer available then the detection section must also be the first body section.
-        if (! ((inspect_section == IS_BODY) && (HttpInspect::get_latest_is(p) == IS_DETECTION)) )
-            return NO_MATCH;
-    }
+    const bool section_match =
+        (HttpInspect::get_latest_is(p) == inspect_section) ||
+        ((HttpInspect::get_latest_is(p) == IS_HEADER) && (inspect_section == IS_FLEX_HEADER)) ||
+        ((HttpInspect::get_latest_is(p) == IS_FIRST_BODY) && (inspect_section == IS_BODY)) ||
+        ((HttpInspect::get_latest_src(p) == SRC_CLIENT) && (inspect_section == IS_FLEX_HEADER));
+    if (!section_match)
+        return NO_MATCH;
 
     InspectionBuffer hb;
 
@@ -277,6 +279,8 @@ static const Parameter http_cookie_params[] =
 {
     { "request", Parameter::PT_IMPLIED, nullptr, nullptr,
         "match against the cookie from the request message even when examining the response" },
+    { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr,
+        "this rule is limited to examining HTTP message headers" },
     { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr,
         "parts of this rule examine HTTP message body" },
     { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr,
@@ -336,6 +340,8 @@ static const Parameter http_header_params[] =
         "restrict to given header. Header name is case insensitive." },
     { "request", Parameter::PT_IMPLIED, nullptr, nullptr,
         "match against the headers from the request message even when examining the response" },
+    { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr,
+        "this rule is limited to examining HTTP message headers" },
     { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr,
         "parts of this rule examine HTTP message body" },
     { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr,
@@ -385,6 +391,8 @@ static const IpsApi header_api =
 
 static const Parameter http_method_params[] =
 {
+    { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr,
+        "this rule is limited to examining HTTP message headers" },
     { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr,
         "parts of this rule examine HTTP message body" },
     { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr,
@@ -476,6 +484,8 @@ static const Parameter http_raw_cookie_params[] =
 {
     { "request", Parameter::PT_IMPLIED, nullptr, nullptr,
         "match against the cookie from the request message even when examining the response" },
+    { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr,
+        "this rule is limited to examining HTTP message headers" },
     { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr,
         "parts of this rule examine HTTP message body" },
     { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr,
@@ -527,6 +537,8 @@ static const Parameter http_raw_header_params[] =
 {
     { "request", Parameter::PT_IMPLIED, nullptr, nullptr,
         "match against the headers from the request message even when examining the response" },
+    { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr,
+        "this rule is limited to examining HTTP message headers" },
     { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr,
         "parts of this rule examine HTTP message body" },
     { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr,
@@ -576,6 +588,8 @@ static const IpsApi raw_header_api =
 
 static const Parameter http_raw_request_params[] =
 {
+    { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr,
+        "this rule is limited to examining HTTP message headers" },
     { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr,
         "parts of this rule examine HTTP message body" },
     { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr,
@@ -726,6 +740,8 @@ static const IpsApi raw_trailer_api =
 
 static const Parameter http_raw_uri_params[] =
 {
+    { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr,
+        "this rule is limited to examining HTTP message headers" },
     { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr,
         "parts of this rule examine HTTP message body" },
     { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr,
@@ -938,6 +954,8 @@ static const IpsApi trailer_api =
 
 static const Parameter http_true_ip_params[] =
 {
+    { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr,
+        "this rule is limited to examining HTTP message headers" },
     { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr,
         "parts of this rule examine HTTP message body" },
     { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr,
@@ -987,6 +1005,8 @@ static const IpsApi true_ip_api =
 
 static const Parameter http_uri_params[] =
 {
+    { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr,
+        "this rule is limited to examining HTTP message headers" },
     { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr,
         "parts of this rule examine HTTP message body" },
     { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr,
@@ -1050,6 +1070,8 @@ static const Parameter http_version_params[] =
 {
     { "request", Parameter::PT_IMPLIED, nullptr, nullptr,
         "match against the version from the request message even when examining the response" },
+    { "with_header", Parameter::PT_IMPLIED, nullptr, nullptr,
+        "this rule is limited to examining HTTP message headers" },
     { "with_body", Parameter::PT_IMPLIED, nullptr, nullptr,
         "parts of this rule examine HTTP message body" },
     { "with_trailer", Parameter::PT_IMPLIED, nullptr, nullptr,