From cd19db3e41bb7c1d04d5b3475b675c7085ed6299 Mon Sep 17 00:00:00 2001 From: Max Leske Date: Thu, 9 Nov 2023 16:36:21 +0100 Subject: [PATCH 1/4] feat: add schema for test overrides --- cmd/generate-doc-yaml-schema/main.go | 4 +- overrides/overrides.go | 122 +++++++++ overrides/overrides_doc.go | 194 ++++++++++++++ overrides/overrides_test.go | 207 ++++++++++++++ spec/v1/ftw.md | 2 +- types/types.go => test/test.go | 8 +- test/test_doc.go | 326 +++++++++++++++++++++++ types/types_test.go => test/test_test.go | 2 +- 8 files changed, 857 insertions(+), 8 deletions(-) create mode 100644 overrides/overrides.go create mode 100644 overrides/overrides_doc.go create mode 100644 overrides/overrides_test.go rename types/types.go => test/test.go (98%) create mode 100644 test/test_doc.go rename types/types_test.go => test/test_test.go (99%) diff --git a/cmd/generate-doc-yaml-schema/main.go b/cmd/generate-doc-yaml-schema/main.go index a6e74e2..e76ac81 100644 --- a/cmd/generate-doc-yaml-schema/main.go +++ b/cmd/generate-doc-yaml-schema/main.go @@ -6,11 +6,11 @@ package main import ( "os" - "github.com/coreruleset/ftw-tests-schema/types" + "github.com/coreruleset/ftw-tests-schema/test" ) func main() { - data, err := types.GetFTWTestDoc().Encode() + data, err := test.GetFTWTestDoc().Encode() if err != nil { panic(err) } diff --git a/overrides/overrides.go b/overrides/overrides.go new file mode 100644 index 0000000..4e63bca --- /dev/null +++ b/overrides/overrides.go @@ -0,0 +1,122 @@ +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 new file mode 100644 index 0000000..a8916d7 --- /dev/null +++ b/overrides/overrides_doc.go @@ -0,0 +1,194 @@ +// 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 new file mode 100644 index 0000000..f01cebb --- /dev/null +++ b/overrides/overrides_test.go @@ -0,0 +1,207 @@ +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/ftw.md b/spec/v1/ftw.md index 60159e8..a9630d6 100644 --- a/spec/v1/ftw.md +++ b/spec/v1/ftw.md @@ -16,7 +16,7 @@ Metadata parameters are present once per test file and are located by convention description: "This file contains example tests." ... ``` -What follows are all the possible Metadata parameters that are current supported +What follows are all the possible Metadata parameters that are currently supported author ------ diff --git a/types/types.go b/test/test.go similarity index 98% rename from types/types.go rename to test/test.go index 51f3e69..eb9c157 100644 --- a/types/types.go +++ b/test/test.go @@ -1,9 +1,9 @@ -package types +package test // Copyright 2023 Felipe Zipitria // SPDX-License-Identifier: Apache-2.0 -//go:generate dstdocgen -package types -path . -structure FTWTest -output ./types_doc.go +//go:generate dstdocgen -package test -path . -structure FTWTest -output ./test_doc.go func intPtr(i int) *int { return &i @@ -20,7 +20,7 @@ func strPtr(s string) *string { var ( exampleStageData = StageData{ Input: exampleInput, - Output: exampleOutput, + Output: ExampleOutput, } exampleStages = []Stage{ { @@ -46,7 +46,7 @@ var ( StopMagic: boolPtr(true), AutocompleteHeaders: boolPtr(false), } - exampleOutput = Output{ + ExampleOutput = Output{ Status: []int{200}, ResponseContains: "", LogContains: "nothing", diff --git a/test/test_doc.go b/test/test_doc.go new file mode 100644 index 0000000..f348399 --- /dev/null +++ b/test/test_doc.go @@ -0,0 +1,326 @@ +// 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/types/types_test.go b/test/test_test.go similarity index 99% rename from types/types_test.go rename to test/test_test.go index cf09e27..516be57 100644 --- a/types/types_test.go +++ b/test/test_test.go @@ -1,4 +1,4 @@ -package types +package test import ( "testing" From 8aae27813a7219d2b12e9babf9e9c05448ac2581 Mon Sep 17 00:00:00 2001 From: Max Leske Date: Sun, 12 Nov 2023 13:26:17 +0100 Subject: [PATCH 2/4] feat: update overrides schema - write and improve tests - deprecate `log_contains` and `no_log_contains` in favour of new `log` struct - add `expect_id` and `no_expect_id` to simplify standard log output matches --- cmd/generate-doc-yaml-schema/main.go | 15 +- go.mod | 6 + go.sum | 4 +- mage.go | 2 +- magefile.go | 4 +- overrides/overrides.go | 122 -- overrides/overrides_doc.go | 194 --- overrides/overrides_test.go | 207 --- spec/v1.1/ftw.md | 1769 ++++++++++++++++++++++++++ test/test_doc.go | 326 ----- test/test_test.go | 193 --- types/examples.go | 70 + types/helpers.go | 16 + types/overrides_doc.go | 233 ++++ types/test_doc.go | 380 ++++++ test/test.go => types/types.go | 183 ++- types/types_test.go | 228 ++++ 17 files changed, 2843 insertions(+), 1109 deletions(-) delete mode 100644 overrides/overrides.go delete mode 100644 overrides/overrides_doc.go delete mode 100644 overrides/overrides_test.go create mode 100644 spec/v1.1/ftw.md delete mode 100644 test/test_doc.go delete mode 100644 test/test_test.go create mode 100644 types/examples.go create mode 100644 types/helpers.go create mode 100644 types/overrides_doc.go create mode 100644 types/test_doc.go rename test/test.go => types/types.go (67%) create mode 100644 types/types_test.go 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 FTWTestMeta + +
+
+ +Meta describes the metadata information of this yaml test file + +
+ +
+ +
+ +tests []Test + +
+
+ +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 []Stage + +
+
+ +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 +``` + + + +
+ +
+ +stage StageData + +
+
+ +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 Input + +
+
+ +Input is the data that is passed to the test + + + +Examples: + + +```yaml +# Input +input: + dest_addr: 192.168.0.1 + port: 8080 + protocol: http + uri: /test + version: HTTP/1.1 + method: REPORT + headers: + Accept: '*/*' + Host: localhost + User-Agent: CRS Tests + save_cookie: false + stop_magic: true + autocomplete_headers: false + encoded_request: TXkgRGF0YQo= +``` + + +
+ +
+ +
+ +output Output + +
+
+ +Output is the data that is returned from the test + + + +Examples: + + +```yaml +# Output +output: + status: 200 + 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 Log + +
+
+ +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 FTWOverridesMeta + +
+
+ +Meta describes the metadata information + + + +Examples: + + +```yaml +meta: + engine: libmodsecurity3 + platform: nginx + annotations: + os: Debian Bullseye + purpose: L7ASR test suite +``` + + +
+ +
+ +
+ +test_overrides []TestOverride + +
+
+ +List of test override specifications + + + +Examples: + + +```yaml +test_overrides: + - rule_id: 920100 + test_ids: + - 4 + - 6 + reason: |- + nginx returns 400 when `Content-Length` header is sent in a + `Transfer-Encoding: chunked` request. + 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 +``` + + +
+ +
+ +
+ +output Output + +
+
+ +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 Log + +
+
+ +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) + } + } +} From 2434b86f2d8a06ba4cd5898017ce5aa95f9c6e4c Mon Sep 17 00:00:00 2001 From: Max Leske Date: Mon, 13 Nov 2023 10:18:20 +0100 Subject: [PATCH 3/4] feat: improve schema - deprecate some fields in favor of new ones - add `retry_once` option --- spec/v1.1/ftw.md | 458 ++++++++++++++++++++++++++++++----------- types/examples.go | 20 +- types/overrides_doc.go | 40 ++-- types/test_doc.go | 105 +++++++--- types/types.go | 134 ++++++++---- types/types_test.go | 109 +++++----- 6 files changed, 602 insertions(+), 264 deletions(-) diff --git a/spec/v1.1/ftw.md b/spec/v1.1/ftw.md index 209981b..c4fa341 100644 --- a/spec/v1.1/ftw.md +++ b/spec/v1.1/ftw.md @@ -46,8 +46,37 @@ Examples: ```yaml -# Tests -tests: the tests +tests: + - test_title: 123456-1 + rule_id: 0 + test_id: 0 + desc: Unix RCE using `time` + stages: + - description: Get cookie from server + input: + dest_addr: 192.168.0.1 + port: 8080 + protocol: http + uri: /test + version: HTTP/1.1 + method: REPORT + headers: + Accept: '*/*' + Host: localhost + User-Agent: CRS Tests + save_cookie: false + stop_magic: true + autocomplete_headers: false + encoded_request: TXkgRGF0YQo= + output: + status: 200 + log_contains: nothing + log: + expect_id: 123456 + no_expect_id: 123456 + match_regex: id[:\s"]*123456 + no_match_regex: id[:\s"]*123456 + expect_error: true ``` @@ -205,8 +234,36 @@ Appears in: ```yaml -# Tests -the tests +- test_title: 123456-1 + rule_id: 0 + test_id: 0 + desc: Unix RCE using `time` + stages: + - description: Get cookie from server + input: + dest_addr: 192.168.0.1 + port: 8080 + protocol: http + uri: /test + version: HTTP/1.1 + method: REPORT + headers: + Accept: '*/*' + Host: localhost + User-Agent: CRS Tests + save_cookie: false + stop_magic: true + autocomplete_headers: false + encoded_request: TXkgRGF0YQo= + output: + status: 200 + log_contains: nothing + log: + expect_id: 123456 + no_expect_id: 123456 + match_regex: id[:\s"]*123456 + no_match_regex: id[:\s"]*123456 + expect_error: true ``` @@ -220,7 +277,7 @@ the tests
-TestTitle the title of this particular test. It is used for inclusion/exclusion of each run by the tool. +TestTitle is the title of this particular test. It is used for inclusion/exclusion of each run by the tool. @@ -228,8 +285,7 @@ Examples: ```yaml -# TestTitle -test_title: 920100-1 +test_title: 123456-1 ``` @@ -239,13 +295,12 @@ test_title: 920100-1
-desc string +rule_id int
-TestDescription is the description for this particular test. Should be used to describe the internals of -the specific things this test is targeting. +RuleId is the ID of the rule this test targets @@ -253,8 +308,8 @@ Examples: ```yaml -# TestDescription -desc: This test targets something +# RuleId +rule_id: 123456 ``` @@ -264,12 +319,12 @@ desc: This test targets something
-stages []Stage +test_id int
-Stages is the list of all the stages to perform this test. +TestId is the ID of the test, in relation to `rule_id` @@ -277,33 +332,8 @@ 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 +# TestId +test_id: 4 ``` @@ -311,59 +341,38 @@ stages:
+
+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. -## Stage - -Appears in: -- Test.stages +Examples: ```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 +desc: Unix RCE using `time` ``` +

-stage StageData +stages []Stage
-StageData is an individual test stage +Stages is the list of all the stages to perform this test. @@ -371,9 +380,9 @@ Examples: ```yaml -# StageData -stage: - input: +stages: + - description: Get cookie from server + input: dest_addr: 192.168.0.1 port: 8080 protocol: http @@ -388,12 +397,12 @@ stage: stop_magic: true autocomplete_headers: false encoded_request: TXkgRGF0YQo= - output: + output: status: 200 log_contains: nothing log: expect_id: 123456 - expect_id: 123456 + no_expect_id: 123456 match_regex: id[:\s"]*123456 no_match_regex: id[:\s"]*123456 expect_error: true @@ -408,16 +417,98 @@ stage: -## StageData +## Stage Appears in: -- Stage.stage +- Test.stages + + +```yaml +- description: Get cookie from server + input: + dest_addr: 192.168.0.1 + port: 8080 + protocol: http + uri: /test + version: HTTP/1.1 + method: REPORT + headers: + Accept: '*/*' + Host: localhost + User-Agent: CRS Tests + save_cookie: false + stop_magic: true + autocomplete_headers: false + encoded_request: TXkgRGF0YQo= + output: + status: 200 + log_contains: nothing + log: + expect_id: 123456 + no_expect_id: 123456 + match_regex: id[:\s"]*123456 + no_match_regex: id[:\s"]*123456 + expect_error: true +``` + + + +
+ +
+ +stage StageData + +
+
+ +StageData is an individual test stage. + +
+ +
+ +
+ +description string + +
+
+ +Describes the purpose of this stage. + + + +Examples: ```yaml -# StageData +description: Get cookie from server +``` + + +
+ +
+ +
+ +input Input + +
+
+ +Input is the data that is passed to the test + + + +Examples: + + +```yaml +# Input input: dest_addr: 192.168.0.1 port: 8080 @@ -433,18 +524,59 @@ input: stop_magic: true autocomplete_headers: false encoded_request: TXkgRGF0YQo= +``` + + +
+ +
+ +
+ +output Output + +
+
+ +Output is the data that is returned from the test + + + +Examples: + + +```yaml +# Output output: status: 200 log_contains: nothing log: expect_id: 123456 - expect_id: 123456 + no_expect_id: 123456 match_regex: id[:\s"]*123456 no_match_regex: id[:\s"]*123456 expect_error: true ``` +
+ +
+ + + + + +## StageData + +Appears in: + + +- Stage.stage + + + +
@@ -507,7 +639,7 @@ output: log_contains: nothing log: expect_id: 123456 - expect_id: 123456 + no_expect_id: 123456 match_regex: id[:\s"]*123456 no_match_regex: id[:\s"]*123456 expect_error: true @@ -527,6 +659,8 @@ output: Appears in: +- Stage.input + - StageData.input @@ -547,6 +681,23 @@ stop_magic: true autocomplete_headers: false encoded_request: TXkgRGF0YQo= ``` +```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= +``` @@ -878,6 +1029,8 @@ raw_request: TXkgRGF0YQo= Appears in: +- Stage.output + - StageData.output @@ -887,7 +1040,18 @@ status: 200 log_contains: nothing log: expect_id: 123456 + no_expect_id: 123456 + match_regex: id[:\s"]*123456 + no_match_regex: id[:\s"]*123456 +expect_error: true +``` +```yaml +# Output +status: 200 +log_contains: nothing +log: expect_id: 123456 + no_expect_id: 123456 match_regex: id[:\s"]*123456 no_match_regex: id[:\s"]*123456 expect_error: true @@ -1010,7 +1174,7 @@ Examples: ```yaml log: expect_id: 123456 - expect_id: 123456 + no_expect_id: 123456 match_regex: id[:\s"]*123456 no_match_regex: id[:\s"]*123456 ``` @@ -1058,7 +1222,7 @@ Appears in: ```yaml expect_id: 123456 -expect_id: 123456 +no_expect_id: 123456 match_regex: id[:\s"]*123456 no_match_regex: id[:\s"]*123456 ``` @@ -1074,10 +1238,17 @@ no_match_regex: id[:\s"]*123456
-description: | - Expect the given ID to be contained in the log output. - examples: - - exampleLog.ExpectId +Expect the given ID to be contained in the log output. + + + +Examples: + + +```yaml +expect_id: 123456 +``` +
@@ -1085,15 +1256,22 @@ description: |
-expect_id int +no_expect_id int
-description: | - Expect the given ID _not_ to be contained in the log output. - examples: - - exampleLog.NoExpectId +Expect the given ID _not_ to be contained in the log output. + + + +Examples: + + +```yaml +no_expect_id: 123456 +``` +
@@ -1230,9 +1408,7 @@ Examples: ```yaml test_overrides: - rule_id: 920100 - test_ids: - - 4 - - 6 + test_ids: [4, 6] reason: |- nginx returns 400 when `Content-Length` header is sent in a `Transfer-Encoding: chunked` request. @@ -1242,7 +1418,7 @@ test_overrides: log_contains: nothing log: expect_id: 123456 - expect_id: 123456 + no_expect_id: 123456 match_regex: id[:\s"]*123456 no_match_regex: id[:\s"]*123456 expect_error: true @@ -1362,9 +1538,7 @@ Appears in: ```yaml - rule_id: 920100 - test_ids: - - 4 - - 6 + test_ids: [4, 6] reason: |- nginx returns 400 when `Content-Length` header is sent in a `Transfer-Encoding: chunked` request. @@ -1374,7 +1548,7 @@ Appears in: log_contains: nothing log: expect_id: 123456 - expect_id: 123456 + no_expect_id: 123456 match_regex: id[:\s"]*123456 no_match_regex: id[:\s"]*123456 expect_error: true @@ -1414,11 +1588,20 @@ rule_id: 920100
-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] +IDs of the tests for rule_id that overrides should be applied to. +If this field is not set, the overrides will be applied to all tests of rule_id. + + + +Examples: + + +```yaml +test_ids: + - 4 + - 6 +``` +
@@ -1469,6 +1652,31 @@ expect_failure: true ``` +
+ +
+ +
+ +retry_once bool + +
+
+ +Whether a stage should be retried once in case of failure. +This option is primarily a workaround for a race condition in phase 5, +where the log entry of a rule may be flushed after the test end marker. + + + +Examples: + + +```yaml +retry_once: true +``` + +

@@ -1629,7 +1837,7 @@ Examples: ```yaml log: expect_id: 123456 - expect_id: 123456 + no_expect_id: 123456 match_regex: id[:\s"]*123456 no_match_regex: id[:\s"]*123456 ``` @@ -1677,7 +1885,7 @@ Appears in: ```yaml expect_id: 123456 -expect_id: 123456 +no_expect_id: 123456 match_regex: id[:\s"]*123456 no_match_regex: id[:\s"]*123456 ``` @@ -1693,10 +1901,17 @@ no_match_regex: id[:\s"]*123456
-description: | - Expect the given ID to be contained in the log output. - examples: - - exampleLog.ExpectId +Expect the given ID to be contained in the log output. + + + +Examples: + + +```yaml +expect_id: 123456 +``` +
@@ -1704,15 +1919,22 @@ description: |
-expect_id int +no_expect_id int
-description: | - Expect the given ID _not_ to be contained in the log output. - examples: - - exampleLog.NoExpectId +Expect the given ID _not_ to be contained in the log output. + + + +Examples: + + +```yaml +no_expect_id: 123456 +``` +
diff --git a/types/examples.go b/types/examples.go index 766b74a..cf7eff6 100644 --- a/types/examples.go +++ b/types/examples.go @@ -4,14 +4,21 @@ package types var ( - exampleStageData = StageData{ - Input: exampleInput, - Output: exampleOutput, + exampleTests = []Test{ + exampleTest, + } + exampleTest = Test{ + TestTitle: "123456-1", + TestDescription: "Unix RCE using `time`", + Stages: exampleStages, + } + exampleStage = Stage{ + Description: "Get cookie from server", + Input: exampleInput, + Output: exampleOutput, } exampleStages = []Stage{ - { - exampleStageData, - }, + exampleStage, } exampleHeaders = map[string]string{ "User-Agent": "CRS Tests", @@ -40,7 +47,6 @@ var ( Log: exampleLog, ExpectError: boolPtr(true), } - exampleLog = Log{ ExpectId: 123456, NoExpectId: 123456, diff --git a/types/overrides_doc.go b/types/overrides_doc.go index a328b1e..b766c18 100644 --- a/types/overrides_doc.go +++ b/types/overrides_doc.go @@ -88,20 +88,21 @@ func init() { FieldName: "test_overrides", }, } - TestOverrideDoc.Fields = make([]encoder.Doc, 5) + 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." TestOverrideDoc.Fields[0].Comments[encoder.LineComment] = "ID of the rule this test targets." - TestOverrideDoc.Fields[0].AddExample("", 920100) + TestOverrideDoc.Fields[0].AddExample("", testOverridesExample[0].RuleId) 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[1].Description = "IDs of the tests for rule_id that overrides should be applied to.\nIf this field is not set, the overrides will be applied to all tests of rule_id." + TestOverrideDoc.Fields[1].Comments[encoder.LineComment] = "IDs of the tests for rule_id that overrides should be applied to." + TestOverrideDoc.Fields[1].AddExample("", testOverridesExample[0].TestIds) TestOverrideDoc.Fields[2].Name = "reason" TestOverrideDoc.Fields[2].Type = "string" TestOverrideDoc.Fields[2].Note = "" @@ -116,13 +117,20 @@ func init() { 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].Name = "retry_once" + TestOverrideDoc.Fields[4].Type = "bool" 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].Description = "Whether a stage should be retried once in case of failure.\nThis option is primarily a workaround for a race condition in phase 5,\nwhere the log entry of a rule may be flushed after the test end marker." + TestOverrideDoc.Fields[4].Comments[encoder.LineComment] = "Whether a stage should be retried once in case of failure." - TestOverrideDoc.Fields[4].AddExample("", 400) + TestOverrideDoc.Fields[4].AddExample("", true) + TestOverrideDoc.Fields[5].Name = "output" + TestOverrideDoc.Fields[5].Type = "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) OutputDoc.Type = "Output" OutputDoc.Comments[encoder.LineComment] = "" @@ -194,13 +202,17 @@ func init() { 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[0].Description = "Expect the given ID to be contained in the log output." + LogDoc.Fields[0].Comments[encoder.LineComment] = "Expect the given ID to be contained in the log output." + + LogDoc.Fields[0].AddExample("", exampleLog.ExpectId) + LogDoc.Fields[1].Name = "no_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[1].Description = "Expect the given ID _not_ to be contained in the log output." + LogDoc.Fields[1].Comments[encoder.LineComment] = "Expect the given ID _not_ to be contained in the log output." + + LogDoc.Fields[1].AddExample("", exampleLog.NoExpectId) LogDoc.Fields[2].Name = "match_regex" LogDoc.Fields[2].Type = "string" LogDoc.Fields[2].Note = "" diff --git a/types/test_doc.go b/types/test_doc.go index 48a3c30..fbdcac3 100644 --- a/types/test_doc.go +++ b/types/test_doc.go @@ -35,7 +35,7 @@ func init() { 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") + FTWTestDoc.Fields[1].AddExample("", exampleTests) FTWTestMetaDoc.Type = "FTWTestMeta" FTWTestMetaDoc.Comments[encoder.LineComment] = "" @@ -87,61 +87,92 @@ func init() { TestDoc.Comments[encoder.LineComment] = "" TestDoc.Description = "" - TestDoc.AddExample("Tests", "the tests") + TestDoc.AddExample("", exampleTests) TestDoc.AppearsIn = []encoder.Appearance{ { TypeName: "FTWTest", FieldName: "tests", }, } - TestDoc.Fields = make([]encoder.Doc, 3) + TestDoc.Fields = make([]encoder.Doc, 5) 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].Description = "TestTitle is 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 is 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[0].AddExample("", exampleTest.TestTitle) + TestDoc.Fields[1].Name = "rule_id" + TestDoc.Fields[1].Type = "int" 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].Description = "RuleId is the ID of the rule this test targets" + TestDoc.Fields[1].Comments[encoder.LineComment] = "RuleId is the ID of the rule this test targets" - TestDoc.Fields[1].AddExample("TestDescription", "This test targets something") - TestDoc.Fields[2].Name = "stages" - TestDoc.Fields[2].Type = "[]Stage" + TestDoc.Fields[1].AddExample("RuleId", 123456) + TestDoc.Fields[2].Name = "test_id" + TestDoc.Fields[2].Type = "int" 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].Description = "TestId is the ID of the test, in relation to `rule_id`" + TestDoc.Fields[2].Comments[encoder.LineComment] = "TestId is the ID of the test, in relation to `rule_id`" - TestDoc.Fields[2].AddExample("Stages", exampleStages) + TestDoc.Fields[2].AddExample("TestId", 4) + TestDoc.Fields[3].Name = "desc" + TestDoc.Fields[3].Type = "string" + TestDoc.Fields[3].Note = "" + TestDoc.Fields[3].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[3].Comments[encoder.LineComment] = "TestDescription is the description for this particular test. Should be used to describe the internals of" + + TestDoc.Fields[3].AddExample("", exampleTest.TestDescription) + TestDoc.Fields[4].Name = "stages" + TestDoc.Fields[4].Type = "[]Stage" + TestDoc.Fields[4].Note = "" + TestDoc.Fields[4].Description = "Stages is the list of all the stages to perform this test." + TestDoc.Fields[4].Comments[encoder.LineComment] = "Stages is the list of all the stages to perform this test." + + TestDoc.Fields[4].AddExample("", exampleStages) StageDoc.Type = "Stage" StageDoc.Comments[encoder.LineComment] = "" StageDoc.Description = "" - StageDoc.AddExample("Stages", exampleStages) + StageDoc.AddExample("", exampleStages) StageDoc.AppearsIn = []encoder.Appearance{ { TypeName: "Test", FieldName: "stages", }, } - StageDoc.Fields = make([]encoder.Doc, 1) + StageDoc.Fields = make([]encoder.Doc, 4) 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) + StageDoc.Fields[0].Description = "StageData is an individual test stage." + StageDoc.Fields[0].Comments[encoder.LineComment] = "StageData is an individual test stage." + StageDoc.Fields[1].Name = "description" + StageDoc.Fields[1].Type = "string" + StageDoc.Fields[1].Note = "" + StageDoc.Fields[1].Description = "Describes the purpose of this stage." + StageDoc.Fields[1].Comments[encoder.LineComment] = "Describes the purpose of this stage." + + StageDoc.Fields[1].AddExample("", exampleStage.Description) + StageDoc.Fields[2].Name = "input" + StageDoc.Fields[2].Type = "Input" + StageDoc.Fields[2].Note = "" + StageDoc.Fields[2].Description = "Input is the data that is passed to the test" + StageDoc.Fields[2].Comments[encoder.LineComment] = "Input is the data that is passed to the test" + + StageDoc.Fields[2].AddExample("Input", exampleInput) + StageDoc.Fields[3].Name = "output" + StageDoc.Fields[3].Type = "Output" + StageDoc.Fields[3].Note = "" + StageDoc.Fields[3].Description = "Output is the data that is returned from the test" + StageDoc.Fields[3].Comments[encoder.LineComment] = "Output is the data that is returned from the test" + + StageDoc.Fields[3].AddExample("Output", exampleOutput) StageDataDoc.Type = "StageData" StageDataDoc.Comments[encoder.LineComment] = "" StageDataDoc.Description = "" - - StageDataDoc.AddExample("StageData", exampleStageData) StageDataDoc.AppearsIn = []encoder.Appearance{ { TypeName: "Stage", @@ -168,8 +199,14 @@ func init() { InputDoc.Comments[encoder.LineComment] = "" InputDoc.Description = "" + InputDoc.AddExample("Input", exampleInput) + InputDoc.AddExample("Input", exampleInput) InputDoc.AppearsIn = []encoder.Appearance{ + { + TypeName: "Stage", + FieldName: "input", + }, { TypeName: "StageData", FieldName: "input", @@ -272,8 +309,14 @@ func init() { FTWTestOutputDoc.Comments[encoder.LineComment] = "" FTWTestOutputDoc.Description = "" + FTWTestOutputDoc.AddExample("Output", exampleOutput) + FTWTestOutputDoc.AddExample("Output", exampleOutput) FTWTestOutputDoc.AppearsIn = []encoder.Appearance{ + { + TypeName: "Stage", + FieldName: "output", + }, { TypeName: "StageData", FieldName: "output", @@ -338,13 +381,17 @@ func init() { 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[0].Description = "Expect the given ID to be contained in the log output." + FTWTestLogDoc.Fields[0].Comments[encoder.LineComment] = "Expect the given ID to be contained in the log output." + + FTWTestLogDoc.Fields[0].AddExample("", exampleLog.ExpectId) + FTWTestLogDoc.Fields[1].Name = "no_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[1].Description = "Expect the given ID _not_ to be contained in the log output." + FTWTestLogDoc.Fields[1].Comments[encoder.LineComment] = "Expect the given ID _not_ to be contained in the log output." + + FTWTestLogDoc.Fields[1].AddExample("", exampleLog.NoExpectId) FTWTestLogDoc.Fields[2].Name = "match_regex" FTWTestLogDoc.Fields[2].Type = "string" FTWTestLogDoc.Fields[2].Note = "" diff --git a/types/types.go b/types/types.go index 65fde7f..b3bf111 100644 --- a/types/types.go +++ b/types/types.go @@ -27,8 +27,7 @@ type FTWTest struct { // description: | // Tests is a list of FTW tests // examples: - // - name: Tests - // value: "\"the tests\"" + // - value: exampleTests Tests []Test `yaml:"tests"` } @@ -46,6 +45,8 @@ type FTWTestMeta struct { // examples: // - name: Enabled // value: false + // + // Deprecated: ignored; use platform specific overrides instead Enabled *bool `yaml:"enabled,omitempty"` // description: | @@ -73,39 +74,72 @@ type FTWTestMeta struct { // Test is an individual test. One test can have multiple stages. type Test struct { // description: | - // TestTitle the title of this particular test. It is used for inclusion/exclusion of each run by the tool. + // TestTitle is the title of this particular test. It is used for inclusion/exclusion of each run by the tool. + // examples: + // - value: exampleTest.TestTitle + // + // Deprecated: use `rule_id` and `test_id` + TestTitle string `yaml:"test_title,omitempty"` + + // description: | + // RuleId is the ID of the rule this test targets + // examples: + // - name: RuleId + // value: 123456 + RuleId int `yaml:"rule_id"` + + // description: | + // TestId is the ID of the test, in relation to `rule_id` // examples: - // - name: TestTitle - // value: "\"920100-1\"" - TestTitle string `yaml:"test_title"` + // - name: TestId + // value: 4 + TestId int `yaml:"test_id"` // description: | // TestDescription is the description for this particular test. Should be used to describe the internals of // the specific things this test is targeting. // examples: - // - name: TestDescription - // value: "\"This test targets something\"" + // - value: exampleTest.TestDescription TestDescription string `yaml:"desc,omitempty"` // description: | // Stages is the list of all the stages to perform this test. // examples: - // - name: Stages - // value: exampleStages + // - value: exampleStages Stages []Stage `yaml:"stages"` } // Stage is a list of stages type Stage struct { // description: | - // StageData is an individual test stage + // StageData is an individual test stage. + // + // Deprecated: use the other fields of `Stage` + SD StageData `yaml:"stage,omitempty"` + // description: | + // Describes the purpose of this stage. + // examples: + // - value: exampleStage.Description + Description string `yaml:"description,omitempty"` + + // description: | + // Input is the data that is passed to the test // examples: - // - name: StageData - // value: exampleStageData - SD StageData `yaml:"stage"` + // - name: Input + // value: exampleInput + Input Input `yaml:"input"` + + // description: | + // Output is the data that is returned from the test + // examples: + // - name: Output + // value: exampleOutput + Output Output `yaml:"output"` } // StageData is the data that is passed to the test, and the data that is returned from the test +// +// Deprecated: use the other fields of `stage` type StageData struct { // description: | // Input is the data that is passed to the test @@ -193,6 +227,8 @@ type Input struct { // examples: // - name: StopMagic // value: false + // + // Deprecated: use AutocompleteHeaders instead StopMagic *bool `yaml:"stop_magic" koanf:"stop_magic,omitempty"` // description: | @@ -216,6 +252,8 @@ type Input struct { // examples: // - name: RAWRequest // value: "\"TXkgRGF0YQo=\"" + // + // Deprecated: use `encoded_request` RAWRequest string `yaml:"raw_request,omitempty" koanf:"raw_request,omitempty"` } @@ -241,7 +279,7 @@ type Output struct { // - name: LogContains // value: "\"id 920100\"" // - // deprecated: use Log instead + // Deprecated: use Log instead LogContains string `yaml:"log_contains,omitempty"` // description: | @@ -250,7 +288,7 @@ type Output struct { // - name: NoLogContains // value: "\"id 920100\"" // - // deprecated: use Log instead + // Deprecated: use Log instead NoLogContains string `yaml:"no_log_contains,omitempty"` // description: | @@ -272,14 +310,14 @@ type Log struct { // description: | // Expect the given ID to be contained in the log output. // examples: - // - exampleLog.ExpectId + // - value: 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"` + // - value: exampleLog.NoExpectId + NoExpectId int `yaml:"no_expect_id,omitempty"` // description: | // Expect the regular expression to match log content for the current test. @@ -297,76 +335,84 @@ type Log struct { // FTWOverrides describes platform specific overrides for tests type FTWOverrides struct { // description: | - // The version field designates the version of the schema that validates this file + // The version field designates the version of the schema that validates this file // examples: - // - value: "\"v0.1.0\"" + // - value: "\"v0.1.0\"" Version string `yaml:"version"` // description: | - // Meta describes the metadata information + // Meta describes the metadata information // examples: - // - value: metaExample + // - value: metaExample Meta FTWOverridesMeta `yaml:"meta"` // description: | - // List of test override specifications + // List of test override specifications // examples: - // - value: testOverridesExample + // - 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 + // The name of the WAF engine the tests are expected to run against // examples: - // - value: "\"coraza\"" + // - value: "\"coraza\"" Engine string `yaml:"engine"` // description: | - // The name of the platform (e.g., web server) the tests are expected to run against + // The name of the platform (e.g., web server) the tests are expected to run against // examples: - // - value: "\"nginx\"" + // - value: "\"nginx\"" Platform string `yaml:"platform"` // description: | - // Custom annotations; can be used to add additional meta information + // Custom annotations; can be used to add additional meta information // examples: - // - value: annotationsExample + // - 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. + // ID of the rule this test targets. // examples: - // - value: 920100 + // - value: testOverridesExample[0].RuleId 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. + // 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"` + // - value: testOverridesExample[0].TestIds + TestIds []int `yaml:"test_ids,flow,omitempty"` // description: | - // Describes why this override is necessary. + // Describes why this override is necessary. // examples: - // - value: reasonExample + // - value: reasonExample Reason string `yaml:"reason"` // description: | - // Whether this test is expected to fail for this particular configuration. - // Default: false + // Whether this test is expected to fail for this particular configuration. + // Default: false // examples: - // - value: true + // - value: true ExpectFailure *bool `yaml:"expect_failure,omitempty"` // description: | - // Specifies overrides on the test output + // Whether a stage should be retried once in case of failure. + // This option is primarily a workaround for a race condition in phase 5, + // where the log entry of a rule may be flushed after the test end marker. + // examples: + // - value: true + RetryOnce *bool `yaml:"retry_once,omitempty"` + + // description: | + // Specifies overrides on the test output // examples: - // - value: 400 + // - value: 400 Output Output `yaml:"output"` } diff --git a/types/types_test.go b/types/types_test.go index f88cde4..d6d9fd9 100644 --- a/types/types_test.go +++ b/types/types_test.go @@ -19,29 +19,40 @@ meta: description: "Simple YAML to test that the schema is working." tests: - test_title: 1234-1 + rule_id: 1234 + test_id: 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"' + - input: + dest_addr: "192.168.0.1" + port: 8080 + method: "REPORT" + headers: + User-Agent: "CRS Tests" + Host: "localhost" + Accept: "*/*" + encoded_request: "TXkgRGF0YQo=" + uri: "/test" + protocol: "http" + autocomplete_headers: false + stop_magic: true + save_cookie: false + output: + status: 200 + response_contains: "" + log_contains: "nothing" + no_log_contains: "" - 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 + - 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{ @@ -55,22 +66,14 @@ var ftwTest = &FTWTest{ Tests: []Test{ { TestTitle: "1234-1", + RuleId: 1234, + TestId: 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\"", - }, - }, + Description: exampleStage.Description, + Input: exampleInput, + Output: exampleOutput, }, }, }, @@ -78,20 +81,18 @@ var ftwTest = &FTWTest{ 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, + 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, + }, }, }, }, @@ -115,22 +116,24 @@ func TestUnmarshalFTWTest(t *testing.T) { for i, test := range ftw.Tests { expectedTest := ftwTest.Tests[i] assert.Equal(expectedTest.TestTitle, test.TestTitle) + assert.Equal(expectedTest.RuleId, test.RuleId) + assert.Equal(expectedTest.TestId, test.TestId) 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)) + assert.Equal(expectedStage.Input.DestAddr, stage.Input.DestAddr) + assert.Equal(expectedStage.Input.Port, stage.Input.Port) + assert.Equal(expectedStage.Input.Method, stage.Input.Method) + assert.Len(stage.Input.Headers, len(expectedStage.Input.Headers)) - for k, header := range stage.SD.Input.Headers { - expectedHeader := expectedStage.SD.Input.Headers[k] + for k, header := range stage.Input.Headers { + expectedHeader := expectedStage.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) + assert.Equal(expectedStage.Output.NoLogContains, stage.Output.NoLogContains) + assert.Equal(expectedStage.Output.Status, stage.Output.Status) } } } @@ -156,6 +159,8 @@ test_overrides: status: 200 log_contains: "nothing" log: + expect_id: 123456 + no_expect_id: 123456 match_regex: 'id[:\s"]*123456' no_match_regex: 'id[:\s"]*123456' expect_error: true From db67df04d864b96d323dba5d9df17de817cdd9f1 Mon Sep 17 00:00:00 2001 From: Max Leske Date: Fri, 26 Jan 2024 18:27:35 +0100 Subject: [PATCH 4/4] fix: modify setup to enable shared code --- .gitignore | 2 +- cmd/generate-doc-yaml-schema/main.go | 7 +- magefile.go | 21 +- types/overrides/examples.go | 28 ++ types/overrides/types.go | 93 ++++++ types/overrides/types_test.go | 103 +++++++ types/overrides_doc.go | 245 --------------- types/{ => test}/examples.go | 48 ++- types/{ => test}/helpers.go | 0 types/{ => test}/types.go | 116 +------- types/{ => test}/types_test.go | 100 +------ types/test_doc.go | 427 --------------------------- 12 files changed, 279 insertions(+), 911 deletions(-) create mode 100644 types/overrides/examples.go create mode 100644 types/overrides/types.go create mode 100644 types/overrides/types_test.go delete mode 100644 types/overrides_doc.go rename types/{ => test}/examples.go (56%) rename types/{ => test}/helpers.go (100%) rename types/{ => test}/types.go (75%) rename types/{ => test}/types_test.go (55%) delete mode 100644 types/test_doc.go diff --git a/.gitignore b/.gitignore index 49ec3c1..3b9eaa7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,4 @@ # vendor/ # Generated files -types/types_doc.go +**/*_doc.go diff --git a/cmd/generate-doc-yaml-schema/main.go b/cmd/generate-doc-yaml-schema/main.go index 1a0f473..d02556f 100644 --- a/cmd/generate-doc-yaml-schema/main.go +++ b/cmd/generate-doc-yaml-schema/main.go @@ -6,11 +6,12 @@ package main import ( "os" - "github.com/coreruleset/ftw-tests-schema/types" + overrides "github.com/coreruleset/ftw-tests-schema/types/overrides" + test "github.com/coreruleset/ftw-tests-schema/types/test" ) func main() { - data, err := types.GetFTWTestDoc().Encode() + data, err := test.GetFTWTestDoc().Encode() if err != nil { panic(err) } @@ -19,7 +20,7 @@ func main() { panic(err) } - data, err = types.GetFTWOverridesDoc().Encode() + data, err = overrides.GetFTWOverridesDoc().Encode() if err != nil { panic(err) } diff --git a/magefile.go b/magefile.go index b3e2bee..acc8ede 100644 --- a/magefile.go +++ b/magefile.go @@ -16,18 +16,24 @@ import ( ) var addLicenseVersion = "v1.1.1" // https://github.com/google/addlicense -var golangCILintVer = "v1.55.1" // https://github.com/golangci/golangci-lint/releases +var golangCILintVer = "v1.55.2" // https://github.com/golangci/golangci-lint/releases var gosImportsVer = "v0.3.8" // https://github.com/rinchsan/gosimports/releases/tag/v0.3.8 var errRunGoModTidy = errors.New("go.mod/sum not formatted, commit changes") var errNoGitDir = errors.New("no .git directory found") var errUpdateGeneratedFiles = errors.New("generated files need to be updated") -// Format formats code in this repository. -func Format() error { +// Generate Go documernation files for YAML structures +func Generate() error { if err := sh.RunV("go", "generate", "./..."); err != nil { return err } + return nil +} + +// Format formats code in this repository. +func Format() error { + mg.SerialDeps(Generate) if err := sh.RunV("go", "mod", "tidy"); err != nil { return err @@ -52,9 +58,7 @@ func Format() error { // Lint verifies code quality. func Lint() error { - if err := sh.RunV("go", "generate", "./..."); err != nil { - return err - } + mg.SerialDeps(Generate) if sh.Run("git", "diff", "--exit-code", "--", "'*_doc.go'") != nil { return errUpdateGeneratedFiles @@ -77,6 +81,8 @@ func Lint() error { // Test runs all tests. func Test() error { + mg.SerialDeps(Generate) + if err := sh.RunV("go", "test", "./..."); err != nil { return err } @@ -84,7 +90,10 @@ func Test() error { return nil } +// Generate Markdown output (printed to terminal) func Markdown() error { + mg.SerialDeps(Generate) + if err := sh.RunV("go", "build", "./cmd/generate-doc-yaml-schema"); err != nil { return err } diff --git a/types/overrides/examples.go b/types/overrides/examples.go new file mode 100644 index 0000000..e41cdd5 --- /dev/null +++ b/types/overrides/examples.go @@ -0,0 +1,28 @@ +// Copyright 2023 OWASP CRS +// SPDX-License-Identifier: Apache-2.0 + +package types + +import test "github.com/coreruleset/ftw-tests-schema/types/test" + +var ( + // imported + AnnotationsExample = test.AnnotationsExample + ReasonExample = test.ReasonExample + ExampleLog = test.ExampleLog + + MetaExample = FTWOverridesMeta{ + Engine: "libmodsecurity3", + Platform: "nginx", + Annotations: test.AnnotationsExample, + } + TestOverridesExample = []TestOverride{ + { + RuleId: 920100, + TestIds: []int{4, 6}, + Reason: test.ReasonExample, + ExpectFailure: func() *bool { b := true; return &b }(), + Output: test.ExampleOutput, + }, + } +) diff --git a/types/overrides/types.go b/types/overrides/types.go new file mode 100644 index 0000000..a3cc363 --- /dev/null +++ b/types/overrides/types.go @@ -0,0 +1,93 @@ +// Copyright 2023 OWASP CRS +// SPDX-License-Identifier: Apache-2.0 + +//go:generate dstdocgen -package types -path . -structure FTWOverrides -output ./overrides_doc.go + +package types + +import test "github.com/coreruleset/ftw-tests-schema/types/test" + +// 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: TestOverridesExample[0].RuleId + 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: TestOverridesExample[0].TestIds + TestIds []int `yaml:"test_ids,flow,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: | + // Whether a stage should be retried once in case of failure. + // This option is primarily a workaround for a race condition in phase 5, + // where the log entry of a rule may be flushed after the test end marker. + // examples: + // - value: true + RetryOnce *bool `yaml:"retry_once,omitempty"` + + // description: | + // Specifies overrides on the test output + // examples: + // - value: 400 + Output test.Output `yaml:"output"` +} diff --git a/types/overrides/types_test.go b/types/overrides/types_test.go new file mode 100644 index 0000000..929ea16 --- /dev/null +++ b/types/overrides/types_test.go @@ -0,0 +1,103 @@ +// 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 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: + expect_id: 123456 + no_expect_id: 123456 + 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) + } + } +} diff --git a/types/overrides_doc.go b/types/overrides_doc.go deleted file mode 100644 index b766c18..0000000 --- a/types/overrides_doc.go +++ /dev/null @@ -1,245 +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 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, 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." - TestOverrideDoc.Fields[0].Comments[encoder.LineComment] = "ID of the rule this test targets." - - TestOverrideDoc.Fields[0].AddExample("", testOverridesExample[0].RuleId) - TestOverrideDoc.Fields[1].Name = "test_ids" - TestOverrideDoc.Fields[1].Type = "[]int" - TestOverrideDoc.Fields[1].Note = "" - TestOverrideDoc.Fields[1].Description = "IDs of the tests for rule_id that overrides should be applied to.\nIf this field is not set, the overrides will be applied to all tests of rule_id." - TestOverrideDoc.Fields[1].Comments[encoder.LineComment] = "IDs of the tests for rule_id that overrides should be applied to." - - TestOverrideDoc.Fields[1].AddExample("", testOverridesExample[0].TestIds) - 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 = "retry_once" - TestOverrideDoc.Fields[4].Type = "bool" - TestOverrideDoc.Fields[4].Note = "" - TestOverrideDoc.Fields[4].Description = "Whether a stage should be retried once in case of failure.\nThis option is primarily a workaround for a race condition in phase 5,\nwhere the log entry of a rule may be flushed after the test end marker." - TestOverrideDoc.Fields[4].Comments[encoder.LineComment] = "Whether a stage should be retried once in case of failure." - - TestOverrideDoc.Fields[4].AddExample("", true) - TestOverrideDoc.Fields[5].Name = "output" - TestOverrideDoc.Fields[5].Type = "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) - - 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 = "Expect the given ID to be contained in the log output." - LogDoc.Fields[0].Comments[encoder.LineComment] = "Expect the given ID to be contained in the log output." - - LogDoc.Fields[0].AddExample("", exampleLog.ExpectId) - LogDoc.Fields[1].Name = "no_expect_id" - LogDoc.Fields[1].Type = "int" - LogDoc.Fields[1].Note = "" - LogDoc.Fields[1].Description = "Expect the given ID _not_ to be contained in the log output." - LogDoc.Fields[1].Comments[encoder.LineComment] = "Expect the given ID _not_ to be contained in the log output." - - LogDoc.Fields[1].AddExample("", exampleLog.NoExpectId) - 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/examples.go b/types/test/examples.go similarity index 56% rename from types/examples.go rename to types/test/examples.go index cf7eff6..a53818c 100644 --- a/types/examples.go +++ b/types/test/examples.go @@ -4,34 +4,34 @@ package types var ( - exampleTests = []Test{ - exampleTest, + ExampleTests = []Test{ + ExampleTest, } - exampleTest = Test{ + ExampleTest = Test{ TestTitle: "123456-1", TestDescription: "Unix RCE using `time`", - Stages: exampleStages, + Stages: ExampleStages, } - exampleStage = Stage{ + ExampleStage = Stage{ Description: "Get cookie from server", - Input: exampleInput, - Output: exampleOutput, + Input: ExampleInput, + Output: ExampleOutput, } - exampleStages = []Stage{ - exampleStage, + ExampleStages = []Stage{ + ExampleStage, } - exampleHeaders = map[string]string{ + ExampleHeaders = map[string]string{ "User-Agent": "CRS Tests", "Host": "localhost", "Accept": "*/*", } - exampleInput = Input{ + ExampleInput = Input{ DestAddr: strPtr("192.168.0.1"), Port: intPtr(8080), Protocol: strPtr("http"), URI: strPtr("/test"), Version: strPtr("HTTP/1.1"), - Headers: exampleHeaders, + Headers: ExampleHeaders, Method: strPtr("REPORT"), Data: nil, EncodedRequest: "TXkgRGF0YQo=", @@ -39,38 +39,24 @@ var ( StopMagic: boolPtr(true), AutocompleteHeaders: boolPtr(false), } - exampleOutput = Output{ + ExampleOutput = Output{ Status: 200, ResponseContains: "", LogContains: "nothing", NoLogContains: "", - Log: exampleLog, + Log: ExampleLog, ExpectError: boolPtr(true), } - exampleLog = Log{ + 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{ + AnnotationsExample = map[string]string{ "os": "Debian Bullseye", "purpose": "L7ASR test suite", } - reasonExample = "nginx returns 400 when `Content-Length` header is sent in a\n" + + 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/test/helpers.go similarity index 100% rename from types/helpers.go rename to types/test/helpers.go diff --git a/types/types.go b/types/test/types.go similarity index 75% rename from types/types.go rename to types/test/types.go index b3bf111..1db55df 100644 --- a/types/types.go +++ b/types/test/types.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:generate dstdocgen -package types -path . -structure FTWTest -output ./test_doc.go -//go:generate dstdocgen -package types -path . -structure FTWOverrides -output ./overrides_doc.go package types @@ -27,7 +26,7 @@ type FTWTest struct { // description: | // Tests is a list of FTW tests // examples: - // - value: exampleTests + // - value: ExampleTests Tests []Test `yaml:"tests"` } @@ -76,7 +75,7 @@ type Test struct { // description: | // TestTitle is the title of this particular test. It is used for inclusion/exclusion of each run by the tool. // examples: - // - value: exampleTest.TestTitle + // - value: ExampleTest.TestTitle // // Deprecated: use `rule_id` and `test_id` TestTitle string `yaml:"test_title,omitempty"` @@ -99,13 +98,13 @@ type Test struct { // TestDescription is the description for this particular test. Should be used to describe the internals of // the specific things this test is targeting. // examples: - // - value: exampleTest.TestDescription + // - value: ExampleTest.TestDescription TestDescription string `yaml:"desc,omitempty"` // description: | // Stages is the list of all the stages to perform this test. // examples: - // - value: exampleStages + // - value: ExampleStages Stages []Stage `yaml:"stages"` } @@ -119,21 +118,21 @@ type Stage struct { // description: | // Describes the purpose of this stage. // examples: - // - value: exampleStage.Description + // - value: ExampleStage.Description Description string `yaml:"description,omitempty"` // description: | // Input is the data that is passed to the test // examples: // - name: Input - // value: exampleInput + // value: ExampleInput Input Input `yaml:"input"` // description: | // Output is the data that is returned from the test // examples: // - name: Output - // value: exampleOutput + // value: ExampleOutput Output Output `yaml:"output"` } @@ -145,14 +144,14 @@ type StageData struct { // Input is the data that is passed to the test // examples: // - name: Input - // value: exampleInput + // value: ExampleInput Input Input `yaml:"input"` // description: | // Output is the data that is returned from the test // examples: // - name: Output - // value: exampleOutput + // value: ExampleOutput Output Output `yaml:"output"` } @@ -205,7 +204,7 @@ type Input struct { // Method allows you to declare headers that the test should send. // examples: // - name: Headers - // value: exampleHeaders + // value: ExampleHeaders Headers map[string]string `yaml:"headers,omitempty" koanf:"headers,omitempty"` // description: | @@ -294,7 +293,7 @@ type Output struct { // description: | // Log is used to configure expectations about the log contents. // examples: - // - value: exampleLog + // - value: ExampleLog Log Log `yaml:"log,omitempty"` // description: | @@ -310,109 +309,24 @@ type Log struct { // description: | // Expect the given ID to be contained in the log output. // examples: - // - value: exampleLog.ExpectId + // - value: ExampleLog.ExpectId ExpectId int `yaml:"expect_id,omitempty"` // description: | // Expect the given ID _not_ to be contained in the log output. // examples: - // - value: exampleLog.NoExpectId + // - value: ExampleLog.NoExpectId NoExpectId int `yaml:"no_expect_id,omitempty"` // description: | // Expect the regular expression to match log content for the current test. // examples: - // - value: exampleLog.MatchRegex + // - 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 + // - 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: testOverridesExample[0].RuleId - 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: testOverridesExample[0].TestIds - TestIds []int `yaml:"test_ids,flow,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: | - // Whether a stage should be retried once in case of failure. - // This option is primarily a workaround for a race condition in phase 5, - // where the log entry of a rule may be flushed after the test end marker. - // examples: - // - value: true - RetryOnce *bool `yaml:"retry_once,omitempty"` - - // description: | - // Specifies overrides on the test output - // examples: - // - value: 400 - Output Output `yaml:"output"` -} diff --git a/types/types_test.go b/types/test/types_test.go similarity index 55% rename from types/types_test.go rename to types/test/types_test.go index d6d9fd9..47a8a8b 100644 --- a/types/types_test.go +++ b/types/test/types_test.go @@ -71,9 +71,9 @@ var ftwTest = &FTWTest{ TestDescription: "Test that the schema is working.", Stages: []Stage{ { - Description: exampleStage.Description, - Input: exampleInput, - Output: exampleOutput, + Description: ExampleStage.Description, + Input: ExampleInput, + Output: ExampleOutput, }, }, }, @@ -137,97 +137,3 @@ func TestUnmarshalFTWTest(t *testing.T) { } } } - -// 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: - expect_id: 123456 - no_expect_id: 123456 - 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) - } - } -} diff --git a/types/test_doc.go b/types/test_doc.go deleted file mode 100644 index fbdcac3..0000000 --- a/types/test_doc.go +++ /dev/null @@ -1,427 +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 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("", exampleTests) - - 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("", exampleTests) - TestDoc.AppearsIn = []encoder.Appearance{ - { - TypeName: "FTWTest", - FieldName: "tests", - }, - } - TestDoc.Fields = make([]encoder.Doc, 5) - TestDoc.Fields[0].Name = "test_title" - TestDoc.Fields[0].Type = "string" - TestDoc.Fields[0].Note = "" - TestDoc.Fields[0].Description = "TestTitle is 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 is the title of this particular test. It is used for inclusion/exclusion of each run by the tool." - - TestDoc.Fields[0].AddExample("", exampleTest.TestTitle) - TestDoc.Fields[1].Name = "rule_id" - TestDoc.Fields[1].Type = "int" - TestDoc.Fields[1].Note = "" - TestDoc.Fields[1].Description = "RuleId is the ID of the rule this test targets" - TestDoc.Fields[1].Comments[encoder.LineComment] = "RuleId is the ID of the rule this test targets" - - TestDoc.Fields[1].AddExample("RuleId", 123456) - TestDoc.Fields[2].Name = "test_id" - TestDoc.Fields[2].Type = "int" - TestDoc.Fields[2].Note = "" - TestDoc.Fields[2].Description = "TestId is the ID of the test, in relation to `rule_id`" - TestDoc.Fields[2].Comments[encoder.LineComment] = "TestId is the ID of the test, in relation to `rule_id`" - - TestDoc.Fields[2].AddExample("TestId", 4) - TestDoc.Fields[3].Name = "desc" - TestDoc.Fields[3].Type = "string" - TestDoc.Fields[3].Note = "" - TestDoc.Fields[3].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[3].Comments[encoder.LineComment] = "TestDescription is the description for this particular test. Should be used to describe the internals of" - - TestDoc.Fields[3].AddExample("", exampleTest.TestDescription) - TestDoc.Fields[4].Name = "stages" - TestDoc.Fields[4].Type = "[]Stage" - TestDoc.Fields[4].Note = "" - TestDoc.Fields[4].Description = "Stages is the list of all the stages to perform this test." - TestDoc.Fields[4].Comments[encoder.LineComment] = "Stages is the list of all the stages to perform this test." - - TestDoc.Fields[4].AddExample("", exampleStages) - - StageDoc.Type = "Stage" - StageDoc.Comments[encoder.LineComment] = "" - StageDoc.Description = "" - - StageDoc.AddExample("", exampleStages) - StageDoc.AppearsIn = []encoder.Appearance{ - { - TypeName: "Test", - FieldName: "stages", - }, - } - StageDoc.Fields = make([]encoder.Doc, 4) - 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[1].Name = "description" - StageDoc.Fields[1].Type = "string" - StageDoc.Fields[1].Note = "" - StageDoc.Fields[1].Description = "Describes the purpose of this stage." - StageDoc.Fields[1].Comments[encoder.LineComment] = "Describes the purpose of this stage." - - StageDoc.Fields[1].AddExample("", exampleStage.Description) - StageDoc.Fields[2].Name = "input" - StageDoc.Fields[2].Type = "Input" - StageDoc.Fields[2].Note = "" - StageDoc.Fields[2].Description = "Input is the data that is passed to the test" - StageDoc.Fields[2].Comments[encoder.LineComment] = "Input is the data that is passed to the test" - - StageDoc.Fields[2].AddExample("Input", exampleInput) - StageDoc.Fields[3].Name = "output" - StageDoc.Fields[3].Type = "Output" - StageDoc.Fields[3].Note = "" - StageDoc.Fields[3].Description = "Output is the data that is returned from the test" - StageDoc.Fields[3].Comments[encoder.LineComment] = "Output is the data that is returned from the test" - - StageDoc.Fields[3].AddExample("Output", exampleOutput) - - StageDataDoc.Type = "StageData" - StageDataDoc.Comments[encoder.LineComment] = "" - StageDataDoc.Description = "" - 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.AddExample("Input", exampleInput) - InputDoc.AppearsIn = []encoder.Appearance{ - { - TypeName: "Stage", - FieldName: "input", - }, - { - 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.AddExample("Output", exampleOutput) - FTWTestOutputDoc.AppearsIn = []encoder.Appearance{ - { - TypeName: "Stage", - FieldName: "output", - }, - { - 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 = "Expect the given ID to be contained in the log output." - FTWTestLogDoc.Fields[0].Comments[encoder.LineComment] = "Expect the given ID to be contained in the log output." - - FTWTestLogDoc.Fields[0].AddExample("", exampleLog.ExpectId) - FTWTestLogDoc.Fields[1].Name = "no_expect_id" - FTWTestLogDoc.Fields[1].Type = "int" - FTWTestLogDoc.Fields[1].Note = "" - FTWTestLogDoc.Fields[1].Description = "Expect the given ID _not_ to be contained in the log output." - FTWTestLogDoc.Fields[1].Comments[encoder.LineComment] = "Expect the given ID _not_ to be contained in the log output." - - FTWTestLogDoc.Fields[1].AddExample("", exampleLog.NoExpectId) - 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, - }, - } -}