From aeac79145c5bfc772bfbd01178cfda1f672e42a6 Mon Sep 17 00:00:00 2001
From: Max Leske <250711+theseion@users.noreply.github.com>
Date: Sat, 7 Dec 2024 10:07:48 +0100
Subject: [PATCH] feat: add VirtualHostMode setting
refs https://github.com/coreruleset/go-ftw/issues/361
---
spec/v2.1.1/ftw.md | 2247 +++++++++++++++++
.../waf-platform-overrides-schema-v2.1.1.json | 1 +
spec/v2.1.1/waf-tests-schema-v2.1.1.json | 1 +
types/types.go | 33 +-
4 files changed, 2278 insertions(+), 4 deletions(-)
create mode 100644 spec/v2.1.1/ftw.md
create mode 100755 spec/v2.1.1/waf-platform-overrides-schema-v2.1.1.json
create mode 100755 spec/v2.1.1/waf-tests-schema-v2.1.1.json
diff --git a/spec/v2.1.1/ftw.md b/spec/v2.1.1/ftw.md
new file mode 100644
index 0000000..df33bbc
--- /dev/null
+++ b/spec/v2.1.1/ftw.md
@@ -0,0 +1,2247 @@
+## FTWTest
+Welcome to the FTW YAMLFormat documentation.
+ In this document we will explain all the possible options that can be used within the YAML format.
+ Generally this is the preferred format for writing tests in as they don't require any programming skills
+ in order to understand and change. If you find a bug in this format please open an issue.
+
+
+ FTWTest is the base type used when unmarshaling YAML tests files
+
+
+
+
+
+
+
+
+
+
+
+Meta describes the metadata information of this yaml test file
+
+
+
+
+
+
+
+rule_id
uint
+
+
+
+
+RuleId is the ID of the rule this test targets.
+
+
+
+Examples:
+
+
+```yaml
+# RuleId
+rule_id: 123456
+```
+
+
+
+
+
+
+
+
+
+Tests is a list of FTW tests
+
+
+
+Examples:
+
+
+```yaml
+tests:
+ - test_title: 123456-1
+ ruleid: 0
+ test_id: 0
+ desc: Unix RCE using `time`
+ stages:
+ - description: Get cookie from server
+ input:
+ dest_addr: 192.168.0.1
+ port: 8080
+ protocol: http
+ uri: /test
+ version: HTTP/1.1
+ method: REPORT
+ headers:
+ Accept: '*/*'
+ Host: localhost
+ User-Agent: CRS Tests
+ save_cookie: false
+ stop_magic: true
+ autocomplete_headers: false
+ encoded_request: TXkgRGF0YQo=
+ output:
+ status: 200
+ response_contains: HTTP/1.1
+ log_contains: nothing
+ no_log_contains: everything
+ log:
+ expect_ids:
+ - 123456
+ no_expect_ids:
+ - 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+ expect_error: true
+```
+
+
+
+
+
+
+
+
+
+
+## FTWTestMeta
+
+Appears in:
+
+
+- FTWTest.meta
+
+
+
+
+
+
+
+
+
+author
string
+
+
+
+
+Author is the list of authors that added content to this file
+
+
+
+Examples:
+
+
+```yaml
+# Author
+author: Felipe Zipitria
+```
+
+
+
+
+
+
+
+
+enabled
bool
+
+
+
+
+Enabled indicates if the tests are enabled to be run by the engine or not.
+
+
+
+Examples:
+
+
+```yaml
+# Enabled
+enabled: false
+```
+
+
+
+
+
+
+
+
+name
string
+
+
+
+
+Name is the name of the tests contained in this file.
+
+
+
+Examples:
+
+
+```yaml
+# Name
+name: test01
+```
+
+
+
+
+
+
+
+
+description
string
+
+
+
+
+Description is a textual description of the tests contained in this file.
+
+
+
+Examples:
+
+
+```yaml
+# Description
+description: The tests here target SQL injection.
+```
+
+
+
+
+
+
+
+
+version
string
+
+
+
+
+Version is the version of the YAML Schema.
+
+
+
+Examples:
+
+
+```yaml
+# Version
+version: v1
+```
+
+
+
+
+
+
+
+
+tags
[]string
+
+
+
+
+description: |
+ Tags is list of strings that can be used for arbitrary grouping of tests.
+ examples:
+ - name: Tags
+ value: ["PHP", "bug-123"]
+
+
+
+
+
+
+
+
+
+## Test
+
+Appears in:
+
+
+- FTWTest.tests
+
+
+```yaml
+- test_title: 123456-1
+ ruleid: 0
+ test_id: 0
+ desc: Unix RCE using `time`
+ stages:
+ - description: Get cookie from server
+ input:
+ dest_addr: 192.168.0.1
+ port: 8080
+ protocol: http
+ uri: /test
+ version: HTTP/1.1
+ method: REPORT
+ headers:
+ Accept: '*/*'
+ Host: localhost
+ User-Agent: CRS Tests
+ save_cookie: false
+ stop_magic: true
+ autocomplete_headers: false
+ encoded_request: TXkgRGF0YQo=
+ output:
+ status: 200
+ response_contains: HTTP/1.1
+ log_contains: nothing
+ no_log_contains: everything
+ log:
+ expect_ids:
+ - 123456
+ no_expect_ids:
+ - 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+ expect_error: true
+```
+
+
+
+
+
+
+
+test_title
string
+
+
+
+
+TestTitle is the title of this particular types. It is used for inclusion/exclusion of each run by the tool.
+
+
+
+Examples:
+
+
+```yaml
+test_title: 123456-1
+```
+
+
+
+
+
+
+
+
+test_id
uint
+
+
+
+
+TestId is the ID of the test, in relation to `rule_id`.
+
+When this field is not set, the ID will be inferred from the
+position.
+
+
+
+Examples:
+
+
+```yaml
+# TestId
+test_id: 4
+```
+
+
+
+
+
+
+
+
+desc
string
+
+
+
+
+TestDescription is the description for this particular test.
+
+Should be used to describe the internals of the specific things this test is targeting.
+
+
+
+Examples:
+
+
+```yaml
+desc: Unix RCE using `time`
+```
+
+
+
+
+
+
+
+
+
+Stages is the list of all the stages to perform this test.
+
+
+
+Examples:
+
+
+```yaml
+stages:
+ - description: Get cookie from server
+ input:
+ dest_addr: 192.168.0.1
+ port: 8080
+ protocol: http
+ uri: /test
+ version: HTTP/1.1
+ method: REPORT
+ headers:
+ Accept: '*/*'
+ Host: localhost
+ User-Agent: CRS Tests
+ save_cookie: false
+ stop_magic: true
+ autocomplete_headers: false
+ encoded_request: TXkgRGF0YQo=
+ output:
+ status: 200
+ response_contains: HTTP/1.1
+ log_contains: nothing
+ no_log_contains: everything
+ log:
+ expect_ids:
+ - 123456
+ no_expect_ids:
+ - 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+ expect_error: true
+```
+
+
+
+
+
+
+
+
+tags
[]string
+
+
+
+
+description: |
+ Tags is list of strings that can be used for arbitrary grouping of tests.
+ examples:
+ - name: Tags
+ value: ["PHP", "bug-123"]
+
+
+
+
+
+
+
+
+
+## Stage
+
+Appears in:
+
+
+- Test.stages
+
+
+```yaml
+- description: Get cookie from server
+ input:
+ dest_addr: 192.168.0.1
+ port: 8080
+ protocol: http
+ uri: /test
+ version: HTTP/1.1
+ method: REPORT
+ headers:
+ Accept: '*/*'
+ Host: localhost
+ User-Agent: CRS Tests
+ save_cookie: false
+ stop_magic: true
+ autocomplete_headers: false
+ encoded_request: TXkgRGF0YQo=
+ output:
+ status: 200
+ response_contains: HTTP/1.1
+ log_contains: nothing
+ no_log_contains: everything
+ log:
+ expect_ids:
+ - 123456
+ no_expect_ids:
+ - 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+ expect_error: true
+```
+
+
+
+
+
+
+
+description
string
+
+
+
+
+Describes the purpose of this stage.
+
+
+
+Examples:
+
+
+```yaml
+description: Get cookie from server
+```
+
+
+
+
+
+
+
+
+
+Input is the data that is passed to the test
+
+
+
+Examples:
+
+
+```yaml
+# Input
+input:
+ dest_addr: 192.168.0.1
+ port: 8080
+ protocol: http
+ uri: /test
+ version: HTTP/1.1
+ method: REPORT
+ headers:
+ Accept: '*/*'
+ Host: localhost
+ User-Agent: CRS Tests
+ save_cookie: false
+ stop_magic: true
+ autocomplete_headers: false
+ encoded_request: TXkgRGF0YQo=
+```
+
+
+
+
+
+
+
+
+
+Output is the data that is returned from the test
+
+
+
+Examples:
+
+
+```yaml
+# Output
+output:
+ status: 200
+ response_contains: HTTP/1.1
+ log_contains: nothing
+ no_log_contains: everything
+ log:
+ expect_ids:
+ - 123456
+ no_expect_ids:
+ - 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+ expect_error: true
+```
+
+
+
+
+
+
+
+
+
+
+## Input
+
+Appears in:
+
+
+- Stage.input
+
+
+```yaml
+# Input
+dest_addr: 192.168.0.1
+port: 8080
+protocol: http
+uri: /test
+version: HTTP/1.1
+method: REPORT
+headers:
+ Accept: '*/*'
+ Host: localhost
+ User-Agent: CRS Tests
+save_cookie: false
+stop_magic: true
+autocomplete_headers: false
+encoded_request: TXkgRGF0YQo=
+```
+
+
+
+
+
+
+
+dest_addr
string
+
+
+
+
+DestAddr is the IP of the destination host that the test will send the message to.
+
+
+
+Examples:
+
+
+```yaml
+# DestAddr
+dest_addr: 127.0.0.1
+```
+
+
+
+
+
+
+
+
+port
int
+
+
+
+
+Port allows you to declare which port on the destination host the test should connect to.
+
+
+
+Examples:
+
+
+```yaml
+# Port
+port: 80
+```
+
+
+
+
+
+
+
+
+protocol
string
+
+
+
+
+Protocol allows you to declare which protocol the test should use when sending the request.
+
+
+
+Examples:
+
+
+```yaml
+# Protocol
+protocol: http
+```
+
+
+
+
+
+
+
+
+uri
string
+
+
+
+
+URI allows you to declare the URI the test should use as part of the request line.
+
+
+
+Examples:
+
+
+```yaml
+# URI
+uri: /get?hello=world
+```
+
+
+
+
+
+
+
+
+follow_redirect
bool
+
+
+
+
+FollowRedirect will expect the previous stage of the same test to have received a
+redirect response, it will fail the test otherwise. The redirect location will be used
+to send the request for the current stage and any settings for port, protocol, address,
+or URI will be ignored.
+
+
+
+Examples:
+
+
+```yaml
+# follow_redirect
+follow_redirect: true
+```
+
+
+
+
+
+
+
+
+version
string
+
+
+
+
+Version allows you to declare the HTTP version the test should use as part of the request line.
+
+
+
+Examples:
+
+
+```yaml
+# Version
+version: "1.1"
+```
+
+
+
+
+
+
+
+
+method
string
+
+
+
+
+Method allows you to declare the HTTP method the test should use as part of the request line.
+
+
+
+Examples:
+
+
+```yaml
+# Method
+method: GET
+```
+
+
+
+
+
+
+
+
+headers
map[string]string
+
+
+
+
+Headers allows you to declare headers that the test should send.
+
+
+
+Examples:
+
+
+```yaml
+# Headers
+headers:
+ Accept: '*/*'
+ Host: localhost
+ User-Agent: CRS Tests
+```
+
+
+
+
+
+
+
+
+data
string
+
+
+
+
+Data allows you to declare the payload that the test should in the request body.
+
+
+
+Examples:
+
+
+```yaml
+# Data
+data: Bibitti bopi
+```
+
+
+
+
+
+
+
+
+encoded_data
string
+
+
+
+
+EncodedData allows you to declare the payload as a base64 encoded string, which
+will be decoded into bytes and sent verbatimt to the server. This allows for complex
+payloads that include invisible characters or invalid Unicode byte sequences.
+
+
+
+Examples:
+
+
+```yaml
+# encoded_data
+encoded_data: c29tZXRoaW5nIHdpdGgKbmV3bGluZQo=
+```
+
+
+
+
+
+
+
+
+save_cookie
bool
+
+
+
+
+SaveCookie allows you to automatically provide cookies if there are multiple stages and save cookie is set
+
+
+
+Examples:
+
+
+```yaml
+# SaveCookie
+save_cookie: 80
+```
+
+
+
+
+
+
+
+
+stop_magic
bool
+
+
+
+
+StopMagic is deprecated.
+
+
+
+Examples:
+
+
+```yaml
+# StopMagic
+stop_magic: false
+```
+
+
+
+
+
+
+
+
+autocomplete_headers
bool
+
+
+
+
+AutocompleteHeaders allows the test framework to automatically fill the request with Content-Type and Connection headers.
+
+Defaults: `true`.
+
+
+
+Examples:
+
+
+```yaml
+# StopMagic
+autocomplete_headers: false
+```
+
+
+
+
+
+
+
+
+encoded_request
string
+
+
+
+
+EncodedRequest will take a base64 encoded string that will be decoded and sent through as the request.
+
+It will override all other settings
+
+
+
+Examples:
+
+
+```yaml
+# EncodedRequest
+encoded_request: a
+```
+
+
+
+
+
+
+
+
+raw_request
string
+
+
+
+
+RAWRequest is deprecated.
+
+
+
+Examples:
+
+
+```yaml
+# RAWRequest
+raw_request: TXkgRGF0YQo=
+```
+
+
+
+
+
+
+
+
+
+Response describes a response from the web server that a WAF is expected to analyse.
+
+Note: This functionality requires a backend that can send the specified request to the
+ reverse proxy. Currently, only Albedo (https://github.com/coreruleset/albedo) is supported.
+
+
+
+
+
+
+
+virtual_host_mode
bool
+
+
+
+
+VirtualHostMode determines the value of the `Host` header for internal requests (e.g., the
+requests used to insert markers into the web server log). This is useful for running tests
+against a virtual host, as the log entries for all requests must end up in the same log file,
+and often, log files are segregated by virtual host.
+
+If `true`, internal requests will use the same value for the `Host` header as the test request.
+
+If `false`, the value for the `Host` header of internal requests will be `localhost`.
+
+Default: `false`.
+
+
+
+
+
+
+
+
+
+## Response
+
+Appears in:
+
+
+- Input.response
+
+
+
+
+
+
+
+
+
+headers
map[string]string
+
+
+
+
+Headers defines the headers the response will carry.
+
+
+
+Examples:
+
+
+```yaml
+# Headers
+headers:
+ Accept: '*/*'
+ Host: localhost
+ User-Agent: CRS Tests
+```
+
+
+
+
+
+
+
+
+status
int
+
+
+
+
+Status describes the HTTP status code of the response.
+
+Default: `200` if omitted.
+
+
+
+Examples:
+
+
+```yaml
+# Status
+status: 302
+```
+
+
+
+
+
+
+
+
+body
string
+
+
+
+
+Body defines the body of the response as a plain string.
+
+
+
+Examples:
+
+
+```yaml
+# Body
+body: |
+ {"aJsonDocument": ["in the response"]}
+```
+
+
+
+
+
+
+
+
+encoded_body
string
+
+
+
+
+EncodedBody defines the body of the response as a base64 encoded string. This is useful if the response
+needs to contain non-printable characters.
+
+
+
+Examples:
+
+
+```yaml
+# EncodedBody
+encoded_body: eyJhSnNvbkRvY3VtZW50IjogWyJpbiB0aGUgcmVzcG9uc2UiXX0=
+```
+
+
+
+
+
+
+
+
+log_message
string
+
+
+
+
+LogMessage specifies a message to be printed in the log of the backend server that sends the response.
+This can be helpful when debugging, to match resopnses sent by the backend to test executions.
+
+
+
+Examples:
+
+
+```yaml
+# LogMessage
+log_message: Response splitting test 1
+```
+
+
+
+
+
+
+
+
+
+
+## Output
+
+Appears in:
+
+
+- Stage.output
+
+
+```yaml
+# Output
+status: 200
+response_contains: HTTP/1.1
+log_contains: nothing
+no_log_contains: everything
+log:
+ expect_ids:
+ - 123456
+ no_expect_ids:
+ - 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+expect_error: true
+```
+
+
+
+
+
+
+
+status
int
+
+
+
+
+Status describes the HTTP status code expected in the response.
+
+
+
+Examples:
+
+
+```yaml
+# Status
+status: 200
+```
+
+
+
+
+
+
+
+
+response_contains
string
+
+
+
+
+ResponseContains describes the text that should be contained in the HTTP response.
+
+
+
+Examples:
+
+
+```yaml
+# ResponseContains
+response_contains: Hello, World
+```
+
+
+
+
+
+
+
+
+log_contains
string
+
+
+
+
+LogContains describes the text that should be contained in the WAF logs.
+
+
+
+Examples:
+
+
+```yaml
+# LogContains
+log_contains: id 920100
+```
+
+
+
+
+
+
+
+
+no_log_contains
string
+
+
+
+
+NoLogContains describes the text that should not be contained in the WAF logs.
+
+
+
+Examples:
+
+
+```yaml
+# NoLogContains
+no_log_contains: id 920100
+```
+
+
+
+
+
+
+
+
+
+Log is used to configure expectations about the log contents.
+
+
+
+Examples:
+
+
+```yaml
+log:
+ expect_ids:
+ - 123456
+ no_expect_ids:
+ - 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+```
+
+
+
+
+
+
+
+
+expect_error
bool
+
+
+
+
+When `ExpectError` is true, we don't expect an answer from the WAF, just an error.
+
+
+
+Examples:
+
+
+```yaml
+# ExpectError
+expect_error: false
+```
+
+
+
+
+
+
+
+
+retry_once
bool
+
+
+
+
+When `RetryOnce` is true, the test run will be retried once upon failures. This options
+primary purpose is to work around a race condition in phase 5, where the log entry for
+a phase 5 rule may appear after the end marker of the previous test.
+
+
+
+
+
+
+
+isolated
bool
+
+
+
+
+Isolated specifies that the test is expected to trigger a single rule only.
+If the rule triggers any other rule than the (single) one specified in
+expect_ids, the test fill be considered a failure.
+
+Default: `false`
+
+
+
+Examples:
+
+
+```yaml
+# Isolated
+isolated: true
+```
+
+
+
+
+
+
+
+
+
+
+## Log
+
+Appears in:
+
+
+- Output.log
+
+
+```yaml
+expect_ids:
+ - 123456
+no_expect_ids:
+ - 123456
+match_regex: id[:\s"]*123456
+no_match_regex: id[:\s"]*123456
+```
+
+
+
+
+
+
+
+expect_ids
[]uint
+
+
+
+
+description: |
+ Expect the given IDs to be contained in the log output.
+ examples:
+ -value: ExampleLog.ExpectIds
+
+
+
+
+
+
+
+no_expect_ids
[]uint
+
+
+
+
+Expect the given IDs _not_ to be contained in the log output.
+
+
+
+Examples:
+
+
+```yaml
+no_expect_ids:
+ - 123456
+```
+
+
+
+
+
+
+
+
+match_regex
string
+
+
+
+
+Expect the regular expression to match log content for the current types.
+
+
+
+Examples:
+
+
+```yaml
+match_regex: id[:\s"]*123456
+```
+
+
+
+
+
+
+
+
+no_match_regex
string
+
+
+
+
+Expect the regular expression to _not_ match log content for the current types.
+
+
+
+Examples:
+
+
+```yaml
+no_match_regex: id[:\s"]*123456
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+## FTWOverrides
+FTWOverrides describes platform specific overrides for tests
+
+
+
+
+
+
+
+
+
+
+version
string
+
+
+
+
+The version field designates the version of the schema that validates this file
+
+
+
+Examples:
+
+
+```yaml
+version: v0.1.0
+```
+
+
+
+
+
+
+
+
+
+Meta describes the metadata information
+
+
+
+Examples:
+
+
+```yaml
+meta:
+ engine: libmodsecurity3
+ platform: nginx
+ annotations:
+ os: Debian Bullseye
+ purpose: L7ASR test suite
+```
+
+
+
+
+
+
+
+
+
+List of test override specifications
+
+
+
+Examples:
+
+
+```yaml
+test_overrides:
+ - rule_id: 920100
+ test_ids: [4, 6]
+ reason: |-
+ nginx returns 400 when `Content-Length` header is sent in a
+ `Transfer-Encoding: chunked` request.
+ output:
+ status: 200
+ response_contains: HTTP/1.1
+ log_contains: nothing
+ no_log_contains: everything
+ log:
+ expect_ids:
+ - 123456
+ no_expect_ids:
+ - 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+ expect_error: true
+```
+
+
+
+
+
+
+
+
+
+
+## FTWOverridesMeta
+
+Appears in:
+
+
+- FTWOverrides.meta
+
+
+```yaml
+engine: libmodsecurity3
+platform: nginx
+annotations:
+ os: Debian Bullseye
+ purpose: L7ASR test suite
+```
+
+
+
+
+
+
+
+engine
string
+
+
+
+
+The name of the WAF engine the tests are expected to run against
+
+
+
+Examples:
+
+
+```yaml
+engine: coraza
+```
+
+
+
+
+
+
+
+
+platform
string
+
+
+
+
+The name of the platform (e.g., web server) the tests are expected to run against
+
+
+
+Examples:
+
+
+```yaml
+platform: nginx
+```
+
+
+
+
+
+
+
+
+annotations
map[string]string
+
+
+
+
+Custom annotations; can be used to add additional meta information
+
+
+
+Examples:
+
+
+```yaml
+annotations:
+ os: Debian Bullseye
+ purpose: L7ASR test suite
+```
+
+
+
+
+
+
+
+
+
+
+## TestOverride
+
+Appears in:
+
+
+- FTWOverrides.test_overrides
+
+
+```yaml
+- rule_id: 920100
+ test_ids: [4, 6]
+ reason: |-
+ nginx returns 400 when `Content-Length` header is sent in a
+ `Transfer-Encoding: chunked` request.
+ output:
+ status: 200
+ response_contains: HTTP/1.1
+ log_contains: nothing
+ no_log_contains: everything
+ log:
+ expect_ids:
+ - 123456
+ no_expect_ids:
+ - 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+ expect_error: true
+```
+
+
+
+
+
+
+
+rule_id
uint
+
+
+
+
+ID of the rule this test targets.
+
+
+
+Examples:
+
+
+```yaml
+rule_id: 920100
+```
+
+
+
+
+
+
+
+
+test_ids
[]uint
+
+
+
+
+IDs of the tests for rule_id that overrides should be applied to.
+If this field is not set, the overrides will be applied to all tests of rule_id.
+
+
+
+Examples:
+
+
+```yaml
+test_ids:
+ - 4
+ - 6
+```
+
+
+
+
+
+
+
+
+stage_ids
[]uint
+
+
+
+
+IDs of the stages to which overrides should be applied.
+Stage IDs listed will be overridden for all test IDs listed in `TestIds`.
+If this field is not set, the overrides will be applied to all stages.
+
+
+
+
+
+
+
+reason
string
+
+
+
+
+Describes why this override is necessary.
+
+
+
+Examples:
+
+
+```yaml
+reason: |-
+ nginx returns 400 when `Content-Length` header is sent in a
+ `Transfer-Encoding: chunked` request.
+```
+
+
+
+
+
+
+
+
+retry_once
bool
+
+
+
+
+Whether a stage should be retried once in case of failure.
+This option is primarily a workaround for a race condition in phase 5,
+where the log entry of a rule may be flushed after the test end marker.
+
+
+
+Examples:
+
+
+```yaml
+retry_once: true
+```
+
+
+
+
+
+
+
+
+
+Specifies overrides on the test output.
+This definition *replaces* the output definition of the test.
+
+
+
+Examples:
+
+
+```yaml
+output:
+ status: 200
+ response_contains: HTTP/1.1
+ log_contains: nothing
+ no_log_contains: everything
+ log:
+ expect_ids:
+ - 123456
+ no_expect_ids:
+ - 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+ expect_error: true
+```
+
+
+
+
+
+
+
+
+
+
+## types.Output
+Output defines the expectations of a test
+
+Appears in:
+
+
+- TestOverride.output
+
+
+```yaml
+status: 200
+response_contains: HTTP/1.1
+log_contains: nothing
+no_log_contains: everything
+log:
+ expect_ids:
+ - 123456
+ no_expect_ids:
+ - 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+expect_error: true
+```
+
+
+
+
+
+
+
+status
int
+
+
+
+
+Status describes the HTTP status code expected in the response.
+
+
+
+Examples:
+
+
+```yaml
+# Status
+status: 200
+```
+
+
+
+
+
+
+
+
+response_contains
string
+
+
+
+
+ResponseContains describes the text that should be contained in the HTTP response.
+
+
+
+Examples:
+
+
+```yaml
+# ResponseContains
+response_contains: Hello, World
+```
+
+
+
+
+
+
+
+
+log_contains
string
+
+
+
+
+LogContains describes the text that should be contained in the WAF logs.
+
+
+
+Examples:
+
+
+```yaml
+# LogContains
+log_contains: id 920100
+```
+
+
+
+
+
+
+
+
+no_log_contains
string
+
+
+
+
+NoLogContains describes the text that should not be contained in the WAF logs.
+
+
+
+Examples:
+
+
+```yaml
+# NoLogContains
+no_log_contains: id 920100
+```
+
+
+
+
+
+
+
+
+log
Log
+
+
+
+
+Log is used to configure expectations about the log contents.
+
+
+
+Examples:
+
+
+```yaml
+log:
+ expect_ids:
+ - 123456
+ no_expect_ids:
+ - 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+```
+
+
+
+
+
+
+
+
+expect_error
types.bool
+
+
+
+
+When `ExpectError` is true, we don't expect an answer from the WAF, just an error.
+
+
+
+Examples:
+
+
+```yaml
+# ExpectError
+expect_error: false
+```
+
+
+
+
+
+
+
+
+retry_once
types.bool
+
+
+
+
+When `RetryOnce` is true, the test run will be retried once upon failures. This options
+primary purpose is to work around a race condition in phase 5, where the log entry for
+a phase 5 rule may appear after the end marker of the previous test.
+
+
+
+
+
+
+
+isolated
bool
+
+
+
+
+Isolated specifies that the test is expected to trigger a single rule only.
+If the rule triggers any other rule than the (single) one specified in
+expect_ids, the test fill be considered a failure.
+
+Default: `false`
+
+
+
+Examples:
+
+
+```yaml
+# Isolated
+isolated: true
+```
+
+
+
+
+
+
+
+
+
+
+## types.Log
+
+
+
+
+
+
+
+
+
+
+expect_ids
[]uint
+
+
+
+
+description: |
+ Expect the given IDs to be contained in the log output.
+ examples:
+ -value: ExampleLog.ExpectIds
+
+
+
+
+
+
+
+no_expect_ids
[]uint
+
+
+
+
+Expect the given IDs _not_ to be contained in the log output.
+
+
+
+Examples:
+
+
+```yaml
+no_expect_ids:
+ - 123456
+```
+
+
+
+
+
+
+
+
+match_regex
string
+
+
+
+
+Expect the regular expression to match log content for the current types.
+
+
+
+Examples:
+
+
+```yaml
+match_regex: id[:\s"]*123456
+```
+
+
+
+
+
+
+
+
+no_match_regex
string
+
+
+
+
+Expect the regular expression to _not_ match log content for the current types.
+
+
+
+Examples:
+
+
+```yaml
+no_match_regex: id[:\s"]*123456
+```
+
+
+
+
+
+
+
+
+
diff --git a/spec/v2.1.1/waf-platform-overrides-schema-v2.1.1.json b/spec/v2.1.1/waf-platform-overrides-schema-v2.1.1.json
new file mode 100755
index 0000000..138a705
--- /dev/null
+++ b/spec/v2.1.1/waf-platform-overrides-schema-v2.1.1.json
@@ -0,0 +1 @@
+{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://github.com/coreruleset/ftw-tests-schema/v2/types/overrides/ftw-overrides","$ref":"#/$defs/FTWOverrides","$defs":{"FTWOverrides":{"properties":{"version":{"type":"string"},"meta":{"$ref":"#/$defs/FTWOverridesMeta"},"test_overrides":{"items":{"$ref":"#/$defs/TestOverride"},"type":"array"}},"additionalProperties":false,"type":"object","required":["version","meta","test_overrides"]},"FTWOverridesMeta":{"properties":{"engine":{"type":"string"},"platform":{"type":"string"},"annotations":{"additionalProperties":{"type":"string"},"type":"array"}},"additionalProperties":false,"type":"object","required":["engine","platform","annotations"]},"Log":{"properties":{"expect_ids":{"items":{"type":"integer"},"type":"array"},"no_expect_ids":{"items":{"type":"integer"},"type":"array"},"match_regex":{"type":"string"},"no_match_regex":{"type":"string"}},"additionalProperties":false,"type":"object"},"Output":{"properties":{"status":{"type":"integer"},"response_contains":{"type":"string"},"log_contains":{"type":"string"},"no_log_contains":{"type":"string"},"log":{"$ref":"#/$defs/Log"},"expect_error":{"type":"boolean"},"retry_once":{"type":"boolean"},"isolated":{"type":"boolean"}},"additionalProperties":false,"type":"object"},"TestOverride":{"properties":{"rule_id":{"type":"integer"},"test_ids":{"items":{"type":"integer"},"type":"array"},"stage_ids":{"items":{"type":"integer"},"type":"array"},"reason":{"type":"string"},"retry_once":{"type":"boolean"},"output":{"$ref":"#/$defs/Output"}},"additionalProperties":false,"type":"object","required":["rule_id","reason","output"]}}}
\ No newline at end of file
diff --git a/spec/v2.1.1/waf-tests-schema-v2.1.1.json b/spec/v2.1.1/waf-tests-schema-v2.1.1.json
new file mode 100755
index 0000000..d692d6b
--- /dev/null
+++ b/spec/v2.1.1/waf-tests-schema-v2.1.1.json
@@ -0,0 +1 @@
+{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://github.com/coreruleset/ftw-tests-schema/v2/types/ftw-test","$ref":"#/$defs/FTWTest","$defs":{"FTWTest":{"properties":{"meta":{"$ref":"#/$defs/FTWTestMeta"},"rule_id":{"type":"integer"},"tests":{"items":{"$ref":"#/$defs/Test"},"type":"array"}},"additionalProperties":false,"type":"object","required":["meta","rule_id","tests"]},"FTWTestMeta":{"properties":{"author":{"type":"string"},"enabled":{"type":"boolean"},"name":{"type":"string"},"description":{"type":"string"},"version":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"}},"additionalProperties":false,"type":"object"},"Input":{"properties":{"dest_addr":{"type":"string"},"port":{"type":"integer"},"protocol":{"type":"string"},"uri":{"type":"string"},"follow_redirect":{"type":"boolean"},"version":{"type":"string"},"method":{"type":"string"},"headers":{"additionalProperties":{"type":"string"},"type":"object"},"data":{"type":"string"},"encoded_data":{"type":"string"},"save_cookie":{"type":"boolean"},"stop_magic":{"type":"boolean"},"autocomplete_headers":{"type":"boolean"},"encoded_request":{"type":"string"},"raw_request":{"type":"string"},"response":{"$ref":"#/$defs/Response"},"virtual_host_mode":{"type":"boolean"}},"additionalProperties":false,"type":"object"},"Log":{"properties":{"expect_ids":{"items":{"type":"integer"},"type":"array"},"no_expect_ids":{"items":{"type":"integer"},"type":"array"},"match_regex":{"type":"string"},"no_match_regex":{"type":"string"}},"additionalProperties":false,"type":"object"},"Output":{"properties":{"status":{"type":"integer"},"response_contains":{"type":"string"},"log_contains":{"type":"string"},"no_log_contains":{"type":"string"},"log":{"$ref":"#/$defs/Log"},"expect_error":{"type":"boolean"},"retry_once":{"type":"boolean"},"isolated":{"type":"boolean"}},"additionalProperties":false,"type":"object"},"Response":{"properties":{"headers":{"additionalProperties":{"type":"string"},"type":"object"},"status":{"type":"integer"},"body":{"type":"string"},"encoded_body":{"type":"string"},"log_message":{"type":"string"}},"additionalProperties":false,"type":"object"},"Stage":{"properties":{"description":{"type":"string"},"input":{"$ref":"#/$defs/Input"},"output":{"$ref":"#/$defs/Output"}},"additionalProperties":false,"type":"object","required":["input","output"]},"Test":{"properties":{"test_title":{"type":"string"},"test_id":{"type":"integer"},"desc":{"type":"string"},"stages":{"items":{"$ref":"#/$defs/Stage"},"type":"array"},"tags":{"items":{"type":"string"},"type":"array"}},"additionalProperties":false,"type":"object","required":["test_id","stages"]}}}
\ No newline at end of file
diff --git a/types/types.go b/types/types.go
index 81f22c0..c6fef4c 100644
--- a/types/types.go
+++ b/types/types.go
@@ -91,6 +91,7 @@ type Test struct {
// description: |
// RuleId is the ID of the rule this test targets.
+ //
// This field is for internal use and not exposed via YAML.
// examples:
// - name: RuleId
@@ -99,6 +100,7 @@ type Test struct {
// description: |
// TestId is the ID of the test, in relation to `rule_id`.
+ //
// When this field is not set, the ID will be inferred from the
// position.
// examples:
@@ -108,6 +110,7 @@ type Test struct {
// description: |
// TestDescription is the description for this particular test.
+ //
// Should be used to describe the internals of the specific things this test is targeting.
// examples:
// - value: ExampleTest.TestDescription
@@ -271,7 +274,8 @@ type Input struct {
// description: |
// AutocompleteHeaders allows the test framework to automatically fill the request with Content-Type and Connection headers.
- // Defaults to true.
+ //
+ // Defaults: `true`.
// examples:
// - name: StopMagic
// value: false
@@ -279,6 +283,7 @@ type Input struct {
// description: |
// EncodedRequest will take a base64 encoded string that will be decoded and sent through as the request.
+ //
// It will override all other settings
// examples:
// - name: EncodedRequest
@@ -296,12 +301,29 @@ type Input struct {
// description: |
// Response describes a response from the web server that a WAF is expected to analyse.
+ //
// Note: This functionality requires a backend that can send the specified request to the
// reverse proxy. Currently, only Albedo (https://github.com/coreruleset/albedo) is supported.
// example:
- // - name Response
+ // - name: Response
// value: ExampleResponse
Response Response `yaml:"response,omitempty" json:"response,omitempty" koanf:"response,omitempty"`
+
+ // description: |
+ // VirtualHostMode determines the value of the `Host` header for internal requests (e.g., the
+ // requests used to insert markers into the web server log). This is useful for running tests
+ // against a virtual host, as the log entries for all requests must end up in the same log file,
+ // and often, log files are segregated by virtual host.
+ //
+ // If `true`, internal requests will use the same value for the `Host` header as the test request.
+ //
+ // If `false`, the value for the `Host` header of internal requests will be `localhost`.
+ //
+ // Default: `false`.
+ // example:
+ // - name: VirtualHostMode
+ // value: true
+ VirtualHostMode bool `yaml:"virtual_host_mode,omitempty" json:"virtual_host_mode,omitempty" koanf:"virtual_host_mode,omitempty"`
}
type Response struct {
@@ -313,7 +335,9 @@ type Response struct {
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty" koanf:"headers,omitempty"`
// description: |
- // Status describes the HTTP status code of the response. Defaults to `200` if omitted.
+ // Status describes the HTTP status code of the response.
+ //
+ // Default: `200` if omitted.
// examples:
// - name: Status
// value: 302
@@ -400,7 +424,8 @@ type Output struct {
// Isolated specifies that the test is expected to trigger a single rule only.
// If the rule triggers any other rule than the (single) one specified in
// expect_ids, the test fill be considered a failure.
- // Default: false
+ //
+ // Default: `false`
// examples:
// - name: Isolated
// value: true