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 FTWTestMeta + +
+
+ +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 []Test + +
+
+ +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 []Stage + +
+
+ +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 Input + +
+
+ +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 Output + +
+
+ +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 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 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 FTWOverridesMeta + +
+
+ +Meta describes the metadata information + + + +Examples: + + +```yaml +meta: + engine: libmodsecurity3 + platform: nginx + annotations: + os: Debian Bullseye + purpose: L7ASR test suite +``` + + +
+ +
+ +
+ +test_overrides []TestOverride + +
+
+ +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 +``` + + +
+ +
+ +
+ +output types.Output + +
+
+ +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))