diff --git a/cmd/generate-doc-yaml-schema/main.go b/cmd/generate-doc-yaml-schema/main.go
index e76ac81..1a0f473 100644
--- a/cmd/generate-doc-yaml-schema/main.go
+++ b/cmd/generate-doc-yaml-schema/main.go
@@ -1,4 +1,4 @@
-// Copyright 2023 Felipe Zipitria
+// Copyright 2023 OWASP CRS
// SPDX-License-Identifier: Apache-2.0
package main
@@ -6,11 +6,20 @@ package main
import (
"os"
- "github.com/coreruleset/ftw-tests-schema/test"
+ "github.com/coreruleset/ftw-tests-schema/types"
)
func main() {
- data, err := test.GetFTWTestDoc().Encode()
+ data, err := types.GetFTWTestDoc().Encode()
+ if err != nil {
+ panic(err)
+ }
+ _, err = os.Stdout.Write(data)
+ if err != nil {
+ panic(err)
+ }
+
+ data, err = types.GetFTWOverridesDoc().Encode()
if err != nil {
panic(err)
}
diff --git a/go.mod b/go.mod
index 3cc4b1e..fa04b94 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,11 @@ go 1.21
require github.com/magefile/mage v1.15.0
+require (
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+)
+
require (
// Goccy verion is expected to stay at 1.9.2 (Same of go-ftw) because of https://github.com/goccy/go-yaml/issues/325
github.com/goccy/go-yaml v1.9.2
@@ -14,6 +19,7 @@ require (
github.com/fatih/color v1.10.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
+ github.com/stretchr/testify v1.8.4
golang.org/x/sys v0.6.0 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/go.sum b/go.sum
index e5f645a..b3697e2 100644
--- a/go.sum
+++ b/go.sum
@@ -26,8 +26,8 @@ github.com/projectdiscovery/yamldoc-go v1.0.4 h1:eZoESapnMw6WAHiVgRwNqvbJEfNHEH1
github.com/projectdiscovery/yamldoc-go v1.0.4/go.mod h1:8PIPRcUD55UbtQdcfFR1hpIGRWG0P7alClXNGt1TBik=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
-github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
diff --git a/mage.go b/mage.go
index 580ba72..3144464 100644
--- a/mage.go
+++ b/mage.go
@@ -1,4 +1,4 @@
-// Copyright 2022 Juan Pablo Tosso and the OWASP Coraza contributors
+// Copyright 2023 CRS
// SPDX-License-Identifier: Apache-2.0
//go:build ignore
diff --git a/magefile.go b/magefile.go
index 399bb52..b3e2bee 100644
--- a/magefile.go
+++ b/magefile.go
@@ -1,4 +1,4 @@
-// Copyright 2023 Felipe Zipitria
+// Copyright 2023 CRS
// SPDX-License-Identifier: Apache-2.0
//go:build mage
@@ -36,7 +36,7 @@ func Format() error {
// addlicense strangely logs skipped files to stderr despite not being erroneous, so use the long sh.Exec form to
// discard stderr too.
if _, err := sh.Exec(map[string]string{}, io.Discard, io.Discard, "go", "run", fmt.Sprintf("github.com/google/addlicense@%s", addLicenseVersion),
- "-c", "Felipe Zipitria",
+ "-c", "OWASP CRS",
"-s=only",
"-ignore", "**/*.yml",
"-ignore", "**/*.yaml",
diff --git a/overrides/overrides.go b/overrides/overrides.go
deleted file mode 100644
index 4e63bca..0000000
--- a/overrides/overrides.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package overrides
-
-import "github.com/coreruleset/ftw-tests-schema/test"
-
-// Copyright 2023 Felipe Zipitria
-// SPDX-License-Identifier: Apache-2.0
-
-//go:generate dstdocgen -package overrides -path . -structure FTWOverrides -output ./overrides_doc.go
-
-var (
- metaExample = Meta{
- Engine: "libmodsecurity3",
- Platform: "nginx",
- Annotations: annotationsExample,
- }
- annotationsExample = map[string]string{
- "os": "Debian Bullseye",
- "purpose": "Bullseye integration",
- }
- reasonExample = "nginx returns 400 when `Content-Length` header is sent in a\n" +
- "`Transfer-Encoding: chunked` request."
-
- testOverridesExample = []TestOverride{
- TestOverride{
- RuleId: 920100,
- TestId: 4,
- Reason: reasonExample,
- ExpectFailure: true,
- Output: test.ExampleOutput,
- },
- }
-)
-
-// TODO: 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.
-
-// TODO: FTWTest is the base type used when unmarshaling YAML tests files
-type FTWOverrides struct {
- // description: |
- // The version field designates the version of the schema that validates this file
- // examples:
- // - value: "\"v0.1.0\""
- Version string `yaml:"version"`
-
- // description: |
- // Meta describes the metadata information
- // examples:
- // - value: metaExample
- Meta Meta `yaml:"meta"`
-
- // description: |
- // List of test override specifications
- // examples:
- // - value: testOverridesExample
- TestOverrides []TestOverride `yaml:"test_overrides"`
-}
-
-// Meta describes the metadata information of this yaml file
-type Meta struct {
- // description: |
- // The name of the WAF engine the tests are expected to run against
- // examples:
- // - value: "\"coraza\""
- Engine string `yaml:"engine"`
-
- // description: |
- // The name of the platform (e.g., web server) the tests are expected to run against
- // examples:
- // - value: "\"nginx\""
- Platform string `yaml:"platform"`
-
- // description: |
- // Custom annotations; can be used to add additional meta information
- // examples:
- // - value: annotationsExample
- Annotations map[string]string `yaml:"annotations"`
-}
-
-// TestOverride describes overrides for a single test
-type TestOverride struct {
- // description: |
- // ID of the rule this test targets.
- // If this field is not empty, `test_id` must also be set.
- // examples:
- // - value: "\"920100\""
- RuleId int `yaml:"rule_id,omitempty"`
-
- // description: |
- // ID of the test this override applies to.
- // If this field is not empty, `rule_id` must also be set.
- // examples:
- // - value: 5
- TestId int `yaml:"test_id,omitempty"`
-
- // description: |
- // Regular expression matching test names (can match multiple tests).
- // If this field is empty, `rule_id` and `test_id` must be set.
- // examples:
- // - value: "\"^910.*$\""
- NameRegex string `yaml:"name_regex,omitempty"`
-
- // description: |
- // Describes why this override is necessary.
- // examples:
- // - 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: |
- // Specifies overrides on the test output
- // examples:
- // - value: 400
- Output test.Output `yaml:"output"`
-}
diff --git a/overrides/overrides_doc.go b/overrides/overrides_doc.go
deleted file mode 100644
index a8916d7..0000000
--- a/overrides/overrides_doc.go
+++ /dev/null
@@ -1,194 +0,0 @@
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-// DO NOT EDIT: this file is automatically generated by docgen
-package overrides
-
-import (
- "github.com/projectdiscovery/yamldoc-go/encoder"
-)
-
-var (
- FTWOverridesDoc encoder.Doc
- MetaDoc encoder.Doc
- TestOverrideDoc encoder.Doc
- TESTOutputDoc encoder.Doc
-)
-
-func init() {
- FTWOverridesDoc.Type = "FTWOverrides"
- FTWOverridesDoc.Comments[encoder.LineComment] = " TODO: Welcome to the FTW YAMLFormat documentation."
- FTWOverridesDoc.Description = "TODO: Welcome to the FTW YAMLFormat documentation.\n In this document we will explain all the possible options that can be used within the YAML format.\n Generally this is the preferred format for writing tests in as they don't require any programming skills\n in order to understand and change. If you find a bug in this format please open an issue.\n\n\n TODO: FTWTest is the base type used when unmarshaling YAML tests files"
- FTWOverridesDoc.Fields = make([]encoder.Doc, 3)
- FTWOverridesDoc.Fields[0].Name = "version"
- FTWOverridesDoc.Fields[0].Type = "string"
- FTWOverridesDoc.Fields[0].Note = ""
- FTWOverridesDoc.Fields[0].Description = "The version field designates the version of the schema that validates this file"
- FTWOverridesDoc.Fields[0].Comments[encoder.LineComment] = "The version field designates the version of the schema that validates this file"
-
- FTWOverridesDoc.Fields[0].AddExample("", "v0.1.0")
- FTWOverridesDoc.Fields[1].Name = "meta"
- FTWOverridesDoc.Fields[1].Type = "Meta"
- FTWOverridesDoc.Fields[1].Note = ""
- FTWOverridesDoc.Fields[1].Description = "Meta describes the metadata information"
- FTWOverridesDoc.Fields[1].Comments[encoder.LineComment] = "Meta describes the metadata information"
-
- FTWOverridesDoc.Fields[1].AddExample("", metaExample)
- FTWOverridesDoc.Fields[2].Name = "test_overrides"
- FTWOverridesDoc.Fields[2].Type = "[]TestOverride"
- FTWOverridesDoc.Fields[2].Note = ""
- FTWOverridesDoc.Fields[2].Description = "List of test override specifications"
- FTWOverridesDoc.Fields[2].Comments[encoder.LineComment] = "List of test override specifications"
-
- FTWOverridesDoc.Fields[2].AddExample("", testOverridesExample)
-
- MetaDoc.Type = "Meta"
- MetaDoc.Comments[encoder.LineComment] = ""
- MetaDoc.Description = ""
-
- MetaDoc.AddExample("", metaExample)
- MetaDoc.AppearsIn = []encoder.Appearance{
- {
- TypeName: "FTWOverrides",
- FieldName: "meta",
- },
- }
- MetaDoc.Fields = make([]encoder.Doc, 3)
- MetaDoc.Fields[0].Name = "engine"
- MetaDoc.Fields[0].Type = "string"
- MetaDoc.Fields[0].Note = ""
- MetaDoc.Fields[0].Description = "The name of the WAF engine the tests are expected to run against"
- MetaDoc.Fields[0].Comments[encoder.LineComment] = "The name of the WAF engine the tests are expected to run against"
-
- MetaDoc.Fields[0].AddExample("", "coraza")
- MetaDoc.Fields[1].Name = "platform"
- MetaDoc.Fields[1].Type = "string"
- MetaDoc.Fields[1].Note = ""
- MetaDoc.Fields[1].Description = "The name of the platform (e.g., web server) the tests are expected to run against"
- MetaDoc.Fields[1].Comments[encoder.LineComment] = "The name of the platform (e.g., web server) the tests are expected to run against"
-
- MetaDoc.Fields[1].AddExample("", "nginx")
- MetaDoc.Fields[2].Name = "annotations"
- MetaDoc.Fields[2].Type = "map[string]string"
- MetaDoc.Fields[2].Note = ""
- MetaDoc.Fields[2].Description = "Custom annotations; can be used to add additional meta information"
- MetaDoc.Fields[2].Comments[encoder.LineComment] = "Custom annotations; can be used to add additional meta information"
-
- MetaDoc.Fields[2].AddExample("", annotationsExample)
-
- TestOverrideDoc.Type = "TestOverride"
- TestOverrideDoc.Comments[encoder.LineComment] = ""
- TestOverrideDoc.Description = ""
-
- TestOverrideDoc.AddExample("", testOverridesExample)
- TestOverrideDoc.AppearsIn = []encoder.Appearance{
- {
- TypeName: "FTWOverrides",
- FieldName: "test_overrides",
- },
- }
- TestOverrideDoc.Fields = make([]encoder.Doc, 6)
- TestOverrideDoc.Fields[0].Name = "rule_id"
- TestOverrideDoc.Fields[0].Type = "int"
- TestOverrideDoc.Fields[0].Note = ""
- TestOverrideDoc.Fields[0].Description = "ID of the rule this test targets.\nIf this field is not empty, `test_id` must also be set."
- TestOverrideDoc.Fields[0].Comments[encoder.LineComment] = "ID of the rule this test targets."
-
- TestOverrideDoc.Fields[0].AddExample("", "920100")
- TestOverrideDoc.Fields[1].Name = "test_id"
- TestOverrideDoc.Fields[1].Type = "int"
- TestOverrideDoc.Fields[1].Note = ""
- TestOverrideDoc.Fields[1].Description = "ID of the test this override applies to.\nIf this field is not empty, `rule_id` must also be set."
- TestOverrideDoc.Fields[1].Comments[encoder.LineComment] = "ID of the test this override applies to."
-
- TestOverrideDoc.Fields[1].AddExample("", 5)
- TestOverrideDoc.Fields[2].Name = "name_regex"
- TestOverrideDoc.Fields[2].Type = "string"
- TestOverrideDoc.Fields[2].Note = ""
- TestOverrideDoc.Fields[2].Description = "Regular expression matching test names (can match multiple tests).\nIf this field is empty, `rule_id` and `test_id` must be set."
- TestOverrideDoc.Fields[2].Comments[encoder.LineComment] = "Regular expression matching test names (can match multiple tests)."
-
- TestOverrideDoc.Fields[2].AddExample("", "^910.*$")
- TestOverrideDoc.Fields[3].Name = "reason"
- TestOverrideDoc.Fields[3].Type = "string"
- TestOverrideDoc.Fields[3].Note = ""
- TestOverrideDoc.Fields[3].Description = "Describes why this override is necessary."
- TestOverrideDoc.Fields[3].Comments[encoder.LineComment] = "Describes why this override is necessary."
-
- TestOverrideDoc.Fields[3].AddExample("", reasonExample)
- TestOverrideDoc.Fields[4].Name = "expect_failure"
- TestOverrideDoc.Fields[4].Type = "bool"
- TestOverrideDoc.Fields[4].Note = ""
- TestOverrideDoc.Fields[4].Description = "Whether this test is expected to fail for this particular configuration.\nDefault: false"
- TestOverrideDoc.Fields[4].Comments[encoder.LineComment] = "Whether this test is expected to fail for this particular configuration."
-
- TestOverrideDoc.Fields[4].AddExample("", true)
- TestOverrideDoc.Fields[5].Name = "output"
- TestOverrideDoc.Fields[5].Type = "test.Output"
- TestOverrideDoc.Fields[5].Note = ""
- TestOverrideDoc.Fields[5].Description = "Specifies overrides on the test output"
- TestOverrideDoc.Fields[5].Comments[encoder.LineComment] = "Specifies overrides on the test output"
-
- TestOverrideDoc.Fields[5].AddExample("", 400)
-
- TESTOutputDoc.Type = "test.Output"
- TESTOutputDoc.Comments[encoder.LineComment] = " Output is the response expected from the test"
- TESTOutputDoc.Description = "Output is the response expected from the test"
-
- TESTOutputDoc.AddExample("", 400)
- TESTOutputDoc.AppearsIn = []encoder.Appearance{
- {
- TypeName: "TestOverride",
- FieldName: "output",
- },
- }
- TESTOutputDoc.Fields = make([]encoder.Doc, 5)
- TESTOutputDoc.Fields[0].Name = "status"
- TESTOutputDoc.Fields[0].Type = "[]int"
- TESTOutputDoc.Fields[0].Note = ""
- TESTOutputDoc.Fields[0].Description = "description: |\n Status describes the HTTP status error code expected as response.\n examples:\n - name: Status\n value: [200]"
- TESTOutputDoc.Fields[0].Comments[encoder.LineComment] = " description: |"
-
- TESTOutputDoc.Fields[1].Name = "response_contains"
- TESTOutputDoc.Fields[1].Type = "string"
- TESTOutputDoc.Fields[1].Note = ""
- TESTOutputDoc.Fields[1].Description = "ResponseContains describes the text that should be contained in the HTTP response."
- TESTOutputDoc.Fields[1].Comments[encoder.LineComment] = "ResponseContains describes the text that should be contained in the HTTP response."
-
- TESTOutputDoc.Fields[1].AddExample("ResponseContains", "Hello, World")
- TESTOutputDoc.Fields[2].Name = "log_contains"
- TESTOutputDoc.Fields[2].Type = "string"
- TESTOutputDoc.Fields[2].Note = ""
- TESTOutputDoc.Fields[2].Description = "LogContains describes the text that should be contained in the WAF logs."
- TESTOutputDoc.Fields[2].Comments[encoder.LineComment] = "LogContains describes the text that should be contained in the WAF logs."
-
- TESTOutputDoc.Fields[2].AddExample("LogContains", "id 920100")
- TESTOutputDoc.Fields[3].Name = "no_log_contains"
- TESTOutputDoc.Fields[3].Type = "string"
- TESTOutputDoc.Fields[3].Note = ""
- TESTOutputDoc.Fields[3].Description = "NoLogContains describes the text that should be contained in the WAF logs."
- TESTOutputDoc.Fields[3].Comments[encoder.LineComment] = "NoLogContains describes the text that should be contained in the WAF logs."
-
- TESTOutputDoc.Fields[3].AddExample("NoLogContains", "id 920100")
- TESTOutputDoc.Fields[4].Name = "expect_error"
- TESTOutputDoc.Fields[4].Type = "bool"
- TESTOutputDoc.Fields[4].Note = ""
- TESTOutputDoc.Fields[4].Description = "When `ExpectError` is true, we don't expect an answer from the WAF, just an error."
- TESTOutputDoc.Fields[4].Comments[encoder.LineComment] = "When `ExpectError` is true, we don't expect an answer from the WAF, just an error."
-
- TESTOutputDoc.Fields[4].AddExample("ExpectError", false)
-}
-
-// GetFTWOverridesDoc returns documentation for the file ./overrides_doc.go.
-func GetFTWOverridesDoc() *encoder.FileDoc {
- return &encoder.FileDoc{
- Name: "FTWOverrides",
- Description: "",
- Structs: []*encoder.Doc{
- &FTWOverridesDoc,
- &MetaDoc,
- &TestOverrideDoc,
- &TESTOutputDoc,
- },
- }
-}
diff --git a/overrides/overrides_test.go b/overrides/overrides_test.go
deleted file mode 100644
index f01cebb..0000000
--- a/overrides/overrides_test.go
+++ /dev/null
@@ -1,207 +0,0 @@
-package overrides
-
-import (
- "testing"
-
- "github.com/coreruleset/ftw-tests-schema/test"
- "github.com/goccy/go-yaml"
-)
-
-var testInput = `---
-dest_addr: "127.0.0.1"
-port: 80
-headers:
- User-Agent: "FTW Schema Tests"
- Host: "localhost"
-`
-
-var testYaml = `---
-filename: "testYaml.yaml"
-meta:
- author: "ftw-tests-schema"
- enabled: true
- name: "testYaml"
- description: "Simple YAML to test that the schema is working."
-tests:
- - test_title: 1234-1
- desc: "Test that the schema is working."
- stages:
- - stage:
- input:
- dest_addr: "127.0.0.1"
- port: 80
- headers:
- User-Agent: "FTW Schema Tests"
- Host: "localhost"
- output:
- no_log_contains: 'id "1234"'
- - test_title: 1234-2
- stages:
- - stage:
- input:
- dest_addr: "127.0.0.1"
- port: 80
- method: "OPTIONS"
- headers:
- User-Agent: "FTW Schema Tests"
- Host: "localhost"
- output:
- status: [200, 204]
-`
-var overridesYaml = `---
-meta:
- engine: "libmodsecurity3"
- platform: "nginx"
- annotations:
- - os: "Debian Bullseye"
- - purpose: "CRS test suite"
-test_overrides:
- - rule_id: "920110"
- test_id: "4"
- reason: "just doesn't work"
- expect_failure: true
-`
-
-var inputTest = &test.Input{
- DestAddr: strPtr("127.0.0.1"),
- Port: intPtr(80),
- Headers: map[string]string{
- "User-Agent": "FTW Schema Tests",
- "Host": "localhost",
- },
-}
-
-var ftwTest = &FTWTest{
- FileName: "testYaml.yaml",
- Meta: Meta{
- Author: "ftw-tests-schema",
- Enabled: true,
- Name: "testYaml",
- Description: "Simple YAML to test that the schema is working.",
- },
- Tests: []Test{
- {
- TestTitle: "1234-1",
- TestDescription: "Test that the schema is working.",
- Stages: []Stage{
- {
- SD: StageData{
- Input: Input{
- DestAddr: strPtr("127.0.0.1"),
- Port: intPtr(80),
- Headers: map[string]string{
- "User-Agent": "FTW Schema Tests",
- "Host": "localhost",
- },
- },
- Output: Output{
- NoLogContains: "id \"1234\"",
- },
- },
- },
- },
- },
- {
- TestTitle: "1234-2",
- Stages: []Stage{
- {
- SD: StageData{
- Input: Input{
- DestAddr: strPtr("127.0.0.1"),
- Port: intPtr(80),
- Method: strPtr("OPTIONS"),
- Headers: map[string]string{
- "User-Agent": "FTW Schema Tests",
- "Host": "localhost",
- },
- },
- Output: Output{
- Status: []int{200, 204},
- },
- },
- },
- },
- },
- },
-}
-
-func TestUnmarshalFTWTest(t *testing.T) {
- var ftw FTWTest
-
- err := yaml.Unmarshal([]byte(testYaml), &ftw)
-
- if err != nil {
- t.Errorf("Unmarshal: %v", err)
- }
-
- if ftw.FileName != ftwTest.FileName {
- t.Errorf("FileName: %v != %v", ftw.FileName, ftwTest.FileName)
- }
- if ftw.Meta.Author != ftwTest.Meta.Author {
- t.Errorf("Author: %v != %v", ftw.Meta.Author, ftwTest.Meta.Author)
- }
- if ftw.Meta.Enabled != ftwTest.Meta.Enabled {
- t.Errorf("Enabled: %v != %v", ftw.Meta.Enabled, ftwTest.Meta.Enabled)
- }
- if ftw.Meta.Name != ftwTest.Meta.Name {
- t.Errorf("Name: %v != %v", ftw.Meta.Name, ftwTest.Meta.Name)
- }
- if ftw.Meta.Description != ftwTest.Meta.Description {
- t.Errorf("Description: %v != %v", ftw.Meta.Description, ftwTest.Meta.Description)
- }
- if len(ftw.Tests) != len(ftwTest.Tests) {
- t.Errorf("Tests: %v != %v", len(ftw.Tests), len(ftwTest.Tests))
- }
- for i, test := range ftw.Tests {
- if test.TestTitle != ftwTest.Tests[i].TestTitle {
- t.Errorf("TestTitle: %v != %v", test.TestTitle, ftwTest.Tests[i].TestTitle)
- }
- if len(test.Stages) != len(ftwTest.Tests[i].Stages) {
- t.Errorf("Stages: %v != %v", len(test.Stages), len(ftwTest.Tests[i].Stages))
- }
- for j, stage := range test.Stages {
- if *stage.SD.Input.DestAddr != *ftwTest.Tests[i].Stages[j].SD.Input.DestAddr {
- t.Errorf("DestAddr: %v != %v", *stage.SD.Input.DestAddr, *ftwTest.Tests[i].Stages[j].SD.Input.DestAddr)
- }
- if *stage.SD.Input.Port != *ftwTest.Tests[i].Stages[j].SD.Input.Port {
- t.Errorf("Port: %v != %v", *stage.SD.Input.Port, *ftwTest.Tests[i].Stages[j].SD.Input.Port)
- }
- if stage.SD.Input.Method != nil && *stage.SD.Input.Method != *ftwTest.Tests[i].Stages[j].SD.Input.Method {
- t.Errorf("Method: %v != %v", stage.SD.Input.Method, ftwTest.Tests[i].Stages[j].SD.Input.Method)
- }
- if len(stage.SD.Input.Headers) != len(ftwTest.Tests[i].Stages[j].SD.Input.Headers) {
- t.Errorf("Headers: %v != %v", len(stage.SD.Input.Headers), len(ftwTest.Tests[i].Stages[j].SD.Input.Headers))
- }
- for k, header := range stage.SD.Input.Headers {
- if header != ftwTest.Tests[i].Stages[j].SD.Input.Headers[k] {
- t.Errorf("Header: %v != %v", header, ftwTest.Tests[i].Stages[j].SD.Input.Headers[k])
- }
- }
- if stage.SD.Output.NoLogContains != ftwTest.Tests[i].Stages[j].SD.Output.NoLogContains {
- t.Errorf("NoLogContains: %v != %v", stage.SD.Output.NoLogContains, ftwTest.Tests[i].Stages[j].SD.Output.NoLogContains)
- }
- if len(stage.SD.Output.Status) != len(ftwTest.Tests[i].Stages[j].SD.Output.Status) {
- t.Errorf("Status: %v != %v", len(stage.SD.Output.Status), len(ftwTest.Tests[i].Stages[j].SD.Output.Status))
- }
- }
- }
-}
-
-func TestUnmarshalInput(t *testing.T) {
- var input Input
-
- err := yaml.Unmarshal([]byte(testInput), &input)
- if err != nil {
- t.Errorf("Unmarshal: %v", err)
- }
-
- if input.DestAddr != nil && *input.DestAddr != *inputTest.DestAddr {
- t.Errorf("DestAddr: %v != %v", *input.DestAddr, *inputTest.DestAddr)
- }
- if input.Port != nil && *input.Port != *inputTest.Port {
- t.Errorf("Port: %v != %v", *input.Port, *inputTest.Port)
- }
- if input.Method != nil && *input.Method != *inputTest.Method {
- t.Errorf("Method: %v != %v", *input.Method, *inputTest.Method)
- }
-}
diff --git a/spec/v1.1/ftw.md b/spec/v1.1/ftw.md
new file mode 100644
index 0000000..209981b
--- /dev/null
+++ b/spec/v1.1/ftw.md
@@ -0,0 +1,1769 @@
+
+
+
+
+## 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
+
+
+
+
+
+
+
+
+Tests is a list of FTW tests
+
+
+
+Examples:
+
+
+```yaml
+# Tests
+tests: the tests
+```
+
+
+
+
+
+
+
+
+
+
+## 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
+# Tests
+the tests
+```
+
+
+
+
+
+
+
+test_title
string
+
+
+
+
+TestTitle the title of this particular test. It is used for inclusion/exclusion of each run by the tool.
+
+
+
+Examples:
+
+
+```yaml
+# TestTitle
+test_title: 920100-1
+```
+
+
+
+
+
+
+
+
+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
+# TestDescription
+desc: This test targets something
+```
+
+
+
+
+
+
+
+
+
+Stages is the list of all the stages to perform this test.
+
+
+
+Examples:
+
+
+```yaml
+# Stages
+stages:
+ - stage:
+ 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
+ log_contains: nothing
+ log:
+ expect_id: 123456
+ expect_id: 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+ expect_error: true
+```
+
+
+
+
+
+
+
+
+
+
+## Stage
+
+Appears in:
+
+
+- Test.stages
+
+
+```yaml
+# Stages
+- stage:
+ 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
+ log_contains: nothing
+ log:
+ expect_id: 123456
+ expect_id: 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+ expect_error: true
+```
+
+
+
+
+
+
+
+
+StageData is an individual test stage
+
+
+
+Examples:
+
+
+```yaml
+# StageData
+stage:
+ 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
+ log_contains: nothing
+ log:
+ expect_id: 123456
+ expect_id: 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+ expect_error: true
+```
+
+
+
+
+
+
+
+
+
+
+## StageData
+
+Appears in:
+
+
+- Stage.stage
+
+
+```yaml
+# StageData
+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
+ log_contains: nothing
+ log:
+ expect_id: 123456
+ expect_id: 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+ expect_error: true
+```
+
+
+
+
+
+
+
+
+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
+ log_contains: nothing
+ log:
+ expect_id: 123456
+ expect_id: 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+ expect_error: true
+```
+
+
+
+
+
+
+
+
+
+
+## Input
+
+Appears in:
+
+
+- StageData.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
+```
+
+
+
+
+
+
+
+
+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
+```
+
+
+
+
+
+
+
+
+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:
+
+
+- StageData.output
+
+
+```yaml
+# Output
+status: 200
+log_contains: nothing
+log:
+ expect_id: 123456
+ expect_id: 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_id: 123456
+ expect_id: 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
+```
+
+
+
+
+
+
+
+
+
+
+## Log
+
+Appears in:
+
+
+- Output.log
+
+
+```yaml
+expect_id: 123456
+expect_id: 123456
+match_regex: id[:\s"]*123456
+no_match_regex: id[:\s"]*123456
+```
+
+
+
+
+
+
+
+expect_id
int
+
+
+
+
+description: |
+ Expect the given ID to be contained in the log output.
+ examples:
+ - exampleLog.ExpectId
+
+
+
+
+
+
+
+expect_id
int
+
+
+
+
+description: |
+ Expect the given ID _not_ to be contained in the log output.
+ examples:
+ - exampleLog.NoExpectId
+
+
+
+
+
+
+
+match_regex
string
+
+
+
+
+Expect the regular expression to match log content for the current test.
+
+
+
+Examples:
+
+
+```yaml
+match_regex: id[:\s"]*123456
+```
+
+
+
+
+
+
+
+
+no_match_regex
string
+
+
+
+
+Expect the regular expression to _not_ match log content for the current test.
+
+
+
+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.
+ expect_failure: true
+ output:
+ status: 200
+ log_contains: nothing
+ log:
+ expect_id: 123456
+ expect_id: 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.
+ expect_failure: true
+ output:
+ status: 200
+ log_contains: nothing
+ log:
+ expect_id: 123456
+ expect_id: 123456
+ match_regex: id[:\s"]*123456
+ no_match_regex: id[:\s"]*123456
+ expect_error: true
+```
+
+
+
+
+
+
+
+rule_id
int
+
+
+
+
+ID of the rule this test targets.
+
+
+
+Examples:
+
+
+```yaml
+rule_id: 920100
+```
+
+
+
+
+
+
+
+
+test_ids
[]int
+
+
+
+
+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: [5, 6]
+
+
+
+
+
+
+
+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.
+```
+
+
+
+
+
+
+
+
+expect_failure
bool
+
+
+
+
+Whether this test is expected to fail for this particular configuration.
+Default: false
+
+
+
+Examples:
+
+
+```yaml
+expect_failure: true
+```
+
+
+
+
+
+
+
+
+
+Specifies overrides on the test output
+
+
+
+Examples:
+
+
+```yaml
+output: 400
+```
+
+
+
+
+
+
+
+
+
+
+## Output
+
+Appears in:
+
+
+- TestOverride.output
+
+
+```yaml
+400
+```
+
+
+
+
+
+
+
+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_id: 123456
+ expect_id: 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
+```
+
+
+
+
+
+
+
+
+
+
+## Log
+
+Appears in:
+
+
+- Output.log
+
+
+```yaml
+expect_id: 123456
+expect_id: 123456
+match_regex: id[:\s"]*123456
+no_match_regex: id[:\s"]*123456
+```
+
+
+
+
+
+
+
+expect_id
int
+
+
+
+
+description: |
+ Expect the given ID to be contained in the log output.
+ examples:
+ - exampleLog.ExpectId
+
+
+
+
+
+
+
+expect_id
int
+
+
+
+
+description: |
+ Expect the given ID _not_ to be contained in the log output.
+ examples:
+ - exampleLog.NoExpectId
+
+
+
+
+
+
+
+match_regex
string
+
+
+
+
+Expect the regular expression to match log content for the current test.
+
+
+
+Examples:
+
+
+```yaml
+match_regex: id[:\s"]*123456
+```
+
+
+
+
+
+
+
+
+no_match_regex
string
+
+
+
+
+Expect the regular expression to _not_ match log content for the current test.
+
+
+
+Examples:
+
+
+```yaml
+no_match_regex: id[:\s"]*123456
+```
+
+
+
+
+
+
+
+
+
diff --git a/test/test_doc.go b/test/test_doc.go
deleted file mode 100644
index f348399..0000000
--- a/test/test_doc.go
+++ /dev/null
@@ -1,326 +0,0 @@
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-// DO NOT EDIT: this file is automatically generated by docgen
-package test
-
-import (
- "github.com/projectdiscovery/yamldoc-go/encoder"
-)
-
-var (
- FTWTestDoc encoder.Doc
- MetaDoc encoder.Doc
- TestDoc encoder.Doc
- StageDoc encoder.Doc
- StageDataDoc encoder.Doc
- InputDoc encoder.Doc
- OutputDoc encoder.Doc
-)
-
-func init() {
- FTWTestDoc.Type = "FTWTest"
- FTWTestDoc.Comments[encoder.LineComment] = " Welcome to the FTW YAMLFormat documentation."
- FTWTestDoc.Description = "Welcome to the FTW YAMLFormat documentation.\n In this document we will explain all the possible options that can be used within the YAML format.\n Generally this is the preferred format for writing tests in as they don't require any programming skills\n in order to understand and change. If you find a bug in this format please open an issue.\n\n\n FTWTest is the base type used when unmarshaling YAML tests files"
- FTWTestDoc.Fields = make([]encoder.Doc, 2)
- FTWTestDoc.Fields[0].Name = "meta"
- FTWTestDoc.Fields[0].Type = "Meta"
- FTWTestDoc.Fields[0].Note = ""
- FTWTestDoc.Fields[0].Description = "Meta describes the metadata information of this yaml test file"
- FTWTestDoc.Fields[0].Comments[encoder.LineComment] = "Meta describes the metadata information of this yaml test file"
- FTWTestDoc.Fields[1].Name = "tests"
- FTWTestDoc.Fields[1].Type = "[]Test"
- FTWTestDoc.Fields[1].Note = ""
- FTWTestDoc.Fields[1].Description = "Tests is a list of FTW tests"
- FTWTestDoc.Fields[1].Comments[encoder.LineComment] = "Tests is a list of FTW tests"
-
- FTWTestDoc.Fields[1].AddExample("Tests", "the tests")
-
- MetaDoc.Type = "Meta"
- MetaDoc.Comments[encoder.LineComment] = ""
- MetaDoc.Description = ""
- MetaDoc.AppearsIn = []encoder.Appearance{
- {
- TypeName: "FTWTest",
- FieldName: "meta",
- },
- }
- MetaDoc.Fields = make([]encoder.Doc, 5)
- MetaDoc.Fields[0].Name = "author"
- MetaDoc.Fields[0].Type = "string"
- MetaDoc.Fields[0].Note = ""
- MetaDoc.Fields[0].Description = "Author is the list of authors that added content to this file"
- MetaDoc.Fields[0].Comments[encoder.LineComment] = "Author is the list of authors that added content to this file"
-
- MetaDoc.Fields[0].AddExample("Author", "Felipe Zipitria")
- MetaDoc.Fields[1].Name = "enabled"
- MetaDoc.Fields[1].Type = "bool"
- MetaDoc.Fields[1].Note = ""
- MetaDoc.Fields[1].Description = "Enabled indicates if the tests are enabled to be run by the engine or not."
- MetaDoc.Fields[1].Comments[encoder.LineComment] = "Enabled indicates if the tests are enabled to be run by the engine or not."
-
- MetaDoc.Fields[1].AddExample("Enabled", false)
- MetaDoc.Fields[2].Name = "name"
- MetaDoc.Fields[2].Type = "string"
- MetaDoc.Fields[2].Note = ""
- MetaDoc.Fields[2].Description = "Name is the name of the tests contained in this file."
- MetaDoc.Fields[2].Comments[encoder.LineComment] = "Name is the name of the tests contained in this file."
-
- MetaDoc.Fields[2].AddExample("Name", "test01")
- MetaDoc.Fields[3].Name = "description"
- MetaDoc.Fields[3].Type = "string"
- MetaDoc.Fields[3].Note = ""
- MetaDoc.Fields[3].Description = "Description is a textual description of the tests contained in this file."
- MetaDoc.Fields[3].Comments[encoder.LineComment] = "Description is a textual description of the tests contained in this file."
-
- MetaDoc.Fields[3].AddExample("Description", "The tests here target SQL injection.")
- MetaDoc.Fields[4].Name = "version"
- MetaDoc.Fields[4].Type = "string"
- MetaDoc.Fields[4].Note = ""
- MetaDoc.Fields[4].Description = "Version is the version of the YAML Schema."
- MetaDoc.Fields[4].Comments[encoder.LineComment] = "Version is the version of the YAML Schema."
-
- MetaDoc.Fields[4].AddExample("Version", "v1")
-
- TestDoc.Type = "Test"
- TestDoc.Comments[encoder.LineComment] = ""
- TestDoc.Description = ""
-
- TestDoc.AddExample("Tests", "the tests")
- TestDoc.AppearsIn = []encoder.Appearance{
- {
- TypeName: "FTWTest",
- FieldName: "tests",
- },
- }
- TestDoc.Fields = make([]encoder.Doc, 3)
- TestDoc.Fields[0].Name = "test_title"
- TestDoc.Fields[0].Type = "string"
- TestDoc.Fields[0].Note = ""
- TestDoc.Fields[0].Description = "TestTitle the title of this particular test. It is used for inclusion/exclusion of each run by the tool."
- TestDoc.Fields[0].Comments[encoder.LineComment] = "TestTitle the title of this particular test. It is used for inclusion/exclusion of each run by the tool."
-
- TestDoc.Fields[0].AddExample("TestTitle", "920100-1")
- TestDoc.Fields[1].Name = "desc"
- TestDoc.Fields[1].Type = "string"
- TestDoc.Fields[1].Note = ""
- TestDoc.Fields[1].Description = "TestDescription is the description for this particular test. Should be used to describe the internals of\nthe specific things this test is targeting."
- TestDoc.Fields[1].Comments[encoder.LineComment] = "TestDescription is the description for this particular test. Should be used to describe the internals of"
-
- TestDoc.Fields[1].AddExample("TestDescription", "This test targets something")
- TestDoc.Fields[2].Name = "stages"
- TestDoc.Fields[2].Type = "[]Stage"
- TestDoc.Fields[2].Note = ""
- TestDoc.Fields[2].Description = "Stages is the list of all the stages to perform this test."
- TestDoc.Fields[2].Comments[encoder.LineComment] = "Stages is the list of all the stages to perform this test."
-
- TestDoc.Fields[2].AddExample("Stages", exampleStages)
-
- StageDoc.Type = "Stage"
- StageDoc.Comments[encoder.LineComment] = ""
- StageDoc.Description = ""
-
- StageDoc.AddExample("Stages", exampleStages)
- StageDoc.AppearsIn = []encoder.Appearance{
- {
- TypeName: "Test",
- FieldName: "stages",
- },
- }
- StageDoc.Fields = make([]encoder.Doc, 1)
- StageDoc.Fields[0].Name = "stage"
- StageDoc.Fields[0].Type = "StageData"
- StageDoc.Fields[0].Note = ""
- StageDoc.Fields[0].Description = "StageData is an individual test stage"
- StageDoc.Fields[0].Comments[encoder.LineComment] = "StageData is an individual test stage"
-
- StageDoc.Fields[0].AddExample("StageData", exampleStageData)
-
- StageDataDoc.Type = "StageData"
- StageDataDoc.Comments[encoder.LineComment] = ""
- StageDataDoc.Description = ""
-
- StageDataDoc.AddExample("StageData", exampleStageData)
- StageDataDoc.AppearsIn = []encoder.Appearance{
- {
- TypeName: "Stage",
- FieldName: "stage",
- },
- }
- StageDataDoc.Fields = make([]encoder.Doc, 2)
- StageDataDoc.Fields[0].Name = "input"
- StageDataDoc.Fields[0].Type = "Input"
- StageDataDoc.Fields[0].Note = ""
- StageDataDoc.Fields[0].Description = "Input is the data that is passed to the test"
- StageDataDoc.Fields[0].Comments[encoder.LineComment] = "Input is the data that is passed to the test"
-
- StageDataDoc.Fields[0].AddExample("Input", exampleInput)
- StageDataDoc.Fields[1].Name = "output"
- StageDataDoc.Fields[1].Type = "Output"
- StageDataDoc.Fields[1].Note = ""
- StageDataDoc.Fields[1].Description = "Output is the data that is returned from the test"
- StageDataDoc.Fields[1].Comments[encoder.LineComment] = "Output is the data that is returned from the test"
-
- StageDataDoc.Fields[1].AddExample("Output", ExampleOutput)
-
- InputDoc.Type = "Input"
- InputDoc.Comments[encoder.LineComment] = ""
- InputDoc.Description = ""
-
- InputDoc.AddExample("Input", exampleInput)
- InputDoc.AppearsIn = []encoder.Appearance{
- {
- TypeName: "StageData",
- FieldName: "input",
- },
- }
- InputDoc.Fields = make([]encoder.Doc, 12)
- InputDoc.Fields[0].Name = "dest_addr"
- InputDoc.Fields[0].Type = "string"
- InputDoc.Fields[0].Note = ""
- InputDoc.Fields[0].Description = "DestAddr is the IP of the destination host that the test will send the message to."
- InputDoc.Fields[0].Comments[encoder.LineComment] = "DestAddr is the IP of the destination host that the test will send the message to."
-
- InputDoc.Fields[0].AddExample("DestAddr", "127.0.0.1")
- InputDoc.Fields[1].Name = "port"
- InputDoc.Fields[1].Type = "int"
- InputDoc.Fields[1].Note = ""
- InputDoc.Fields[1].Description = "Port allows you to declare which port on the destination host the tests should connect to."
- InputDoc.Fields[1].Comments[encoder.LineComment] = "Port allows you to declare which port on the destination host the tests should connect to."
-
- InputDoc.Fields[1].AddExample("Port", 80)
- InputDoc.Fields[2].Name = "protocol"
- InputDoc.Fields[2].Type = "string"
- InputDoc.Fields[2].Note = ""
- InputDoc.Fields[2].Description = "Protocol allows you to declare which port on the destination host the tests should connect to."
- InputDoc.Fields[2].Comments[encoder.LineComment] = "Protocol allows you to declare which port on the destination host the tests should connect to."
-
- InputDoc.Fields[2].AddExample("Protocol", "http")
- InputDoc.Fields[3].Name = "uri"
- InputDoc.Fields[3].Type = "string"
- InputDoc.Fields[3].Note = ""
- InputDoc.Fields[3].Description = "URI allows you to declare which port on the destination host the tests should connect to."
- InputDoc.Fields[3].Comments[encoder.LineComment] = "URI allows you to declare which port on the destination host the tests should connect to."
-
- InputDoc.Fields[3].AddExample("URI", "/get?hello=world")
- InputDoc.Fields[4].Name = "version"
- InputDoc.Fields[4].Type = "string"
- InputDoc.Fields[4].Note = ""
- InputDoc.Fields[4].Description = "Version it the HTTP version used."
- InputDoc.Fields[4].Comments[encoder.LineComment] = "Version it the HTTP version used."
-
- InputDoc.Fields[4].AddExample("Version", "1.1")
- InputDoc.Fields[5].Name = "method"
- InputDoc.Fields[5].Type = "string"
- InputDoc.Fields[5].Note = ""
- InputDoc.Fields[5].Description = "Method allows you to declare which port on the destination host the tests should connect to."
- InputDoc.Fields[5].Comments[encoder.LineComment] = "Method allows you to declare which port on the destination host the tests should connect to."
-
- InputDoc.Fields[5].AddExample("Method", "GET")
- InputDoc.Fields[6].Name = "headers"
- InputDoc.Fields[6].Type = "map[string]string"
- InputDoc.Fields[6].Note = ""
- InputDoc.Fields[6].Description = "Method allows you to declare which port on the destination host the tests should connect to."
- InputDoc.Fields[6].Comments[encoder.LineComment] = "Method allows you to declare which port on the destination host the tests should connect to."
-
- InputDoc.Fields[6].AddExample("Headers", exampleHeaders)
- InputDoc.Fields[7].Name = "data"
- InputDoc.Fields[7].Type = "string"
- InputDoc.Fields[7].Note = ""
- InputDoc.Fields[7].Description = "Data allows you to declare which port on the destination host the tests should connect to."
- InputDoc.Fields[7].Comments[encoder.LineComment] = "Data allows you to declare which port on the destination host the tests should connect to."
-
- InputDoc.Fields[7].AddExample("Data", "Bibitti bopi")
- InputDoc.Fields[8].Name = "save_cookie"
- InputDoc.Fields[8].Type = "bool"
- InputDoc.Fields[8].Note = ""
- InputDoc.Fields[8].Description = "SaveCookie allows you to declare which port on the destination host the tests should connect to."
- InputDoc.Fields[8].Comments[encoder.LineComment] = "SaveCookie allows you to declare which port on the destination host the tests should connect to."
-
- InputDoc.Fields[8].AddExample("SaveCookie", 80)
- InputDoc.Fields[9].Name = "stop_magic"
- InputDoc.Fields[9].Type = "bool"
- InputDoc.Fields[9].Note = ""
- InputDoc.Fields[9].Description = "StopMagic allows you to declare which port on the destination host the tests should connect to."
- InputDoc.Fields[9].Comments[encoder.LineComment] = "StopMagic allows you to declare which port on the destination host the tests should connect to."
-
- InputDoc.Fields[9].AddExample("StopMagic", false)
- InputDoc.Fields[10].Name = "encoded_request"
- InputDoc.Fields[10].Type = "string"
- InputDoc.Fields[10].Note = ""
- InputDoc.Fields[10].Description = "EncodedRequest allows you to declare which port on the destination host the tests should connect to."
- InputDoc.Fields[10].Comments[encoder.LineComment] = "EncodedRequest allows you to declare which port on the destination host the tests should connect to."
-
- InputDoc.Fields[10].AddExample("EncodedRequest", "a")
- InputDoc.Fields[11].Name = "raw_request"
- InputDoc.Fields[11].Type = "string"
- InputDoc.Fields[11].Note = ""
- InputDoc.Fields[11].Description = "RAWRequest is deprecated."
- InputDoc.Fields[11].Comments[encoder.LineComment] = "RAWRequest is deprecated."
-
- InputDoc.Fields[11].AddExample("RAWRequest", "TXkgRGF0YQo=")
-
- OutputDoc.Type = "Output"
- OutputDoc.Comments[encoder.LineComment] = ""
- OutputDoc.Description = ""
-
- OutputDoc.AddExample("Output", ExampleOutput)
- OutputDoc.AppearsIn = []encoder.Appearance{
- {
- TypeName: "StageData",
- FieldName: "output",
- },
- }
- OutputDoc.Fields = make([]encoder.Doc, 5)
- OutputDoc.Fields[0].Name = "status"
- OutputDoc.Fields[0].Type = "[]int"
- OutputDoc.Fields[0].Note = ""
- OutputDoc.Fields[0].Description = "description: |\n Status describes the HTTP status error code expected as response.\n examples:\n - name: Status\n value: [200]"
- OutputDoc.Fields[0].Comments[encoder.LineComment] = " description: |"
-
- OutputDoc.Fields[1].Name = "response_contains"
- OutputDoc.Fields[1].Type = "string"
- OutputDoc.Fields[1].Note = ""
- OutputDoc.Fields[1].Description = "ResponseContains describes the text that should be contained in the HTTP response."
- OutputDoc.Fields[1].Comments[encoder.LineComment] = "ResponseContains describes the text that should be contained in the HTTP response."
-
- OutputDoc.Fields[1].AddExample("ResponseContains", "Hello, World")
- OutputDoc.Fields[2].Name = "log_contains"
- OutputDoc.Fields[2].Type = "string"
- OutputDoc.Fields[2].Note = ""
- OutputDoc.Fields[2].Description = "LogContains describes the text that should be contained in the WAF logs."
- OutputDoc.Fields[2].Comments[encoder.LineComment] = "LogContains describes the text that should be contained in the WAF logs."
-
- OutputDoc.Fields[2].AddExample("LogContains", "id 920100")
- OutputDoc.Fields[3].Name = "no_log_contains"
- OutputDoc.Fields[3].Type = "string"
- OutputDoc.Fields[3].Note = ""
- OutputDoc.Fields[3].Description = "NoLogContains describes the text that should be contained in the WAF logs."
- OutputDoc.Fields[3].Comments[encoder.LineComment] = "NoLogContains describes the text that should be contained in the WAF logs."
-
- OutputDoc.Fields[3].AddExample("NoLogContains", "id 920100")
- OutputDoc.Fields[4].Name = "expect_error"
- OutputDoc.Fields[4].Type = "bool"
- OutputDoc.Fields[4].Note = ""
- OutputDoc.Fields[4].Description = "When `ExpectError` is true, we don't expect an answer from the WAF, just an error."
- OutputDoc.Fields[4].Comments[encoder.LineComment] = "When `ExpectError` is true, we don't expect an answer from the WAF, just an error."
-
- OutputDoc.Fields[4].AddExample("ExpectError", false)
-}
-
-// GetFTWTestDoc returns documentation for the file ./test_doc.go.
-func GetFTWTestDoc() *encoder.FileDoc {
- return &encoder.FileDoc{
- Name: "FTWTest",
- Description: "",
- Structs: []*encoder.Doc{
- &FTWTestDoc,
- &MetaDoc,
- &TestDoc,
- &StageDoc,
- &StageDataDoc,
- &InputDoc,
- &OutputDoc,
- },
- }
-}
diff --git a/test/test_test.go b/test/test_test.go
deleted file mode 100644
index 516be57..0000000
--- a/test/test_test.go
+++ /dev/null
@@ -1,193 +0,0 @@
-package test
-
-import (
- "testing"
-
- "github.com/goccy/go-yaml"
-)
-
-var testInput = `---
-dest_addr: "127.0.0.1"
-port: 80
-headers:
- User-Agent: "FTW Schema Tests"
- Host: "localhost"
-`
-
-var testYaml = `---
-filename: "testYaml.yaml"
-meta:
- author: "ftw-tests-schema"
- enabled: true
- name: "testYaml"
- description: "Simple YAML to test that the schema is working."
-tests:
- - test_title: 1234-1
- desc: "Test that the schema is working."
- stages:
- - stage:
- input:
- dest_addr: "127.0.0.1"
- port: 80
- headers:
- User-Agent: "FTW Schema Tests"
- Host: "localhost"
- output:
- no_log_contains: 'id "1234"'
- - test_title: 1234-2
- stages:
- - stage:
- input:
- dest_addr: "127.0.0.1"
- port: 80
- method: "OPTIONS"
- headers:
- User-Agent: "FTW Schema Tests"
- Host: "localhost"
- output:
- status: [200, 204]
-`
-
-var inputTest = &Input{
- DestAddr: strPtr("127.0.0.1"),
- Port: intPtr(80),
- Headers: map[string]string{
- "User-Agent": "FTW Schema Tests",
- "Host": "localhost",
- },
-}
-
-var ftwTest = &FTWTest{
- FileName: "testYaml.yaml",
- Meta: Meta{
- Author: "ftw-tests-schema",
- Enabled: boolPtr(true),
- Name: "testYaml",
- Description: "Simple YAML to test that the schema is working.",
- },
- Tests: []Test{
- {
- TestTitle: "1234-1",
- TestDescription: "Test that the schema is working.",
- Stages: []Stage{
- {
- SD: StageData{
- Input: Input{
- DestAddr: strPtr("127.0.0.1"),
- Port: intPtr(80),
- Headers: map[string]string{
- "User-Agent": "FTW Schema Tests",
- "Host": "localhost",
- },
- },
- Output: Output{
- NoLogContains: "id \"1234\"",
- },
- },
- },
- },
- },
- {
- TestTitle: "1234-2",
- Stages: []Stage{
- {
- SD: StageData{
- Input: Input{
- DestAddr: strPtr("127.0.0.1"),
- Port: intPtr(80),
- Method: strPtr("OPTIONS"),
- Headers: map[string]string{
- "User-Agent": "FTW Schema Tests",
- "Host": "localhost",
- },
- },
- Output: Output{
- Status: []int{200, 204},
- },
- },
- },
- },
- },
- },
-}
-
-func TestUnmarshalFTWTest(t *testing.T) {
- var ftw FTWTest
-
- err := yaml.Unmarshal([]byte(testYaml), &ftw)
-
- if err != nil {
- t.Errorf("Unmarshal: %v", err)
- }
-
- if ftw.FileName != ftwTest.FileName {
- t.Errorf("FileName: %v != %v", ftw.FileName, ftwTest.FileName)
- }
- if ftw.Meta.Author != ftwTest.Meta.Author {
- t.Errorf("Author: %v != %v", ftw.Meta.Author, ftwTest.Meta.Author)
- }
- if *ftw.Meta.Enabled != *ftwTest.Meta.Enabled {
- t.Errorf("Enabled: %v != %v", ftw.Meta.Enabled, ftwTest.Meta.Enabled)
- }
- if ftw.Meta.Name != ftwTest.Meta.Name {
- t.Errorf("Name: %v != %v", ftw.Meta.Name, ftwTest.Meta.Name)
- }
- if ftw.Meta.Description != ftwTest.Meta.Description {
- t.Errorf("Description: %v != %v", ftw.Meta.Description, ftwTest.Meta.Description)
- }
- if len(ftw.Tests) != len(ftwTest.Tests) {
- t.Errorf("Tests: %v != %v", len(ftw.Tests), len(ftwTest.Tests))
- }
- for i, test := range ftw.Tests {
- if test.TestTitle != ftwTest.Tests[i].TestTitle {
- t.Errorf("TestTitle: %v != %v", test.TestTitle, ftwTest.Tests[i].TestTitle)
- }
- if len(test.Stages) != len(ftwTest.Tests[i].Stages) {
- t.Errorf("Stages: %v != %v", len(test.Stages), len(ftwTest.Tests[i].Stages))
- }
- for j, stage := range test.Stages {
- if *stage.SD.Input.DestAddr != *ftwTest.Tests[i].Stages[j].SD.Input.DestAddr {
- t.Errorf("DestAddr: %v != %v", *stage.SD.Input.DestAddr, *ftwTest.Tests[i].Stages[j].SD.Input.DestAddr)
- }
- if *stage.SD.Input.Port != *ftwTest.Tests[i].Stages[j].SD.Input.Port {
- t.Errorf("Port: %v != %v", *stage.SD.Input.Port, *ftwTest.Tests[i].Stages[j].SD.Input.Port)
- }
- if stage.SD.Input.Method != nil && *stage.SD.Input.Method != *ftwTest.Tests[i].Stages[j].SD.Input.Method {
- t.Errorf("Method: %v != %v", stage.SD.Input.Method, ftwTest.Tests[i].Stages[j].SD.Input.Method)
- }
- if len(stage.SD.Input.Headers) != len(ftwTest.Tests[i].Stages[j].SD.Input.Headers) {
- t.Errorf("Headers: %v != %v", len(stage.SD.Input.Headers), len(ftwTest.Tests[i].Stages[j].SD.Input.Headers))
- }
- for k, header := range stage.SD.Input.Headers {
- if header != ftwTest.Tests[i].Stages[j].SD.Input.Headers[k] {
- t.Errorf("Header: %v != %v", header, ftwTest.Tests[i].Stages[j].SD.Input.Headers[k])
- }
- }
- if stage.SD.Output.NoLogContains != ftwTest.Tests[i].Stages[j].SD.Output.NoLogContains {
- t.Errorf("NoLogContains: %v != %v", stage.SD.Output.NoLogContains, ftwTest.Tests[i].Stages[j].SD.Output.NoLogContains)
- }
- if len(stage.SD.Output.Status) != len(ftwTest.Tests[i].Stages[j].SD.Output.Status) {
- t.Errorf("Status: %v != %v", len(stage.SD.Output.Status), len(ftwTest.Tests[i].Stages[j].SD.Output.Status))
- }
- }
- }
-}
-
-func TestUnmarshalInput(t *testing.T) {
- var input Input
-
- err := yaml.Unmarshal([]byte(testInput), &input)
- if err != nil {
- t.Errorf("Unmarshal: %v", err)
- }
-
- if input.DestAddr != nil && *input.DestAddr != *inputTest.DestAddr {
- t.Errorf("DestAddr: %v != %v", *input.DestAddr, *inputTest.DestAddr)
- }
- if input.Port != nil && *input.Port != *inputTest.Port {
- t.Errorf("Port: %v != %v", *input.Port, *inputTest.Port)
- }
- if input.Method != nil && *input.Method != *inputTest.Method {
- t.Errorf("Method: %v != %v", *input.Method, *inputTest.Method)
- }
-}
diff --git a/types/examples.go b/types/examples.go
new file mode 100644
index 0000000..766b74a
--- /dev/null
+++ b/types/examples.go
@@ -0,0 +1,70 @@
+// Copyright 2023 OWASP CRS
+// SPDX-License-Identifier: Apache-2.0
+
+package types
+
+var (
+ exampleStageData = StageData{
+ Input: exampleInput,
+ Output: exampleOutput,
+ }
+ exampleStages = []Stage{
+ {
+ exampleStageData,
+ },
+ }
+ exampleHeaders = map[string]string{
+ "User-Agent": "CRS Tests",
+ "Host": "localhost",
+ "Accept": "*/*",
+ }
+ exampleInput = Input{
+ DestAddr: strPtr("192.168.0.1"),
+ Port: intPtr(8080),
+ Protocol: strPtr("http"),
+ URI: strPtr("/test"),
+ Version: strPtr("HTTP/1.1"),
+ Headers: exampleHeaders,
+ Method: strPtr("REPORT"),
+ Data: nil,
+ EncodedRequest: "TXkgRGF0YQo=",
+ SaveCookie: boolPtr(false),
+ StopMagic: boolPtr(true),
+ AutocompleteHeaders: boolPtr(false),
+ }
+ exampleOutput = Output{
+ Status: 200,
+ ResponseContains: "",
+ LogContains: "nothing",
+ NoLogContains: "",
+ Log: exampleLog,
+ ExpectError: boolPtr(true),
+ }
+
+ exampleLog = Log{
+ ExpectId: 123456,
+ NoExpectId: 123456,
+ MatchRegex: `id[:\s"]*123456`,
+ NoMatchRegex: `id[:\s"]*123456`,
+ }
+ metaExample = FTWOverridesMeta{
+ Engine: "libmodsecurity3",
+ Platform: "nginx",
+ Annotations: annotationsExample,
+ }
+ annotationsExample = map[string]string{
+ "os": "Debian Bullseye",
+ "purpose": "L7ASR test suite",
+ }
+ reasonExample = "nginx returns 400 when `Content-Length` header is sent in a\n" +
+ "`Transfer-Encoding: chunked` request."
+ testOverridesExample = []TestOverride{
+ {
+ RuleId: 920100,
+ TestIds: []int{4, 6},
+ Reason: reasonExample,
+ ExpectFailure: func() *bool { b := true; return &b }(),
+ Output: exampleOutput,
+ },
+ }
+)
diff --git a/types/helpers.go b/types/helpers.go
new file mode 100644
index 0000000..4c38d7b
--- /dev/null
+++ b/types/helpers.go
@@ -0,0 +1,16 @@
+// Copyright 2023 OWASP CRS
+// SPDX-License-Identifier: Apache-2.0
+
+package types
+
+func intPtr(i int) *int {
+ return &i
+}
+
+func boolPtr(b bool) *bool {
+ return &b
+}
+
+func strPtr(s string) *string {
+ return &s
+}
diff --git a/types/overrides_doc.go b/types/overrides_doc.go
new file mode 100644
index 0000000..a328b1e
--- /dev/null
+++ b/types/overrides_doc.go
@@ -0,0 +1,233 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// DO NOT EDIT: this file is automatically generated by docgen
+package types
+
+import (
+ "github.com/projectdiscovery/yamldoc-go/encoder"
+)
+
+var (
+ FTWOverridesDoc encoder.Doc
+ FTWOverridesMetaDoc encoder.Doc
+ TestOverrideDoc encoder.Doc
+ OutputDoc encoder.Doc
+ LogDoc encoder.Doc
+)
+
+func init() {
+ FTWOverridesDoc.Type = "FTWOverrides"
+ FTWOverridesDoc.Comments[encoder.LineComment] = " FTWOverrides describes platform specific overrides for tests"
+ FTWOverridesDoc.Description = "FTWOverrides describes platform specific overrides for tests"
+ FTWOverridesDoc.Fields = make([]encoder.Doc, 3)
+ FTWOverridesDoc.Fields[0].Name = "version"
+ FTWOverridesDoc.Fields[0].Type = "string"
+ FTWOverridesDoc.Fields[0].Note = ""
+ FTWOverridesDoc.Fields[0].Description = "The version field designates the version of the schema that validates this file"
+ FTWOverridesDoc.Fields[0].Comments[encoder.LineComment] = "The version field designates the version of the schema that validates this file"
+
+ FTWOverridesDoc.Fields[0].AddExample("", "v0.1.0")
+ FTWOverridesDoc.Fields[1].Name = "meta"
+ FTWOverridesDoc.Fields[1].Type = "FTWOverridesMeta"
+ FTWOverridesDoc.Fields[1].Note = ""
+ FTWOverridesDoc.Fields[1].Description = "Meta describes the metadata information"
+ FTWOverridesDoc.Fields[1].Comments[encoder.LineComment] = "Meta describes the metadata information"
+
+ FTWOverridesDoc.Fields[1].AddExample("", metaExample)
+ FTWOverridesDoc.Fields[2].Name = "test_overrides"
+ FTWOverridesDoc.Fields[2].Type = "[]TestOverride"
+ FTWOverridesDoc.Fields[2].Note = ""
+ FTWOverridesDoc.Fields[2].Description = "List of test override specifications"
+ FTWOverridesDoc.Fields[2].Comments[encoder.LineComment] = "List of test override specifications"
+
+ FTWOverridesDoc.Fields[2].AddExample("", testOverridesExample)
+
+ FTWOverridesMetaDoc.Type = "FTWOverridesMeta"
+ FTWOverridesMetaDoc.Comments[encoder.LineComment] = ""
+ FTWOverridesMetaDoc.Description = ""
+
+ FTWOverridesMetaDoc.AddExample("", metaExample)
+ FTWOverridesMetaDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "FTWOverrides",
+ FieldName: "meta",
+ },
+ }
+ FTWOverridesMetaDoc.Fields = make([]encoder.Doc, 3)
+ FTWOverridesMetaDoc.Fields[0].Name = "engine"
+ FTWOverridesMetaDoc.Fields[0].Type = "string"
+ FTWOverridesMetaDoc.Fields[0].Note = ""
+ FTWOverridesMetaDoc.Fields[0].Description = "The name of the WAF engine the tests are expected to run against"
+ FTWOverridesMetaDoc.Fields[0].Comments[encoder.LineComment] = "The name of the WAF engine the tests are expected to run against"
+
+ FTWOverridesMetaDoc.Fields[0].AddExample("", "coraza")
+ FTWOverridesMetaDoc.Fields[1].Name = "platform"
+ FTWOverridesMetaDoc.Fields[1].Type = "string"
+ FTWOverridesMetaDoc.Fields[1].Note = ""
+ FTWOverridesMetaDoc.Fields[1].Description = "The name of the platform (e.g., web server) the tests are expected to run against"
+ FTWOverridesMetaDoc.Fields[1].Comments[encoder.LineComment] = "The name of the platform (e.g., web server) the tests are expected to run against"
+
+ FTWOverridesMetaDoc.Fields[1].AddExample("", "nginx")
+ FTWOverridesMetaDoc.Fields[2].Name = "annotations"
+ FTWOverridesMetaDoc.Fields[2].Type = "map[string]string"
+ FTWOverridesMetaDoc.Fields[2].Note = ""
+ FTWOverridesMetaDoc.Fields[2].Description = "Custom annotations; can be used to add additional meta information"
+ FTWOverridesMetaDoc.Fields[2].Comments[encoder.LineComment] = "Custom annotations; can be used to add additional meta information"
+
+ FTWOverridesMetaDoc.Fields[2].AddExample("", annotationsExample)
+
+ TestOverrideDoc.Type = "TestOverride"
+ TestOverrideDoc.Comments[encoder.LineComment] = ""
+ TestOverrideDoc.Description = ""
+
+ TestOverrideDoc.AddExample("", testOverridesExample)
+ TestOverrideDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "FTWOverrides",
+ FieldName: "test_overrides",
+ },
+ }
+ TestOverrideDoc.Fields = make([]encoder.Doc, 5)
+ TestOverrideDoc.Fields[0].Name = "rule_id"
+ TestOverrideDoc.Fields[0].Type = "int"
+ TestOverrideDoc.Fields[0].Note = ""
+ TestOverrideDoc.Fields[0].Description = "ID of the rule this test targets."
+ TestOverrideDoc.Fields[0].Comments[encoder.LineComment] = "ID of the rule this test targets."
+
+ TestOverrideDoc.Fields[0].AddExample("", 920100)
+ TestOverrideDoc.Fields[1].Name = "test_ids"
+ TestOverrideDoc.Fields[1].Type = "[]int"
+ TestOverrideDoc.Fields[1].Note = ""
+ TestOverrideDoc.Fields[1].Description = "description: |\n IDs of the tests for rule_id that overrides should be applied to.\n If this field is not set, the overrides will be applied to all tests of rule_id.\n examples:\n - value: [5, 6]"
+ TestOverrideDoc.Fields[1].Comments[encoder.LineComment] = " description: |"
+
+ TestOverrideDoc.Fields[2].Name = "reason"
+ TestOverrideDoc.Fields[2].Type = "string"
+ TestOverrideDoc.Fields[2].Note = ""
+ TestOverrideDoc.Fields[2].Description = "Describes why this override is necessary."
+ TestOverrideDoc.Fields[2].Comments[encoder.LineComment] = "Describes why this override is necessary."
+
+ TestOverrideDoc.Fields[2].AddExample("", reasonExample)
+ TestOverrideDoc.Fields[3].Name = "expect_failure"
+ TestOverrideDoc.Fields[3].Type = "bool"
+ TestOverrideDoc.Fields[3].Note = ""
+ TestOverrideDoc.Fields[3].Description = "Whether this test is expected to fail for this particular configuration.\nDefault: false"
+ TestOverrideDoc.Fields[3].Comments[encoder.LineComment] = "Whether this test is expected to fail for this particular configuration."
+
+ TestOverrideDoc.Fields[3].AddExample("", true)
+ TestOverrideDoc.Fields[4].Name = "output"
+ TestOverrideDoc.Fields[4].Type = "Output"
+ TestOverrideDoc.Fields[4].Note = ""
+ TestOverrideDoc.Fields[4].Description = "Specifies overrides on the test output"
+ TestOverrideDoc.Fields[4].Comments[encoder.LineComment] = "Specifies overrides on the test output"
+
+ TestOverrideDoc.Fields[4].AddExample("", 400)
+
+ OutputDoc.Type = "Output"
+ OutputDoc.Comments[encoder.LineComment] = ""
+ OutputDoc.Description = ""
+
+ OutputDoc.AddExample("", 400)
+ OutputDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "TestOverride",
+ FieldName: "output",
+ },
+ }
+ OutputDoc.Fields = make([]encoder.Doc, 6)
+ OutputDoc.Fields[0].Name = "status"
+ OutputDoc.Fields[0].Type = "int"
+ OutputDoc.Fields[0].Note = ""
+ OutputDoc.Fields[0].Description = "Status describes the HTTP status code expected in the response."
+ OutputDoc.Fields[0].Comments[encoder.LineComment] = "Status describes the HTTP status code expected in the response."
+
+ OutputDoc.Fields[0].AddExample("Status", 200)
+ OutputDoc.Fields[1].Name = "response_contains"
+ OutputDoc.Fields[1].Type = "string"
+ OutputDoc.Fields[1].Note = ""
+ OutputDoc.Fields[1].Description = "ResponseContains describes the text that should be contained in the HTTP response."
+ OutputDoc.Fields[1].Comments[encoder.LineComment] = "ResponseContains describes the text that should be contained in the HTTP response."
+
+ OutputDoc.Fields[1].AddExample("ResponseContains", "Hello, World")
+ OutputDoc.Fields[2].Name = "log_contains"
+ OutputDoc.Fields[2].Type = "string"
+ OutputDoc.Fields[2].Note = ""
+ OutputDoc.Fields[2].Description = "LogContains describes the text that should be contained in the WAF logs."
+ OutputDoc.Fields[2].Comments[encoder.LineComment] = "LogContains describes the text that should be contained in the WAF logs."
+
+ OutputDoc.Fields[2].AddExample("LogContains", "id 920100")
+ OutputDoc.Fields[3].Name = "no_log_contains"
+ OutputDoc.Fields[3].Type = "string"
+ OutputDoc.Fields[3].Note = ""
+ OutputDoc.Fields[3].Description = "NoLogContains describes the text that should not be contained in the WAF logs."
+ OutputDoc.Fields[3].Comments[encoder.LineComment] = "NoLogContains describes the text that should not be contained in the WAF logs."
+
+ OutputDoc.Fields[3].AddExample("NoLogContains", "id 920100")
+ OutputDoc.Fields[4].Name = "log"
+ OutputDoc.Fields[4].Type = "Log"
+ OutputDoc.Fields[4].Note = ""
+ OutputDoc.Fields[4].Description = "Log is used to configure expectations about the log contents."
+ OutputDoc.Fields[4].Comments[encoder.LineComment] = "Log is used to configure expectations about the log contents."
+
+ OutputDoc.Fields[4].AddExample("", exampleLog)
+ OutputDoc.Fields[5].Name = "expect_error"
+ OutputDoc.Fields[5].Type = "bool"
+ OutputDoc.Fields[5].Note = ""
+ OutputDoc.Fields[5].Description = "When `ExpectError` is true, we don't expect an answer from the WAF, just an error."
+ OutputDoc.Fields[5].Comments[encoder.LineComment] = "When `ExpectError` is true, we don't expect an answer from the WAF, just an error."
+
+ OutputDoc.Fields[5].AddExample("ExpectError", false)
+
+ LogDoc.Type = "Log"
+ LogDoc.Comments[encoder.LineComment] = ""
+ LogDoc.Description = ""
+
+ LogDoc.AddExample("", exampleLog)
+ LogDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "Output",
+ FieldName: "log",
+ },
+ }
+ LogDoc.Fields = make([]encoder.Doc, 4)
+ LogDoc.Fields[0].Name = "expect_id"
+ LogDoc.Fields[0].Type = "int"
+ LogDoc.Fields[0].Note = ""
+ LogDoc.Fields[0].Description = "description: |\n Expect the given ID to be contained in the log output.\n examples:\n - exampleLog.ExpectId"
+ LogDoc.Fields[0].Comments[encoder.LineComment] = " description: |"
+ LogDoc.Fields[1].Name = "expect_id"
+ LogDoc.Fields[1].Type = "int"
+ LogDoc.Fields[1].Note = ""
+ LogDoc.Fields[1].Description = "description: |\n Expect the given ID _not_ to be contained in the log output.\n examples:\n - exampleLog.NoExpectId"
+ LogDoc.Fields[1].Comments[encoder.LineComment] = " description: |"
+ LogDoc.Fields[2].Name = "match_regex"
+ LogDoc.Fields[2].Type = "string"
+ LogDoc.Fields[2].Note = ""
+ LogDoc.Fields[2].Description = "Expect the regular expression to match log content for the current test."
+ LogDoc.Fields[2].Comments[encoder.LineComment] = "Expect the regular expression to match log content for the current test."
+
+ LogDoc.Fields[2].AddExample("", exampleLog.MatchRegex)
+ LogDoc.Fields[3].Name = "no_match_regex"
+ LogDoc.Fields[3].Type = "string"
+ LogDoc.Fields[3].Note = ""
+ LogDoc.Fields[3].Description = "Expect the regular expression to _not_ match log content for the current test."
+ LogDoc.Fields[3].Comments[encoder.LineComment] = "Expect the regular expression to _not_ match log content for the current test."
+
+ LogDoc.Fields[3].AddExample("", exampleLog.NoMatchRegex)
+}
+
+// GetFTWOverridesDoc returns documentation for the file ./overrides_doc.go.
+func GetFTWOverridesDoc() *encoder.FileDoc {
+ return &encoder.FileDoc{
+ Name: "FTWOverrides",
+ Description: "",
+ Structs: []*encoder.Doc{
+ &FTWOverridesDoc,
+ &FTWOverridesMetaDoc,
+ &TestOverrideDoc,
+ &OutputDoc,
+ &LogDoc,
+ },
+ }
+}
diff --git a/types/test_doc.go b/types/test_doc.go
new file mode 100644
index 0000000..48a3c30
--- /dev/null
+++ b/types/test_doc.go
@@ -0,0 +1,380 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// DO NOT EDIT: this file is automatically generated by docgen
+package types
+
+import (
+ "github.com/projectdiscovery/yamldoc-go/encoder"
+)
+
+var (
+ FTWTestDoc encoder.Doc
+ FTWTestMetaDoc encoder.Doc
+ TestDoc encoder.Doc
+ StageDoc encoder.Doc
+ StageDataDoc encoder.Doc
+ InputDoc encoder.Doc
+ FTWTestOutputDoc encoder.Doc
+ FTWTestLogDoc encoder.Doc
+)
+
+func init() {
+ FTWTestDoc.Type = "FTWTest"
+ FTWTestDoc.Comments[encoder.LineComment] = " Welcome to the FTW YAMLFormat documentation."
+ FTWTestDoc.Description = "Welcome to the FTW YAMLFormat documentation.\n In this document we will explain all the possible options that can be used within the YAML format.\n Generally this is the preferred format for writing tests in as they don't require any programming skills\n in order to understand and change. If you find a bug in this format please open an issue.\n\n\n FTWTest is the base type used when unmarshaling YAML tests files"
+ FTWTestDoc.Fields = make([]encoder.Doc, 2)
+ FTWTestDoc.Fields[0].Name = "meta"
+ FTWTestDoc.Fields[0].Type = "FTWTestMeta"
+ FTWTestDoc.Fields[0].Note = ""
+ FTWTestDoc.Fields[0].Description = "Meta describes the metadata information of this yaml test file"
+ FTWTestDoc.Fields[0].Comments[encoder.LineComment] = "Meta describes the metadata information of this yaml test file"
+ FTWTestDoc.Fields[1].Name = "tests"
+ FTWTestDoc.Fields[1].Type = "[]Test"
+ FTWTestDoc.Fields[1].Note = ""
+ FTWTestDoc.Fields[1].Description = "Tests is a list of FTW tests"
+ FTWTestDoc.Fields[1].Comments[encoder.LineComment] = "Tests is a list of FTW tests"
+
+ FTWTestDoc.Fields[1].AddExample("Tests", "the tests")
+
+ FTWTestMetaDoc.Type = "FTWTestMeta"
+ FTWTestMetaDoc.Comments[encoder.LineComment] = ""
+ FTWTestMetaDoc.Description = ""
+ FTWTestMetaDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "FTWTest",
+ FieldName: "meta",
+ },
+ }
+ FTWTestMetaDoc.Fields = make([]encoder.Doc, 5)
+ FTWTestMetaDoc.Fields[0].Name = "author"
+ FTWTestMetaDoc.Fields[0].Type = "string"
+ FTWTestMetaDoc.Fields[0].Note = ""
+ FTWTestMetaDoc.Fields[0].Description = "Author is the list of authors that added content to this file"
+ FTWTestMetaDoc.Fields[0].Comments[encoder.LineComment] = "Author is the list of authors that added content to this file"
+
+ FTWTestMetaDoc.Fields[0].AddExample("Author", "Felipe Zipitria")
+ FTWTestMetaDoc.Fields[1].Name = "enabled"
+ FTWTestMetaDoc.Fields[1].Type = "bool"
+ FTWTestMetaDoc.Fields[1].Note = ""
+ FTWTestMetaDoc.Fields[1].Description = "Enabled indicates if the tests are enabled to be run by the engine or not."
+ FTWTestMetaDoc.Fields[1].Comments[encoder.LineComment] = "Enabled indicates if the tests are enabled to be run by the engine or not."
+
+ FTWTestMetaDoc.Fields[1].AddExample("Enabled", false)
+ FTWTestMetaDoc.Fields[2].Name = "name"
+ FTWTestMetaDoc.Fields[2].Type = "string"
+ FTWTestMetaDoc.Fields[2].Note = ""
+ FTWTestMetaDoc.Fields[2].Description = "Name is the name of the tests contained in this file."
+ FTWTestMetaDoc.Fields[2].Comments[encoder.LineComment] = "Name is the name of the tests contained in this file."
+
+ FTWTestMetaDoc.Fields[2].AddExample("Name", "test01")
+ FTWTestMetaDoc.Fields[3].Name = "description"
+ FTWTestMetaDoc.Fields[3].Type = "string"
+ FTWTestMetaDoc.Fields[3].Note = ""
+ FTWTestMetaDoc.Fields[3].Description = "Description is a textual description of the tests contained in this file."
+ FTWTestMetaDoc.Fields[3].Comments[encoder.LineComment] = "Description is a textual description of the tests contained in this file."
+
+ FTWTestMetaDoc.Fields[3].AddExample("Description", "The tests here target SQL injection.")
+ FTWTestMetaDoc.Fields[4].Name = "version"
+ FTWTestMetaDoc.Fields[4].Type = "string"
+ FTWTestMetaDoc.Fields[4].Note = ""
+ FTWTestMetaDoc.Fields[4].Description = "Version is the version of the YAML Schema."
+ FTWTestMetaDoc.Fields[4].Comments[encoder.LineComment] = "Version is the version of the YAML Schema."
+
+ FTWTestMetaDoc.Fields[4].AddExample("Version", "v1")
+
+ TestDoc.Type = "Test"
+ TestDoc.Comments[encoder.LineComment] = ""
+ TestDoc.Description = ""
+
+ TestDoc.AddExample("Tests", "the tests")
+ TestDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "FTWTest",
+ FieldName: "tests",
+ },
+ }
+ TestDoc.Fields = make([]encoder.Doc, 3)
+ TestDoc.Fields[0].Name = "test_title"
+ TestDoc.Fields[0].Type = "string"
+ TestDoc.Fields[0].Note = ""
+ TestDoc.Fields[0].Description = "TestTitle the title of this particular test. It is used for inclusion/exclusion of each run by the tool."
+ TestDoc.Fields[0].Comments[encoder.LineComment] = "TestTitle the title of this particular test. It is used for inclusion/exclusion of each run by the tool."
+
+ TestDoc.Fields[0].AddExample("TestTitle", "920100-1")
+ TestDoc.Fields[1].Name = "desc"
+ TestDoc.Fields[1].Type = "string"
+ TestDoc.Fields[1].Note = ""
+ TestDoc.Fields[1].Description = "TestDescription is the description for this particular test. Should be used to describe the internals of\nthe specific things this test is targeting."
+ TestDoc.Fields[1].Comments[encoder.LineComment] = "TestDescription is the description for this particular test. Should be used to describe the internals of"
+
+ TestDoc.Fields[1].AddExample("TestDescription", "This test targets something")
+ TestDoc.Fields[2].Name = "stages"
+ TestDoc.Fields[2].Type = "[]Stage"
+ TestDoc.Fields[2].Note = ""
+ TestDoc.Fields[2].Description = "Stages is the list of all the stages to perform this test."
+ TestDoc.Fields[2].Comments[encoder.LineComment] = "Stages is the list of all the stages to perform this test."
+
+ TestDoc.Fields[2].AddExample("Stages", exampleStages)
+
+ StageDoc.Type = "Stage"
+ StageDoc.Comments[encoder.LineComment] = ""
+ StageDoc.Description = ""
+
+ StageDoc.AddExample("Stages", exampleStages)
+ StageDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "Test",
+ FieldName: "stages",
+ },
+ }
+ StageDoc.Fields = make([]encoder.Doc, 1)
+ StageDoc.Fields[0].Name = "stage"
+ StageDoc.Fields[0].Type = "StageData"
+ StageDoc.Fields[0].Note = ""
+ StageDoc.Fields[0].Description = "StageData is an individual test stage"
+ StageDoc.Fields[0].Comments[encoder.LineComment] = "StageData is an individual test stage"
+
+ StageDoc.Fields[0].AddExample("StageData", exampleStageData)
+
+ StageDataDoc.Type = "StageData"
+ StageDataDoc.Comments[encoder.LineComment] = ""
+ StageDataDoc.Description = ""
+
+ StageDataDoc.AddExample("StageData", exampleStageData)
+ StageDataDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "Stage",
+ FieldName: "stage",
+ },
+ }
+ StageDataDoc.Fields = make([]encoder.Doc, 2)
+ StageDataDoc.Fields[0].Name = "input"
+ StageDataDoc.Fields[0].Type = "Input"
+ StageDataDoc.Fields[0].Note = ""
+ StageDataDoc.Fields[0].Description = "Input is the data that is passed to the test"
+ StageDataDoc.Fields[0].Comments[encoder.LineComment] = "Input is the data that is passed to the test"
+
+ StageDataDoc.Fields[0].AddExample("Input", exampleInput)
+ StageDataDoc.Fields[1].Name = "output"
+ StageDataDoc.Fields[1].Type = "Output"
+ StageDataDoc.Fields[1].Note = ""
+ StageDataDoc.Fields[1].Description = "Output is the data that is returned from the test"
+ StageDataDoc.Fields[1].Comments[encoder.LineComment] = "Output is the data that is returned from the test"
+
+ StageDataDoc.Fields[1].AddExample("Output", exampleOutput)
+
+ InputDoc.Type = "Input"
+ InputDoc.Comments[encoder.LineComment] = ""
+ InputDoc.Description = ""
+
+ InputDoc.AddExample("Input", exampleInput)
+ InputDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "StageData",
+ FieldName: "input",
+ },
+ }
+ InputDoc.Fields = make([]encoder.Doc, 13)
+ InputDoc.Fields[0].Name = "dest_addr"
+ InputDoc.Fields[0].Type = "string"
+ InputDoc.Fields[0].Note = ""
+ InputDoc.Fields[0].Description = "DestAddr is the IP of the destination host that the test will send the message to."
+ InputDoc.Fields[0].Comments[encoder.LineComment] = "DestAddr is the IP of the destination host that the test will send the message to."
+
+ InputDoc.Fields[0].AddExample("DestAddr", "127.0.0.1")
+ InputDoc.Fields[1].Name = "port"
+ InputDoc.Fields[1].Type = "int"
+ InputDoc.Fields[1].Note = ""
+ InputDoc.Fields[1].Description = "Port allows you to declare which port on the destination host the test should connect to."
+ InputDoc.Fields[1].Comments[encoder.LineComment] = "Port allows you to declare which port on the destination host the test should connect to."
+
+ InputDoc.Fields[1].AddExample("Port", 80)
+ InputDoc.Fields[2].Name = "protocol"
+ InputDoc.Fields[2].Type = "string"
+ InputDoc.Fields[2].Note = ""
+ InputDoc.Fields[2].Description = "Protocol allows you to declare which protocol the test should use when sending the request."
+ InputDoc.Fields[2].Comments[encoder.LineComment] = "Protocol allows you to declare which protocol the test should use when sending the request."
+
+ InputDoc.Fields[2].AddExample("Protocol", "http")
+ InputDoc.Fields[3].Name = "uri"
+ InputDoc.Fields[3].Type = "string"
+ InputDoc.Fields[3].Note = ""
+ InputDoc.Fields[3].Description = "URI allows you to declare the URI the test should use as part of the request line."
+ InputDoc.Fields[3].Comments[encoder.LineComment] = "URI allows you to declare the URI the test should use as part of the request line."
+
+ InputDoc.Fields[3].AddExample("URI", "/get?hello=world")
+ InputDoc.Fields[4].Name = "version"
+ InputDoc.Fields[4].Type = "string"
+ InputDoc.Fields[4].Note = ""
+ InputDoc.Fields[4].Description = "Version allows you to declare the HTTP version the test should use as part of the request line."
+ InputDoc.Fields[4].Comments[encoder.LineComment] = "Version allows you to declare the HTTP version the test should use as part of the request line."
+
+ InputDoc.Fields[4].AddExample("Version", "1.1")
+ InputDoc.Fields[5].Name = "method"
+ InputDoc.Fields[5].Type = "string"
+ InputDoc.Fields[5].Note = ""
+ InputDoc.Fields[5].Description = "Method allows you to declare the HTTP method the test should use as part of the request line."
+ InputDoc.Fields[5].Comments[encoder.LineComment] = "Method allows you to declare the HTTP method the test should use as part of the request line."
+
+ InputDoc.Fields[5].AddExample("Method", "GET")
+ InputDoc.Fields[6].Name = "headers"
+ InputDoc.Fields[6].Type = "map[string]string"
+ InputDoc.Fields[6].Note = ""
+ InputDoc.Fields[6].Description = "Method allows you to declare headers that the test should send."
+ InputDoc.Fields[6].Comments[encoder.LineComment] = "Method allows you to declare headers that the test should send."
+
+ InputDoc.Fields[6].AddExample("Headers", exampleHeaders)
+ InputDoc.Fields[7].Name = "data"
+ InputDoc.Fields[7].Type = "string"
+ InputDoc.Fields[7].Note = ""
+ InputDoc.Fields[7].Description = "Data allows you to declare the payload that the test should in the request body."
+ InputDoc.Fields[7].Comments[encoder.LineComment] = "Data allows you to declare the payload that the test should in the request body."
+
+ InputDoc.Fields[7].AddExample("Data", "Bibitti bopi")
+ InputDoc.Fields[8].Name = "save_cookie"
+ InputDoc.Fields[8].Type = "bool"
+ InputDoc.Fields[8].Note = ""
+ InputDoc.Fields[8].Description = "SaveCookie allows you to automatically provide cookies if there are multiple stages and save cookie is set"
+ InputDoc.Fields[8].Comments[encoder.LineComment] = "SaveCookie allows you to automatically provide cookies if there are multiple stages and save cookie is set"
+
+ InputDoc.Fields[8].AddExample("SaveCookie", 80)
+ InputDoc.Fields[9].Name = "stop_magic"
+ InputDoc.Fields[9].Type = "bool"
+ InputDoc.Fields[9].Note = ""
+ InputDoc.Fields[9].Description = "StopMagic is deprecated."
+ InputDoc.Fields[9].Comments[encoder.LineComment] = "StopMagic is deprecated."
+
+ InputDoc.Fields[9].AddExample("StopMagic", false)
+ InputDoc.Fields[10].Name = "autocomplete_headers"
+ InputDoc.Fields[10].Type = "bool"
+ InputDoc.Fields[10].Note = ""
+ InputDoc.Fields[10].Description = "AutocompleteHeaders allows the test framework to automatically fill the request with Content-Type and Connection headers.\nDefaults to true."
+ InputDoc.Fields[10].Comments[encoder.LineComment] = "AutocompleteHeaders allows the test framework to automatically fill the request with Content-Type and Connection headers."
+
+ InputDoc.Fields[10].AddExample("StopMagic", false)
+ InputDoc.Fields[11].Name = "encoded_request"
+ InputDoc.Fields[11].Type = "string"
+ InputDoc.Fields[11].Note = ""
+ InputDoc.Fields[11].Description = "EncodedRequest will take a base64 encoded string that will be decoded and sent through as the request.\nIt will override all other settings"
+ InputDoc.Fields[11].Comments[encoder.LineComment] = "EncodedRequest will take a base64 encoded string that will be decoded and sent through as the request."
+
+ InputDoc.Fields[11].AddExample("EncodedRequest", "a")
+ InputDoc.Fields[12].Name = "raw_request"
+ InputDoc.Fields[12].Type = "string"
+ InputDoc.Fields[12].Note = ""
+ InputDoc.Fields[12].Description = "RAWRequest is deprecated."
+ InputDoc.Fields[12].Comments[encoder.LineComment] = "RAWRequest is deprecated."
+
+ InputDoc.Fields[12].AddExample("RAWRequest", "TXkgRGF0YQo=")
+
+ FTWTestOutputDoc.Type = "Output"
+ FTWTestOutputDoc.Comments[encoder.LineComment] = ""
+ FTWTestOutputDoc.Description = ""
+
+ FTWTestOutputDoc.AddExample("Output", exampleOutput)
+ FTWTestOutputDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "StageData",
+ FieldName: "output",
+ },
+ }
+ FTWTestOutputDoc.Fields = make([]encoder.Doc, 6)
+ FTWTestOutputDoc.Fields[0].Name = "status"
+ FTWTestOutputDoc.Fields[0].Type = "int"
+ FTWTestOutputDoc.Fields[0].Note = ""
+ FTWTestOutputDoc.Fields[0].Description = "Status describes the HTTP status code expected in the response."
+ FTWTestOutputDoc.Fields[0].Comments[encoder.LineComment] = "Status describes the HTTP status code expected in the response."
+
+ FTWTestOutputDoc.Fields[0].AddExample("Status", 200)
+ FTWTestOutputDoc.Fields[1].Name = "response_contains"
+ FTWTestOutputDoc.Fields[1].Type = "string"
+ FTWTestOutputDoc.Fields[1].Note = ""
+ FTWTestOutputDoc.Fields[1].Description = "ResponseContains describes the text that should be contained in the HTTP response."
+ FTWTestOutputDoc.Fields[1].Comments[encoder.LineComment] = "ResponseContains describes the text that should be contained in the HTTP response."
+
+ FTWTestOutputDoc.Fields[1].AddExample("ResponseContains", "Hello, World")
+ FTWTestOutputDoc.Fields[2].Name = "log_contains"
+ FTWTestOutputDoc.Fields[2].Type = "string"
+ FTWTestOutputDoc.Fields[2].Note = ""
+ FTWTestOutputDoc.Fields[2].Description = "LogContains describes the text that should be contained in the WAF logs."
+ FTWTestOutputDoc.Fields[2].Comments[encoder.LineComment] = "LogContains describes the text that should be contained in the WAF logs."
+
+ FTWTestOutputDoc.Fields[2].AddExample("LogContains", "id 920100")
+ FTWTestOutputDoc.Fields[3].Name = "no_log_contains"
+ FTWTestOutputDoc.Fields[3].Type = "string"
+ FTWTestOutputDoc.Fields[3].Note = ""
+ FTWTestOutputDoc.Fields[3].Description = "NoLogContains describes the text that should not be contained in the WAF logs."
+ FTWTestOutputDoc.Fields[3].Comments[encoder.LineComment] = "NoLogContains describes the text that should not be contained in the WAF logs."
+
+ FTWTestOutputDoc.Fields[3].AddExample("NoLogContains", "id 920100")
+ FTWTestOutputDoc.Fields[4].Name = "log"
+ FTWTestOutputDoc.Fields[4].Type = "Log"
+ FTWTestOutputDoc.Fields[4].Note = ""
+ FTWTestOutputDoc.Fields[4].Description = "Log is used to configure expectations about the log contents."
+ FTWTestOutputDoc.Fields[4].Comments[encoder.LineComment] = "Log is used to configure expectations about the log contents."
+
+ FTWTestOutputDoc.Fields[4].AddExample("", exampleLog)
+ FTWTestOutputDoc.Fields[5].Name = "expect_error"
+ FTWTestOutputDoc.Fields[5].Type = "bool"
+ FTWTestOutputDoc.Fields[5].Note = ""
+ FTWTestOutputDoc.Fields[5].Description = "When `ExpectError` is true, we don't expect an answer from the WAF, just an error."
+ FTWTestOutputDoc.Fields[5].Comments[encoder.LineComment] = "When `ExpectError` is true, we don't expect an answer from the WAF, just an error."
+
+ FTWTestOutputDoc.Fields[5].AddExample("ExpectError", false)
+
+ FTWTestLogDoc.Type = "Log"
+ FTWTestLogDoc.Comments[encoder.LineComment] = ""
+ FTWTestLogDoc.Description = ""
+
+ FTWTestLogDoc.AddExample("", exampleLog)
+ FTWTestLogDoc.AppearsIn = []encoder.Appearance{
+ {
+ TypeName: "Output",
+ FieldName: "log",
+ },
+ }
+ FTWTestLogDoc.Fields = make([]encoder.Doc, 4)
+ FTWTestLogDoc.Fields[0].Name = "expect_id"
+ FTWTestLogDoc.Fields[0].Type = "int"
+ FTWTestLogDoc.Fields[0].Note = ""
+ FTWTestLogDoc.Fields[0].Description = "description: |\n Expect the given ID to be contained in the log output.\n examples:\n - exampleLog.ExpectId"
+ FTWTestLogDoc.Fields[0].Comments[encoder.LineComment] = " description: |"
+ FTWTestLogDoc.Fields[1].Name = "expect_id"
+ FTWTestLogDoc.Fields[1].Type = "int"
+ FTWTestLogDoc.Fields[1].Note = ""
+ FTWTestLogDoc.Fields[1].Description = "description: |\n Expect the given ID _not_ to be contained in the log output.\n examples:\n - exampleLog.NoExpectId"
+ FTWTestLogDoc.Fields[1].Comments[encoder.LineComment] = " description: |"
+ FTWTestLogDoc.Fields[2].Name = "match_regex"
+ FTWTestLogDoc.Fields[2].Type = "string"
+ FTWTestLogDoc.Fields[2].Note = ""
+ FTWTestLogDoc.Fields[2].Description = "Expect the regular expression to match log content for the current test."
+ FTWTestLogDoc.Fields[2].Comments[encoder.LineComment] = "Expect the regular expression to match log content for the current test."
+
+ FTWTestLogDoc.Fields[2].AddExample("", exampleLog.MatchRegex)
+ FTWTestLogDoc.Fields[3].Name = "no_match_regex"
+ FTWTestLogDoc.Fields[3].Type = "string"
+ FTWTestLogDoc.Fields[3].Note = ""
+ FTWTestLogDoc.Fields[3].Description = "Expect the regular expression to _not_ match log content for the current test."
+ FTWTestLogDoc.Fields[3].Comments[encoder.LineComment] = "Expect the regular expression to _not_ match log content for the current test."
+
+ FTWTestLogDoc.Fields[3].AddExample("", exampleLog.NoMatchRegex)
+}
+
+// GetFTWTestDoc returns documentation for the file ./test_doc.go.
+func GetFTWTestDoc() *encoder.FileDoc {
+ return &encoder.FileDoc{
+ Name: "FTWTest",
+ Description: "",
+ Structs: []*encoder.Doc{
+ &FTWTestDoc,
+ &FTWTestMetaDoc,
+ &TestDoc,
+ &StageDoc,
+ &StageDataDoc,
+ &InputDoc,
+ &FTWTestOutputDoc,
+ &FTWTestLogDoc,
+ },
+ }
+}
diff --git a/test/test.go b/types/types.go
similarity index 67%
rename from test/test.go
rename to types/types.go
index eb9c157..65fde7f 100644
--- a/test/test.go
+++ b/types/types.go
@@ -1,59 +1,10 @@
-package test
-
-// Copyright 2023 Felipe Zipitria
+// Copyright 2023 OWASP CRS
// SPDX-License-Identifier: Apache-2.0
-//go:generate dstdocgen -package test -path . -structure FTWTest -output ./test_doc.go
-
-func intPtr(i int) *int {
- return &i
-}
-
-func boolPtr(b bool) *bool {
- return &b
-}
-
-func strPtr(s string) *string {
- return &s
-}
+//go:generate dstdocgen -package types -path . -structure FTWTest -output ./test_doc.go
+//go:generate dstdocgen -package types -path . -structure FTWOverrides -output ./overrides_doc.go
-var (
- exampleStageData = StageData{
- Input: exampleInput,
- Output: ExampleOutput,
- }
- exampleStages = []Stage{
- {
- exampleStageData,
- },
- }
- exampleHeaders = map[string]string{
- "User-Agent": "CRS Tests",
- "Host": "localhost",
- "Accept": "*/*",
- }
- exampleInput = Input{
- DestAddr: strPtr("192.168.0.1"),
- Port: intPtr(8080),
- Protocol: strPtr("http"),
- URI: strPtr("/test"),
- Version: strPtr("HTTP/1.1"),
- Headers: exampleHeaders,
- Method: strPtr("REPORT"),
- Data: nil,
- EncodedRequest: "TXkgRGF0YQo=",
- SaveCookie: boolPtr(false),
- StopMagic: boolPtr(true),
- AutocompleteHeaders: boolPtr(false),
- }
- ExampleOutput = Output{
- Status: []int{200},
- ResponseContains: "",
- LogContains: "nothing",
- NoLogContains: "",
- ExpectError: boolPtr(true),
- }
-)
+package types
// Welcome to the FTW YAMLFormat documentation.
// In this document we will explain all the possible options that can be used within the YAML format.
@@ -64,7 +15,7 @@ var (
type FTWTest struct {
// description: |
// Meta describes the metadata information of this yaml test file
- Meta Meta `yaml:"meta"`
+ Meta FTWTestMeta `yaml:"meta"`
// description: |
// FileName is the name of the file where these tests are.
@@ -81,8 +32,8 @@ type FTWTest struct {
Tests []Test `yaml:"tests"`
}
-// Meta describes the metadata information of this yaml test file
-type Meta struct {
+// FTWTestMeta describes the metadata information of this yaml test file
+type FTWTestMeta struct {
// description: |
// Author is the list of authors that added content to this file
// examples:
@@ -271,11 +222,11 @@ type Input struct {
// Output is the response expected from the test
type Output struct {
// description: |
- // Status describes the HTTP status error code expected as response.
+ // Status describes the HTTP status code expected in the response.
// examples:
// - name: Status
- // value: [200]
- Status []int `yaml:"status,flow,omitempty"`
+ // value: 200
+ Status int `yaml:"status,omitempty"`
// description: |
// ResponseContains describes the text that should be contained in the HTTP response.
@@ -289,6 +240,8 @@ type Output struct {
// examples:
// - name: LogContains
// value: "\"id 920100\""
+ //
+ // deprecated: use Log instead
LogContains string `yaml:"log_contains,omitempty"`
// description: |
@@ -296,8 +249,16 @@ type Output struct {
// examples:
// - name: NoLogContains
// value: "\"id 920100\""
+ //
+ // deprecated: use Log instead
NoLogContains string `yaml:"no_log_contains,omitempty"`
+ // description: |
+ // Log is used to configure expectations about the log contents.
+ // examples:
+ // - value: exampleLog
+ Log Log `yaml:"log,omitempty"`
+
// description: |
// When `ExpectError` is true, we don't expect an answer from the WAF, just an error.
// examples:
@@ -305,3 +266,107 @@ type Output struct {
// value: false
ExpectError *bool `yaml:"expect_error,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.
+ // examples:
+ // - exampleLog.ExpectId
+ ExpectId int `yaml:"expect_id,omitempty"`
+
+ // description: |
+ // Expect the given ID _not_ to be contained in the log output.
+ // examples:
+ // - exampleLog.NoExpectId
+ NoExpectId int `yaml:"expect_id,omitempty"`
+
+ // description: |
+ // Expect the regular expression to match log content for the current test.
+ // examples:
+ // - value: exampleLog.MatchRegex
+ MatchRegex string `yaml:"match_regex,omitempty"`
+
+ // description: |
+ // Expect the regular expression to _not_ match log content for the current test.
+ // examples:
+ // - value: exampleLog.NoMatchRegex
+ NoMatchRegex string `yaml:"no_match_regex,omitempty"`
+}
+
+// FTWOverrides describes platform specific overrides for tests
+type FTWOverrides struct {
+ // description: |
+ // The version field designates the version of the schema that validates this file
+ // examples:
+ // - value: "\"v0.1.0\""
+ Version string `yaml:"version"`
+
+ // description: |
+ // Meta describes the metadata information
+ // examples:
+ // - value: metaExample
+ Meta FTWOverridesMeta `yaml:"meta"`
+
+ // description: |
+ // List of test override specifications
+ // examples:
+ // - value: testOverridesExample
+ TestOverrides []TestOverride `yaml:"test_overrides"`
+}
+
+// FTWOverridesMeta describes the metadata information of this yaml file
+type FTWOverridesMeta struct {
+ // description: |
+ // The name of the WAF engine the tests are expected to run against
+ // examples:
+ // - value: "\"coraza\""
+ Engine string `yaml:"engine"`
+
+ // description: |
+ // The name of the platform (e.g., web server) the tests are expected to run against
+ // examples:
+ // - value: "\"nginx\""
+ Platform string `yaml:"platform"`
+
+ // description: |
+ // Custom annotations; can be used to add additional meta information
+ // examples:
+ // - value: annotationsExample
+ Annotations map[string]string `yaml:"annotations"`
+}
+
+// TestOverride describes overrides for a single test
+type TestOverride struct {
+ // description: |
+ // ID of the rule this test targets.
+ // examples:
+ // - value: 920100
+ RuleId int `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: [5, 6]
+ TestIds []int `yaml:"test_ids,omitempty"`
+
+ // description: |
+ // Describes why this override is necessary.
+ // examples:
+ // - 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: |
+ // Specifies overrides on the test output
+ // examples:
+ // - value: 400
+ Output Output `yaml:"output"`
+}
diff --git a/types/types_test.go b/types/types_test.go
new file mode 100644
index 0000000..f88cde4
--- /dev/null
+++ b/types/types_test.go
@@ -0,0 +1,228 @@
+// Copyright 2023 OWASP CRS
+// SPDX-License-Identifier: Apache-2.0
+
+package types
+
+import (
+ "testing"
+
+ "github.com/goccy/go-yaml"
+ "github.com/stretchr/testify/assert"
+)
+
+var testYaml = `---
+filename: "testYaml.yaml"
+meta:
+ author: "ftw-tests-schema"
+ enabled: true
+ name: "testYaml"
+ description: "Simple YAML to test that the schema is working."
+tests:
+ - test_title: 1234-1
+ desc: "Test that the schema is working."
+ stages:
+ - stage:
+ input:
+ dest_addr: "127.0.0.1"
+ port: 80
+ headers:
+ User-Agent: "FTW Schema Tests"
+ Host: "localhost"
+ output:
+ no_log_contains: 'id "1234"'
+ - test_title: 1234-2
+ stages:
+ - stage:
+ input:
+ dest_addr: "127.0.0.1"
+ port: 80
+ method: "OPTIONS"
+ headers:
+ User-Agent: "FTW Schema Tests"
+ Host: "localhost"
+ output:
+ status: 200
+`
+
+var ftwTest = &FTWTest{
+ FileName: "testYaml.yaml",
+ Meta: FTWTestMeta{
+ Author: "ftw-tests-schema",
+ Enabled: boolPtr(true),
+ Name: "testYaml",
+ Description: "Simple YAML to test that the schema is working.",
+ },
+ Tests: []Test{
+ {
+ TestTitle: "1234-1",
+ TestDescription: "Test that the schema is working.",
+ Stages: []Stage{
+ {
+ SD: StageData{
+ Input: Input{
+ DestAddr: strPtr("127.0.0.1"),
+ Port: intPtr(80),
+ Headers: map[string]string{
+ "User-Agent": "FTW Schema Tests",
+ "Host": "localhost",
+ },
+ },
+ Output: Output{
+ NoLogContains: "id \"1234\"",
+ },
+ },
+ },
+ },
+ },
+ {
+ TestTitle: "1234-2",
+ Stages: []Stage{
+ {
+ SD: StageData{
+ Input: Input{
+ DestAddr: strPtr("127.0.0.1"),
+ Port: intPtr(80),
+ Method: strPtr("OPTIONS"),
+ Headers: map[string]string{
+ "User-Agent": "FTW Schema Tests",
+ "Host": "localhost",
+ },
+ },
+ Output: Output{
+ Status: 200,
+ },
+ },
+ },
+ },
+ },
+ },
+}
+
+func TestUnmarshalFTWTest(t *testing.T) {
+ var ftw FTWTest
+ assert := assert.New(t)
+
+ err := yaml.Unmarshal([]byte(testYaml), &ftw)
+ assert.NoError(err)
+
+ assert.Equal(ftwTest.FileName, ftw.FileName)
+ assert.Equal(ftwTest.Meta.Author, ftw.Meta.Author)
+ assert.Equal(ftwTest.Meta.Enabled, ftw.Meta.Enabled)
+ assert.Equal(ftwTest.Meta.Name, ftw.Meta.Name)
+ assert.Equal(ftwTest.Meta.Description, ftw.Meta.Description)
+ assert.Len(ftwTest.Tests, len(ftw.Tests))
+
+ for i, test := range ftw.Tests {
+ expectedTest := ftwTest.Tests[i]
+ assert.Equal(expectedTest.TestTitle, test.TestTitle)
+ assert.Len(test.Stages, len(expectedTest.Stages))
+
+ for j, stage := range test.Stages {
+ expectedStage := expectedTest.Stages[j]
+ assert.Equal(expectedStage.SD.Input.DestAddr, stage.SD.Input.DestAddr)
+ assert.Equal(expectedStage.SD.Input.Port, stage.SD.Input.Port)
+ assert.Equal(expectedStage.SD.Input.Method, stage.SD.Input.Method)
+ assert.Len(stage.SD.Input.Headers, len(expectedStage.SD.Input.Headers))
+
+ for k, header := range stage.SD.Input.Headers {
+ expectedHeader := expectedStage.SD.Input.Headers[k]
+ assert.Equal(expectedHeader, header)
+ }
+
+ assert.Equal(expectedStage.SD.Output.NoLogContains, stage.SD.Output.NoLogContains)
+ assert.Equal(expectedStage.SD.Output.Status, stage.SD.Output.Status)
+ }
+ }
+}
+
+// overrides
+
+var overridesYaml = `---
+version: "v0.0.0"
+meta:
+ engine: "libmodsecurity3"
+ platform: "nginx"
+ annotations:
+ os: "Debian Bullseye"
+ purpose: "L7ASR test suite"
+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.
+ expect_failure: true
+ output:
+ status: 200
+ log_contains: "nothing"
+ log:
+ match_regex: 'id[:\s"]*123456'
+ no_match_regex: 'id[:\s"]*123456'
+ expect_error: true
+`
+
+var ftwOverrides = &FTWOverrides{
+ Version: "v0.0.0",
+ Meta: metaExample,
+ TestOverrides: testOverridesExample,
+}
+
+func TestUnmarshalFTWOverrides(t *testing.T) {
+ var overrides FTWOverrides
+
+ err := yaml.Unmarshal([]byte(overridesYaml), &overrides)
+
+ if err != nil {
+ t.Errorf("Unmarshal: %v", err)
+ }
+
+ assert.Equal(t, ftwOverrides.Version, overrides.Version)
+}
+
+func TestUnmarshalMeta(t *testing.T) {
+ var overrides FTWOverrides
+ assert := assert.New(t)
+
+ err := yaml.Unmarshal([]byte(overridesYaml), &overrides)
+ if err != nil {
+ t.Errorf("Unmarshal: %v", err)
+ }
+
+ meta := overrides.Meta
+ expectedMeta := ftwOverrides.Meta
+ assert.Equal(expectedMeta.Engine, meta.Engine)
+ assert.Equal(expectedMeta.Platform, meta.Platform)
+ assert.NotNil(meta.Annotations)
+ assert.Len(meta.Annotations, len(expectedMeta.Annotations))
+
+ for key, value := range meta.Annotations {
+ expectedValue := expectedMeta.Annotations[key]
+ assert.Equal(expectedValue, value)
+ }
+}
+
+func TestUnmarmarshalTestOverrides(t *testing.T) {
+ var overrides FTWOverrides
+ asserter := assert.New(t)
+
+ err := yaml.Unmarshal([]byte(overridesYaml), &overrides)
+ if err != nil {
+ t.Errorf("Unmarshal: %v", err)
+ }
+
+ testOverrides := overrides.TestOverrides
+ expectedTestOverrides := ftwOverrides.TestOverrides
+ asserter.NotNil(testOverrides)
+ asserter.Len(testOverrides, len(expectedTestOverrides))
+
+ for index, testOverride := range testOverrides {
+ expectedTestOverride := expectedTestOverrides[index]
+ 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)
+ }
+ }
+}