-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Felipe Zipitria <felipe.zipitria@owasp.org>
- Loading branch information
Showing
13 changed files
with
1,916 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// Copyright 2023 OWASP ModSecurity Core Rule Set Project | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package cmd | ||
|
||
import ( | ||
"github.com/coreruleset/go-ftw/internal/quantitative" | ||
"github.com/coreruleset/go-ftw/output" | ||
"github.com/spf13/cobra" | ||
"os" | ||
) | ||
|
||
// NewQuantitativeCmd | ||
// Returns a new cobra command for running quantitative tests | ||
func NewQuantitativeCmd() *cobra.Command { | ||
runCmd := &cobra.Command{ | ||
Use: "quantitative", | ||
Short: "Run Quantitative Tests", | ||
Long: `Run all quantitative tests`, | ||
RunE: runQuantitativeE, | ||
} | ||
|
||
runCmd.Flags().BoolP("markdown", "m", false, "Markdown table output mode") | ||
runCmd.Flags().IntP("fast", "x", 0, "Process 1 in every X lines of input ('fast run' mode)") | ||
runCmd.Flags().IntP("lines", "l", 0, "Number of lines of input to process before stopping") | ||
runCmd.Flags().IntP("paranoia-level", "P", 1, "Paranoia level used to run the quantitative tests") | ||
runCmd.Flags().IntP("number", "n", 0, "Number is the payload line from the corpus to exclusively send") | ||
runCmd.Flags().StringP("payload", "p", "", "Payload is a string you want to test using quantitative tests. Will not use the corpus.") | ||
runCmd.Flags().IntP("rule", "r", 0, "Rule ID of interest: only show false positives for specified rule ID") | ||
runCmd.Flags().StringP("corpus", "c", "leipzig", "Corpus to use for the quantitative tests") | ||
runCmd.Flags().StringP("corpus-lang", "L", "eng", "Corpus language to use for the quantitative tests.") | ||
runCmd.Flags().StringP("corpus-size", "s", "100K", "Corpus size to use for the quantitative tests. Most corpus will have a size like \"100K\", \"1M\", etc.") | ||
runCmd.Flags().StringP("corpus-year", "y", "2023", "Corpus year to use for the quantitative tests. Most corpus will have a year like \"2023\", \"2022\", etc.") | ||
runCmd.Flags().StringP("corpus-source", "S", "news", "Corpus source to use for the quantitative tests. Most corpus will have a source like \"news\", \"web\", \"wikipedia\", etc.") | ||
runCmd.Flags().StringP("directory", "d", ".", "Directory where the CRS rules are stored") | ||
runCmd.Flags().StringP("file", "f", "", "output file path for quantitative tests. Prints to standard output by default.") | ||
runCmd.Flags().StringP("output", "o", "normal", "output type for quantitative tests. \"normal\" is the default.") | ||
|
||
return runCmd | ||
} | ||
|
||
func runQuantitativeE(cmd *cobra.Command, _ []string) error { | ||
cmd.SilenceUsage = true | ||
|
||
corpus, _ := cmd.Flags().GetString("corpus") | ||
corpusSize, _ := cmd.Flags().GetString("corpus-size") | ||
corpusLang, _ := cmd.Flags().GetString("corpus-lang") | ||
corpusYear, _ := cmd.Flags().GetString("corpus-year") | ||
corpusSource, _ := cmd.Flags().GetString("corpus-source") | ||
directory, _ := cmd.Flags().GetString("directory") | ||
fast, _ := cmd.Flags().GetInt("fast") | ||
lines, _ := cmd.Flags().GetInt("lines") | ||
markdown, _ := cmd.Flags().GetBool("markdown") | ||
outputFilename, _ := cmd.Flags().GetString("file") | ||
paranoiaLevel, _ := cmd.Flags().GetInt("paranoia-level") | ||
payload, _ := cmd.Flags().GetString("payload") | ||
number, _ := cmd.Flags().GetInt("number") | ||
rule, _ := cmd.Flags().GetInt("rule") | ||
wantedOutput, _ := cmd.Flags().GetString("output") | ||
|
||
// use outputFile to write to file | ||
var outputFile *os.File | ||
var err error | ||
if outputFilename == "" { | ||
outputFile = os.Stdout | ||
} else { | ||
outputFile, err = os.Open(outputFilename) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
out := output.NewOutput(wantedOutput, outputFile) | ||
|
||
params := quantitative.QuantitativeParams{ | ||
Corpus: corpus, | ||
CorpusSize: corpusSize, | ||
CorpusYear: corpusYear, | ||
CorpusLang: corpusLang, | ||
CorpusSource: corpusSource, | ||
Directory: directory, | ||
Fast: fast, | ||
Lines: lines, | ||
Markdown: markdown, | ||
ParanoiaLevel: paranoiaLevel, | ||
Number: number, | ||
Payload: payload, | ||
Rule: rule, | ||
} | ||
|
||
return quantitative.RunQuantitativeTests(params, out) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright 2023 OWASP ModSecurity Core Rule Set Project | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package cmd | ||
|
||
import ( | ||
"context" | ||
"github.com/spf13/cobra" | ||
"github.com/stretchr/testify/suite" | ||
"io/fs" | ||
"os" | ||
"path" | ||
"testing" | ||
) | ||
|
||
var crsSetupFileContents = `# CRS Setup Configuration File` | ||
var emptyRulesFile = `# Empty Rules File` | ||
|
||
type quantitativeCmdTestSuite struct { | ||
suite.Suite | ||
tempDir string | ||
rootCmd *cobra.Command | ||
} | ||
|
||
func TestQuantitativeTestSuite(t *testing.T) { | ||
suite.Run(t, new(quantitativeCmdTestSuite)) | ||
} | ||
|
||
func (s *quantitativeCmdTestSuite) SetupTest() { | ||
s.rootCmd = NewRootCommand() | ||
s.tempDir = s.T().TempDir() | ||
|
||
err := os.MkdirAll(path.Join(s.tempDir, "rules"), fs.ModePerm) | ||
s.Require().NoError(err) | ||
fakeCRSSetupConf, err := os.Create(path.Join(s.tempDir, "crs-setup.conf.example")) | ||
s.Require().NoError(err) | ||
n, err := fakeCRSSetupConf.WriteString(crsSetupFileContents) | ||
s.Require().NoError(err) | ||
s.Equal(len(crsSetupFileContents), n) | ||
err = fakeCRSSetupConf.Close() | ||
s.Require().NoError(err) | ||
fakeRulesFile, err := os.Create(path.Join(s.tempDir, "rules", "rules1.conf")) | ||
s.Require().NoError(err) | ||
n, err = fakeRulesFile.WriteString(emptyRulesFile) | ||
s.Require().NoError(err) | ||
s.Equal(len(emptyRulesFile), n) | ||
s.rootCmd.AddCommand(NewQuantitativeCmd()) | ||
} | ||
|
||
func (s *quantitativeCmdTestSuite) TearDownTest() { | ||
err := os.RemoveAll(s.tempDir) | ||
s.Require().NoError(err) | ||
} | ||
|
||
func (s *quantitativeCmdTestSuite) TestQuantitativeCommand() { | ||
s.rootCmd.SetArgs([]string{"quantitative", "-d", s.tempDir}) | ||
cmd, err := s.rootCmd.ExecuteContextC(context.Background()) | ||
s.Require().NoError(err, "quantitative command should not return an error") | ||
s.Equal("quantitative", cmd.Name(), "quantitative command should have the name 'quantitative'") | ||
s.Require().NoError(err) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package corpus | ||
|
||
// CorpusFile contains the cache directory and file name | ||
type CorpusFile struct { | ||
// CacheDir is the directory where files are cached | ||
CacheDir string | ||
// FilePath is the path to the cached file | ||
FilePath string | ||
} | ||
|
||
// Corpus is the interface that needs to be implemented for getting the payload from a corpus | ||
type Corpus interface { | ||
// URL returns the URL of the corpus | ||
URL() string | ||
|
||
// WithURL sets the URL of the corpus | ||
WithURL(url string) Corpus | ||
|
||
// GetCorpusFile gets the file from the remote url. | ||
// It returns the local file path were the corpus is stored. | ||
GetCorpusFile() CorpusFile | ||
|
||
// GetIterator returns an iterator for the corpus | ||
GetIterator(c CorpusFile) Iterator | ||
|
||
// GetPayload returns the payload given a line from the Corpus Iterator | ||
GetPayload(line string) string | ||
|
||
// Size returns the size of the corpus | ||
Size() string | ||
// WithSize sets the size of the corpus | ||
// Most corpus will have a size like "100K", "1M", etc., related to the amount of sentences in the corpus | ||
WithSize(size string) Corpus | ||
|
||
// Year returns the year of the corpus | ||
Year() string | ||
// WithYear sets the year of the corpus | ||
// Most corpus will have a year like "2023", "2022", etc. | ||
WithYear(year string) Corpus | ||
|
||
// Source returns the source of the corpus | ||
Source() string | ||
// WithSource sets the source of the corpus | ||
// Most corpus will have a source like "news", "web", "wikipedia", etc. | ||
WithSource(source string) Corpus | ||
|
||
// Lang returns the language of the corpus | ||
Lang() string | ||
// WithLanguage sets the language of the corpus | ||
// Most corpus will have a language like "eng", "de", etc. | ||
WithLanguage(lang string) Corpus | ||
} | ||
|
||
// Iterator is an interface for iterating over a corpus | ||
type Iterator interface { | ||
// Next returns the next sentence from the corpus | ||
Next() string | ||
// HasNext returns true if there is another sentence in the corpus | ||
// false otherwise | ||
HasNext() bool | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.