diff --git a/spec/v2.0/ftw.md b/spec/v2.0/ftw.md
new file mode 100644
index 0000000..4f61c45
--- /dev/null
+++ b/spec/v2.0/ftw.md
@@ -0,0 +1,1947 @@
+## 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:
+ - testtitle: 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
+```
+
+
+
+
+
+
+
+
+
+
+## Test
+
+Appears in:
+
+
+- FTWTest.tests
+
+
+```yaml
+- testtitle: 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_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
+```
+
+
+
+
+
+
+
+
+
+
+## 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
+
+
+
+
+Method 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 to 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=
+```
+
+
+
+
+
+
+
+
+
+
+## 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.
+
+
+
+
+
+
+
+
+
+## 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
[]int
+
+
+
+
+description: |
+ Expect the given IDs to be contained in the log output.
+ examples:
+ -value: ExampleLog.ExpectIds
+
+
+
+
+
+
+
+no_expect_ids
[]int
+
+
+
+
+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 is the response expected from the 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.
+
+
+
+
+
+
+
+
+
+## types.Log
+
+
+
+
+
+
+
+
+
+
+expect_ids
[]int
+
+
+
+
+description: |
+ Expect the given IDs to be contained in the log output.
+ examples:
+ -value: ExampleLog.ExpectIds
+
+
+
+
+
+
+
+no_expect_ids
[]int
+
+
+
+
+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/types/examples.go b/types/examples.go
index 1f4a330..f8cc71f 100644
--- a/types/examples.go
+++ b/types/examples.go
@@ -43,15 +43,15 @@ var (
}
ExampleOutput = Output{
Status: 200,
- ResponseContains: "",
+ ResponseContains: "HTTP/1.1",
LogContains: "nothing",
- NoLogContains: "",
+ NoLogContains: "everything",
Log: ExampleLog,
ExpectError: helpers.BoolPtr(true),
}
ExampleLog = Log{
- ExpectId: 123456,
- NoExpectId: 123456,
+ ExpectIds: []int{123456},
+ NoExpectIds: []int{123456},
MatchRegex: `id[:\s"]*123456`,
NoMatchRegex: `id[:\s"]*123456`,
}
@@ -61,4 +61,5 @@ var (
}
ReasonExample = "nginx returns 400 when `Content-Length` header is sent in a\n" +
"`Transfer-Encoding: chunked` request."
+ ExampleEncodedData = "c29tZXRoaW5nIHdpdGgKbmV3bGluZQo="
)
diff --git a/types/overrides/examples.go b/types/overrides/examples.go
index 2a6eb27..e94eda9 100644
--- a/types/overrides/examples.go
+++ b/types/overrides/examples.go
@@ -10,6 +10,7 @@ var (
AnnotationsExample = types.AnnotationsExample
ReasonExample = types.ReasonExample
ExampleLog = types.ExampleLog
+ ExampleOutput = types.ExampleOutput
MetaExample = FTWOverridesMeta{
Engine: "libmodsecurity3",
@@ -18,11 +19,10 @@ var (
}
TestOverridesExample = []TestOverride{
{
- RuleId: 920100,
- TestIds: []int{4, 6},
- Reason: types.ReasonExample,
- ExpectFailure: func() *bool { b := true; return &b }(),
- Output: types.ExampleOutput,
+ RuleId: 920100,
+ TestIds: []uint{4, 6},
+ Reason: types.ReasonExample,
+ Output: types.ExampleOutput,
},
}
)
diff --git a/types/overrides/types.go b/types/overrides/types.go
index c5c7df7..0631c9f 100644
--- a/types/overrides/types.go
+++ b/types/overrides/types.go
@@ -5,7 +5,9 @@
package overrides
-import "github.com/coreruleset/ftw-tests-schema/types"
+import (
+ "github.com/coreruleset/ftw-tests-schema/types"
+)
// FTWOverrides describes platform specific overrides for tests
type FTWOverrides struct {
@@ -55,14 +57,20 @@ type TestOverride struct {
// ID of the rule this test targets.
// examples:
// - value: TestOverridesExample[0].RuleId
- RuleId int `yaml:"rule_id"`
+ RuleId uint `yaml:"rule_id"`
// description: |
// 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:
// - value: TestOverridesExample[0].TestIds
- TestIds []int `yaml:"test_ids,flow,omitempty"`
+ TestIds []uint `yaml:"test_ids,flow,omitempty"`
+
+ // description: |
+ // 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.
+ StageIds []uint `yaml:"stage_ids,omitempty"`
// description: |
// Describes why this override is necessary.
@@ -70,13 +78,6 @@ type TestOverride struct {
// - value: ReasonExample
Reason string `yaml:"reason"`
- // description: |
- // Whether this test is expected to fail for this particular configuration.
- // Default: false
- // examples:
- // - value: true
- ExpectFailure *bool `yaml:"expect_failure,omitempty"`
-
// description: |
// Whether a stage should be retried once in case of failure.
// This option is primarily a workaround for a race condition in phase 5,
@@ -86,8 +87,9 @@ type TestOverride struct {
RetryOnce *bool `yaml:"retry_once,omitempty"`
// description: |
- // Specifies overrides on the test output
+ // Specifies overrides on the test output.
+ // This definition *replaces* the output definition of the test.
// examples:
- // - value: 400
+ // - value: ExampleOutput
Output types.Output `yaml:"output"`
}
diff --git a/types/overrides/types_test.go b/types/overrides/types_test.go
index 324ea86..07aa1de 100644
--- a/types/overrides/types_test.go
+++ b/types/overrides/types_test.go
@@ -24,13 +24,14 @@ test_overrides:
reason: |-
nginx returns 400 when ` + "`" + "Content-Length" + "`" + ` header is sent in a
` + "`" + `Transfer-Encoding: chunked` + "`" + ` request.
- expect_failure: true
output:
status: 200
+ response_contains: "HTTP/1.1"
log_contains: "nothing"
+ no_log_contains: "everything"
log:
- expect_id: 123456
- no_expect_id: 123456
+ expect_ids: [123456]
+ no_expect_ids: [123456]
match_regex: 'id[:\s"]*123456'
no_match_regex: 'id[:\s"]*123456'
expect_error: true
@@ -95,7 +96,6 @@ func TestUnmarmarshalTestOverrides(t *testing.T) {
asserter.Equal(expectedTestOverride.RuleId, testOverride.RuleId)
asserter.ElementsMatch(expectedTestOverride.TestIds, testOverride.TestIds)
asserter.Equal(expectedTestOverride.Reason, testOverride.Reason)
- asserter.Equal(expectedTestOverride.ExpectFailure, testOverride.ExpectFailure)
if !assert.ObjectsAreEqual(expectedTestOverride.Output, testOverride.Output) {
asserter.Failf("Output:", "%v != %v", testOverride.Output, expectedTestOverride.Output)
}
diff --git a/types/types.go b/types/types.go
index 8de2617..cb5d520 100644
--- a/types/types.go
+++ b/types/types.go
@@ -5,6 +5,8 @@
package types
+import "fmt"
+
// 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
@@ -17,11 +19,11 @@ type FTWTest struct {
Meta FTWTestMeta `yaml:"meta"`
// description: |
- // FileName is the name of the file where these tests are.
+ // RuleId is the ID of the rule this test targets.
// examples:
- // - name: FileName
- // value: "\"test-1234.yaml\""
- FileName string
+ // - name: RuleId
+ // value: 123456
+ RuleId uint `yaml:"rule_id"`
// description: |
// Tests is a list of FTW tests
@@ -70,7 +72,7 @@ type FTWTestMeta struct {
Version string `yaml:"version,omitempty"`
}
-// Test is an individual types. One test can have multiple stages.
+// Test is an individual test case. One test can have multiple stages.
type Test struct {
// description: |
// TestTitle is the title of this particular types. It is used for inclusion/exclusion of each run by the tool.
@@ -78,43 +80,48 @@ type Test struct {
// - value: ExampleTest.TestTitle
//
// Deprecated: use `rule_id` and `test_id`
- TestTitle string `yaml:"test_title,omitempty"`
+ TestTitle string
// description: |
- // RuleId is the ID of the rule this test targets
+ // RuleId is the ID of the rule this test targets.
+ // This field is for internal use and not exposed via YAML.
// examples:
// - name: RuleId
// value: 123456
- RuleId int `yaml:"rule_id"`
+ RuleId uint
// description: |
- // TestId is the ID of the test, in relation to `rule_id`
+ // 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:
// - name: TestId
// value: 4
- TestId int `yaml:"test_id"`
+ TestId uint `yaml:"test_id"`
// description: |
- // TestDescription is the description for this particular types. Should be used to describe the internals of
- // the specific things this test is targeting.
+ // 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
TestDescription string `yaml:"desc,omitempty"`
// description: |
- // Stages is the list of all the stages to perform this types.
+ // Stages is the list of all the stages to perform this test.
// examples:
// - value: ExampleStages
Stages []Stage `yaml:"stages"`
}
+// IdString prints the human readable ID of a test in the format
+// -. This format is also used when matching
+// the include / exclude regular expressions.
+func (t *Test) IdString() string {
+ return fmt.Sprintf("%d-%d", t.RuleId, t.TestId)
+}
+
// Stage is a list of stages
type Stage struct {
- // description: |
- // StageData is an individual test stage.
- //
- // Deprecated: use the other fields of `Stage`
- SD StageData `yaml:"stage,omitempty"`
// description: |
// Describes the purpose of this stage.
// examples:
@@ -156,7 +163,6 @@ type StageData struct {
}
// Input represents the input request in a stage
-// The fields `Version`, `Method` and `URI` we want to explicitly now when they are set to ""
type Input struct {
// description: |
// DestAddr is the IP of the destination host that the test will send the message to.
@@ -186,6 +192,16 @@ type Input struct {
// value: "\"/get?hello=world\""
URI *string `yaml:"uri,omitempty" koanf:"uri,omitempty"`
+ // description: |
+ // 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:
+ // - name: follow_redirect
+ // value: true
+ FollowRedirect *bool `yaml:"follow_redirect,omitempty" koanf:"follow_redirect,omitempty"`
+
// description: |
// Version allows you to declare the HTTP version the test should use as part of the request line.
// examples:
@@ -214,6 +230,15 @@ type Input struct {
// value: "\"Bibitti bopi\""
Data *string `yaml:"data,omitempty" koanf:"data,omitempty"`
+ // description: |
+ // 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:
+ // - name: encoded_data
+ // value: ExampleEncodedData
+ EncodedData *string `yaml:"encoded_data,omitempty" koanf:"encoded_data,omitempty"`
+
// description: |
// SaveCookie allows you to automatically provide cookies if there are multiple stages and save cookie is set
// examples:
@@ -302,21 +327,27 @@ type Output struct {
// - name: ExpectError
// value: false
ExpectError *bool `yaml:"expect_error,omitempty"`
+
+ // description: |
+ // 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.
+ RetryOnce *bool `yaml:"retry_once,omitempty"`
}
// Log is used to configure expectations about the log contents.
type Log struct {
// description: |
- // Expect the given ID to be contained in the log output.
+ // Expect the given IDs to be contained in the log output.
// examples:
- // - value: ExampleLog.ExpectId
- ExpectId int `yaml:"expect_id,omitempty"`
+ // -value: ExampleLog.ExpectIds
+ ExpectIds []int `yaml:"expect_ids,omitempty"`
// description: |
- // Expect the given ID _not_ to be contained in the log output.
+ // Expect the given IDs _not_ to be contained in the log output.
// examples:
- // - value: ExampleLog.NoExpectId
- NoExpectId int `yaml:"no_expect_id,omitempty"`
+ // - value: ExampleLog.NoExpectIds
+ NoExpectIds []int `yaml:"no_expect_ids,omitempty"`
// description: |
// Expect the regular expression to match log content for the current types.
diff --git a/types/types_test.go b/types/types_test.go
index 34a9d05..f8ce8fe 100644
--- a/types/types_test.go
+++ b/types/types_test.go
@@ -13,16 +13,14 @@ import (
)
var testYaml = `---
-filename: "testYaml.yaml"
meta:
author: "ftw-tests-schema"
enabled: true
name: "testYaml"
description: "Simple YAML to test that the schema is working."
+rule_id: 123456
tests:
- - test_title: 1234-1
- rule_id: 1234
- test_id: 1
+ - test_id: 1
desc: "Test that the schema is working."
stages:
- input:
@@ -43,9 +41,8 @@ tests:
status: 200
response_contains: ""
log_contains: "nothing"
- no_log_contains: ""
- - test_title: 1234-2
- stages:
+ no_log_contains: "everything"
+ - stages:
- input:
dest_addr: "127.0.0.1"
port: 80
@@ -58,7 +55,7 @@ tests:
`
var ftwTest = &FTWTest{
- FileName: "testYaml.yaml",
+ RuleId: 123456,
Meta: FTWTestMeta{
Author: "ftw-tests-schema",
Enabled: helpers.BoolPtr(true),
@@ -67,8 +64,6 @@ var ftwTest = &FTWTest{
},
Tests: []Test{
{
- TestTitle: "1234-1",
- RuleId: 1234,
TestId: 1,
TestDescription: "Test that the schema is working.",
Stages: []Stage{
@@ -80,7 +75,6 @@ var ftwTest = &FTWTest{
},
},
{
- TestTitle: "1234-2",
Stages: []Stage{
{
Input: Input{
@@ -108,7 +102,7 @@ func TestUnmarshalFTWTest(t *testing.T) {
err := yaml.Unmarshal([]byte(testYaml), &ftw)
assertions.NoError(err)
- assertions.Equal(ftwTest.FileName, ftw.FileName)
+ assertions.Equal(ftwTest.RuleId, ftw.RuleId)
assertions.Equal(ftwTest.Meta.Author, ftw.Meta.Author)
assertions.Equal(ftwTest.Meta.Enabled, ftw.Meta.Enabled)
assertions.Equal(ftwTest.Meta.Name, ftw.Meta.Name)
@@ -118,7 +112,6 @@ func TestUnmarshalFTWTest(t *testing.T) {
for i, test := range ftw.Tests {
expectedTest := ftwTest.Tests[i]
assertions.Equal(expectedTest.TestTitle, test.TestTitle)
- assertions.Equal(expectedTest.RuleId, test.RuleId)
assertions.Equal(expectedTest.TestId, test.TestId)
assertions.Len(test.Stages, len(expectedTest.Stages))