mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-17 22:05:27 +00:00
Merge branch 'dev' into issue-3081-retry-gh
This commit is contained in:
commit
409393b64b
27
integration_tests/code/test.json
Normal file
27
integration_tests/code/test.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"id": "go-integration-test",
|
||||||
|
"info": {
|
||||||
|
"name": "Basic Go Integration Test",
|
||||||
|
"author": "pdteam",
|
||||||
|
"severity": "info"
|
||||||
|
},
|
||||||
|
"requests": [
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": [
|
||||||
|
"{{BaseURL}}"
|
||||||
|
],
|
||||||
|
"headers": {
|
||||||
|
"test": "nuclei"
|
||||||
|
},
|
||||||
|
"matchers": [
|
||||||
|
{
|
||||||
|
"type": "word",
|
||||||
|
"words": [
|
||||||
|
"This is test headers matcher text"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -36,6 +36,7 @@ import (
|
|||||||
|
|
||||||
var codeTestcases = map[string]testutils.TestCase{
|
var codeTestcases = map[string]testutils.TestCase{
|
||||||
"code/test.yaml": &goIntegrationTest{},
|
"code/test.yaml": &goIntegrationTest{},
|
||||||
|
"code/test.json": &goIntegrationTest{},
|
||||||
}
|
}
|
||||||
|
|
||||||
type goIntegrationTest struct{}
|
type goIntegrationTest struct{}
|
||||||
|
|||||||
148
v2/cmd/sign-templates/main.go
Normal file
148
v2/cmd/sign-templates/main.go
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/projectdiscovery/goflags"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/templates/signer"
|
||||||
|
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
Templates goflags.StringSlice
|
||||||
|
Algorithm string
|
||||||
|
PrivateKeyName string
|
||||||
|
PrivateKeyPassPhrase string
|
||||||
|
PublicKeyName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseOptions() (*options, error) {
|
||||||
|
opts := &options{}
|
||||||
|
flagSet := goflags.NewFlagSet()
|
||||||
|
flagSet.SetDescription(`sign-templates is a utility to perform template signature`)
|
||||||
|
|
||||||
|
flagSet.CreateGroup("sign", "sign",
|
||||||
|
flagSet.StringSliceVarP(&opts.Templates, "templates", "t", nil, "templates files/folders to sign", goflags.CommaSeparatedStringSliceOptions),
|
||||||
|
flagSet.StringVarP(&opts.Algorithm, "algorithm", "a", "ecdsa", "signature algorithm (ecdsa, rsa)"),
|
||||||
|
flagSet.StringVarP(&opts.PrivateKeyName, "private-key", "prk", "", "private key env var name or file location"),
|
||||||
|
flagSet.StringVarP(&opts.PrivateKeyPassPhrase, "private-key-pass", "prkp", "", "private key passphrase env var name or file location"),
|
||||||
|
flagSet.StringVarP(&opts.PublicKeyName, "public-key", "puk", "", "public key env var name or file location"),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := flagSet.Parse(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
opts, err := ParseOptions()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("couldn't parse options: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var algo signer.AlgorithmType
|
||||||
|
switch opts.Algorithm {
|
||||||
|
case "rsa":
|
||||||
|
algo = signer.RSA
|
||||||
|
case "ecdsa":
|
||||||
|
algo = signer.ECDSA
|
||||||
|
default:
|
||||||
|
log.Fatal("unknown algorithm type")
|
||||||
|
}
|
||||||
|
|
||||||
|
signerOptions := &signer.Options{
|
||||||
|
PrivateKeyName: opts.PrivateKeyName,
|
||||||
|
PassphraseName: opts.PrivateKeyPassPhrase,
|
||||||
|
PublicKeyName: opts.PublicKeyName,
|
||||||
|
Algorithm: algo,
|
||||||
|
}
|
||||||
|
sign, err := signer.New(signerOptions)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("couldn't create crypto engine: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, templateItem := range opts.Templates {
|
||||||
|
if err := processItem(sign, templateItem); err != nil {
|
||||||
|
log.Fatalf("Could not walk directory: %s\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func processItem(sign *signer.Signer, item string) error {
|
||||||
|
return filepath.WalkDir(item, func(iterItem string, d fs.DirEntry, err error) error {
|
||||||
|
if err != nil || d.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := processFile(sign, iterItem); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func processFile(sign *signer.Signer, filePath string) error {
|
||||||
|
ext := filepath.Ext(filePath)
|
||||||
|
if !stringsutil.EqualFoldAny(ext, ".yaml") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := signTemplate(sign, filePath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not sign template: %s", filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := verifyTemplateSignature(sign, filePath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not verify template: %s", filePath)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return errors.Wrapf(err, "template signature doesn't match: %s", filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendToFile(path string, data []byte, digest string) error {
|
||||||
|
file, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
if _, err := file.Write(data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := file.WriteString("\n" + digest); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func signTemplate(sign *signer.Signer, templatePath string) error {
|
||||||
|
templateData, err := os.ReadFile(templatePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
signatureData, err := signer.Sign(sign, templateData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dataWithoutSignature := signer.RemoveSignatureFromData(templateData)
|
||||||
|
return appendToFile(templatePath, dataWithoutSignature, signatureData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyTemplateSignature(sign *signer.Signer, templatePath string) (bool, error) {
|
||||||
|
templateData, err := os.ReadFile(templatePath)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return signer.Verify(sign, templateData)
|
||||||
|
}
|
||||||
13
v2/go.mod
13
v2/go.mod
@ -62,11 +62,12 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.3
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.3
|
||||||
github.com/docker/go-units v0.5.0
|
github.com/docker/go-units v0.5.0
|
||||||
github.com/fatih/structs v1.1.0
|
github.com/fatih/structs v1.1.0
|
||||||
|
github.com/go-faker/faker/v4 v4.0.0
|
||||||
github.com/go-git/go-git/v5 v5.5.2
|
github.com/go-git/go-git/v5 v5.5.2
|
||||||
github.com/h2non/filetype v1.1.3
|
github.com/h2non/filetype v1.1.3
|
||||||
github.com/hashicorp/go-version v1.6.0
|
github.com/hashicorp/go-version v1.6.0
|
||||||
github.com/kataras/jwt v0.1.8
|
github.com/kataras/jwt v0.1.8
|
||||||
github.com/klauspost/compress v1.15.15
|
github.com/klauspost/compress v1.16.0
|
||||||
github.com/labstack/echo/v4 v4.10.0
|
github.com/labstack/echo/v4 v4.10.0
|
||||||
github.com/mholt/archiver v3.1.1+incompatible
|
github.com/mholt/archiver v3.1.1+incompatible
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
@ -74,6 +75,7 @@ require (
|
|||||||
github.com/projectdiscovery/goflags v0.1.6
|
github.com/projectdiscovery/goflags v0.1.6
|
||||||
github.com/projectdiscovery/gologger v1.1.8
|
github.com/projectdiscovery/gologger v1.1.8
|
||||||
github.com/projectdiscovery/httpx v1.2.7
|
github.com/projectdiscovery/httpx v1.2.7
|
||||||
|
github.com/projectdiscovery/mapcidr v1.1.0
|
||||||
github.com/projectdiscovery/nvd v1.0.9
|
github.com/projectdiscovery/nvd v1.0.9
|
||||||
github.com/projectdiscovery/ratelimit v0.0.6
|
github.com/projectdiscovery/ratelimit v0.0.6
|
||||||
github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917
|
github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917
|
||||||
@ -82,7 +84,7 @@ require (
|
|||||||
github.com/projectdiscovery/uncover v1.0.2
|
github.com/projectdiscovery/uncover v1.0.2
|
||||||
github.com/projectdiscovery/utils v0.0.10-0.20230217185600-008d111dd1c1
|
github.com/projectdiscovery/utils v0.0.10-0.20230217185600-008d111dd1c1
|
||||||
github.com/projectdiscovery/wappalyzergo v0.0.81
|
github.com/projectdiscovery/wappalyzergo v0.0.81
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.2
|
||||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
@ -107,7 +109,7 @@ require (
|
|||||||
github.com/karlseguin/expect v1.0.8 // indirect
|
github.com/karlseguin/expect v1.0.8 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/pjbgf/sha1cd v0.2.3 // indirect
|
github.com/pjbgf/sha1cd v0.2.3 // indirect
|
||||||
github.com/projectdiscovery/asnmap v0.0.1 // indirect
|
github.com/projectdiscovery/asnmap v1.0.0 // indirect
|
||||||
github.com/projectdiscovery/cdncheck v0.0.4-0.20220413175814-b47bc2d578b1 // indirect
|
github.com/projectdiscovery/cdncheck v0.0.4-0.20220413175814-b47bc2d578b1 // indirect
|
||||||
github.com/projectdiscovery/freeport v0.0.4 // indirect
|
github.com/projectdiscovery/freeport v0.0.4 // indirect
|
||||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
github.com/prometheus/client_golang v1.14.0 // indirect
|
||||||
@ -187,8 +189,7 @@ require (
|
|||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
github.com/projectdiscovery/blackrock v0.0.0-20220628111055-35616c71b2dc // indirect
|
github.com/projectdiscovery/blackrock v0.0.0-20221025011524-9e4efe804fb4 // indirect
|
||||||
github.com/projectdiscovery/mapcidr v1.0.3
|
|
||||||
github.com/projectdiscovery/networkpolicy v0.0.4
|
github.com/projectdiscovery/networkpolicy v0.0.4
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||||
@ -209,7 +210,7 @@ require (
|
|||||||
go.etcd.io/bbolt v1.3.7 // indirect
|
go.etcd.io/bbolt v1.3.7 // indirect
|
||||||
go.uber.org/zap v1.23.0 // indirect
|
go.uber.org/zap v1.23.0 // indirect
|
||||||
goftp.io/server/v2 v2.0.0 // indirect
|
goftp.io/server/v2 v2.0.0 // indirect
|
||||||
golang.org/x/crypto v0.5.0 // indirect
|
golang.org/x/crypto v0.5.0
|
||||||
golang.org/x/exp v0.0.0-20230206171751-46f607a40771
|
golang.org/x/exp v0.0.0-20230206171751-46f607a40771
|
||||||
golang.org/x/mod v0.8.0 // indirect
|
golang.org/x/mod v0.8.0 // indirect
|
||||||
golang.org/x/sys v0.5.0 // indirect
|
golang.org/x/sys v0.5.0 // indirect
|
||||||
|
|||||||
22
v2/go.sum
22
v2/go.sum
@ -210,6 +210,8 @@ github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4x
|
|||||||
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
|
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||||
|
github.com/go-faker/faker/v4 v4.0.0 h1:tfgFaeizVlYGOS1tVo/vcWcKhkNgG1NWm8ibRG0f+aQ=
|
||||||
|
github.com/go-faker/faker/v4 v4.0.0/go.mod h1:uuNc0PSRxF8nMgjGrrrU4Nw5cF30Jc6Kd0/FUTTYbhg=
|
||||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||||
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||||
@ -395,8 +397,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
|||||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
|
||||||
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
|
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
|
github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0=
|
||||||
@ -519,10 +521,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/projectdiscovery/asnmap v0.0.1 h1:n4YCz1ljUaDA3dOUCkjI/bUOtiS7ge1KJ39qpURCd/o=
|
github.com/projectdiscovery/asnmap v1.0.0 h1:h9aUEHT3gEWgeTxDCd0UMxBw1yPthDwL1ogqUWnkBXo=
|
||||||
github.com/projectdiscovery/asnmap v0.0.1/go.mod h1:CjCVDhQPVtmlE247L6YFeIVX9c4m8pOX8V8BmB0JkX8=
|
github.com/projectdiscovery/asnmap v1.0.0/go.mod h1:m+qedSAZERz7Ds942hWANjU9kg4ADAikFHfwSXN1Ey8=
|
||||||
github.com/projectdiscovery/blackrock v0.0.0-20220628111055-35616c71b2dc h1:jqZK68yPOnNNRmwuXqytl+T9EbwneEUCvMDRjLe0J04=
|
github.com/projectdiscovery/blackrock v0.0.0-20221025011524-9e4efe804fb4 h1:EsrQ/zkotVodSJLOch3pV/UYt1vQcwyIs5HX0sm1ljE=
|
||||||
github.com/projectdiscovery/blackrock v0.0.0-20220628111055-35616c71b2dc/go.mod h1:5tNGQP9kOfW+X5+40pZP8aqPYLHs45nJkFaSHLxdeH8=
|
github.com/projectdiscovery/blackrock v0.0.0-20221025011524-9e4efe804fb4/go.mod h1:5tNGQP9kOfW+X5+40pZP8aqPYLHs45nJkFaSHLxdeH8=
|
||||||
github.com/projectdiscovery/cdncheck v0.0.4-0.20220413175814-b47bc2d578b1 h1:QtTPPx0uu42AsQJiXT86/wqdHS7/iVcgz1VM38tjv20=
|
github.com/projectdiscovery/cdncheck v0.0.4-0.20220413175814-b47bc2d578b1 h1:QtTPPx0uu42AsQJiXT86/wqdHS7/iVcgz1VM38tjv20=
|
||||||
github.com/projectdiscovery/cdncheck v0.0.4-0.20220413175814-b47bc2d578b1/go.mod h1:EevMeCG1ogBoUJYaa0Mv9R1VUboDm/DiynId7DboKy0=
|
github.com/projectdiscovery/cdncheck v0.0.4-0.20220413175814-b47bc2d578b1/go.mod h1:EevMeCG1ogBoUJYaa0Mv9R1VUboDm/DiynId7DboKy0=
|
||||||
github.com/projectdiscovery/clistats v0.0.12 h1:KLYJxpiwEFidduU4PbcwEcCQ2L7c5wrf7DI5IN5fZ+8=
|
github.com/projectdiscovery/clistats v0.0.12 h1:KLYJxpiwEFidduU4PbcwEcCQ2L7c5wrf7DI5IN5fZ+8=
|
||||||
@ -547,8 +549,8 @@ github.com/projectdiscovery/interactsh v1.0.6-0.20220827132222-460cc6270053 h1:8
|
|||||||
github.com/projectdiscovery/interactsh v1.0.6-0.20220827132222-460cc6270053/go.mod h1:7lLz3Rt+Lxt8xhK0EUYkgxoa9RXRL3honxHeAu+ivuk=
|
github.com/projectdiscovery/interactsh v1.0.6-0.20220827132222-460cc6270053/go.mod h1:7lLz3Rt+Lxt8xhK0EUYkgxoa9RXRL3honxHeAu+ivuk=
|
||||||
github.com/projectdiscovery/iputil v0.0.2 h1:f6IGnZF4RImJLysPSPG3D84jyTH34q3lihCFeP+eZzI=
|
github.com/projectdiscovery/iputil v0.0.2 h1:f6IGnZF4RImJLysPSPG3D84jyTH34q3lihCFeP+eZzI=
|
||||||
github.com/projectdiscovery/iputil v0.0.2/go.mod h1:J3Pcz1q51pi4/JL871mQztg0KOzyWDPxnPLOYJm2pVQ=
|
github.com/projectdiscovery/iputil v0.0.2/go.mod h1:J3Pcz1q51pi4/JL871mQztg0KOzyWDPxnPLOYJm2pVQ=
|
||||||
github.com/projectdiscovery/mapcidr v1.0.3 h1:SGtOOEz0AxthVO7ZonMvhrJ/AQkHIXCVgyZqJdY0cAY=
|
github.com/projectdiscovery/mapcidr v1.1.0 h1:Yeb+CGVsRYvHmZ9YSHb9iy4tzY9YuOm3oTFX/xzGhVU=
|
||||||
github.com/projectdiscovery/mapcidr v1.0.3/go.mod h1:/0lEXlu/q0t5u34vIVF6odHR+JCdD3CIHNsMXo7nwrU=
|
github.com/projectdiscovery/mapcidr v1.1.0/go.mod h1:hck0bWXka5ZkUaBG+TWt99bzLy+4hAg9oANhEmm3GNs=
|
||||||
github.com/projectdiscovery/networkpolicy v0.0.4 h1:zcGjEqZbyECZEdyCy1jVuwOS7Ww1mzgCefQU75XqdJA=
|
github.com/projectdiscovery/networkpolicy v0.0.4 h1:zcGjEqZbyECZEdyCy1jVuwOS7Ww1mzgCefQU75XqdJA=
|
||||||
github.com/projectdiscovery/networkpolicy v0.0.4/go.mod h1:DIXwKs3sQyfCoWHKRLQiRrEorSQW4Zrh4ftu7oDVK6w=
|
github.com/projectdiscovery/networkpolicy v0.0.4/go.mod h1:DIXwKs3sQyfCoWHKRLQiRrEorSQW4Zrh4ftu7oDVK6w=
|
||||||
github.com/projectdiscovery/nvd v1.0.9 h1:2DdMm7lu3GnCQsyYDEQiQ/LRYDmpEm654kvGQS6jzjE=
|
github.com/projectdiscovery/nvd v1.0.9 h1:2DdMm7lu3GnCQsyYDEQiQ/LRYDmpEm654kvGQS6jzjE=
|
||||||
@ -565,7 +567,6 @@ github.com/projectdiscovery/retryablehttp-go v1.0.12-0.20230220094538-f406add578
|
|||||||
github.com/projectdiscovery/retryablehttp-go v1.0.12-0.20230220094538-f406add578ab/go.mod h1:dnJK347etdVKz7ljhemUhBK6EpDnVf0U1O8dGj7MM+0=
|
github.com/projectdiscovery/retryablehttp-go v1.0.12-0.20230220094538-f406add578ab/go.mod h1:dnJK347etdVKz7ljhemUhBK6EpDnVf0U1O8dGj7MM+0=
|
||||||
github.com/projectdiscovery/sarif v0.0.1 h1:C2Tyj0SGOKbCLgHrx83vaE6YkzXEVrMXYRGLkKCr/us=
|
github.com/projectdiscovery/sarif v0.0.1 h1:C2Tyj0SGOKbCLgHrx83vaE6YkzXEVrMXYRGLkKCr/us=
|
||||||
github.com/projectdiscovery/sarif v0.0.1/go.mod h1:cEYlDu8amcPf6b9dSakcz2nNnJsoz4aR6peERwV+wuQ=
|
github.com/projectdiscovery/sarif v0.0.1/go.mod h1:cEYlDu8amcPf6b9dSakcz2nNnJsoz4aR6peERwV+wuQ=
|
||||||
github.com/projectdiscovery/sliceutil v0.0.1 h1:YoCqCMcdwz+gqNfW5hFY8UvNHoA6SfyBSNkVahatleg=
|
|
||||||
github.com/projectdiscovery/stringsutil v0.0.2 h1:uzmw3IVLJSMW1kEg8eCStG/cGbYYZAja8BH3LqqJXMA=
|
github.com/projectdiscovery/stringsutil v0.0.2 h1:uzmw3IVLJSMW1kEg8eCStG/cGbYYZAja8BH3LqqJXMA=
|
||||||
github.com/projectdiscovery/stringsutil v0.0.2/go.mod h1:EJ3w6bC5fBYjVou6ryzodQq37D5c6qbAYQpGmAy+DC0=
|
github.com/projectdiscovery/stringsutil v0.0.2/go.mod h1:EJ3w6bC5fBYjVou6ryzodQq37D5c6qbAYQpGmAy+DC0=
|
||||||
github.com/projectdiscovery/tlsx v1.0.5 h1:ZDMcwqjwXB0x2XBzvdra7HYiN8yLGBhHc5qE2mjJchM=
|
github.com/projectdiscovery/tlsx v1.0.5 h1:ZDMcwqjwXB0x2XBzvdra7HYiN8yLGBhHc5qE2mjJchM=
|
||||||
@ -655,8 +656,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||||
github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI=
|
github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI=
|
||||||
|
|||||||
@ -70,7 +70,7 @@ type Runner struct {
|
|||||||
catalog catalog.Catalog
|
catalog catalog.Catalog
|
||||||
progress progress.Progress
|
progress progress.Progress
|
||||||
colorizer aurora.Aurora
|
colorizer aurora.Aurora
|
||||||
issuesClient *reporting.Client
|
issuesClient reporting.Client
|
||||||
hmapInputProvider *hybrid.Input
|
hmapInputProvider *hybrid.Input
|
||||||
browser *engine.Browser
|
browser *engine.Browser
|
||||||
ratelimiter *ratelimit.Limiter
|
ratelimiter *ratelimit.Limiter
|
||||||
|
|||||||
@ -3,15 +3,13 @@ package filter
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/dns"
|
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/http"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity"
|
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
|
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/dns"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/http"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/templates/types"
|
"github.com/projectdiscovery/nuclei/v2/pkg/templates/types"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTagBasedFilter(t *testing.T) {
|
func TestTagBasedFilter(t *testing.T) {
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import (
|
|||||||
"github.com/projectdiscovery/hmap/filekv"
|
"github.com/projectdiscovery/hmap/filekv"
|
||||||
"github.com/projectdiscovery/hmap/store/hybrid"
|
"github.com/projectdiscovery/hmap/store/hybrid"
|
||||||
"github.com/projectdiscovery/mapcidr"
|
"github.com/projectdiscovery/mapcidr"
|
||||||
asn "github.com/projectdiscovery/mapcidr/asn"
|
"github.com/projectdiscovery/mapcidr/asn"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate"
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/uncover"
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/uncover"
|
||||||
@ -330,8 +330,7 @@ func (i *Input) expandCIDRInputValue(value string) {
|
|||||||
|
|
||||||
// expandASNInputValue expands CIDRs for given ASN and stores expanded IPs
|
// expandASNInputValue expands CIDRs for given ASN and stores expanded IPs
|
||||||
func (i *Input) expandASNInputValue(value string) {
|
func (i *Input) expandASNInputValue(value string) {
|
||||||
asnClient := asn.New()
|
cidrs, _ := asn.GetCIDRsForASNNum(value)
|
||||||
cidrs, _ := asnClient.GetCIDRsForASNNum(value)
|
|
||||||
for _, cidr := range cidrs {
|
for _, cidr := range cidrs {
|
||||||
i.expandCIDRInputValue(cidr.String())
|
i.expandCIDRInputValue(cidr.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,6 @@ package core
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
|
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/operators"
|
"github.com/projectdiscovery/nuclei/v2/pkg/operators"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||||
@ -13,6 +11,7 @@ import (
|
|||||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/workflows"
|
"github.com/projectdiscovery/nuclei/v2/pkg/workflows"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWorkflowsSimple(t *testing.T) {
|
func TestWorkflowsSimple(t *testing.T) {
|
||||||
|
|||||||
@ -47,7 +47,7 @@ type Info struct {
|
|||||||
// examples:
|
// examples:
|
||||||
// - value: >
|
// - value: >
|
||||||
// []string{"https://github.com/strapi/strapi", "https://github.com/getgrav/grav"}
|
// []string{"https://github.com/strapi/strapi", "https://github.com/getgrav/grav"}
|
||||||
Reference stringslice.StringSlice `json:"reference,omitempty" yaml:"reference,omitempty" jsonschema:"title=references for the template,description=Links relevant to the template"`
|
Reference stringslice.RawStringSlice `json:"reference,omitempty" yaml:"reference,omitempty" jsonschema:"title=references for the template,description=Links relevant to the template"`
|
||||||
// description: |
|
// description: |
|
||||||
// Severity of the template.
|
// Severity of the template.
|
||||||
SeverityHolder severity.Holder `json:"severity,omitempty" yaml:"severity,omitempty"`
|
SeverityHolder severity.Holder `json:"severity,omitempty" yaml:"severity,omitempty"`
|
||||||
|
|||||||
@ -7,10 +7,8 @@ import (
|
|||||||
|
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity"
|
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
|
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInfoJsonMarshal(t *testing.T) {
|
func TestInfoJsonMarshal(t *testing.T) {
|
||||||
@ -20,7 +18,7 @@ func TestInfoJsonMarshal(t *testing.T) {
|
|||||||
Description: "Test description",
|
Description: "Test description",
|
||||||
SeverityHolder: severity.Holder{Severity: severity.High},
|
SeverityHolder: severity.Holder{Severity: severity.High},
|
||||||
Tags: stringslice.StringSlice{Value: []string{"cve", "misc"}},
|
Tags: stringslice.StringSlice{Value: []string{"cve", "misc"}},
|
||||||
Reference: stringslice.StringSlice{Value: "reference1"},
|
Reference: stringslice.NewRaw("Reference1"),
|
||||||
Metadata: map[string]interface{}{
|
Metadata: map[string]interface{}{
|
||||||
"string_key": "string_value",
|
"string_key": "string_value",
|
||||||
"array_key": []string{"array_value1", "array_value2"},
|
"array_key": []string{"array_value1", "array_value2"},
|
||||||
@ -33,7 +31,7 @@ func TestInfoJsonMarshal(t *testing.T) {
|
|||||||
result, err := json.Marshal(&info)
|
result, err := json.Marshal(&info)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
expected := `{"name":"Test Template Name","author":["forgedhallpass","ice3man"],"tags":["cve","misc"],"description":"Test description","reference":"reference1","severity":"high","metadata":{"array_key":["array_value1","array_value2"],"map_key":{"key1":"val1"},"string_key":"string_value"}}`
|
expected := `{"name":"Test Template Name","author":["forgedhallpass","ice3man"],"tags":["cve","misc"],"description":"Test description","reference":"Reference1","severity":"high","metadata":{"array_key":["array_value1","array_value2"],"map_key":{"key1":"val1"},"string_key":"string_value"}}`
|
||||||
assert.Equal(t, expected, string(result))
|
assert.Equal(t, expected, string(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +42,7 @@ func TestInfoYamlMarshal(t *testing.T) {
|
|||||||
Description: "Test description",
|
Description: "Test description",
|
||||||
SeverityHolder: severity.Holder{Severity: severity.High},
|
SeverityHolder: severity.Holder{Severity: severity.High},
|
||||||
Tags: stringslice.StringSlice{Value: []string{"cve", "misc"}},
|
Tags: stringslice.StringSlice{Value: []string{"cve", "misc"}},
|
||||||
Reference: stringslice.StringSlice{Value: "reference1"},
|
Reference: stringslice.NewRaw("Reference1"),
|
||||||
Metadata: map[string]interface{}{
|
Metadata: map[string]interface{}{
|
||||||
"string_key": "string_value",
|
"string_key": "string_value",
|
||||||
"array_key": []string{"array_value1", "array_value2"},
|
"array_key": []string{"array_value1", "array_value2"},
|
||||||
@ -65,7 +63,7 @@ tags:
|
|||||||
- cve
|
- cve
|
||||||
- misc
|
- misc
|
||||||
description: Test description
|
description: Test description
|
||||||
reference: reference1
|
reference: Reference1
|
||||||
severity: high
|
severity: high
|
||||||
metadata:
|
metadata:
|
||||||
array_key:
|
array_key:
|
||||||
|
|||||||
@ -113,7 +113,7 @@ func (severityHolder *Holder) UnmarshalJSON(data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (severityHolder *Holder) MarshalJSON() ([]byte, error) {
|
func (severityHolder Holder) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(severityHolder.Severity.String())
|
return json.Marshal(severityHolder.Severity.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,10 @@ type StringSlice struct {
|
|||||||
Value interface{}
|
Value interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func New(value interface{}) StringSlice {
|
||||||
|
return StringSlice{Value: value}
|
||||||
|
}
|
||||||
|
|
||||||
func (stringSlice StringSlice) JSONSchemaType() *jsonschema.Type {
|
func (stringSlice StringSlice) JSONSchemaType() *jsonschema.Type {
|
||||||
gotType := &jsonschema.Type{
|
gotType := &jsonschema.Type{
|
||||||
OneOf: []*jsonschema.Type{{Type: "string"}, {Type: "array"}},
|
OneOf: []*jsonschema.Type{{Type: "string"}, {Type: "array"}},
|
||||||
@ -52,13 +56,13 @@ func (stringSlice *StringSlice) UnmarshalYAML(unmarshal func(interface{}) error)
|
|||||||
|
|
||||||
result := make([]string, 0, len(marshalledSlice))
|
result := make([]string, 0, len(marshalledSlice))
|
||||||
for _, value := range marshalledSlice {
|
for _, value := range marshalledSlice {
|
||||||
result = append(result, stringSlice.normalize(value))
|
result = append(result, stringSlice.Normalize(value))
|
||||||
}
|
}
|
||||||
stringSlice.Value = result
|
stringSlice.Value = result
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (stringSlice StringSlice) normalize(value string) string {
|
func (stringSlice StringSlice) Normalize(value string) string {
|
||||||
return strings.ToLower(strings.TrimSpace(value))
|
return strings.ToLower(strings.TrimSpace(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +98,7 @@ func (stringSlice *StringSlice) UnmarshalJSON(data []byte) error {
|
|||||||
|
|
||||||
values := make([]string, 0, len(result))
|
values := make([]string, 0, len(result))
|
||||||
for _, value := range result {
|
for _, value := range result {
|
||||||
values = append(values, stringSlice.normalize(value))
|
values = append(values, stringSlice.Normalize(value))
|
||||||
}
|
}
|
||||||
stringSlice.Value = values
|
stringSlice.Value = values
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
13
v2/pkg/model/types/stringslice/stringslice_raw.go
Normal file
13
v2/pkg/model/types/stringslice/stringslice_raw.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package stringslice
|
||||||
|
|
||||||
|
type RawStringSlice struct {
|
||||||
|
StringSlice
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRaw(value interface{}) RawStringSlice {
|
||||||
|
return RawStringSlice{StringSlice: StringSlice{Value: value}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rawStringSlice RawStringSlice) Normalize(value string) string {
|
||||||
|
return value
|
||||||
|
}
|
||||||
@ -86,6 +86,20 @@ func (userAgentHolder *UserAgentHolder) UnmarshalYAML(unmarshal func(interface{}
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (userAgentHolder *UserAgentHolder) UnmarshalJSON(data []byte) error {
|
||||||
|
s := strings.Trim(string(data), `"`)
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
computedUserAgent, err := toUserAgent(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
userAgentHolder.Value = computedUserAgent
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (userAgentHolder *UserAgentHolder) MarshalJSON() ([]byte, error) {
|
func (userAgentHolder *UserAgentHolder) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(userAgentHolder.Value.String())
|
return json.Marshal(userAgentHolder.Value.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,20 +49,18 @@ const (
|
|||||||
letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
)
|
)
|
||||||
|
|
||||||
var invalidDslFunctionError = errors.New("invalid DSL function signature")
|
|
||||||
var invalidDslFunctionMessageTemplate = "%w. correct method signature %q"
|
|
||||||
|
|
||||||
var dslFunctions map[string]dslFunction
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
ErrinvalidDslFunction = errors.New("invalid DSL function signature")
|
||||||
|
dslFunctions map[string]dslFunction
|
||||||
|
|
||||||
// FunctionNames is a list of function names for expression evaluation usages
|
// FunctionNames is a list of function names for expression evaluation usages
|
||||||
FunctionNames []string
|
FunctionNames []string
|
||||||
// HelperFunctions is a pre-compiled list of govaluate DSL functions
|
// HelperFunctions is a pre-compiled list of govaluate DSL functions
|
||||||
HelperFunctions map[string]govaluate.ExpressionFunction
|
HelperFunctions map[string]govaluate.ExpressionFunction
|
||||||
)
|
|
||||||
|
|
||||||
var functionSignaturePattern = regexp.MustCompile(`(\w+)\s*\((?:([\w\d,\s]+)\s+([.\w\d{}&*]+))?\)([\s.\w\d{}&*]+)?`)
|
functionSignaturePattern = regexp.MustCompile(`(\w+)\s*\((?:([\w\d,\s]+)\s+([.\w\d{}&*]+))?\)([\s.\w\d{}&*]+)?`)
|
||||||
var dateFormatRegex = regexp.MustCompile("%([A-Za-z])")
|
dateFormatRegex = regexp.MustCompile("%([A-Za-z])")
|
||||||
|
)
|
||||||
|
|
||||||
type dslFunction struct {
|
type dslFunction struct {
|
||||||
signatures []string
|
signatures []string
|
||||||
@ -98,7 +96,7 @@ func init() {
|
|||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
argCount := len(args)
|
argCount := len(args)
|
||||||
if argCount == 0 {
|
if argCount == 0 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
} else if argCount == 1 {
|
} else if argCount == 1 {
|
||||||
runes := []rune(types.ToString(args[0]))
|
runes := []rune(types.ToString(args[0]))
|
||||||
sort.Slice(runes, func(i int, j int) bool {
|
sort.Slice(runes, func(i int, j int) bool {
|
||||||
@ -122,7 +120,7 @@ func init() {
|
|||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
argCount := len(args)
|
argCount := len(args)
|
||||||
if argCount == 0 {
|
if argCount == 0 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
} else if argCount == 1 {
|
} else if argCount == 1 {
|
||||||
builder := &strings.Builder{}
|
builder := &strings.Builder{}
|
||||||
visited := make(map[rune]struct{})
|
visited := make(map[rune]struct{})
|
||||||
@ -149,7 +147,7 @@ func init() {
|
|||||||
"repeat": makeDslFunction(2, func(args ...interface{}) (interface{}, error) {
|
"repeat": makeDslFunction(2, func(args ...interface{}) (interface{}, error) {
|
||||||
count, err := strconv.Atoi(types.ToString(args[1]))
|
count, err := strconv.Atoi(types.ToString(args[1]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
return strings.Repeat(types.ToString(args[0]), count), nil
|
return strings.Repeat(types.ToString(args[0]), count), nil
|
||||||
}),
|
}),
|
||||||
@ -243,7 +241,7 @@ func init() {
|
|||||||
|
|
||||||
argumentsSize := len(arguments)
|
argumentsSize := len(arguments)
|
||||||
if argumentsSize < 1 && argumentsSize > 2 {
|
if argumentsSize < 1 && argumentsSize > 2 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTime, err := getCurrentTimeFromUserInput(arguments)
|
currentTime, err := getCurrentTimeFromUserInput(arguments)
|
||||||
@ -353,7 +351,7 @@ func init() {
|
|||||||
"(str string, prefix ...string) bool",
|
"(str string, prefix ...string) bool",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
for _, prefix := range args[1:] {
|
for _, prefix := range args[1:] {
|
||||||
if strings.HasPrefix(types.ToString(args[0]), types.ToString(prefix)) {
|
if strings.HasPrefix(types.ToString(args[0]), types.ToString(prefix)) {
|
||||||
@ -366,7 +364,7 @@ func init() {
|
|||||||
"line_starts_with": makeDslWithOptionalArgsFunction(
|
"line_starts_with": makeDslWithOptionalArgsFunction(
|
||||||
"(str string, prefix ...string) bool", func(args ...interface{}) (interface{}, error) {
|
"(str string, prefix ...string) bool", func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
for _, line := range strings.Split(types.ToString(args[0]), "\n") {
|
for _, line := range strings.Split(types.ToString(args[0]), "\n") {
|
||||||
for _, prefix := range args[1:] {
|
for _, prefix := range args[1:] {
|
||||||
@ -382,7 +380,7 @@ func init() {
|
|||||||
"(str string, suffix ...string) bool",
|
"(str string, suffix ...string) bool",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
for _, suffix := range args[1:] {
|
for _, suffix := range args[1:] {
|
||||||
if strings.HasSuffix(types.ToString(args[0]), types.ToString(suffix)) {
|
if strings.HasSuffix(types.ToString(args[0]), types.ToString(suffix)) {
|
||||||
@ -395,7 +393,7 @@ func init() {
|
|||||||
"line_ends_with": makeDslWithOptionalArgsFunction(
|
"line_ends_with": makeDslWithOptionalArgsFunction(
|
||||||
"(str string, suffix ...string) bool", func(args ...interface{}) (interface{}, error) {
|
"(str string, suffix ...string) bool", func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
for _, line := range strings.Split(types.ToString(args[0]), "\n") {
|
for _, line := range strings.Split(types.ToString(args[0]), "\n") {
|
||||||
for _, suffix := range args[1:] {
|
for _, suffix := range args[1:] {
|
||||||
@ -436,11 +434,11 @@ func init() {
|
|||||||
separator := types.ToString(arguments[1])
|
separator := types.ToString(arguments[1])
|
||||||
count, err := strconv.Atoi(types.ToString(arguments[2]))
|
count, err := strconv.Atoi(types.ToString(arguments[2]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
return strings.SplitN(input, separator, count), nil
|
return strings.SplitN(input, separator, count), nil
|
||||||
} else {
|
} else {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -450,7 +448,7 @@ func init() {
|
|||||||
func(arguments ...interface{}) (interface{}, error) {
|
func(arguments ...interface{}) (interface{}, error) {
|
||||||
argumentsSize := len(arguments)
|
argumentsSize := len(arguments)
|
||||||
if argumentsSize < 2 {
|
if argumentsSize < 2 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
} else if argumentsSize == 2 {
|
} else if argumentsSize == 2 {
|
||||||
separator := types.ToString(arguments[0])
|
separator := types.ToString(arguments[0])
|
||||||
elements, ok := arguments[1].([]string)
|
elements, ok := arguments[1].([]string)
|
||||||
@ -495,7 +493,7 @@ func init() {
|
|||||||
|
|
||||||
argSize := len(args)
|
argSize := len(args)
|
||||||
if argSize != 0 && argSize != 1 {
|
if argSize != 0 && argSize != 1 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
if argSize >= 1 {
|
if argSize >= 1 {
|
||||||
@ -516,7 +514,7 @@ func init() {
|
|||||||
|
|
||||||
argSize := len(args)
|
argSize := len(args)
|
||||||
if argSize < 1 || argSize > 3 {
|
if argSize < 1 || argSize > 3 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
length = int(args[0].(float64))
|
length = int(args[0].(float64))
|
||||||
@ -538,7 +536,7 @@ func init() {
|
|||||||
|
|
||||||
argSize := len(args)
|
argSize := len(args)
|
||||||
if argSize != 1 && argSize != 2 {
|
if argSize != 1 && argSize != 2 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
length = int(args[0].(float64))
|
length = int(args[0].(float64))
|
||||||
@ -558,7 +556,7 @@ func init() {
|
|||||||
|
|
||||||
argSize := len(args)
|
argSize := len(args)
|
||||||
if argSize != 1 && argSize != 2 {
|
if argSize != 1 && argSize != 2 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
length = int(args[0].(float64))
|
length = int(args[0].(float64))
|
||||||
@ -575,7 +573,7 @@ func init() {
|
|||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
argSize := len(args)
|
argSize := len(args)
|
||||||
if argSize != 1 && argSize != 2 {
|
if argSize != 1 && argSize != 2 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
length := int(args[0].(float64))
|
length := int(args[0].(float64))
|
||||||
@ -594,7 +592,7 @@ func init() {
|
|||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
argSize := len(args)
|
argSize := len(args)
|
||||||
if argSize > 2 {
|
if argSize > 2 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
min := 0
|
min := 0
|
||||||
@ -613,7 +611,7 @@ func init() {
|
|||||||
"(cidr ...string) string",
|
"(cidr ...string) string",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
var cidrs []string
|
var cidrs []string
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
@ -635,7 +633,7 @@ func init() {
|
|||||||
|
|
||||||
argSize := len(args)
|
argSize := len(args)
|
||||||
if argSize != 0 && argSize != 1 {
|
if argSize != 0 && argSize != 1 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
} else if argSize == 1 {
|
} else if argSize == 1 {
|
||||||
seconds = int(args[0].(float64))
|
seconds = int(args[0].(float64))
|
||||||
}
|
}
|
||||||
@ -670,7 +668,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
return parsedTime.Unix(), err
|
return parsedTime.Unix(), err
|
||||||
} else {
|
} else {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -678,7 +676,7 @@ func init() {
|
|||||||
"(seconds uint)",
|
"(seconds uint)",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
seconds := args[0].(float64)
|
seconds := args[0].(float64)
|
||||||
time.Sleep(time.Duration(seconds) * time.Second)
|
time.Sleep(time.Duration(seconds) * time.Second)
|
||||||
@ -689,7 +687,7 @@ func init() {
|
|||||||
"(firstVersion, constraints ...string) bool",
|
"(firstVersion, constraints ...string) bool",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
firstParsed, parseErr := version.NewVersion(types.ToString(args[0]))
|
firstParsed, parseErr := version.NewVersion(types.ToString(args[0]))
|
||||||
@ -713,7 +711,7 @@ func init() {
|
|||||||
"(args ...interface{})",
|
"(args ...interface{})",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
gologger.Info().Msgf("print_debug value: %s", fmt.Sprint(args))
|
gologger.Info().Msgf("print_debug value: %s", fmt.Sprint(args))
|
||||||
return true, nil
|
return true, nil
|
||||||
@ -753,7 +751,7 @@ func init() {
|
|||||||
"(str string, start int, optionalEnd int)",
|
"(str string, start int, optionalEnd int)",
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
argStr := types.ToString(args[0])
|
argStr := types.ToString(args[0])
|
||||||
start, err := strconv.Atoi(types.ToString(args[1]))
|
start, err := strconv.Atoi(types.ToString(args[1]))
|
||||||
@ -817,7 +815,7 @@ func init() {
|
|||||||
argSize := len(args)
|
argSize := len(args)
|
||||||
|
|
||||||
if argSize < 1 || argSize > 4 {
|
if argSize < 1 || argSize > 4 {
|
||||||
return nil, invalidDslFunctionError
|
return nil, ErrinvalidDslFunction
|
||||||
}
|
}
|
||||||
jsonString := args[0].(string)
|
jsonString := args[0].(string)
|
||||||
|
|
||||||
@ -968,7 +966,7 @@ func makeDslFunction(numberOfParameters int, dslFunctionLogic govaluate.Expressi
|
|||||||
[]string{signature},
|
[]string{signature},
|
||||||
func(args ...interface{}) (interface{}, error) {
|
func(args ...interface{}) (interface{}, error) {
|
||||||
if len(args) != numberOfParameters {
|
if len(args) != numberOfParameters {
|
||||||
return nil, fmt.Errorf(invalidDslFunctionMessageTemplate, invalidDslFunctionError, signature)
|
return nil, fmt.Errorf("%w. correct method signature %q", ErrinvalidDslFunction, signature)
|
||||||
}
|
}
|
||||||
return dslFunctionLogic(args...)
|
return dslFunctionLogic(args...)
|
||||||
},
|
},
|
||||||
|
|||||||
@ -53,7 +53,7 @@ func TestDSLGzipSerialize(t *testing.T) {
|
|||||||
|
|
||||||
func TestDslFunctionSignatures(t *testing.T) {
|
func TestDslFunctionSignatures(t *testing.T) {
|
||||||
createSignatureError := func(signature string) string {
|
createSignatureError := func(signature string) string {
|
||||||
return fmt.Errorf(invalidDslFunctionMessageTemplate, invalidDslFunctionError, signature).Error()
|
return fmt.Errorf("%w. correct method signature %q", ErrinvalidDslFunction, signature).Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
toUpperSignatureError := createSignatureError("to_upper(arg1 interface{}) interface{}")
|
toUpperSignatureError := createSignatureError("to_upper(arg1 interface{}) interface{}")
|
||||||
|
|||||||
@ -99,6 +99,20 @@ func (holder *ExtractorTypeHolder) UnmarshalYAML(unmarshal func(interface{}) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (holder *ExtractorTypeHolder) UnmarshalJSON(data []byte) error {
|
||||||
|
s := strings.Trim(string(data), `"`)
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
computedType, err := toExtractorTypes(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.ExtractorType = computedType
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (holder *ExtractorTypeHolder) MarshalJSON() ([]byte, error) {
|
func (holder *ExtractorTypeHolder) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(holder.ExtractorType.String())
|
return json.Marshal(holder.ExtractorType.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,10 +14,10 @@ type Extractor struct {
|
|||||||
// spaces or underscores (_).
|
// spaces or underscores (_).
|
||||||
// examples:
|
// examples:
|
||||||
// - value: "\"cookie-extractor\""
|
// - value: "\"cookie-extractor\""
|
||||||
Name string `yaml:"name,omitempty" jsonschema:"title=name of the extractor,description=Name of the extractor"`
|
Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"title=name of the extractor,description=Name of the extractor"`
|
||||||
// description: |
|
// description: |
|
||||||
// Type is the type of the extractor.
|
// Type is the type of the extractor.
|
||||||
Type ExtractorTypeHolder `json:"name,omitempty" yaml:"type"`
|
Type ExtractorTypeHolder `json:"type" yaml:"type"`
|
||||||
// extractorType is the internal type of the extractor
|
// extractorType is the internal type of the extractor
|
||||||
extractorType ExtractorType
|
extractorType ExtractorType
|
||||||
|
|
||||||
@ -33,13 +33,13 @@ type Extractor struct {
|
|||||||
// - name: Wordpress Author Extraction regex
|
// - name: Wordpress Author Extraction regex
|
||||||
// value: >
|
// value: >
|
||||||
// []string{"Author:(?:[A-Za-z0-9 -\\_=\"]+)?<span(?:[A-Za-z0-9 -\\_=\"]+)?>([A-Za-z0-9]+)<\\/span>"}
|
// []string{"Author:(?:[A-Za-z0-9 -\\_=\"]+)?<span(?:[A-Za-z0-9 -\\_=\"]+)?>([A-Za-z0-9]+)<\\/span>"}
|
||||||
Regex []string `yaml:"regex,omitempty" jsonschema:"title=regex to extract from part,description=Regex to extract from part"`
|
Regex []string `yaml:"regex,omitempty" json:"regex,omitempty" jsonschema:"title=regex to extract from part,description=Regex to extract from part"`
|
||||||
// description: |
|
// description: |
|
||||||
// Group specifies a numbered group to extract from the regex.
|
// Group specifies a numbered group to extract from the regex.
|
||||||
// examples:
|
// examples:
|
||||||
// - name: Example Regex Group
|
// - name: Example Regex Group
|
||||||
// value: "1"
|
// value: "1"
|
||||||
RegexGroup int `yaml:"group,omitempty" jsonschema:"title=group to extract from regex,description=Group to extract from regex"`
|
RegexGroup int `yaml:"group,omitempty" json:"group,omitempty" jsonschema:"title=group to extract from regex,description=Group to extract from regex"`
|
||||||
// regexCompiled is the compiled variant
|
// regexCompiled is the compiled variant
|
||||||
regexCompiled []*regexp.Regexp
|
regexCompiled []*regexp.Regexp
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ type Extractor struct {
|
|||||||
// - name: Extracting value of Content-Type Cookie
|
// - name: Extracting value of Content-Type Cookie
|
||||||
// value: >
|
// value: >
|
||||||
// []string{"content_type"}
|
// []string{"content_type"}
|
||||||
KVal []string `yaml:"kval,omitempty" jsonschema:"title=kval pairs to extract from response,description=Kval pairs to extract from response"`
|
KVal []string `yaml:"kval,omitempty" json:"kval,omitempty" jsonschema:"title=kval pairs to extract from response,description=Kval pairs to extract from response"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// JSON allows using jq-style syntax to extract items from json response
|
// JSON allows using jq-style syntax to extract items from json response
|
||||||
@ -70,27 +70,27 @@ type Extractor struct {
|
|||||||
// []string{".[] | .id"}
|
// []string{".[] | .id"}
|
||||||
// - value: >
|
// - value: >
|
||||||
// []string{".batters | .batter | .[] | .id"}
|
// []string{".batters | .batter | .[] | .id"}
|
||||||
JSON []string `yaml:"json,omitempty" jsonschema:"title=json jq expressions to extract data,description=JSON JQ expressions to evaluate from response part"`
|
JSON []string `yaml:"json,omitempty" json:"json,omitempty" jsonschema:"title=json jq expressions to extract data,description=JSON JQ expressions to evaluate from response part"`
|
||||||
// description: |
|
// description: |
|
||||||
// XPath allows using xpath expressions to extract items from html response
|
// XPath allows using xpath expressions to extract items from html response
|
||||||
//
|
//
|
||||||
// examples:
|
// examples:
|
||||||
// - value: >
|
// - value: >
|
||||||
// []string{"/html/body/div/p[2]/a"}
|
// []string{"/html/body/div/p[2]/a"}
|
||||||
XPath []string `yaml:"xpath,omitempty" jsonschema:"title=html xpath expressions to extract data,description=XPath allows using xpath expressions to extract items from html response"`
|
XPath []string `yaml:"xpath,omitempty" json:"xpath,omitempty" jsonschema:"title=html xpath expressions to extract data,description=XPath allows using xpath expressions to extract items from html response"`
|
||||||
// description: |
|
// description: |
|
||||||
// Attribute is an optional attribute to extract from response XPath.
|
// Attribute is an optional attribute to extract from response XPath.
|
||||||
//
|
//
|
||||||
// examples:
|
// examples:
|
||||||
// - value: "\"href\""
|
// - value: "\"href\""
|
||||||
Attribute string `yaml:"attribute,omitempty" jsonschema:"title=optional attribute to extract from xpath,description=Optional attribute to extract from response XPath"`
|
Attribute string `yaml:"attribute,omitempty" json:"attribute,omitempty" jsonschema:"title=optional attribute to extract from xpath,description=Optional attribute to extract from response XPath"`
|
||||||
|
|
||||||
// jsonCompiled is the compiled variant
|
// jsonCompiled is the compiled variant
|
||||||
jsonCompiled []*gojq.Code
|
jsonCompiled []*gojq.Code
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Extracts using DSL expressions.
|
// Extracts using DSL expressions.
|
||||||
DSL []string `yaml:"dsl,omitempty" jsonschema:"title=dsl expressions to extract,description=Optional attribute to extract from response dsl"`
|
DSL []string `yaml:"dsl,omitempty" json:"dsl,omitempty" jsonschema:"title=dsl expressions to extract,description=Optional attribute to extract from response dsl"`
|
||||||
dslCompiled []*govaluate.EvaluableExpression
|
dslCompiled []*govaluate.EvaluableExpression
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
@ -101,16 +101,16 @@ type Extractor struct {
|
|||||||
// examples:
|
// examples:
|
||||||
// - value: "\"body\""
|
// - value: "\"body\""
|
||||||
// - value: "\"raw\""
|
// - value: "\"raw\""
|
||||||
Part string `yaml:"part,omitempty" jsonschema:"title=part of response to extract data from,description=Part of the request response to extract data from"`
|
Part string `yaml:"part,omitempty" json:"part,omitempty" jsonschema:"title=part of response to extract data from,description=Part of the request response to extract data from"`
|
||||||
// description: |
|
// description: |
|
||||||
// Internal, when set to true will allow using the value extracted
|
// Internal, when set to true will allow using the value extracted
|
||||||
// in the next request for some protocols (like HTTP).
|
// in the next request for some protocols (like HTTP).
|
||||||
Internal bool `yaml:"internal,omitempty" jsonschema:"title=mark extracted value for internal variable use,description=Internal when set to true will allow using the value extracted in the next request for some protocols"`
|
Internal bool `yaml:"internal,omitempty" json:"internal,omitempty" jsonschema:"title=mark extracted value for internal variable use,description=Internal when set to true will allow using the value extracted in the next request for some protocols"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// CaseInsensitive enables case-insensitive extractions. Default is false.
|
// CaseInsensitive enables case-insensitive extractions. Default is false.
|
||||||
// values:
|
// values:
|
||||||
// - false
|
// - false
|
||||||
// - true
|
// - true
|
||||||
CaseInsensitive bool `yaml:"case-insensitive,omitempty" jsonschema:"title=use case insensitive extract,description=use case insensitive extract"`
|
CaseInsensitive bool `yaml:"case-insensitive,omitempty" json:"case-insensitive,omitempty" jsonschema:"title=use case insensitive extract,description=use case insensitive extract"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,14 +10,14 @@ import (
|
|||||||
type Matcher struct {
|
type Matcher struct {
|
||||||
// description: |
|
// description: |
|
||||||
// Type is the type of the matcher.
|
// Type is the type of the matcher.
|
||||||
Type MatcherTypeHolder `yaml:"type" jsonschema:"title=type of matcher,description=Type of the matcher,enum=status,enum=size,enum=word,enum=regex,enum=binary,enum=dsl"`
|
Type MatcherTypeHolder `yaml:"type" json:"type" jsonschema:"title=type of matcher,description=Type of the matcher,enum=status,enum=size,enum=word,enum=regex,enum=binary,enum=dsl"`
|
||||||
// description: |
|
// description: |
|
||||||
// Condition is the optional condition between two matcher variables. By default,
|
// Condition is the optional condition between two matcher variables. By default,
|
||||||
// the condition is assumed to be OR.
|
// the condition is assumed to be OR.
|
||||||
// values:
|
// values:
|
||||||
// - "and"
|
// - "and"
|
||||||
// - "or"
|
// - "or"
|
||||||
Condition string `yaml:"condition,omitempty" jsonschema:"title=condition between matcher variables,description=Condition between the matcher variables,enum=and,enum=or"`
|
Condition string `yaml:"condition,omitempty" json:"condition,omitempty" jsonschema:"title=condition between matcher variables,description=Condition between the matcher variables,enum=and,enum=or"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Part is the part of the request response to match data from.
|
// Part is the part of the request response to match data from.
|
||||||
@ -27,31 +27,31 @@ type Matcher struct {
|
|||||||
// examples:
|
// examples:
|
||||||
// - value: "\"body\""
|
// - value: "\"body\""
|
||||||
// - value: "\"raw\""
|
// - value: "\"raw\""
|
||||||
Part string `yaml:"part,omitempty" jsonschema:"title=part of response to match,description=Part of response to match data from"`
|
Part string `yaml:"part,omitempty" json:"part,omitempty" jsonschema:"title=part of response to match,description=Part of response to match data from"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Negative specifies if the match should be reversed
|
// Negative specifies if the match should be reversed
|
||||||
// It will only match if the condition is not true.
|
// It will only match if the condition is not true.
|
||||||
Negative bool `yaml:"negative,omitempty" jsonschema:"title=negative specifies if match reversed,description=Negative specifies if the match should be reversed. It will only match if the condition is not true"`
|
Negative bool `yaml:"negative,omitempty" json:"negative,omitempty" jsonschema:"title=negative specifies if match reversed,description=Negative specifies if the match should be reversed. It will only match if the condition is not true"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Name of the matcher. Name should be lowercase and must not contain
|
// Name of the matcher. Name should be lowercase and must not contain
|
||||||
// spaces or underscores (_).
|
// spaces or underscores (_).
|
||||||
// examples:
|
// examples:
|
||||||
// - value: "\"cookie-matcher\""
|
// - value: "\"cookie-matcher\""
|
||||||
Name string `yaml:"name,omitempty" jsonschema:"title=name of the matcher,description=Name of the matcher"`
|
Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"title=name of the matcher,description=Name of the matcher"`
|
||||||
// description: |
|
// description: |
|
||||||
// Status are the acceptable status codes for the response.
|
// Status are the acceptable status codes for the response.
|
||||||
// examples:
|
// examples:
|
||||||
// - value: >
|
// - value: >
|
||||||
// []int{200, 302}
|
// []int{200, 302}
|
||||||
Status []int `yaml:"status,omitempty" jsonschema:"title=status to match,description=Status to match for the response"`
|
Status []int `yaml:"status,omitempty" json:"status,omitempty" jsonschema:"title=status to match,description=Status to match for the response"`
|
||||||
// description: |
|
// description: |
|
||||||
// Size is the acceptable size for the response
|
// Size is the acceptable size for the response
|
||||||
// examples:
|
// examples:
|
||||||
// - value: >
|
// - value: >
|
||||||
// []int{3029, 2042}
|
// []int{3029, 2042}
|
||||||
Size []int `yaml:"size,omitempty" jsonschema:"title=acceptable size for response,description=Size is the acceptable size for the response"`
|
Size []int `yaml:"size,omitempty" json:"size,omitempty" jsonschema:"title=acceptable size for response,description=Size is the acceptable size for the response"`
|
||||||
// description: |
|
// description: |
|
||||||
// Words contains word patterns required to be present in the response part.
|
// Words contains word patterns required to be present in the response part.
|
||||||
// examples:
|
// examples:
|
||||||
@ -61,7 +61,7 @@ type Matcher struct {
|
|||||||
// - name: Match for application/json in response headers
|
// - name: Match for application/json in response headers
|
||||||
// value: >
|
// value: >
|
||||||
// []string{"application/json"}
|
// []string{"application/json"}
|
||||||
Words []string `yaml:"words,omitempty" jsonschema:"title=words to match in response,description= Words contains word patterns required to be present in the response part"`
|
Words []string `yaml:"words,omitempty" json:"words,omitempty" jsonschema:"title=words to match in response,description= Words contains word patterns required to be present in the response part"`
|
||||||
// description: |
|
// description: |
|
||||||
// Regex contains Regular Expression patterns required to be present in the response part.
|
// Regex contains Regular Expression patterns required to be present in the response part.
|
||||||
// examples:
|
// examples:
|
||||||
@ -71,7 +71,7 @@ type Matcher struct {
|
|||||||
// - name: Match for Open Redirect via Location header
|
// - name: Match for Open Redirect via Location header
|
||||||
// value: >
|
// value: >
|
||||||
// []string{`(?m)^(?:Location\\s*?:\\s*?)(?:https?://|//)?(?:[a-zA-Z0-9\\-_\\.@]*)example\\.com.*$`}
|
// []string{`(?m)^(?:Location\\s*?:\\s*?)(?:https?://|//)?(?:[a-zA-Z0-9\\-_\\.@]*)example\\.com.*$`}
|
||||||
Regex []string `yaml:"regex,omitempty" jsonschema:"title=regex to match in response,description=Regex contains regex patterns required to be present in the response part"`
|
Regex []string `yaml:"regex,omitempty" json:"regex,omitempty" jsonschema:"title=regex to match in response,description=Regex contains regex patterns required to be present in the response part"`
|
||||||
// description: |
|
// description: |
|
||||||
// Binary are the binary patterns required to be present in the response part.
|
// Binary are the binary patterns required to be present in the response part.
|
||||||
// examples:
|
// examples:
|
||||||
@ -81,7 +81,7 @@ type Matcher struct {
|
|||||||
// - name: Match for 7zip files
|
// - name: Match for 7zip files
|
||||||
// value: >
|
// value: >
|
||||||
// []string{"377ABCAF271C"}
|
// []string{"377ABCAF271C"}
|
||||||
Binary []string `yaml:"binary,omitempty" jsonschema:"title=binary patterns to match in response,description=Binary are the binary patterns required to be present in the response part"`
|
Binary []string `yaml:"binary,omitempty" json:"binary,omitempty" jsonschema:"title=binary patterns to match in response,description=Binary are the binary patterns required to be present in the response part"`
|
||||||
// description: |
|
// description: |
|
||||||
// DSL are the dsl expressions that will be evaluated as part of nuclei matching rules.
|
// DSL are the dsl expressions that will be evaluated as part of nuclei matching rules.
|
||||||
// A list of these helper functions are available [here](https://nuclei.projectdiscovery.io/templating-guide/helper-functions/).
|
// A list of these helper functions are available [here](https://nuclei.projectdiscovery.io/templating-guide/helper-functions/).
|
||||||
@ -92,24 +92,24 @@ type Matcher struct {
|
|||||||
// - name: DSL Matcher for missing strict transport security header
|
// - name: DSL Matcher for missing strict transport security header
|
||||||
// value: >
|
// value: >
|
||||||
// []string{"!contains(tolower(all_headers), ''strict-transport-security'')"}
|
// []string{"!contains(tolower(all_headers), ''strict-transport-security'')"}
|
||||||
DSL []string `yaml:"dsl,omitempty" jsonschema:"title=dsl expressions to match in response,description=DSL are the dsl expressions that will be evaluated as part of nuclei matching rules"`
|
DSL []string `yaml:"dsl,omitempty" json:"dsl,omitempty" jsonschema:"title=dsl expressions to match in response,description=DSL are the dsl expressions that will be evaluated as part of nuclei matching rules"`
|
||||||
// description: |
|
// description: |
|
||||||
// Encoding specifies the encoding for the words field if any.
|
// Encoding specifies the encoding for the words field if any.
|
||||||
// values:
|
// values:
|
||||||
// - "hex"
|
// - "hex"
|
||||||
Encoding string `yaml:"encoding,omitempty" jsonschema:"title=encoding for word field,description=Optional encoding for the word fields,enum=hex"`
|
Encoding string `yaml:"encoding,omitempty" json:"encoding,omitempty" jsonschema:"title=encoding for word field,description=Optional encoding for the word fields,enum=hex"`
|
||||||
// description: |
|
// description: |
|
||||||
// CaseInsensitive enables case-insensitive matches. Default is false.
|
// CaseInsensitive enables case-insensitive matches. Default is false.
|
||||||
// values:
|
// values:
|
||||||
// - false
|
// - false
|
||||||
// - true
|
// - true
|
||||||
CaseInsensitive bool `yaml:"case-insensitive,omitempty" jsonschema:"title=use case insensitive match,description=use case insensitive match"`
|
CaseInsensitive bool `yaml:"case-insensitive,omitempty" json:"case-insensitive,omitempty" jsonschema:"title=use case insensitive match,description=use case insensitive match"`
|
||||||
// description: |
|
// description: |
|
||||||
// MatchAll enables matching for all matcher values. Default is false.
|
// MatchAll enables matching for all matcher values. Default is false.
|
||||||
// values:
|
// values:
|
||||||
// - false
|
// - false
|
||||||
// - true
|
// - true
|
||||||
MatchAll bool `yaml:"match-all,omitempty" jsonschema:"title=match all values,description=match all matcher values ignoring condition"`
|
MatchAll bool `yaml:"match-all,omitempty" json:"match-all,omitempty" jsonschema:"title=match all values,description=match all matcher values ignoring condition"`
|
||||||
|
|
||||||
// cached data for the compiled matcher
|
// cached data for the compiled matcher
|
||||||
condition ConditionType
|
condition ConditionType
|
||||||
|
|||||||
@ -106,6 +106,20 @@ func (holder *MatcherTypeHolder) UnmarshalYAML(unmarshal func(interface{}) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (holder *MatcherTypeHolder) UnmarshalJSON(data []byte) error {
|
||||||
|
s := strings.Trim(string(data), `"`)
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
computedType, err := toMatcherTypes(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.MatcherType = computedType
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (holder MatcherTypeHolder) MarshalJSON() ([]byte, error) {
|
func (holder MatcherTypeHolder) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(holder.MatcherType.String())
|
return json.Marshal(holder.MatcherType.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,17 +23,17 @@ type Operators struct {
|
|||||||
//
|
//
|
||||||
// Multiple matchers can be combined with `matcher-condition` flag
|
// Multiple matchers can be combined with `matcher-condition` flag
|
||||||
// which accepts either `and` or `or` as argument.
|
// which accepts either `and` or `or` as argument.
|
||||||
Matchers []*matchers.Matcher `yaml:"matchers,omitempty" jsonschema:"title=matchers to run on response,description=Detection mechanism to identify whether the request was successful by doing pattern matching"`
|
Matchers []*matchers.Matcher `yaml:"matchers,omitempty" json:"matchers,omitempty" jsonschema:"title=matchers to run on response,description=Detection mechanism to identify whether the request was successful by doing pattern matching"`
|
||||||
// description: |
|
// description: |
|
||||||
// Extractors contains the extraction mechanism for the request to identify
|
// Extractors contains the extraction mechanism for the request to identify
|
||||||
// and extract parts of the response.
|
// and extract parts of the response.
|
||||||
Extractors []*extractors.Extractor `yaml:"extractors,omitempty" jsonschema:"title=extractors to run on response,description=Extractors contains the extraction mechanism for the request to identify and extract parts of the response"`
|
Extractors []*extractors.Extractor `yaml:"extractors,omitempty" json:"extractors,omitempty" jsonschema:"title=extractors to run on response,description=Extractors contains the extraction mechanism for the request to identify and extract parts of the response"`
|
||||||
// description: |
|
// description: |
|
||||||
// MatchersCondition is the condition between the matchers. Default is OR.
|
// MatchersCondition is the condition between the matchers. Default is OR.
|
||||||
// values:
|
// values:
|
||||||
// - "and"
|
// - "and"
|
||||||
// - "or"
|
// - "or"
|
||||||
MatchersCondition string `yaml:"matchers-condition,omitempty" jsonschema:"title=condition between the matchers,description=Conditions between the matchers,enum=and,enum=or"`
|
MatchersCondition string `yaml:"matchers-condition,omitempty" json:"matchers-condition,omitempty" jsonschema:"title=condition between the matchers,description=Conditions between the matchers,enum=and,enum=or"`
|
||||||
// cached variables that may be used along with request.
|
// cached variables that may be used along with request.
|
||||||
matchersCondition matchers.ConditionType
|
matchersCondition matchers.ConditionType
|
||||||
|
|
||||||
|
|||||||
@ -9,14 +9,15 @@ import (
|
|||||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader/filter"
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader/filter"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/templates/cache"
|
"github.com/projectdiscovery/nuclei/v2/pkg/templates/cache"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/templates/signer"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/utils"
|
"github.com/projectdiscovery/nuclei/v2/pkg/utils"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/utils/stats"
|
"github.com/projectdiscovery/nuclei/v2/pkg/utils/stats"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
mandatoryFieldMissingTemplate = "mandatory '%s' field is missing"
|
errMandatoryFieldMissingFmt = "mandatory '%s' field is missing"
|
||||||
invalidFieldFormatTemplate = "invalid field format for '%s' (allowed format is %s)"
|
errInvalidFieldFmt = "invalid field format for '%s' (allowed format is %s)"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LoadTemplate returns true if the template is valid and matches the filtering criteria.
|
// LoadTemplate returns true if the template is valid and matches the filtering criteria.
|
||||||
@ -71,17 +72,17 @@ func validateTemplateFields(template *templates.Template) error {
|
|||||||
var errors []string
|
var errors []string
|
||||||
|
|
||||||
if utils.IsBlank(info.Name) {
|
if utils.IsBlank(info.Name) {
|
||||||
errors = append(errors, fmt.Sprintf(mandatoryFieldMissingTemplate, "name"))
|
errors = append(errors, fmt.Sprintf(errMandatoryFieldMissingFmt, "name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if info.Authors.IsEmpty() {
|
if info.Authors.IsEmpty() {
|
||||||
errors = append(errors, fmt.Sprintf(mandatoryFieldMissingTemplate, "author"))
|
errors = append(errors, fmt.Sprintf(errMandatoryFieldMissingFmt, "author"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if template.ID == "" {
|
if template.ID == "" {
|
||||||
errors = append(errors, fmt.Sprintf(mandatoryFieldMissingTemplate, "id"))
|
errors = append(errors, fmt.Sprintf(errMandatoryFieldMissingFmt, "id"))
|
||||||
} else if !templateIDRegexp.MatchString(template.ID) {
|
} else if !templateIDRegexp.MatchString(template.ID) {
|
||||||
errors = append(errors, fmt.Sprintf(invalidFieldFormatTemplate, "id", templateIDRegexp.String()))
|
errors = append(errors, fmt.Sprintf(errInvalidFieldFmt, "id", templateIDRegexp.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
@ -105,7 +106,6 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
parsedTemplatesCache = cache.New()
|
parsedTemplatesCache = cache.New()
|
||||||
|
|
||||||
stats.NewEntry(SyntaxWarningStats, "Found %d templates with syntax warning (use -validate flag for further examination)")
|
stats.NewEntry(SyntaxWarningStats, "Found %d templates with syntax warning (use -validate flag for further examination)")
|
||||||
@ -124,6 +124,12 @@ func ParseTemplate(templatePath string, catalog catalog.Catalog) (*templates.Tem
|
|||||||
}
|
}
|
||||||
|
|
||||||
template := &templates.Template{}
|
template := &templates.Template{}
|
||||||
|
|
||||||
|
// check if the template is verified
|
||||||
|
if signer.DefaultVerifier != nil {
|
||||||
|
template.Verified, _ = signer.Verify(signer.DefaultVerifier, data)
|
||||||
|
}
|
||||||
|
|
||||||
if NoStrictSyntax {
|
if NoStrictSyntax {
|
||||||
err = yaml.Unmarshal(data, template)
|
err = yaml.Unmarshal(data, template)
|
||||||
} else {
|
} else {
|
||||||
@ -133,6 +139,7 @@ func ParseTemplate(templatePath string, catalog catalog.Catalog) (*templates.Tem
|
|||||||
stats.Increment(SyntaxErrorStats)
|
stats.Increment(SyntaxErrorStats)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedTemplatesCache.Store(templatePath, template, nil)
|
parsedTemplatesCache.Store(templatePath, template, nil)
|
||||||
return template, nil
|
return template, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,13 +5,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/disk"
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/disk"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader/filter"
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader/filter"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/model"
|
"github.com/projectdiscovery/nuclei/v2/pkg/model"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
|
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLoadTemplate(t *testing.T) {
|
func TestLoadTemplate(t *testing.T) {
|
||||||
|
|||||||
@ -88,6 +88,20 @@ func (holder *AttackTypeHolder) UnmarshalYAML(unmarshal func(interface{}) error)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (holder *AttackTypeHolder) UnmarshalJSON(data []byte) error {
|
||||||
|
s := strings.Trim(string(data), `"`)
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
computedType, err := toAttackType(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.Value = computedType
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (holder *AttackTypeHolder) MarshalJSON() ([]byte, error) {
|
func (holder *AttackTypeHolder) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(holder.Value.String())
|
return json.Marshal(holder.Value.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// WriteResult is a helper for writing results to the output
|
// WriteResult is a helper for writing results to the output
|
||||||
func WriteResult(data *output.InternalWrappedEvent, output output.Writer, progress progress.Progress, issuesClient *reporting.Client) bool {
|
func WriteResult(data *output.InternalWrappedEvent, output output.Writer, progress progress.Progress, issuesClient reporting.Client) bool {
|
||||||
// Handle the case where no result found for the template.
|
// Handle the case where no result found for the template.
|
||||||
// In this case, we just show misc information about the failed
|
// In this case, we just show misc information about the failed
|
||||||
// match for the template.
|
// match for the template.
|
||||||
|
|||||||
@ -85,7 +85,7 @@ type Options struct {
|
|||||||
// Output is the output writer for nuclei
|
// Output is the output writer for nuclei
|
||||||
Output output.Writer
|
Output output.Writer
|
||||||
// IssuesClient is a client for issue exporting
|
// IssuesClient is a client for issue exporting
|
||||||
IssuesClient *reporting.Client
|
IssuesClient reporting.Client
|
||||||
// Progress is the nuclei progress bar implementation.
|
// Progress is the nuclei progress bar implementation.
|
||||||
Progress progress.Progress
|
Progress progress.Progress
|
||||||
// Debug specifies whether debugging output should be shown for interactsh-client
|
// Debug specifies whether debugging output should be shown for interactsh-client
|
||||||
@ -133,7 +133,7 @@ func New(options *Options) (*Client, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewDefaultOptions returns the default options for interactsh client
|
// NewDefaultOptions returns the default options for interactsh client
|
||||||
func NewDefaultOptions(output output.Writer, reporting *reporting.Client, progress progress.Progress) *Options {
|
func NewDefaultOptions(output output.Writer, reporting reporting.Client, progress progress.Progress) *Options {
|
||||||
return &Options{
|
return &Options{
|
||||||
ServerURL: client.DefaultOptions.ServerURL,
|
ServerURL: client.DefaultOptions.ServerURL,
|
||||||
CacheSize: 5000,
|
CacheSize: 5000,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package variables
|
package variables
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/alecthomas/jsonschema"
|
"github.com/alecthomas/jsonschema"
|
||||||
@ -39,6 +40,19 @@ func (variables *Variable) UnmarshalYAML(unmarshal func(interface{}) error) erro
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (variables *Variable) UnmarshalJSON(data []byte) error {
|
||||||
|
variables.InsertionOrderedStringMap = utils.InsertionOrderedStringMap{}
|
||||||
|
if err := json.Unmarshal(data, &variables.InsertionOrderedStringMap); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
evaluated := variables.Evaluate(map[string]interface{}{})
|
||||||
|
|
||||||
|
for k, v := range evaluated {
|
||||||
|
variables.Set(k, v)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Evaluate returns a finished map of variables based on set values
|
// Evaluate returns a finished map of variables based on set values
|
||||||
func (variables *Variable) Evaluate(values map[string]interface{}) map[string]interface{} {
|
func (variables *Variable) Evaluate(values map[string]interface{}) map[string]interface{} {
|
||||||
result := make(map[string]interface{}, variables.Len())
|
result := make(map[string]interface{}, variables.Len())
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package variables
|
package variables
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -22,4 +23,21 @@ a6: "123456"`
|
|||||||
result := variables.Evaluate(map[string]interface{}{"hostname": "google.com"})
|
result := variables.Evaluate(map[string]interface{}{"hostname": "google.com"})
|
||||||
a4 := time.Now().Format("2006-01-02")
|
a4 := time.Now().Format("2006-01-02")
|
||||||
require.Equal(t, map[string]interface{}{"a2": "098f6bcd4621d373cade4e832627b4f6", "a3": "this_is_random_text", "a4": a4, "a5": "moc.elgoog", "a6": "123456"}, result, "could not get correct elements")
|
require.Equal(t, map[string]interface{}{"a2": "098f6bcd4621d373cade4e832627b4f6", "a3": "this_is_random_text", "a4": a4, "a5": "moc.elgoog", "a6": "123456"}, result, "could not get correct elements")
|
||||||
|
|
||||||
|
// json
|
||||||
|
data = `{
|
||||||
|
"a2": "{{md5('test')}}",
|
||||||
|
"a3": "this_is_random_text",
|
||||||
|
"a4": "{{date_time('%Y-%M-%D')}}",
|
||||||
|
"a5": "{{reverse(hostname)}}",
|
||||||
|
"a6": "123456"
|
||||||
|
}`
|
||||||
|
variables = Variable{}
|
||||||
|
err = json.Unmarshal([]byte(data), &variables)
|
||||||
|
require.NoError(t, err, "could not unmarshal json variables")
|
||||||
|
|
||||||
|
result = variables.Evaluate(map[string]interface{}{"hostname": "google.com"})
|
||||||
|
a4 = time.Now().Format("2006-01-02")
|
||||||
|
require.Equal(t, map[string]interface{}{"a2": "098f6bcd4621d373cade4e832627b4f6", "a3": "this_is_random_text", "a4": a4, "a5": "moc.elgoog", "a6": "123456"}, result, "could not get correct elements")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ type Request struct {
|
|||||||
operators.Operators `yaml:",inline"`
|
operators.Operators `yaml:",inline"`
|
||||||
|
|
||||||
// ID is the optional id of the request
|
// ID is the optional id of the request
|
||||||
ID string `yaml:"id,omitempty" jsonschema:"title=id of the dns request,description=ID is the optional ID of the DNS Request"`
|
ID string `yaml:"id,omitempty" json:"id,omitempty" jsonschema:"title=id of the dns request,description=ID is the optional ID of the DNS Request"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Name is the Hostname to make DNS request for.
|
// Name is the Hostname to make DNS request for.
|
||||||
@ -30,10 +30,10 @@ type Request struct {
|
|||||||
// Generally, it is set to {{FQDN}} which is the domain we get from input.
|
// Generally, it is set to {{FQDN}} which is the domain we get from input.
|
||||||
// examples:
|
// examples:
|
||||||
// - value: "\"{{FQDN}}\""
|
// - value: "\"{{FQDN}}\""
|
||||||
Name string `yaml:"name,omitempty" jsonschema:"title=hostname to make dns request for,description=Name is the Hostname to make DNS request for"`
|
Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"title=hostname to make dns request for,description=Name is the Hostname to make DNS request for"`
|
||||||
// description: |
|
// description: |
|
||||||
// RequestType is the type of DNS request to make.
|
// RequestType is the type of DNS request to make.
|
||||||
RequestType DNSRequestTypeHolder `yaml:"type,omitempty" jsonschema:"title=type of dns request to make,description=Type is the type of DNS request to make,enum=A,enum=NS,enum=DS,enum=CNAME,enum=SOA,enum=PTR,enum=MX,enum=TXT,enum=AAAA"`
|
RequestType DNSRequestTypeHolder `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"title=type of dns request to make,description=Type is the type of DNS request to make,enum=A,enum=NS,enum=DS,enum=CNAME,enum=SOA,enum=PTR,enum=MX,enum=TXT,enum=AAAA"`
|
||||||
// description: |
|
// description: |
|
||||||
// Class is the class of the DNS request.
|
// Class is the class of the DNS request.
|
||||||
//
|
//
|
||||||
@ -45,16 +45,16 @@ type Request struct {
|
|||||||
// - "hesiod"
|
// - "hesiod"
|
||||||
// - "none"
|
// - "none"
|
||||||
// - "any"
|
// - "any"
|
||||||
Class string `yaml:"class,omitempty" jsonschema:"title=class of DNS request,description=Class is the class of the DNS request,enum=inet,enum=csnet,enum=chaos,enum=hesiod,enum=none,enum=any"`
|
Class string `yaml:"class,omitempty" json:"class,omitempty" jsonschema:"title=class of DNS request,description=Class is the class of the DNS request,enum=inet,enum=csnet,enum=chaos,enum=hesiod,enum=none,enum=any"`
|
||||||
// description: |
|
// description: |
|
||||||
// Retries is the number of retries for the DNS request
|
// Retries is the number of retries for the DNS request
|
||||||
// examples:
|
// examples:
|
||||||
// - name: Use a retry of 3 to 5 generally
|
// - name: Use a retry of 3 to 5 generally
|
||||||
// value: 5
|
// value: 5
|
||||||
Retries int `yaml:"retries,omitempty" jsonschema:"title=retries for dns request,description=Retries is the number of retries for the DNS request"`
|
Retries int `yaml:"retries,omitempty" json:"retries,omitempty" jsonschema:"title=retries for dns request,description=Retries is the number of retries for the DNS request"`
|
||||||
// description: |
|
// description: |
|
||||||
// Trace performs a trace operation for the target.
|
// Trace performs a trace operation for the target.
|
||||||
Trace bool `yaml:"trace,omitempty" jsonschema:"title=trace operation,description=Trace performs a trace operation for the target."`
|
Trace bool `yaml:"trace,omitempty" json:"trace,omitempty" jsonschema:"title=trace operation,description=Trace performs a trace operation for the target."`
|
||||||
// description: |
|
// description: |
|
||||||
// TraceMaxRecursion is the number of max recursion allowed for trace operations
|
// TraceMaxRecursion is the number of max recursion allowed for trace operations
|
||||||
// examples:
|
// examples:
|
||||||
@ -72,9 +72,9 @@ type Request struct {
|
|||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Recursion determines if resolver should recurse all records to get fresh results.
|
// Recursion determines if resolver should recurse all records to get fresh results.
|
||||||
Recursion *bool `yaml:"recursion,omitempty" jsonschema:"title=recurse all servers,description=Recursion determines if resolver should recurse all records to get fresh results"`
|
Recursion *bool `yaml:"recursion,omitempty" json:"recursion,omitempty" jsonschema:"title=recurse all servers,description=Recursion determines if resolver should recurse all records to get fresh results"`
|
||||||
// Resolvers to use for the dns requests
|
// Resolvers to use for the dns requests
|
||||||
Resolvers []string `yaml:"resolvers,omitempty" jsonschema:"title=Resolvers,description=Define resolvers to use within the template"`
|
Resolvers []string `yaml:"resolvers,omitempty" json:"resolvers,omitempty" jsonschema:"title=Resolvers,description=Define resolvers to use within the template"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestPartDefinitions contains a mapping of request part definitions and their
|
// RequestPartDefinitions contains a mapping of request part definitions and their
|
||||||
|
|||||||
@ -116,6 +116,20 @@ func (holder *DNSRequestTypeHolder) UnmarshalYAML(unmarshal func(interface{}) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (holder *DNSRequestTypeHolder) UnmarshalJSON(data []byte) error {
|
||||||
|
s := strings.Trim(string(data), `"`)
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
computedType, err := toDNSRequestTypes(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.DNSRequestType = computedType
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (holder *DNSRequestTypeHolder) MarshalJSON() ([]byte, error) {
|
func (holder *DNSRequestTypeHolder) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(holder.DNSRequestType.String())
|
return json.Marshal(holder.DNSRequestType.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ type Request struct {
|
|||||||
// Extensions is the list of extensions or mime types to perform matching on.
|
// Extensions is the list of extensions or mime types to perform matching on.
|
||||||
// examples:
|
// examples:
|
||||||
// - value: '[]string{".txt", ".go", ".json"}'
|
// - value: '[]string{".txt", ".go", ".json"}'
|
||||||
Extensions []string `yaml:"extensions,omitempty" jsonschema:"title=extensions to match,description=List of extensions to perform matching on"`
|
Extensions []string `yaml:"extensions,omitempty" json:"extensions,omitempty" jsonschema:"title=extensions to match,description=List of extensions to perform matching on"`
|
||||||
// description: |
|
// description: |
|
||||||
// DenyList is the list of file, directories, mime types or extensions to deny during matching.
|
// DenyList is the list of file, directories, mime types or extensions to deny during matching.
|
||||||
//
|
//
|
||||||
@ -33,10 +33,10 @@ type Request struct {
|
|||||||
// in nuclei.
|
// in nuclei.
|
||||||
// examples:
|
// examples:
|
||||||
// - value: '[]string{".avi", ".mov", ".mp3"}'
|
// - value: '[]string{".avi", ".mov", ".mp3"}'
|
||||||
DenyList []string `yaml:"denylist,omitempty" jsonschema:"title=denylist, directories and extensions to deny match,description=List of files, directories and extensions to deny during matching"`
|
DenyList []string `yaml:"denylist,omitempty" json:"denylist,omitempty" jsonschema:"title=denylist, directories and extensions to deny match,description=List of files, directories and extensions to deny during matching"`
|
||||||
|
|
||||||
// ID is the optional id of the request
|
// ID is the optional id of the request
|
||||||
ID string `yaml:"id,omitempty" jsonschema:"title=id of the request,description=ID is the optional ID for the request"`
|
ID string `yaml:"id,omitempty" json:"id,omitempty" jsonschema:"title=id of the request,description=ID is the optional ID for the request"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// MaxSize is the maximum size of the file to run request on.
|
// MaxSize is the maximum size of the file to run request on.
|
||||||
@ -46,7 +46,7 @@ type Request struct {
|
|||||||
// If set to "no" then all content will be processed
|
// If set to "no" then all content will be processed
|
||||||
// examples:
|
// examples:
|
||||||
// - value: "\"5Mb\""
|
// - value: "\"5Mb\""
|
||||||
MaxSize string `yaml:"max-size,omitempty" jsonschema:"title=max size data to run request on,description=Maximum size of the file to run request on"`
|
MaxSize string `yaml:"max-size,omitempty" json:"max-size,omitempty" jsonschema:"title=max size data to run request on,description=Maximum size of the file to run request on"`
|
||||||
maxSize int64
|
maxSize int64
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
@ -57,7 +57,7 @@ type Request struct {
|
|||||||
// enables mime types check
|
// enables mime types check
|
||||||
MimeType bool
|
MimeType bool
|
||||||
|
|
||||||
CompiledOperators *operators.Operators `yaml:"-"`
|
CompiledOperators *operators.Operators `yaml:"-" json:"-"`
|
||||||
|
|
||||||
// cache any variables that may be needed for operation.
|
// cache any variables that may be needed for operation.
|
||||||
options *protocols.ExecuterOptions
|
options *protocols.ExecuterOptions
|
||||||
@ -68,7 +68,7 @@ type Request struct {
|
|||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// NoRecursive specifies whether to not do recursive checks if folders are provided.
|
// NoRecursive specifies whether to not do recursive checks if folders are provided.
|
||||||
NoRecursive bool `yaml:"no-recursive,omitempty" jsonschema:"title=do not perform recursion,description=Specifies whether to not do recursive checks if folders are provided"`
|
NoRecursive bool `yaml:"no-recursive,omitempty" json:"no-recursive,omitempty" jsonschema:"title=do not perform recursion,description=Specifies whether to not do recursive checks if folders are provided"`
|
||||||
|
|
||||||
allExtensions bool
|
allExtensions bool
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,20 +13,20 @@ type Action struct {
|
|||||||
// Args contain arguments for the headless action.
|
// Args contain arguments for the headless action.
|
||||||
//
|
//
|
||||||
// Per action arguments are described in detail [here](https://nuclei.projectdiscovery.io/templating-guide/protocols/headless/).
|
// Per action arguments are described in detail [here](https://nuclei.projectdiscovery.io/templating-guide/protocols/headless/).
|
||||||
Data map[string]string `yaml:"args,omitempty" jsonschema:"title=arguments for headless action,description=Args contain arguments for the headless action"`
|
Data map[string]string `yaml:"args,omitempty" json:"args,omitempty" jsonschema:"title=arguments for headless action,description=Args contain arguments for the headless action"`
|
||||||
// description: |
|
// description: |
|
||||||
// Name is the name assigned to the headless action.
|
// Name is the name assigned to the headless action.
|
||||||
//
|
//
|
||||||
// This can be used to execute code, for instance in browser
|
// This can be used to execute code, for instance in browser
|
||||||
// DOM using script action, and get the result in a variable
|
// DOM using script action, and get the result in a variable
|
||||||
// which can be matched upon by nuclei. An Example template [here](https://github.com/projectdiscovery/nuclei-templates/blob/master/headless/prototype-pollution-check.yaml).
|
// which can be matched upon by nuclei. An Example template [here](https://github.com/projectdiscovery/nuclei-templates/blob/master/headless/prototype-pollution-check.yaml).
|
||||||
Name string `yaml:"name,omitempty" jsonschema:"title=name for headless action,description=Name is the name assigned to the headless action"`
|
Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"title=name for headless action,description=Name is the name assigned to the headless action"`
|
||||||
// description: |
|
// description: |
|
||||||
// Description is the optional description of the headless action
|
// Description is the optional description of the headless action
|
||||||
Description string `yaml:"description,omitempty" jsonschema:"title=description for headless action,description=Description of the headless action"`
|
Description string `yaml:"description,omitempty" json:"description,omitempty" jsonschema:"title=description for headless action,description=Description of the headless action"`
|
||||||
// description: |
|
// description: |
|
||||||
// Action is the type of the action to perform.
|
// Action is the type of the action to perform.
|
||||||
ActionType ActionTypeHolder `yaml:"action" jsonschema:"title=action to perform,description=Type of actions to perform,enum=navigate,enum=script,enum=click,enum=rightclick,enum=text,enum=screenshot,enum=time,enum=select,enum=files,enum=waitload,enum=getresource,enum=extract,enum=setmethod,enum=addheader,enum=setheader,enum=deleteheader,enum=setbody,enum=waitevent,enum=keyboard,enum=debug,enum=sleep"`
|
ActionType ActionTypeHolder `yaml:"action" json:"action" jsonschema:"title=action to perform,description=Type of actions to perform,enum=navigate,enum=script,enum=click,enum=rightclick,enum=text,enum=screenshot,enum=time,enum=select,enum=files,enum=waitload,enum=getresource,enum=extract,enum=setmethod,enum=addheader,enum=setheader,enum=deleteheader,enum=setbody,enum=waitevent,enum=keyboard,enum=debug,enum=sleep"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the string representation of an action
|
// String returns the string representation of an action
|
||||||
|
|||||||
@ -198,6 +198,20 @@ func (holder *ActionTypeHolder) UnmarshalYAML(unmarshal func(interface{}) error)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (holder *ActionTypeHolder) UnmarshalJSON(data []byte) error {
|
||||||
|
s := strings.Trim(string(data), `"`)
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
computedType, err := toActionTypes(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.ActionType = computedType
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (holder *ActionTypeHolder) MarshalJSON() ([]byte, error) {
|
func (holder *ActionTypeHolder) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(holder.ActionType.String())
|
return json.Marshal(holder.ActionType.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,41 +15,41 @@ import (
|
|||||||
// Request contains a Headless protocol request to be made from a template
|
// Request contains a Headless protocol request to be made from a template
|
||||||
type Request struct {
|
type Request struct {
|
||||||
// ID is the optional id of the request
|
// ID is the optional id of the request
|
||||||
ID string `yaml:"id,omitempty" jsonschema:"title=id of the request,description=Optional ID of the headless request"`
|
ID string `yaml:"id,omitempty" json:"id,omitempty" jsonschema:"title=id of the request,description=Optional ID of the headless request"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Attack is the type of payload combinations to perform.
|
// Attack is the type of payload combinations to perform.
|
||||||
//
|
//
|
||||||
// Batteringram is inserts the same payload into all defined payload positions at once, pitchfork combines multiple payload sets and clusterbomb generates
|
// Batteringram is inserts the same payload into all defined payload positions at once, pitchfork combines multiple payload sets and clusterbomb generates
|
||||||
// permutations and combinations for all payloads.
|
// permutations and combinations for all payloads.
|
||||||
AttackType generators.AttackTypeHolder `yaml:"attack,omitempty" jsonschema:"title=attack is the payload combination,description=Attack is the type of payload combinations to perform,enum=batteringram,enum=pitchfork,enum=clusterbomb"`
|
AttackType generators.AttackTypeHolder `yaml:"attack,omitempty" json:"attack,omitempty" jsonschema:"title=attack is the payload combination,description=Attack is the type of payload combinations to perform,enum=batteringram,enum=pitchfork,enum=clusterbomb"`
|
||||||
// description: |
|
// description: |
|
||||||
// Payloads contains any payloads for the current request.
|
// Payloads contains any payloads for the current request.
|
||||||
//
|
//
|
||||||
// Payloads support both key-values combinations where a list
|
// Payloads support both key-values combinations where a list
|
||||||
// of payloads is provided, or optionally a single file can also
|
// of payloads is provided, or optionally a single file can also
|
||||||
// be provided as payload which will be read on run-time.
|
// be provided as payload which will be read on run-time.
|
||||||
Payloads map[string]interface{} `yaml:"payloads,omitempty" jsonschema:"title=payloads for the headless request,description=Payloads contains any payloads for the current request"`
|
Payloads map[string]interface{} `yaml:"payloads,omitempty" json:"payloads,omitempty" jsonschema:"title=payloads for the headless request,description=Payloads contains any payloads for the current request"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Steps is the list of actions to run for headless request
|
// Steps is the list of actions to run for headless request
|
||||||
Steps []*engine.Action `yaml:"steps,omitempty" jsonschema:"title=list of actions for headless request,description=List of actions to run for headless request"`
|
Steps []*engine.Action `yaml:"steps,omitempty" json:"steps,omitempty" jsonschema:"title=list of actions for headless request,description=List of actions to run for headless request"`
|
||||||
|
|
||||||
// descriptions: |
|
// descriptions: |
|
||||||
// User-Agent is the type of user-agent to use for the request.
|
// User-Agent is the type of user-agent to use for the request.
|
||||||
UserAgent useragent.UserAgentHolder `yaml:"user_agent,omitempty" jsonschema:"title=user agent for the headless request,description=User agent for the headless request"`
|
UserAgent useragent.UserAgentHolder `yaml:"user_agent,omitempty" json:"user_agent,omitempty" jsonschema:"title=user agent for the headless request,description=User agent for the headless request"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// If UserAgent is set to custom, customUserAgent is the custom user-agent to use for the request.
|
// If UserAgent is set to custom, customUserAgent is the custom user-agent to use for the request.
|
||||||
CustomUserAgent string `yaml:"custom_user_agent,omitempty" jsonschema:"title=custom user agent for the headless request,description=Custom user agent for the headless request"`
|
CustomUserAgent string `yaml:"custom_user_agent,omitempty" json:"custom_user_agent,omitempty" jsonschema:"title=custom user agent for the headless request,description=Custom user agent for the headless request"`
|
||||||
compiledUserAgent string
|
compiledUserAgent string
|
||||||
// description: |
|
// description: |
|
||||||
// StopAtFirstMatch stops the execution of the requests and template as soon as a match is found.
|
// StopAtFirstMatch stops the execution of the requests and template as soon as a match is found.
|
||||||
StopAtFirstMatch bool `yaml:"stop-at-first-match,omitempty" jsonschema:"title=stop at first match,description=Stop the execution after a match is found"`
|
StopAtFirstMatch bool `yaml:"stop-at-first-match,omitempty" json:"stop-at-first-match,omitempty" jsonschema:"title=stop at first match,description=Stop the execution after a match is found"`
|
||||||
|
|
||||||
// Operators for the current request go here.
|
// Operators for the current request go here.
|
||||||
operators.Operators `yaml:",inline,omitempty"`
|
operators.Operators `yaml:",inline,omitempty" json:",inline,omitempty"`
|
||||||
CompiledOperators *operators.Operators `yaml:"-"`
|
CompiledOperators *operators.Operators `yaml:"-" json:"-"`
|
||||||
|
|
||||||
// cache any variables that may be needed for operation.
|
// cache any variables that may be needed for operation.
|
||||||
options *protocols.ExecuterOptions
|
options *protocols.ExecuterOptions
|
||||||
|
|||||||
@ -20,7 +20,7 @@ type Rule struct {
|
|||||||
// - "prefix"
|
// - "prefix"
|
||||||
// - "postfix"
|
// - "postfix"
|
||||||
// - "infix"
|
// - "infix"
|
||||||
Type string `yaml:"type,omitempty" jsonschema:"title=type of rule,description=Type of fuzzing rule to perform,enum=replace,enum=prefix,enum=postfix,enum=infix"`
|
Type string `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"title=type of rule,description=Type of fuzzing rule to perform,enum=replace,enum=prefix,enum=postfix,enum=infix"`
|
||||||
ruleType ruleType
|
ruleType ruleType
|
||||||
// description: |
|
// description: |
|
||||||
// Part is the part of request to fuzz.
|
// Part is the part of request to fuzz.
|
||||||
@ -28,7 +28,7 @@ type Rule struct {
|
|||||||
// query fuzzes the query part of url. More parts will be added later.
|
// query fuzzes the query part of url. More parts will be added later.
|
||||||
// values:
|
// values:
|
||||||
// - "query"
|
// - "query"
|
||||||
Part string `yaml:"part,omitempty" jsonschema:"title=part of rule,description=Part of request rule to fuzz,enum=query"`
|
Part string `yaml:"part,omitempty" json:"part,omitempty" jsonschema:"title=part of rule,description=Part of request rule to fuzz,enum=query"`
|
||||||
partType partType
|
partType partType
|
||||||
// description: |
|
// description: |
|
||||||
// Mode is the mode of fuzzing to perform.
|
// Mode is the mode of fuzzing to perform.
|
||||||
@ -37,7 +37,7 @@ type Rule struct {
|
|||||||
// values:
|
// values:
|
||||||
// - "single"
|
// - "single"
|
||||||
// - "multiple"
|
// - "multiple"
|
||||||
Mode string `yaml:"mode,omitempty" jsonschema:"title=mode of rule,description=Mode of request rule to fuzz,enum=single,enum=multiple"`
|
Mode string `yaml:"mode,omitempty" json:"mode,omitempty" jsonschema:"title=mode of rule,description=Mode of request rule to fuzz,enum=single,enum=multiple"`
|
||||||
modeType modeType
|
modeType modeType
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
@ -46,7 +46,7 @@ type Rule struct {
|
|||||||
// - name: Examples of keys
|
// - name: Examples of keys
|
||||||
// value: >
|
// value: >
|
||||||
// []string{"url", "file", "host"}
|
// []string{"url", "file", "host"}
|
||||||
Keys []string `yaml:"keys,omitempty" jsonschema:"title=keys of parameters to fuzz,description=Keys of parameters to fuzz"`
|
Keys []string `yaml:"keys,omitempty" json:"keys,omitempty" jsonschema:"title=keys of parameters to fuzz,description=Keys of parameters to fuzz"`
|
||||||
keysMap map[string]struct{}
|
keysMap map[string]struct{}
|
||||||
// description: |
|
// description: |
|
||||||
// KeysRegex is the optional list of regex key parameters to fuzz.
|
// KeysRegex is the optional list of regex key parameters to fuzz.
|
||||||
@ -54,7 +54,7 @@ type Rule struct {
|
|||||||
// - name: Examples of key regex
|
// - name: Examples of key regex
|
||||||
// value: >
|
// value: >
|
||||||
// []string{"url.*"}
|
// []string{"url.*"}
|
||||||
KeysRegex []string `yaml:"keys-regex,omitempty" jsonschema:"title=keys regex to fuzz,description=Regex of parameter keys to fuzz"`
|
KeysRegex []string `yaml:"keys-regex,omitempty" json:"keys-regex,omitempty" jsonschema:"title=keys regex to fuzz,description=Regex of parameter keys to fuzz"`
|
||||||
keysRegex []*regexp.Regexp
|
keysRegex []*regexp.Regexp
|
||||||
// description: |
|
// description: |
|
||||||
// Values is the optional list of regex value parameters to fuzz.
|
// Values is the optional list of regex value parameters to fuzz.
|
||||||
@ -62,7 +62,7 @@ type Rule struct {
|
|||||||
// - name: Examples of value regex
|
// - name: Examples of value regex
|
||||||
// value: >
|
// value: >
|
||||||
// []string{"https?://.*"}
|
// []string{"https?://.*"}
|
||||||
ValuesRegex []string `yaml:"values,omitempty" jsonschema:"title=values regex to fuzz,description=Regex of parameter values to fuzz"`
|
ValuesRegex []string `yaml:"values,omitempty" json:"values,omitempty" jsonschema:"title=values regex to fuzz,description=Regex of parameter values to fuzz"`
|
||||||
valuesRegex []*regexp.Regexp
|
valuesRegex []*regexp.Regexp
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
@ -71,7 +71,7 @@ type Rule struct {
|
|||||||
// - name: Examples of fuzz
|
// - name: Examples of fuzz
|
||||||
// value: >
|
// value: >
|
||||||
// []string{"{{ssrf}}", "{{interactsh-url}}", "example-value"}
|
// []string{"{{ssrf}}", "{{interactsh-url}}", "example-value"}
|
||||||
Fuzz []string `yaml:"fuzz,omitempty" jsonschema:"title=payloads of fuzz rule,description=Payloads to perform fuzzing substitutions with"`
|
Fuzz []string `yaml:"fuzz,omitempty" json:"fuzz,omitempty" jsonschema:"title=payloads of fuzz rule,description=Payloads to perform fuzzing substitutions with"`
|
||||||
|
|
||||||
options *protocols.ExecuterOptions
|
options *protocols.ExecuterOptions
|
||||||
generator *generators.PayloadGenerator
|
generator *generators.PayloadGenerator
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import (
|
|||||||
// Request contains a http request to be made from a template
|
// Request contains a http request to be made from a template
|
||||||
type Request struct {
|
type Request struct {
|
||||||
// Operators for the current request go here.
|
// Operators for the current request go here.
|
||||||
operators.Operators `yaml:",inline"`
|
operators.Operators `yaml:",inline" json:",inline"`
|
||||||
// description: |
|
// description: |
|
||||||
// Path contains the path/s for the HTTP requests. It supports variables
|
// Path contains the path/s for the HTTP requests. It supports variables
|
||||||
// as placeholders.
|
// as placeholders.
|
||||||
@ -30,22 +30,22 @@ type Request struct {
|
|||||||
// - name: Some example path values
|
// - name: Some example path values
|
||||||
// value: >
|
// value: >
|
||||||
// []string{"{{BaseURL}}", "{{BaseURL}}/+CSCOU+/../+CSCOE+/files/file_list.json?path=/sessions"}
|
// []string{"{{BaseURL}}", "{{BaseURL}}/+CSCOU+/../+CSCOE+/files/file_list.json?path=/sessions"}
|
||||||
Path []string `yaml:"path,omitempty" jsonschema:"title=path(s) for the http request,description=Path(s) to send http requests to"`
|
Path []string `yaml:"path,omitempty" json:"path,omitempty" jsonschema:"title=path(s) for the http request,description=Path(s) to send http requests to"`
|
||||||
// description: |
|
// description: |
|
||||||
// Raw contains HTTP Requests in Raw format.
|
// Raw contains HTTP Requests in Raw format.
|
||||||
// examples:
|
// examples:
|
||||||
// - name: Some example raw requests
|
// - name: Some example raw requests
|
||||||
// value: |
|
// value: |
|
||||||
// []string{"GET /etc/passwd HTTP/1.1\nHost:\nContent-Length: 4", "POST /.%0d./.%0d./.%0d./.%0d./bin/sh HTTP/1.1\nHost: {{Hostname}}\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0\nContent-Length: 1\nConnection: close\n\necho\necho\ncat /etc/passwd 2>&1"}
|
// []string{"GET /etc/passwd HTTP/1.1\nHost:\nContent-Length: 4", "POST /.%0d./.%0d./.%0d./.%0d./bin/sh HTTP/1.1\nHost: {{Hostname}}\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0\nContent-Length: 1\nConnection: close\n\necho\necho\ncat /etc/passwd 2>&1"}
|
||||||
Raw []string `yaml:"raw,omitempty" jsonschema:"http requests in raw format,description=HTTP Requests in Raw Format"`
|
Raw []string `yaml:"raw,omitempty" json:"raw,omitempty" jsonschema:"http requests in raw format,description=HTTP Requests in Raw Format"`
|
||||||
// ID is the optional id of the request
|
// ID is the optional id of the request
|
||||||
ID string `yaml:"id,omitempty" jsonschema:"title=id for the http request,description=ID for the HTTP Request"`
|
ID string `yaml:"id,omitempty" json:"id,omitempty" jsonschema:"title=id for the http request,description=ID for the HTTP Request"`
|
||||||
// description: |
|
// description: |
|
||||||
// Name is the optional name of the request.
|
// Name is the optional name of the request.
|
||||||
//
|
//
|
||||||
// If a name is specified, all the named request in a template can be matched upon
|
// If a name is specified, all the named request in a template can be matched upon
|
||||||
// in a combined manner allowing multi-request based matchers.
|
// in a combined manner allowing multi-request based matchers.
|
||||||
Name string `yaml:"name,omitempty" jsonschema:"title=name for the http request,description=Optional name for the HTTP Request"`
|
Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"title=name for the http request,description=Optional name for the HTTP Request"`
|
||||||
// description: |
|
// description: |
|
||||||
// Attack is the type of payload combinations to perform.
|
// Attack is the type of payload combinations to perform.
|
||||||
//
|
//
|
||||||
@ -55,54 +55,54 @@ type Request struct {
|
|||||||
// - "batteringram"
|
// - "batteringram"
|
||||||
// - "pitchfork"
|
// - "pitchfork"
|
||||||
// - "clusterbomb"
|
// - "clusterbomb"
|
||||||
AttackType generators.AttackTypeHolder `yaml:"attack,omitempty" jsonschema:"title=attack is the payload combination,description=Attack is the type of payload combinations to perform,enum=batteringram,enum=pitchfork,enum=clusterbomb"`
|
AttackType generators.AttackTypeHolder `yaml:"attack,omitempty" json:"attack,omitempty" jsonschema:"title=attack is the payload combination,description=Attack is the type of payload combinations to perform,enum=batteringram,enum=pitchfork,enum=clusterbomb"`
|
||||||
// description: |
|
// description: |
|
||||||
// Method is the HTTP Request Method.
|
// Method is the HTTP Request Method.
|
||||||
Method HTTPMethodTypeHolder `yaml:"method,omitempty" jsonschema:"title=method is the http request method,description=Method is the HTTP Request Method,enum=GET,enum=HEAD,enum=POST,enum=PUT,enum=DELETE,enum=CONNECT,enum=OPTIONS,enum=TRACE,enum=PATCH,enum=PURGE"`
|
Method HTTPMethodTypeHolder `yaml:"method,omitempty" json:"method,omitempty" jsonschema:"title=method is the http request method,description=Method is the HTTP Request Method,enum=GET,enum=HEAD,enum=POST,enum=PUT,enum=DELETE,enum=CONNECT,enum=OPTIONS,enum=TRACE,enum=PATCH,enum=PURGE"`
|
||||||
// description: |
|
// description: |
|
||||||
// Body is an optional parameter which contains HTTP Request body.
|
// Body is an optional parameter which contains HTTP Request body.
|
||||||
// examples:
|
// examples:
|
||||||
// - name: Same Body for a Login POST request
|
// - name: Same Body for a Login POST request
|
||||||
// value: "\"username=test&password=test\""
|
// value: "\"username=test&password=test\""
|
||||||
Body string `yaml:"body,omitempty" jsonschema:"title=body is the http request body,description=Body is an optional parameter which contains HTTP Request body"`
|
Body string `yaml:"body,omitempty" json:"body,omitempty" jsonschema:"title=body is the http request body,description=Body is an optional parameter which contains HTTP Request body"`
|
||||||
// description: |
|
// description: |
|
||||||
// Payloads contains any payloads for the current request.
|
// Payloads contains any payloads for the current request.
|
||||||
//
|
//
|
||||||
// Payloads support both key-values combinations where a list
|
// Payloads support both key-values combinations where a list
|
||||||
// of payloads is provided, or optionally a single file can also
|
// of payloads is provided, or optionally a single file can also
|
||||||
// be provided as payload which will be read on run-time.
|
// be provided as payload which will be read on run-time.
|
||||||
Payloads map[string]interface{} `yaml:"payloads,omitempty" jsonschema:"title=payloads for the http request,description=Payloads contains any payloads for the current request"`
|
Payloads map[string]interface{} `yaml:"payloads,omitempty" json:"payloads,omitempty" jsonschema:"title=payloads for the http request,description=Payloads contains any payloads for the current request"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Headers contains HTTP Headers to send with the request.
|
// Headers contains HTTP Headers to send with the request.
|
||||||
// examples:
|
// examples:
|
||||||
// - value: |
|
// - value: |
|
||||||
// map[string]string{"Content-Type": "application/x-www-form-urlencoded", "Content-Length": "1", "Any-Header": "Any-Value"}
|
// map[string]string{"Content-Type": "application/x-www-form-urlencoded", "Content-Length": "1", "Any-Header": "Any-Value"}
|
||||||
Headers map[string]string `yaml:"headers,omitempty" jsonschema:"title=headers to send with the http request,description=Headers contains HTTP Headers to send with the request"`
|
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty" jsonschema:"title=headers to send with the http request,description=Headers contains HTTP Headers to send with the request"`
|
||||||
// description: |
|
// description: |
|
||||||
// RaceCount is the number of times to send a request in Race Condition Attack.
|
// RaceCount is the number of times to send a request in Race Condition Attack.
|
||||||
// examples:
|
// examples:
|
||||||
// - name: Send a request 5 times
|
// - name: Send a request 5 times
|
||||||
// value: "5"
|
// value: "5"
|
||||||
RaceNumberRequests int `yaml:"race_count,omitempty" jsonschema:"title=number of times to repeat request in race condition,description=Number of times to send a request in Race Condition Attack"`
|
RaceNumberRequests int `yaml:"race_count,omitempty" json:"race_count,omitempty" jsonschema:"title=number of times to repeat request in race condition,description=Number of times to send a request in Race Condition Attack"`
|
||||||
// description: |
|
// description: |
|
||||||
// MaxRedirects is the maximum number of redirects that should be followed.
|
// MaxRedirects is the maximum number of redirects that should be followed.
|
||||||
// examples:
|
// examples:
|
||||||
// - name: Follow up to 5 redirects
|
// - name: Follow up to 5 redirects
|
||||||
// value: "5"
|
// value: "5"
|
||||||
MaxRedirects int `yaml:"max-redirects,omitempty" jsonschema:"title=maximum number of redirects to follow,description=Maximum number of redirects that should be followed"`
|
MaxRedirects int `yaml:"max-redirects,omitempty" json:"max-redirects,omitempty" jsonschema:"title=maximum number of redirects to follow,description=Maximum number of redirects that should be followed"`
|
||||||
// description: |
|
// description: |
|
||||||
// PipelineConcurrentConnections is number of connections to create during pipelining.
|
// PipelineConcurrentConnections is number of connections to create during pipelining.
|
||||||
// examples:
|
// examples:
|
||||||
// - name: Create 40 concurrent connections
|
// - name: Create 40 concurrent connections
|
||||||
// value: 40
|
// value: 40
|
||||||
PipelineConcurrentConnections int `yaml:"pipeline-concurrent-connections,omitempty" jsonschema:"title=number of pipelining connections,description=Number of connections to create during pipelining"`
|
PipelineConcurrentConnections int `yaml:"pipeline-concurrent-connections,omitempty" json:"pipeline-concurrent-connections,omitempty" jsonschema:"title=number of pipelining connections,description=Number of connections to create during pipelining"`
|
||||||
// description: |
|
// description: |
|
||||||
// PipelineRequestsPerConnection is number of requests to send per connection when pipelining.
|
// PipelineRequestsPerConnection is number of requests to send per connection when pipelining.
|
||||||
// examples:
|
// examples:
|
||||||
// - name: Send 100 requests per pipeline connection
|
// - name: Send 100 requests per pipeline connection
|
||||||
// value: 100
|
// value: 100
|
||||||
PipelineRequestsPerConnection int `yaml:"pipeline-requests-per-connection,omitempty" jsonschema:"title=number of requests to send per pipelining connections,description=Number of requests to send per connection when pipelining"`
|
PipelineRequestsPerConnection int `yaml:"pipeline-requests-per-connection,omitempty" json:"pipeline-requests-per-connection,omitempty" jsonschema:"title=number of requests to send per pipelining connections,description=Number of requests to send per connection when pipelining"`
|
||||||
// description: |
|
// description: |
|
||||||
// Threads specifies number of threads to use sending requests. This enables Connection Pooling.
|
// Threads specifies number of threads to use sending requests. This enables Connection Pooling.
|
||||||
//
|
//
|
||||||
@ -111,18 +111,18 @@ type Request struct {
|
|||||||
// examples:
|
// examples:
|
||||||
// - name: Send requests using 10 concurrent threads
|
// - name: Send requests using 10 concurrent threads
|
||||||
// value: 10
|
// value: 10
|
||||||
Threads int `yaml:"threads,omitempty" jsonschema:"title=threads for sending requests,description=Threads specifies number of threads to use sending requests. This enables Connection Pooling"`
|
Threads int `yaml:"threads,omitempty" json:"threads,omitempty" jsonschema:"title=threads for sending requests,description=Threads specifies number of threads to use sending requests. This enables Connection Pooling"`
|
||||||
// description: |
|
// description: |
|
||||||
// MaxSize is the maximum size of http response body to read in bytes.
|
// MaxSize is the maximum size of http response body to read in bytes.
|
||||||
// examples:
|
// examples:
|
||||||
// - name: Read max 2048 bytes of the response
|
// - name: Read max 2048 bytes of the response
|
||||||
// value: 2048
|
// value: 2048
|
||||||
MaxSize int `yaml:"max-size,omitempty" jsonschema:"title=maximum http response body size,description=Maximum size of http response body to read in bytes"`
|
MaxSize int `yaml:"max-size,omitempty" json:"max-size,omitempty" jsonschema:"title=maximum http response body size,description=Maximum size of http response body to read in bytes"`
|
||||||
|
|
||||||
// Fuzzing describes schema to fuzz http requests
|
// Fuzzing describes schema to fuzz http requests
|
||||||
Fuzzing []*fuzz.Rule `yaml:"fuzzing,omitempty" jsonschema:"title=fuzzin rules for http fuzzing,description=Fuzzing describes rule schema to fuzz http requests"`
|
Fuzzing []*fuzz.Rule `yaml:"fuzzing,omitempty" json:"fuzzing,omitempty" jsonschema:"title=fuzzin rules for http fuzzing,description=Fuzzing describes rule schema to fuzz http requests"`
|
||||||
|
|
||||||
CompiledOperators *operators.Operators `yaml:"-"`
|
CompiledOperators *operators.Operators `yaml:"-" json:"-"`
|
||||||
|
|
||||||
options *protocols.ExecuterOptions
|
options *protocols.ExecuterOptions
|
||||||
connConfiguration *httpclientpool.Configuration
|
connConfiguration *httpclientpool.Configuration
|
||||||
@ -140,63 +140,63 @@ type Request struct {
|
|||||||
// Signature is the request signature method
|
// Signature is the request signature method
|
||||||
// values:
|
// values:
|
||||||
// - "AWS"
|
// - "AWS"
|
||||||
Signature SignatureTypeHolder `yaml:"signature,omitempty" jsonschema:"title=signature is the http request signature method,description=Signature is the HTTP Request signature Method,enum=AWS"`
|
Signature SignatureTypeHolder `yaml:"signature,omitempty" json:"signature,omitempty" jsonschema:"title=signature is the http request signature method,description=Signature is the HTTP Request signature Method,enum=AWS"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// CookieReuse is an optional setting that enables cookie reuse for
|
// CookieReuse is an optional setting that enables cookie reuse for
|
||||||
// all requests defined in raw section.
|
// all requests defined in raw section.
|
||||||
CookieReuse bool `yaml:"cookie-reuse,omitempty" jsonschema:"title=optional cookie reuse enable,description=Optional setting that enables cookie reuse"`
|
CookieReuse bool `yaml:"cookie-reuse,omitempty" json:"cookie-reuse,omitempty" jsonschema:"title=optional cookie reuse enable,description=Optional setting that enables cookie reuse"`
|
||||||
// description: |
|
// description: |
|
||||||
// Enables force reading of the entire raw unsafe request body ignoring
|
// Enables force reading of the entire raw unsafe request body ignoring
|
||||||
// any specified content length headers.
|
// any specified content length headers.
|
||||||
ForceReadAllBody bool `yaml:"read-all,omitempty" jsonschema:"title=force read all body,description=Enables force reading of entire unsafe http request body"`
|
ForceReadAllBody bool `yaml:"read-all,omitempty" json:"read-all,omitempty" jsonschema:"title=force read all body,description=Enables force reading of entire unsafe http request body"`
|
||||||
// description: |
|
// description: |
|
||||||
// Redirects specifies whether redirects should be followed by the HTTP Client.
|
// Redirects specifies whether redirects should be followed by the HTTP Client.
|
||||||
//
|
//
|
||||||
// This can be used in conjunction with `max-redirects` to control the HTTP request redirects.
|
// This can be used in conjunction with `max-redirects` to control the HTTP request redirects.
|
||||||
Redirects bool `yaml:"redirects,omitempty" jsonschema:"title=follow http redirects,description=Specifies whether redirects should be followed by the HTTP Client"`
|
Redirects bool `yaml:"redirects,omitempty" json:"redirects,omitempty" jsonschema:"title=follow http redirects,description=Specifies whether redirects should be followed by the HTTP Client"`
|
||||||
// description: |
|
// description: |
|
||||||
// Redirects specifies whether only redirects to the same host should be followed by the HTTP Client.
|
// Redirects specifies whether only redirects to the same host should be followed by the HTTP Client.
|
||||||
//
|
//
|
||||||
// This can be used in conjunction with `max-redirects` to control the HTTP request redirects.
|
// This can be used in conjunction with `max-redirects` to control the HTTP request redirects.
|
||||||
HostRedirects bool `yaml:"host-redirects,omitempty" jsonschema:"title=follow same host http redirects,description=Specifies whether redirects to the same host should be followed by the HTTP Client"`
|
HostRedirects bool `yaml:"host-redirects,omitempty" json:"host-redirects,omitempty" jsonschema:"title=follow same host http redirects,description=Specifies whether redirects to the same host should be followed by the HTTP Client"`
|
||||||
// description: |
|
// description: |
|
||||||
// Pipeline defines if the attack should be performed with HTTP 1.1 Pipelining
|
// Pipeline defines if the attack should be performed with HTTP 1.1 Pipelining
|
||||||
//
|
//
|
||||||
// All requests must be idempotent (GET/POST). This can be used for race conditions/billions requests.
|
// All requests must be idempotent (GET/POST). This can be used for race conditions/billions requests.
|
||||||
Pipeline bool `yaml:"pipeline,omitempty" jsonschema:"title=perform HTTP 1.1 pipelining,description=Pipeline defines if the attack should be performed with HTTP 1.1 Pipelining"`
|
Pipeline bool `yaml:"pipeline,omitempty" json:"pipeline,omitempty" jsonschema:"title=perform HTTP 1.1 pipelining,description=Pipeline defines if the attack should be performed with HTTP 1.1 Pipelining"`
|
||||||
// description: |
|
// description: |
|
||||||
// Unsafe specifies whether to use rawhttp engine for sending Non RFC-Compliant requests.
|
// Unsafe specifies whether to use rawhttp engine for sending Non RFC-Compliant requests.
|
||||||
//
|
//
|
||||||
// This uses the [rawhttp](https://github.com/projectdiscovery/rawhttp) engine to achieve complete
|
// This uses the [rawhttp](https://github.com/projectdiscovery/rawhttp) engine to achieve complete
|
||||||
// control over the request, with no normalization performed by the client.
|
// control over the request, with no normalization performed by the client.
|
||||||
Unsafe bool `yaml:"unsafe,omitempty" jsonschema:"title=use rawhttp non-strict-rfc client,description=Unsafe specifies whether to use rawhttp engine for sending Non RFC-Compliant requests"`
|
Unsafe bool `yaml:"unsafe,omitempty" json:"unsafe,omitempty" jsonschema:"title=use rawhttp non-strict-rfc client,description=Unsafe specifies whether to use rawhttp engine for sending Non RFC-Compliant requests"`
|
||||||
// description: |
|
// description: |
|
||||||
// Race determines if all the request have to be attempted at the same time (Race Condition)
|
// Race determines if all the request have to be attempted at the same time (Race Condition)
|
||||||
//
|
//
|
||||||
// The actual number of requests that will be sent is determined by the `race_count` field.
|
// The actual number of requests that will be sent is determined by the `race_count` field.
|
||||||
Race bool `yaml:"race,omitempty" jsonschema:"title=perform race-http request coordination attack,description=Race determines if all the request have to be attempted at the same time (Race Condition)"`
|
Race bool `yaml:"race,omitempty" json:"race,omitempty" jsonschema:"title=perform race-http request coordination attack,description=Race determines if all the request have to be attempted at the same time (Race Condition)"`
|
||||||
// description: |
|
// description: |
|
||||||
// ReqCondition automatically assigns numbers to requests and preserves their history.
|
// ReqCondition automatically assigns numbers to requests and preserves their history.
|
||||||
//
|
//
|
||||||
// This allows matching on them later for multi-request conditions.
|
// This allows matching on them later for multi-request conditions.
|
||||||
// Deprecated: request condition will be detected automatically (https://github.com/projectdiscovery/nuclei/issues/2393)
|
// Deprecated: request condition will be detected automatically (https://github.com/projectdiscovery/nuclei/issues/2393)
|
||||||
ReqCondition bool `yaml:"req-condition,omitempty" jsonschema:"title=preserve request history,description=Automatically assigns numbers to requests and preserves their history"`
|
ReqCondition bool `yaml:"req-condition,omitempty" json:"req-condition,omitempty" jsonschema:"title=preserve request history,description=Automatically assigns numbers to requests and preserves their history"`
|
||||||
// description: |
|
// description: |
|
||||||
// StopAtFirstMatch stops the execution of the requests and template as soon as a match is found.
|
// StopAtFirstMatch stops the execution of the requests and template as soon as a match is found.
|
||||||
StopAtFirstMatch bool `yaml:"stop-at-first-match,omitempty" jsonschema:"title=stop at first match,description=Stop the execution after a match is found"`
|
StopAtFirstMatch bool `yaml:"stop-at-first-match,omitempty" json:"stop-at-first-match,omitempty" jsonschema:"title=stop at first match,description=Stop the execution after a match is found"`
|
||||||
// description: |
|
// description: |
|
||||||
// SkipVariablesCheck skips the check for unresolved variables in request
|
// SkipVariablesCheck skips the check for unresolved variables in request
|
||||||
SkipVariablesCheck bool `yaml:"skip-variables-check,omitempty" jsonschema:"title=skip variable checks,description=Skips the check for unresolved variables in request"`
|
SkipVariablesCheck bool `yaml:"skip-variables-check,omitempty" json:"skip-variables-check,omitempty" jsonschema:"title=skip variable checks,description=Skips the check for unresolved variables in request"`
|
||||||
// description: |
|
// description: |
|
||||||
// IterateAll iterates all the values extracted from internal extractors
|
// IterateAll iterates all the values extracted from internal extractors
|
||||||
IterateAll bool `yaml:"iterate-all,omitempty" jsonschema:"title=iterate all the values,description=Iterates all the values extracted from internal extractors"`
|
IterateAll bool `yaml:"iterate-all,omitempty" json:"iterate-all,omitempty" jsonschema:"title=iterate all the values,description=Iterates all the values extracted from internal extractors"`
|
||||||
// description: |
|
// description: |
|
||||||
// DigestAuthUsername specifies the username for digest authentication
|
// DigestAuthUsername specifies the username for digest authentication
|
||||||
DigestAuthUsername string `yaml:"digest-username,omitempty" jsonschema:"title=specifies the username for digest authentication,description=Optional parameter which specifies the username for digest auth"`
|
DigestAuthUsername string `yaml:"digest-username,omitempty" json:"digest-username,omitempty" jsonschema:"title=specifies the username for digest authentication,description=Optional parameter which specifies the username for digest auth"`
|
||||||
// description: |
|
// description: |
|
||||||
// DigestAuthPassword specifies the password for digest authentication
|
// DigestAuthPassword specifies the password for digest authentication
|
||||||
DigestAuthPassword string `yaml:"digest-password,omitempty" jsonschema:"title=specifies the password for digest authentication,description=Optional parameter which specifies the password for digest auth"`
|
DigestAuthPassword string `yaml:"digest-password,omitempty" json:"digest-password,omitempty" jsonschema:"title=specifies the password for digest authentication,description=Optional parameter which specifies the password for digest auth"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options returns executer options for http request
|
// Options returns executer options for http request
|
||||||
|
|||||||
@ -116,6 +116,20 @@ func (holder *HTTPMethodTypeHolder) UnmarshalYAML(unmarshal func(interface{}) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (holder *HTTPMethodTypeHolder) UnmarshalJSON(data []byte) error {
|
||||||
|
s := strings.Trim(string(data), `"`)
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
computedType, err := toHTTPMethodTypes(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.MethodType = computedType
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (holder *HTTPMethodTypeHolder) MarshalJSON() ([]byte, error) {
|
func (holder *HTTPMethodTypeHolder) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(holder.MethodType.String())
|
return json.Marshal(holder.MethodType.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package http
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/alecthomas/jsonschema"
|
"github.com/alecthomas/jsonschema"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -77,7 +78,21 @@ func (holder *SignatureTypeHolder) UnmarshalYAML(unmarshal func(interface{}) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (holder *SignatureTypeHolder) MarshalJSON() ([]byte, error) {
|
func (holder *SignatureTypeHolder) UnmarshalJSON(data []byte) error {
|
||||||
|
s := strings.Trim(string(data), `"`)
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
computedType, err := toSignatureType(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.Value = computedType
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (holder SignatureTypeHolder) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(holder.Value.String())
|
return json.Marshal(holder.Value.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import (
|
|||||||
// Request contains a Network protocol request to be made from a template
|
// Request contains a Network protocol request to be made from a template
|
||||||
type Request struct {
|
type Request struct {
|
||||||
// ID is the optional id of the request
|
// ID is the optional id of the request
|
||||||
ID string `yaml:"id,omitempty" jsonschema:"title=id of the request,description=ID of the network request"`
|
ID string `yaml:"id,omitempty" json:"id,omitempty" jsonschema:"title=id of the request,description=ID of the network request"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Host to send network requests to.
|
// Host to send network requests to.
|
||||||
@ -27,7 +27,7 @@ type Request struct {
|
|||||||
// examples:
|
// examples:
|
||||||
// - value: |
|
// - value: |
|
||||||
// []string{"{{Hostname}}"}
|
// []string{"{{Hostname}}"}
|
||||||
Address []string `yaml:"host,omitempty" jsonschema:"title=host to send requests to,description=Host to send network requests to"`
|
Address []string `yaml:"host,omitempty" json:"host,omitempty" jsonschema:"title=host to send requests to,description=Host to send network requests to"`
|
||||||
addresses []addressKV
|
addresses []addressKV
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
@ -35,32 +35,32 @@ type Request struct {
|
|||||||
//
|
//
|
||||||
// Batteringram is inserts the same payload into all defined payload positions at once, pitchfork combines multiple payload sets and clusterbomb generates
|
// Batteringram is inserts the same payload into all defined payload positions at once, pitchfork combines multiple payload sets and clusterbomb generates
|
||||||
// permutations and combinations for all payloads.
|
// permutations and combinations for all payloads.
|
||||||
AttackType generators.AttackTypeHolder `yaml:"attack,omitempty" jsonschema:"title=attack is the payload combination,description=Attack is the type of payload combinations to perform,enum=batteringram,enum=pitchfork,enum=clusterbomb"`
|
AttackType generators.AttackTypeHolder `yaml:"attack,omitempty" json:"attack,omitempty" jsonschema:"title=attack is the payload combination,description=Attack is the type of payload combinations to perform,enum=batteringram,enum=pitchfork,enum=clusterbomb"`
|
||||||
// description: |
|
// description: |
|
||||||
// Payloads contains any payloads for the current request.
|
// Payloads contains any payloads for the current request.
|
||||||
//
|
//
|
||||||
// Payloads support both key-values combinations where a list
|
// Payloads support both key-values combinations where a list
|
||||||
// of payloads is provided, or optionally a single file can also
|
// of payloads is provided, or optionally a single file can also
|
||||||
// be provided as payload which will be read on run-time.
|
// be provided as payload which will be read on run-time.
|
||||||
Payloads map[string]interface{} `yaml:"payloads,omitempty" jsonschema:"title=payloads for the network request,description=Payloads contains any payloads for the current request"`
|
Payloads map[string]interface{} `yaml:"payloads,omitempty" json:"payloads,omitempty" jsonschema:"title=payloads for the network request,description=Payloads contains any payloads for the current request"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Inputs contains inputs for the network socket
|
// Inputs contains inputs for the network socket
|
||||||
Inputs []*Input `yaml:"inputs,omitempty" jsonschema:"title=inputs for the network request,description=Inputs contains any input/output for the current request"`
|
Inputs []*Input `yaml:"inputs,omitempty" json:"inputs,omitempty" jsonschema:"title=inputs for the network request,description=Inputs contains any input/output for the current request"`
|
||||||
// description: |
|
// description: |
|
||||||
// ReadSize is the size of response to read at the end
|
// ReadSize is the size of response to read at the end
|
||||||
//
|
//
|
||||||
// Default value for read-size is 1024.
|
// Default value for read-size is 1024.
|
||||||
// examples:
|
// examples:
|
||||||
// - value: "2048"
|
// - value: "2048"
|
||||||
ReadSize int `yaml:"read-size,omitempty" jsonschema:"title=size of network response to read,description=Size of response to read at the end. Default is 1024 bytes"`
|
ReadSize int `yaml:"read-size,omitempty" json:"read-size,omitempty" jsonschema:"title=size of network response to read,description=Size of response to read at the end. Default is 1024 bytes"`
|
||||||
// description: |
|
// description: |
|
||||||
// ReadAll determines if the data stream should be read till the end regardless of the size
|
// ReadAll determines if the data stream should be read till the end regardless of the size
|
||||||
//
|
//
|
||||||
// Default value for read-all is false.
|
// Default value for read-all is false.
|
||||||
// examples:
|
// examples:
|
||||||
// - value: false
|
// - value: false
|
||||||
ReadAll bool `yaml:"read-all,omitempty" jsonschema:"title=read all response stream,description=Read all response stream till the server stops sending"`
|
ReadAll bool `yaml:"read-all,omitempty" json:"read-all,omitempty" jsonschema:"title=read all response stream,description=Read all response stream till the server stops sending"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// SelfContained specifies if the request is self-contained.
|
// SelfContained specifies if the request is self-contained.
|
||||||
@ -105,7 +105,7 @@ type Input struct {
|
|||||||
// examples:
|
// examples:
|
||||||
// - value: "\"TEST\""
|
// - value: "\"TEST\""
|
||||||
// - value: "\"hex_decode('50494e47')\""
|
// - value: "\"hex_decode('50494e47')\""
|
||||||
Data string `yaml:"data,omitempty" jsonschema:"title=data to send as input,description=Data is the data to send as the input"`
|
Data string `yaml:"data,omitempty" json:"data,omitempty" jsonschema:"title=data to send as input,description=Data is the data to send as the input"`
|
||||||
// description: |
|
// description: |
|
||||||
// Type is the type of input specified in `data` field.
|
// Type is the type of input specified in `data` field.
|
||||||
//
|
//
|
||||||
@ -113,7 +113,7 @@ type Input struct {
|
|||||||
// values:
|
// values:
|
||||||
// - "hex"
|
// - "hex"
|
||||||
// - "text"
|
// - "text"
|
||||||
Type NetworkInputTypeHolder `yaml:"type,omitempty" jsonschema:"title=type is the type of input data,description=Type of input specified in data field,enum=hex,enum=text"`
|
Type NetworkInputTypeHolder `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"title=type is the type of input data,description=Type of input specified in data field,enum=hex,enum=text"`
|
||||||
// description: |
|
// description: |
|
||||||
// Read is the number of bytes to read from socket.
|
// Read is the number of bytes to read from socket.
|
||||||
//
|
//
|
||||||
@ -124,12 +124,12 @@ type Input struct {
|
|||||||
// The [network docs](https://nuclei.projectdiscovery.io/templating-guide/protocols/network/) highlight more on how to do this.
|
// The [network docs](https://nuclei.projectdiscovery.io/templating-guide/protocols/network/) highlight more on how to do this.
|
||||||
// examples:
|
// examples:
|
||||||
// - value: "1024"
|
// - value: "1024"
|
||||||
Read int `yaml:"read,omitempty" jsonschema:"title=bytes to read from socket,description=Number of bytes to read from socket"`
|
Read int `yaml:"read,omitempty" json:"read,omitempty" jsonschema:"title=bytes to read from socket,description=Number of bytes to read from socket"`
|
||||||
// description: |
|
// description: |
|
||||||
// Name is the optional name of the data read to provide matching on.
|
// Name is the optional name of the data read to provide matching on.
|
||||||
// examples:
|
// examples:
|
||||||
// - value: "\"prefix\""
|
// - value: "\"prefix\""
|
||||||
Name string `yaml:"name,omitempty" jsonschema:"title=optional name for data read,description=Optional name of the data read to provide matching on"`
|
Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"title=optional name for data read,description=Optional name of the data read to provide matching on"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the unique ID of the request if any.
|
// GetID returns the unique ID of the request if any.
|
||||||
|
|||||||
@ -93,6 +93,20 @@ func (holder *NetworkInputTypeHolder) UnmarshalYAML(unmarshal func(interface{})
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (holder *NetworkInputTypeHolder) UnmarshalJSON(data []byte) error {
|
||||||
|
s := strings.Trim(string(data), `"`)
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
computedType, err := toNetworkInputTypes(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.NetworkInputType = computedType
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (holder *NetworkInputTypeHolder) MarshalJSON() ([]byte, error) {
|
func (holder *NetworkInputTypeHolder) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(holder.NetworkInputType.String())
|
return json.Marshal(holder.NetworkInputType.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,7 +50,7 @@ type ExecuterOptions struct {
|
|||||||
// Options contains configuration options for the executer.
|
// Options contains configuration options for the executer.
|
||||||
Options *types.Options
|
Options *types.Options
|
||||||
// IssuesClient is a client for nuclei issue tracker reporting
|
// IssuesClient is a client for nuclei issue tracker reporting
|
||||||
IssuesClient *reporting.Client
|
IssuesClient reporting.Client
|
||||||
// Progress is a progress client for scan reporting
|
// Progress is a progress client for scan reporting
|
||||||
Progress progress.Progress
|
Progress progress.Progress
|
||||||
// RateLimiter is a rate-limiter for limiting sent number of requests.
|
// RateLimiter is a rate-limiter for limiting sent number of requests.
|
||||||
|
|||||||
@ -37,12 +37,12 @@ import (
|
|||||||
// Request is a request for the SSL protocol
|
// Request is a request for the SSL protocol
|
||||||
type Request struct {
|
type Request struct {
|
||||||
// Operators for the current request go here.
|
// Operators for the current request go here.
|
||||||
operators.Operators `yaml:",inline,omitempty"`
|
operators.Operators `yaml:",inline,omitempty" json:",inline,omitempty"`
|
||||||
CompiledOperators *operators.Operators `yaml:"-"`
|
CompiledOperators *operators.Operators `yaml:"-" json:"-"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Address contains address for the request
|
// Address contains address for the request
|
||||||
Address string `yaml:"address,omitempty" jsonschema:"title=address for the ssl request,description=Address contains address for the request"`
|
Address string `yaml:"address,omitempty" json:"address,omitempty" jsonschema:"title=address for the ssl request,description=Address contains address for the request"`
|
||||||
// description: |
|
// description: |
|
||||||
// Minimum tls version - auto if not specified.
|
// Minimum tls version - auto if not specified.
|
||||||
// values:
|
// values:
|
||||||
@ -51,7 +51,7 @@ type Request struct {
|
|||||||
// - "tls11"
|
// - "tls11"
|
||||||
// - "tls12"
|
// - "tls12"
|
||||||
// - "tls13"
|
// - "tls13"
|
||||||
MinVersion string `yaml:"min_version,omitempty" jsonschema:"title=Min. TLS version,description=Minimum tls version - automatic if not specified.,enum=sslv3,enum=tls10,enum=tls11,enum=tls12,enum=tls13"`
|
MinVersion string `yaml:"min_version,omitempty" json:"min_version,omitempty" jsonschema:"title=Min. TLS version,description=Minimum tls version - automatic if not specified.,enum=sslv3,enum=tls10,enum=tls11,enum=tls12,enum=tls13"`
|
||||||
// description: |
|
// description: |
|
||||||
// Max tls version - auto if not specified.
|
// Max tls version - auto if not specified.
|
||||||
// values:
|
// values:
|
||||||
@ -60,17 +60,17 @@ type Request struct {
|
|||||||
// - "tls11"
|
// - "tls11"
|
||||||
// - "tls12"
|
// - "tls12"
|
||||||
// - "tls13"
|
// - "tls13"
|
||||||
MaxVersion string `yaml:"max_version,omitempty" jsonschema:"title=Max. TLS version,description=Max tls version - automatic if not specified.,enum=sslv3,enum=tls10,enum=tls11,enum=tls12,enum=tls13"`
|
MaxVersion string `yaml:"max_version,omitempty" json:"max_version,omitempty" jsonschema:"title=Max. TLS version,description=Max tls version - automatic if not specified.,enum=sslv3,enum=tls10,enum=tls11,enum=tls12,enum=tls13"`
|
||||||
// description: |
|
// description: |
|
||||||
// Client Cipher Suites - auto if not specified.
|
// Client Cipher Suites - auto if not specified.
|
||||||
CiperSuites []string `yaml:"cipher_suites,omitempty"`
|
CiperSuites []string `yaml:"cipher_suites,omitempty" json:"cipher_suites,omitempty"`
|
||||||
// description: |
|
// description: |
|
||||||
// Tls Scan Mode - auto if not specified
|
// Tls Scan Mode - auto if not specified
|
||||||
// values:
|
// values:
|
||||||
// - "ctls"
|
// - "ctls"
|
||||||
// - "ztls"
|
// - "ztls"
|
||||||
// - "auto"
|
// - "auto"
|
||||||
ScanMode string `yaml:"scan_mode,omitempty" jsonschema:"title=Scan Mode,description=Scan Mode - auto if not specified.,enum=ctls,enum=ztls,enum=auto"`
|
ScanMode string `yaml:"scan_mode,omitempty" json:"scan_mode,omitempty" jsonschema:"title=Scan Mode,description=Scan Mode - auto if not specified.,enum=ctls,enum=ztls,enum=auto"`
|
||||||
|
|
||||||
// cache any variables that may be needed for operation.
|
// cache any variables that may be needed for operation.
|
||||||
dialer *fastdialer.Dialer
|
dialer *fastdialer.Dialer
|
||||||
|
|||||||
@ -38,32 +38,32 @@ import (
|
|||||||
// Request is a request for the Websocket protocol
|
// Request is a request for the Websocket protocol
|
||||||
type Request struct {
|
type Request struct {
|
||||||
// Operators for the current request go here.
|
// Operators for the current request go here.
|
||||||
operators.Operators `yaml:",inline,omitempty"`
|
operators.Operators `yaml:",inline,omitempty" json:",inline,omitempty"`
|
||||||
CompiledOperators *operators.Operators `yaml:"-"`
|
CompiledOperators *operators.Operators `yaml:"-" json:"-"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Address contains address for the request
|
// Address contains address for the request
|
||||||
Address string `yaml:"address,omitempty" jsonschema:"title=address for the websocket request,description=Address contains address for the request"`
|
Address string `yaml:"address,omitempty" json:"address,omitempty" jsonschema:"title=address for the websocket request,description=Address contains address for the request"`
|
||||||
// description: |
|
// description: |
|
||||||
// Inputs contains inputs for the websocket protocol
|
// Inputs contains inputs for the websocket protocol
|
||||||
Inputs []*Input `yaml:"inputs,omitempty" jsonschema:"title=inputs for the websocket request,description=Inputs contains any input/output for the current request"`
|
Inputs []*Input `yaml:"inputs,omitempty" json:"inputs,omitempty" jsonschema:"title=inputs for the websocket request,description=Inputs contains any input/output for the current request"`
|
||||||
// description: |
|
// description: |
|
||||||
// Headers contains headers for the request.
|
// Headers contains headers for the request.
|
||||||
Headers map[string]string `yaml:"headers,omitempty" jsonschema:"title=headers contains the request headers,description=Headers contains headers for the request"`
|
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty" jsonschema:"title=headers contains the request headers,description=Headers contains headers for the request"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Attack is the type of payload combinations to perform.
|
// Attack is the type of payload combinations to perform.
|
||||||
//
|
//
|
||||||
// Sniper is each payload once, pitchfork combines multiple payload sets and clusterbomb generates
|
// Sniper is each payload once, pitchfork combines multiple payload sets and clusterbomb generates
|
||||||
// permutations and combinations for all payloads.
|
// permutations and combinations for all payloads.
|
||||||
AttackType generators.AttackTypeHolder `yaml:"attack,omitempty" jsonschema:"title=attack is the payload combination,description=Attack is the type of payload combinations to perform,enum=sniper,enum=pitchfork,enum=clusterbomb"`
|
AttackType generators.AttackTypeHolder `yaml:"attack,omitempty" json:"attack,omitempty" jsonschema:"title=attack is the payload combination,description=Attack is the type of payload combinations to perform,enum=sniper,enum=pitchfork,enum=clusterbomb"`
|
||||||
// description: |
|
// description: |
|
||||||
// Payloads contains any payloads for the current request.
|
// Payloads contains any payloads for the current request.
|
||||||
//
|
//
|
||||||
// Payloads support both key-values combinations where a list
|
// Payloads support both key-values combinations where a list
|
||||||
// of payloads is provided, or optionally a single file can also
|
// of payloads is provided, or optionally a single file can also
|
||||||
// be provided as payload which will be read on run-time.
|
// be provided as payload which will be read on run-time.
|
||||||
Payloads map[string]interface{} `yaml:"payloads,omitempty" jsonschema:"title=payloads for the webosocket request,description=Payloads contains any payloads for the current request"`
|
Payloads map[string]interface{} `yaml:"payloads,omitempty" json:"payloads,omitempty" jsonschema:"title=payloads for the webosocket request,description=Payloads contains any payloads for the current request"`
|
||||||
|
|
||||||
generator *generators.PayloadGenerator
|
generator *generators.PayloadGenerator
|
||||||
|
|
||||||
@ -81,12 +81,12 @@ type Input struct {
|
|||||||
// examples:
|
// examples:
|
||||||
// - value: "\"TEST\""
|
// - value: "\"TEST\""
|
||||||
// - value: "\"hex_decode('50494e47')\""
|
// - value: "\"hex_decode('50494e47')\""
|
||||||
Data string `yaml:"data,omitempty" jsonschema:"title=data to send as input,description=Data is the data to send as the input"`
|
Data string `yaml:"data,omitempty" json:"data,omitempty" jsonschema:"title=data to send as input,description=Data is the data to send as the input"`
|
||||||
// description: |
|
// description: |
|
||||||
// Name is the optional name of the data read to provide matching on.
|
// Name is the optional name of the data read to provide matching on.
|
||||||
// examples:
|
// examples:
|
||||||
// - value: "\"prefix\""
|
// - value: "\"prefix\""
|
||||||
Name string `yaml:"name,omitempty" jsonschema:"title=optional name for data read,description=Optional name of the data read to provide matching on"`
|
Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"title=optional name for data read,description=Optional name of the data read to provide matching on"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@ -28,19 +28,19 @@ import (
|
|||||||
// Request is a request for the WHOIS protocol
|
// Request is a request for the WHOIS protocol
|
||||||
type Request struct {
|
type Request struct {
|
||||||
// Operators for the current request go here.
|
// Operators for the current request go here.
|
||||||
operators.Operators `yaml:",inline,omitempty"`
|
operators.Operators `yaml:",inline,omitempty" json:",inline,omitempty"`
|
||||||
CompiledOperators *operators.Operators `yaml:"-"`
|
CompiledOperators *operators.Operators `yaml:"-" json:"-"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Query contains query for the request
|
// Query contains query for the request
|
||||||
Query string `yaml:"query,omitempty" jsonschema:"title=query for the WHOIS request,description=Query contains query for the request"`
|
Query string `yaml:"query,omitempty" json:"query,omitempty" jsonschema:"title=query for the WHOIS request,description=Query contains query for the request"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Optional WHOIS server URL.
|
// Optional WHOIS server URL.
|
||||||
//
|
//
|
||||||
// If present, specifies the WHOIS server to execute the Request on.
|
// If present, specifies the WHOIS server to execute the Request on.
|
||||||
// Otherwise, nil enables bootstrapping
|
// Otherwise, nil enables bootstrapping
|
||||||
Server string `yaml:"server,omitempty" jsonschema:"title=server url to execute the WHOIS request on,description=Server contains the server url to execute the WHOIS request on"`
|
Server string `yaml:"server,omitempty" json:"server,omitempty" jsonschema:"title=server url to execute the WHOIS request on,description=Server contains the server url to execute the WHOIS request on"`
|
||||||
// cache any variables that may be needed for operation.
|
// cache any variables that may be needed for operation.
|
||||||
client *rdap.Client
|
client *rdap.Client
|
||||||
options *protocols.ExecuterOptions
|
options *protocols.ExecuterOptions
|
||||||
|
|||||||
15
v2/pkg/reporting/client.go
Normal file
15
v2/pkg/reporting/client.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package reporting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client is a client for nuclei issue tracking module
|
||||||
|
type Client interface {
|
||||||
|
RegisterTracker(tracker Tracker)
|
||||||
|
RegisterExporter(exporter Exporter)
|
||||||
|
Close()
|
||||||
|
Clear()
|
||||||
|
CreateIssue(event *output.ResultEvent) error
|
||||||
|
GetReportingOptions() *Options
|
||||||
|
}
|
||||||
@ -51,6 +51,18 @@ func New(dbPath string) (*Storage, error) {
|
|||||||
return storage, nil
|
return storage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Storage) Clear() {
|
||||||
|
var keys [][]byte
|
||||||
|
iter := s.storage.NewIterator(nil, nil)
|
||||||
|
for iter.Next() {
|
||||||
|
keys = append(keys, iter.Key())
|
||||||
|
}
|
||||||
|
iter.Release()
|
||||||
|
for _, key := range keys {
|
||||||
|
_ = s.storage.Delete(key, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Close closes the storage for further operations
|
// Close closes the storage for further operations
|
||||||
func (s *Storage) Close() {
|
func (s *Storage) Close() {
|
||||||
s.storage.Close()
|
s.storage.Close()
|
||||||
|
|||||||
@ -18,7 +18,7 @@ func TestToMarkdownTableString(t *testing.T) {
|
|||||||
Description: "Test description",
|
Description: "Test description",
|
||||||
SeverityHolder: severity.Holder{Severity: severity.High},
|
SeverityHolder: severity.Holder{Severity: severity.High},
|
||||||
Tags: stringslice.StringSlice{Value: []string{"cve", "misc"}},
|
Tags: stringslice.StringSlice{Value: []string{"cve", "misc"}},
|
||||||
Reference: stringslice.StringSlice{Value: "reference1"},
|
Reference: stringslice.NewRaw("reference1"),
|
||||||
Metadata: map[string]interface{}{
|
Metadata: map[string]interface{}{
|
||||||
"customDynamicKey1": "customDynamicValue1",
|
"customDynamicKey1": "customDynamicValue1",
|
||||||
"customDynamicKey2": "customDynamicValue2",
|
"customDynamicKey2": "customDynamicValue2",
|
||||||
|
|||||||
36
v2/pkg/reporting/options.go
Normal file
36
v2/pkg/reporting/options.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package reporting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/es"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/markdown"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/sarif"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/splunk"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/github"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/gitlab"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/jira"
|
||||||
|
"github.com/projectdiscovery/retryablehttp-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options is a configuration file for nuclei reporting module
|
||||||
|
type Options struct {
|
||||||
|
// AllowList contains a list of allowed events for reporting module
|
||||||
|
AllowList *Filter `yaml:"allow-list"`
|
||||||
|
// DenyList contains a list of denied events for reporting module
|
||||||
|
DenyList *Filter `yaml:"deny-list"`
|
||||||
|
// GitHub contains configuration options for GitHub Issue Tracker
|
||||||
|
GitHub *github.Options `yaml:"github"`
|
||||||
|
// GitLab contains configuration options for GitLab Issue Tracker
|
||||||
|
GitLab *gitlab.Options `yaml:"gitlab"`
|
||||||
|
// Jira contains configuration options for Jira Issue Tracker
|
||||||
|
Jira *jira.Options `yaml:"jira"`
|
||||||
|
// MarkdownExporter contains configuration options for Markdown Exporter Module
|
||||||
|
MarkdownExporter *markdown.Options `yaml:"markdown"`
|
||||||
|
// SarifExporter contains configuration options for Sarif Exporter Module
|
||||||
|
SarifExporter *sarif.Options `yaml:"sarif"`
|
||||||
|
// ElasticsearchExporter contains configuration options for Elasticsearch Exporter Module
|
||||||
|
ElasticsearchExporter *es.Options `yaml:"elasticsearch"`
|
||||||
|
// SplunkExporter contains configuration options for splunkhec Exporter Module
|
||||||
|
SplunkExporter *splunk.Options `yaml:"splunkhec"`
|
||||||
|
|
||||||
|
HttpClient *retryablehttp.Client `yaml:"-"`
|
||||||
|
}
|
||||||
@ -3,12 +3,12 @@ package reporting
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"go.uber.org/multierr"
|
"go.uber.org/multierr"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity"
|
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
|
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
|
||||||
@ -21,34 +21,11 @@ import (
|
|||||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/github"
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/github"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/gitlab"
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/gitlab"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/jira"
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/trackers/jira"
|
||||||
"github.com/projectdiscovery/retryablehttp-go"
|
errorutil "github.com/projectdiscovery/utils/errors"
|
||||||
fileutil "github.com/projectdiscovery/utils/file"
|
fileutil "github.com/projectdiscovery/utils/file"
|
||||||
|
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options is a configuration file for nuclei reporting module
|
|
||||||
type Options struct {
|
|
||||||
// AllowList contains a list of allowed events for reporting module
|
|
||||||
AllowList *Filter `yaml:"allow-list"`
|
|
||||||
// DenyList contains a list of denied events for reporting module
|
|
||||||
DenyList *Filter `yaml:"deny-list"`
|
|
||||||
// GitHub contains configuration options for GitHub Issue Tracker
|
|
||||||
GitHub *github.Options `yaml:"github"`
|
|
||||||
// GitLab contains configuration options for GitLab Issue Tracker
|
|
||||||
GitLab *gitlab.Options `yaml:"gitlab"`
|
|
||||||
// Jira contains configuration options for Jira Issue Tracker
|
|
||||||
Jira *jira.Options `yaml:"jira"`
|
|
||||||
// MarkdownExporter contains configuration options for Markdown Exporter Module
|
|
||||||
MarkdownExporter *markdown.Options `yaml:"markdown"`
|
|
||||||
// SarifExporter contains configuration options for Sarif Exporter Module
|
|
||||||
SarifExporter *sarif.Options `yaml:"sarif"`
|
|
||||||
// ElasticsearchExporter contains configuration options for Elasticsearch Exporter Module
|
|
||||||
ElasticsearchExporter *es.Options `yaml:"elasticsearch"`
|
|
||||||
// SplunkExporter contains configuration options for splunkhec Exporter Module
|
|
||||||
SplunkExporter *splunk.Options `yaml:"splunkhec"`
|
|
||||||
|
|
||||||
HttpClient *retryablehttp.Client `yaml:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter filters the received event and decides whether to perform
|
// Filter filters the received event and decides whether to perform
|
||||||
// reporting for it or not.
|
// reporting for it or not.
|
||||||
type Filter struct {
|
type Filter struct {
|
||||||
@ -56,9 +33,9 @@ type Filter struct {
|
|||||||
Tags stringslice.StringSlice `yaml:"tags"`
|
Tags stringslice.StringSlice `yaml:"tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
var (
|
||||||
reportingClientCreationErrorMessage = "could not create reporting client"
|
ErrReportingClientCreation = errors.New("could not create reporting client")
|
||||||
exportClientCreationErrorMessage = "could not create exporting client"
|
ErrExportClientCreation = errors.New("could not create exporting client")
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetMatch returns true if a filter matches result event
|
// GetMatch returns true if a filter matches result event
|
||||||
@ -73,8 +50,8 @@ func isTagMatch(event *output.ResultEvent, filter *Filter) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tags := event.Info.Tags.ToSlice()
|
tags := event.Info.Tags.ToSlice()
|
||||||
for _, tag := range filterTags.ToSlice() {
|
for _, filterTag := range filterTags.ToSlice() {
|
||||||
if stringSliceContains(tags, tag) {
|
if sliceutil.Contains(tags, filterTag) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,13 +66,7 @@ func isSeverityMatch(event *output.ResultEvent, filter *Filter) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, current := range filter.Severities {
|
return sliceutil.Contains(filter.Severities, resultEventSeverity)
|
||||||
if current == resultEventSeverity {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tracker is an interface implemented by an issue tracker
|
// Tracker is an interface implemented by an issue tracker
|
||||||
@ -112,8 +83,8 @@ type Exporter interface {
|
|||||||
Export(event *output.ResultEvent) error
|
Export(event *output.ResultEvent) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client is a client for nuclei issue tracking module
|
// ReportingClient is a client for nuclei issue tracking module
|
||||||
type Client struct {
|
type ReportingClient struct {
|
||||||
trackers []Tracker
|
trackers []Tracker
|
||||||
exporters []Exporter
|
exporters []Exporter
|
||||||
options *Options
|
options *Options
|
||||||
@ -121,14 +92,14 @@ type Client struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new nuclei issue tracker reporting client
|
// New creates a new nuclei issue tracker reporting client
|
||||||
func New(options *Options, db string) (*Client, error) {
|
func New(options *Options, db string) (Client, error) {
|
||||||
client := &Client{options: options}
|
client := &ReportingClient{options: options}
|
||||||
|
|
||||||
if options.GitHub != nil {
|
if options.GitHub != nil {
|
||||||
options.GitHub.HttpClient = options.HttpClient
|
options.GitHub.HttpClient = options.HttpClient
|
||||||
tracker, err := github.New(options.GitHub)
|
tracker, err := github.New(options.GitHub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, reportingClientCreationErrorMessage)
|
return nil, errorutil.NewWithErr(err).Wrap(ErrReportingClientCreation)
|
||||||
}
|
}
|
||||||
client.trackers = append(client.trackers, tracker)
|
client.trackers = append(client.trackers, tracker)
|
||||||
}
|
}
|
||||||
@ -136,7 +107,7 @@ func New(options *Options, db string) (*Client, error) {
|
|||||||
options.GitLab.HttpClient = options.HttpClient
|
options.GitLab.HttpClient = options.HttpClient
|
||||||
tracker, err := gitlab.New(options.GitLab)
|
tracker, err := gitlab.New(options.GitLab)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, reportingClientCreationErrorMessage)
|
return nil, errorutil.NewWithErr(err).Wrap(ErrReportingClientCreation)
|
||||||
}
|
}
|
||||||
client.trackers = append(client.trackers, tracker)
|
client.trackers = append(client.trackers, tracker)
|
||||||
}
|
}
|
||||||
@ -144,21 +115,21 @@ func New(options *Options, db string) (*Client, error) {
|
|||||||
options.Jira.HttpClient = options.HttpClient
|
options.Jira.HttpClient = options.HttpClient
|
||||||
tracker, err := jira.New(options.Jira)
|
tracker, err := jira.New(options.Jira)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, reportingClientCreationErrorMessage)
|
return nil, errorutil.NewWithErr(err).Wrap(ErrReportingClientCreation)
|
||||||
}
|
}
|
||||||
client.trackers = append(client.trackers, tracker)
|
client.trackers = append(client.trackers, tracker)
|
||||||
}
|
}
|
||||||
if options.MarkdownExporter != nil {
|
if options.MarkdownExporter != nil {
|
||||||
exporter, err := markdown.New(options.MarkdownExporter)
|
exporter, err := markdown.New(options.MarkdownExporter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, exportClientCreationErrorMessage)
|
return nil, errorutil.NewWithErr(err).Wrap(ErrExportClientCreation)
|
||||||
}
|
}
|
||||||
client.exporters = append(client.exporters, exporter)
|
client.exporters = append(client.exporters, exporter)
|
||||||
}
|
}
|
||||||
if options.SarifExporter != nil {
|
if options.SarifExporter != nil {
|
||||||
exporter, err := sarif.New(options.SarifExporter)
|
exporter, err := sarif.New(options.SarifExporter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, exportClientCreationErrorMessage)
|
return nil, errorutil.NewWithErr(err).Wrap(ErrExportClientCreation)
|
||||||
}
|
}
|
||||||
client.exporters = append(client.exporters, exporter)
|
client.exporters = append(client.exporters, exporter)
|
||||||
}
|
}
|
||||||
@ -166,7 +137,7 @@ func New(options *Options, db string) (*Client, error) {
|
|||||||
options.ElasticsearchExporter.HttpClient = options.HttpClient
|
options.ElasticsearchExporter.HttpClient = options.HttpClient
|
||||||
exporter, err := es.New(options.ElasticsearchExporter)
|
exporter, err := es.New(options.ElasticsearchExporter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, exportClientCreationErrorMessage)
|
return nil, errorutil.NewWithErr(err).Wrap(ErrExportClientCreation)
|
||||||
}
|
}
|
||||||
client.exporters = append(client.exporters, exporter)
|
client.exporters = append(client.exporters, exporter)
|
||||||
}
|
}
|
||||||
@ -174,7 +145,7 @@ func New(options *Options, db string) (*Client, error) {
|
|||||||
options.SplunkExporter.HttpClient = options.HttpClient
|
options.SplunkExporter.HttpClient = options.HttpClient
|
||||||
exporter, err := splunk.New(options.SplunkExporter)
|
exporter, err := splunk.New(options.SplunkExporter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, exportClientCreationErrorMessage)
|
return nil, errorutil.NewWithErr(err).Wrap(ErrExportClientCreation)
|
||||||
}
|
}
|
||||||
client.exporters = append(client.exporters, exporter)
|
client.exporters = append(client.exporters, exporter)
|
||||||
}
|
}
|
||||||
@ -191,7 +162,7 @@ func New(options *Options, db string) (*Client, error) {
|
|||||||
func CreateConfigIfNotExists() error {
|
func CreateConfigIfNotExists() error {
|
||||||
config, err := config.GetConfigDir()
|
config, err := config.GetConfigDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not get config directory")
|
return errorutil.NewWithErr(err).Msgf("could not get config directory")
|
||||||
}
|
}
|
||||||
reportingConfig := filepath.Join(config, "report-config.yaml")
|
reportingConfig := filepath.Join(config, "report-config.yaml")
|
||||||
|
|
||||||
@ -213,7 +184,7 @@ func CreateConfigIfNotExists() error {
|
|||||||
}
|
}
|
||||||
reportingFile, err := os.Create(reportingConfig)
|
reportingFile, err := os.Create(reportingConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not create config file")
|
return errorutil.NewWithErr(err).Msgf("could not create config file")
|
||||||
}
|
}
|
||||||
defer reportingFile.Close()
|
defer reportingFile.Close()
|
||||||
|
|
||||||
@ -222,17 +193,17 @@ func CreateConfigIfNotExists() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RegisterTracker registers a custom tracker to the reporter
|
// RegisterTracker registers a custom tracker to the reporter
|
||||||
func (c *Client) RegisterTracker(tracker Tracker) {
|
func (c *ReportingClient) RegisterTracker(tracker Tracker) {
|
||||||
c.trackers = append(c.trackers, tracker)
|
c.trackers = append(c.trackers, tracker)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterExporter registers a custom exporter to the reporter
|
// RegisterExporter registers a custom exporter to the reporter
|
||||||
func (c *Client) RegisterExporter(exporter Exporter) {
|
func (c *ReportingClient) RegisterExporter(exporter Exporter) {
|
||||||
c.exporters = append(c.exporters, exporter)
|
c.exporters = append(c.exporters, exporter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the issue tracker reporting client
|
// Close closes the issue tracker reporting client
|
||||||
func (c *Client) Close() {
|
func (c *ReportingClient) Close() {
|
||||||
c.dedupe.Close()
|
c.dedupe.Close()
|
||||||
for _, exporter := range c.exporters {
|
for _, exporter := range c.exporters {
|
||||||
exporter.Close()
|
exporter.Close()
|
||||||
@ -240,7 +211,7 @@ func (c *Client) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateIssue creates an issue in the tracker
|
// CreateIssue creates an issue in the tracker
|
||||||
func (c *Client) CreateIssue(event *output.ResultEvent) error {
|
func (c *ReportingClient) CreateIssue(event *output.ResultEvent) error {
|
||||||
if c.options.AllowList != nil && !c.options.AllowList.GetMatch(event) {
|
if c.options.AllowList != nil && !c.options.AllowList.GetMatch(event) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -264,15 +235,10 @@ func (c *Client) CreateIssue(event *output.ResultEvent) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringSliceContains(slice []string, item string) bool {
|
func (c *ReportingClient) GetReportingOptions() *Options {
|
||||||
for _, i := range slice {
|
|
||||||
if strings.EqualFold(i, item) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) GetReportingOptions() *Options {
|
|
||||||
return c.options
|
return c.options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ReportingClient) Clear() {
|
||||||
|
c.dedupe.Clear()
|
||||||
|
}
|
||||||
|
|||||||
7
v2/pkg/templates/signer/default.go
Normal file
7
v2/pkg/templates/signer/default.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package signer
|
||||||
|
|
||||||
|
var DefaultVerifier *Signer
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
DefaultVerifier, _ = NewVerifier(&Options{PublicKeyData: ecdsaPublicKey, Algorithm: ECDSA})
|
||||||
|
}
|
||||||
8
v2/pkg/templates/signer/ecdsa_public_key.go
Normal file
8
v2/pkg/templates/signer/ecdsa_public_key.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package signer
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed ecdsa_public_key.pem
|
||||||
|
var ecdsaPublicKey []byte
|
||||||
4
v2/pkg/templates/signer/ecdsa_public_key.pem
Normal file
4
v2/pkg/templates/signer/ecdsa_public_key.pem
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
34
v2/pkg/templates/signer/options.go
Normal file
34
v2/pkg/templates/signer/options.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package signer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AlgorithmType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
RSA AlgorithmType = iota
|
||||||
|
ECDSA
|
||||||
|
)
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
PrivateKeyName string
|
||||||
|
PrivateKeyData []byte
|
||||||
|
PassphraseName string
|
||||||
|
PassphraseData []byte
|
||||||
|
PublicKeyName string
|
||||||
|
PublicKeyData []byte
|
||||||
|
Algorithm AlgorithmType
|
||||||
|
}
|
||||||
|
|
||||||
|
type EcdsaSignature struct {
|
||||||
|
R *big.Int
|
||||||
|
S *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ReDigest = regexp.MustCompile(`(?m)^#\sdigest:\s.+$`)
|
||||||
|
ErrUnknownAlgorithm = errors.New("unknown algorithm")
|
||||||
|
)
|
||||||
234
v2/pkg/templates/signer/signer.go
Normal file
234
v2/pkg/templates/signer/signer.go
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
package signer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/gob"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
fileutil "github.com/projectdiscovery/utils/file"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Signer struct {
|
||||||
|
options *Options
|
||||||
|
sshSigner ssh.Signer
|
||||||
|
sshVerifier ssh.PublicKey
|
||||||
|
ecdsaSigner *ecdsa.PrivateKey
|
||||||
|
ecdsaVerifier *ecdsa.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(options *Options) (*Signer, error) {
|
||||||
|
var (
|
||||||
|
privateKeyData, passphraseData, publicKeyData []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if options.PrivateKeyName != "" {
|
||||||
|
privateKeyData, err = readKeyFromFileOrEnv(options.PrivateKeyName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
privateKeyData = options.PrivateKeyData
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.PassphraseName != "" {
|
||||||
|
passphraseData = readKeyFromFileOrEnvWithDefault(options.PassphraseName, []byte{})
|
||||||
|
} else {
|
||||||
|
passphraseData = options.PassphraseData
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.PublicKeyName != "" {
|
||||||
|
publicKeyData, err = readKeyFromFileOrEnv(options.PublicKeyName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
publicKeyData = options.PublicKeyData
|
||||||
|
}
|
||||||
|
|
||||||
|
signer := &Signer{options: options}
|
||||||
|
|
||||||
|
switch signer.options.Algorithm {
|
||||||
|
case RSA:
|
||||||
|
signer.sshSigner, signer.sshVerifier, err = parseRsa(privateKeyData, publicKeyData, passphraseData)
|
||||||
|
case ECDSA:
|
||||||
|
signer.ecdsaSigner, signer.ecdsaVerifier, err = parseECDSA(privateKeyData, publicKeyData)
|
||||||
|
default:
|
||||||
|
return nil, ErrUnknownAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVerifier(options *Options) (*Signer, error) {
|
||||||
|
var (
|
||||||
|
publicKeyData []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if options.PublicKeyName != "" {
|
||||||
|
publicKeyData, err = readKeyFromFileOrEnv(options.PrivateKeyName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
publicKeyData = options.PublicKeyData
|
||||||
|
}
|
||||||
|
|
||||||
|
signer := &Signer{options: options}
|
||||||
|
|
||||||
|
switch signer.options.Algorithm {
|
||||||
|
case RSA:
|
||||||
|
signer.sshVerifier, err = parseRsaPublicKey(publicKeyData)
|
||||||
|
case ECDSA:
|
||||||
|
signer.ecdsaVerifier, err = parseECDSAPublicKey(publicKeyData)
|
||||||
|
default:
|
||||||
|
return nil, ErrUnknownAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Signer) Sign(data []byte) ([]byte, error) {
|
||||||
|
dataHash := sha256.Sum256(data)
|
||||||
|
switch s.options.Algorithm {
|
||||||
|
case RSA:
|
||||||
|
sshSignature, err := s.sshSigner.Sign(rand.Reader, dataHash[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var signatureData bytes.Buffer
|
||||||
|
if err := gob.NewEncoder(&signatureData).Encode(sshSignature); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return signatureData.Bytes(), nil
|
||||||
|
case ECDSA:
|
||||||
|
r, s, err := ecdsa.Sign(rand.Reader, s.ecdsaSigner, dataHash[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ecdsaSignature := &EcdsaSignature{R: r, S: s}
|
||||||
|
var signatureData bytes.Buffer
|
||||||
|
if err := gob.NewEncoder(&signatureData).Encode(ecdsaSignature); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return signatureData.Bytes(), nil
|
||||||
|
default:
|
||||||
|
return nil, ErrUnknownAlgorithm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Signer) Verify(data, signatureData []byte) (bool, error) {
|
||||||
|
dataHash := sha256.Sum256(data)
|
||||||
|
switch s.options.Algorithm {
|
||||||
|
case RSA:
|
||||||
|
signature := &ssh.Signature{}
|
||||||
|
if err := gob.NewDecoder(bytes.NewReader(signatureData)).Decode(&signature); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if err := s.sshVerifier.Verify(dataHash[:], signature); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
case ECDSA:
|
||||||
|
signature := &EcdsaSignature{}
|
||||||
|
if err := gob.NewDecoder(bytes.NewReader(signatureData)).Decode(&signature); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return ecdsa.Verify(s.ecdsaVerifier, dataHash[:], signature.R, signature.S), nil
|
||||||
|
default:
|
||||||
|
return false, ErrUnknownAlgorithm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRsa(privateKeyData, passphraseData, publicKeyData []byte) (ssh.Signer, ssh.PublicKey, error) {
|
||||||
|
privateKey, err := parseRsaPrivateKey(privateKeyData, passphraseData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKey, err := parseRsaPublicKey(publicKeyData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return privateKey, publicKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRsaPrivateKey(privateKeyData, passphraseData []byte) (ssh.Signer, error) {
|
||||||
|
if len(passphraseData) > 0 {
|
||||||
|
return ssh.ParsePrivateKeyWithPassphrase(privateKeyData, passphraseData)
|
||||||
|
}
|
||||||
|
return ssh.ParsePrivateKey(privateKeyData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRsaPublicKey(publicKeyData []byte) (ssh.PublicKey, error) {
|
||||||
|
publicKey, _, _, _, err := ssh.ParseAuthorizedKey(publicKeyData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return publicKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseECDSA(privateKeyData, publicKeyData []byte) (*ecdsa.PrivateKey, *ecdsa.PublicKey, error) {
|
||||||
|
privateKey, err := parseECDSAPrivateKey(privateKeyData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
publicKey, err := parseECDSAPublicKey(publicKeyData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return privateKey, publicKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseECDSAPrivateKey(privateKeyData []byte) (*ecdsa.PrivateKey, error) {
|
||||||
|
blockPriv, _ := pem.Decode(privateKeyData)
|
||||||
|
return x509.ParseECPrivateKey(blockPriv.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseECDSAPublicKey(publicKeyData []byte) (*ecdsa.PublicKey, error) {
|
||||||
|
blockPub, _ := pem.Decode(publicKeyData)
|
||||||
|
genericPublicKey, err := x509.ParsePKIXPublicKey(blockPub.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if publicKey, ok := genericPublicKey.(*ecdsa.PublicKey); ok {
|
||||||
|
return publicKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("couldn't parse ecdsa public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
func readKeyFromFileOrEnvWithDefault(keypath string, defaultValue []byte) []byte {
|
||||||
|
keyValue, err := readKeyFromFileOrEnv(keypath)
|
||||||
|
if err != nil {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return keyValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func readKeyFromFileOrEnv(keypath string) ([]byte, error) {
|
||||||
|
if fileutil.FileExists(keypath) {
|
||||||
|
return os.ReadFile(keypath)
|
||||||
|
}
|
||||||
|
if keydata := os.Getenv(keypath); keydata != "" {
|
||||||
|
return []byte(keydata), nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Private key not found in file or environment variable: %s", keypath)
|
||||||
|
}
|
||||||
50
v2/pkg/templates/signer/util.go
Normal file
50
v2/pkg/templates/signer/util.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package signer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SignaturePattern = "# digest: "
|
||||||
|
SignatureFmt = SignaturePattern + "%x"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RemoveSignatureFromData(data []byte) []byte {
|
||||||
|
return bytes.Trim(ReDigest.ReplaceAll(data, []byte("")), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sign(sign *Signer, data []byte) (string, error) {
|
||||||
|
if sign == nil {
|
||||||
|
return "", errors.New("invalid nil signer")
|
||||||
|
}
|
||||||
|
cleanedData := RemoveSignatureFromData(data)
|
||||||
|
signatureData, err := sign.Sign(cleanedData)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(SignatureFmt, signatureData), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Verify(sign *Signer, data []byte) (bool, error) {
|
||||||
|
if sign == nil {
|
||||||
|
return false, errors.New("invalid nil verifier")
|
||||||
|
}
|
||||||
|
digestData := ReDigest.Find(data)
|
||||||
|
if len(digestData) == 0 {
|
||||||
|
return false, errors.New("digest not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
digestData = bytes.TrimSpace(bytes.TrimPrefix(digestData, []byte(SignaturePattern)))
|
||||||
|
digest, err := hex.DecodeString(string(digestData))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanedData := RemoveSignatureFromData(data)
|
||||||
|
|
||||||
|
return sign.Verify(cleanedData, digest)
|
||||||
|
}
|
||||||
@ -42,12 +42,12 @@ type Template struct {
|
|||||||
// examples:
|
// examples:
|
||||||
// - name: ID Example
|
// - name: ID Example
|
||||||
// value: "\"CVE-2021-19520\""
|
// value: "\"CVE-2021-19520\""
|
||||||
ID string `yaml:"id" jsonschema:"title=id of the template,description=The Unique ID for the template,example=cve-2021-19520,pattern=^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$"`
|
ID string `yaml:"id" json:"id" jsonschema:"title=id of the template,description=The Unique ID for the template,example=cve-2021-19520,pattern=^([a-zA-Z0-9]+[-_])*[a-zA-Z0-9]+$"`
|
||||||
// description: |
|
// description: |
|
||||||
// Info contains metadata information about the template.
|
// Info contains metadata information about the template.
|
||||||
// examples:
|
// examples:
|
||||||
// - value: exampleInfoStructure
|
// - value: exampleInfoStructure
|
||||||
Info model.Info `yaml:"info" jsonschema:"title=info for the template,description=Info contains metadata for the template"`
|
Info model.Info `yaml:"info" json:"info" jsonschema:"title=info for the template,description=Info contains metadata for the template"`
|
||||||
// description: |
|
// description: |
|
||||||
// Requests contains the http request to make in the template.
|
// Requests contains the http request to make in the template.
|
||||||
// examples:
|
// examples:
|
||||||
@ -88,20 +88,20 @@ type Template struct {
|
|||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Self Contained marks Requests for the template as self-contained
|
// Self Contained marks Requests for the template as self-contained
|
||||||
SelfContained bool `yaml:"self-contained,omitempty" jsonschema:"title=mark requests as self-contained,description=Mark Requests for the template as self-contained"`
|
SelfContained bool `yaml:"self-contained,omitempty" json:"self-contained,omitempty" jsonschema:"title=mark requests as self-contained,description=Mark Requests for the template as self-contained"`
|
||||||
// description: |
|
// description: |
|
||||||
// Stop execution once first match is found
|
// Stop execution once first match is found
|
||||||
StopAtFirstMatch bool `yaml:"stop-at-first-match,omitempty" jsonschema:"title=stop at first match,description=Stop at first match for the template"`
|
StopAtFirstMatch bool `yaml:"stop-at-first-match,omitempty" json:"stop-at-first-match,omitempty" jsonschema:"title=stop at first match,description=Stop at first match for the template"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Signature is the request signature method
|
// Signature is the request signature method
|
||||||
// values:
|
// values:
|
||||||
// - "AWS"
|
// - "AWS"
|
||||||
Signature http.SignatureTypeHolder `yaml:"signature,omitempty" jsonschema:"title=signature is the http request signature method,description=Signature is the HTTP Request signature Method,enum=AWS"`
|
Signature http.SignatureTypeHolder `yaml:"signature,omitempty" json:"signature,omitempty" jsonschema:"title=signature is the http request signature method,description=Signature is the HTTP Request signature Method,enum=AWS"`
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Variables contains any variables for the current request.
|
// Variables contains any variables for the current request.
|
||||||
Variables variables.Variable `yaml:"variables,omitempty" jsonschema:"title=variables for the http request,description=Variables contains any variables for the current request"`
|
Variables variables.Variable `yaml:"variables,omitempty" json:"variables,omitempty" jsonschema:"title=variables for the http request,description=Variables contains any variables for the current request"`
|
||||||
|
|
||||||
// TotalRequests is the total number of requests for the template.
|
// TotalRequests is the total number of requests for the template.
|
||||||
TotalRequests int `yaml:"-" json:"-"`
|
TotalRequests int `yaml:"-" json:"-"`
|
||||||
@ -109,6 +109,9 @@ type Template struct {
|
|||||||
Executer protocols.Executer `yaml:"-" json:"-"`
|
Executer protocols.Executer `yaml:"-" json:"-"`
|
||||||
|
|
||||||
Path string `yaml:"-" json:"-"`
|
Path string `yaml:"-" json:"-"`
|
||||||
|
|
||||||
|
// Verified defines if the template signature is digitally verified
|
||||||
|
Verified bool `yaml:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TemplateProtocols is a list of accepted template protocols
|
// TemplateProtocols is a list of accepted template protocols
|
||||||
|
|||||||
@ -20,7 +20,7 @@ var (
|
|||||||
Name: "Argument Injection in Ruby Dragonfly",
|
Name: "Argument Injection in Ruby Dragonfly",
|
||||||
Authors: stringslice.StringSlice{Value: "0xspara"},
|
Authors: stringslice.StringSlice{Value: "0xspara"},
|
||||||
SeverityHolder: severity.Holder{Severity: severity.High},
|
SeverityHolder: severity.Holder{Severity: severity.High},
|
||||||
Reference: stringslice.StringSlice{Value: "https://zxsecurity.co.nz/research/argunment-injection-ruby-dragonfly/"},
|
Reference: stringslice.NewRaw("https://zxsecurity.co.nz/research/argunment-injection-ruby-dragonfly/"),
|
||||||
Tags: stringslice.StringSlice{Value: "cve,cve2021,rce,ruby"},
|
Tags: stringslice.StringSlice{Value: "cve,cve2021,rce,ruby"},
|
||||||
}
|
}
|
||||||
exampleNormalHTTPRequest = &http.Request{
|
exampleNormalHTTPRequest = &http.Request{
|
||||||
|
|||||||
@ -1 +1,36 @@
|
|||||||
package templates
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTemplateStruct(t *testing.T) {
|
||||||
|
templatePath := "./tests/match-1.yaml"
|
||||||
|
bin, err := os.ReadFile(templatePath)
|
||||||
|
require.Nil(t, err, "failed to load example template")
|
||||||
|
var yamlTemplate Template
|
||||||
|
err = yaml.Unmarshal(bin, &yamlTemplate)
|
||||||
|
require.Nil(t, err, "failed to unmarshal yaml template")
|
||||||
|
jsonBin, err := json.Marshal(yamlTemplate)
|
||||||
|
require.Nil(t, err, "failed to marshal template to json")
|
||||||
|
var jsonTemplate Template
|
||||||
|
err = json.Unmarshal(jsonBin, &jsonTemplate)
|
||||||
|
require.Nil(t, err, "failed to unmarshal json template")
|
||||||
|
|
||||||
|
templatePath = "./tests/json-template.json"
|
||||||
|
bin, err = os.ReadFile(templatePath)
|
||||||
|
require.Nil(t, err, "failed to load example template")
|
||||||
|
jsonTemplate = Template{}
|
||||||
|
err = json.Unmarshal(bin, &jsonTemplate)
|
||||||
|
require.Nil(t, err, "failed to unmarshal json template")
|
||||||
|
yamlBin, err := yaml.Marshal(jsonTemplate)
|
||||||
|
require.Nil(t, err, "failed to marshal template to yaml")
|
||||||
|
yamlTemplate = Template{}
|
||||||
|
err = yaml.Unmarshal(yamlBin, &yamlTemplate)
|
||||||
|
require.Nil(t, err, "failed to unmarshal yaml template")
|
||||||
|
}
|
||||||
|
|||||||
27
v2/pkg/templates/tests/json-template.json
Normal file
27
v2/pkg/templates/tests/json-template.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"id": "go-integration-test",
|
||||||
|
"info": {
|
||||||
|
"name": "Basic Go Integration Test",
|
||||||
|
"author": "pdteam",
|
||||||
|
"severity": "info"
|
||||||
|
},
|
||||||
|
"requests": [
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"path": [
|
||||||
|
"{{BaseURL}}"
|
||||||
|
],
|
||||||
|
"headers": {
|
||||||
|
"test": "nuclei"
|
||||||
|
},
|
||||||
|
"matchers": [
|
||||||
|
{
|
||||||
|
"type": "word",
|
||||||
|
"words": [
|
||||||
|
"This is test headers matcher text"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/alecthomas/jsonschema"
|
"github.com/alecthomas/jsonschema"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/projectdiscovery/goflags"
|
"github.com/projectdiscovery/goflags"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
|
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/stringslice"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@ -44,6 +45,18 @@ func (insertionOrderedStringMap *InsertionOrderedStringMap) UnmarshalYAML(unmars
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (insertionOrderedStringMap *InsertionOrderedStringMap) UnmarshalJSON(data []byte) error {
|
||||||
|
var dataMap map[string]interface{}
|
||||||
|
if err := json.Unmarshal(data, &dataMap); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
insertionOrderedStringMap.values = make(map[string]interface{})
|
||||||
|
for k, v := range dataMap {
|
||||||
|
insertionOrderedStringMap.Set(k, toString(v))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// toString converts an interface to string in a quick way
|
// toString converts an interface to string in a quick way
|
||||||
func toString(data interface{}) string {
|
func toString(data interface{}) string {
|
||||||
switch s := data.(type) {
|
switch s := data.(type) {
|
||||||
|
|||||||
@ -13,9 +13,9 @@ import (
|
|||||||
type Workflow struct {
|
type Workflow struct {
|
||||||
// description: |
|
// description: |
|
||||||
// Workflows is a list of workflows to execute for a template.
|
// Workflows is a list of workflows to execute for a template.
|
||||||
Workflows []*WorkflowTemplate `yaml:"workflows,omitempty" jsonschema:"title=list of workflows to execute,description=List of workflows to execute for template"`
|
Workflows []*WorkflowTemplate `yaml:"workflows,omitempty" json:"workflows,omitempty" jsonschema:"title=list of workflows to execute,description=List of workflows to execute for template"`
|
||||||
|
|
||||||
Options *protocols.ExecuterOptions `yaml:"-"`
|
Options *protocols.ExecuterOptions `yaml:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// WorkflowTemplate is a template to be run as part of a workflow
|
// WorkflowTemplate is a template to be run as part of a workflow
|
||||||
@ -27,18 +27,18 @@ type WorkflowTemplate struct {
|
|||||||
// value: "\"dns/worksites-detection.yaml\""
|
// value: "\"dns/worksites-detection.yaml\""
|
||||||
// - name: A template directory
|
// - name: A template directory
|
||||||
// value: "\"misconfigurations/aem\""
|
// value: "\"misconfigurations/aem\""
|
||||||
Template string `yaml:"template,omitempty" jsonschema:"title=template/directory to execute,description=Template or directory to execute as part of workflow"`
|
Template string `yaml:"template,omitempty" json:"template,omitempty" jsonschema:"title=template/directory to execute,description=Template or directory to execute as part of workflow"`
|
||||||
// description: |
|
// description: |
|
||||||
// Tags to run templates based on.
|
// Tags to run templates based on.
|
||||||
Tags stringslice.StringSlice `yaml:"tags,omitempty" jsonschema:"title=tags to execute,description=Tags to run template based on"`
|
Tags stringslice.StringSlice `yaml:"tags,omitempty" json:"tags,omitempty" jsonschema:"title=tags to execute,description=Tags to run template based on"`
|
||||||
// description: |
|
// description: |
|
||||||
// Matchers perform name based matching to run subtemplates for a workflow.
|
// Matchers perform name based matching to run subtemplates for a workflow.
|
||||||
Matchers []*Matcher `yaml:"matchers,omitempty" jsonschema:"title=name based template result matchers,description=Matchers perform name based matching to run subtemplates for a workflow"`
|
Matchers []*Matcher `yaml:"matchers,omitempty" json:"matchers,omitempty" jsonschema:"title=name based template result matchers,description=Matchers perform name based matching to run subtemplates for a workflow"`
|
||||||
// description: |
|
// description: |
|
||||||
// Subtemplates are run if the `template` field Template matches.
|
// Subtemplates are run if the `template` field Template matches.
|
||||||
Subtemplates []*WorkflowTemplate `yaml:"subtemplates,omitempty" jsonschema:"title=subtemplate based result matchers,description=Subtemplates are ran if the template field Template matches"`
|
Subtemplates []*WorkflowTemplate `yaml:"subtemplates,omitempty" json:"subtemplates,omitempty" jsonschema:"title=subtemplate based result matchers,description=Subtemplates are ran if the template field Template matches"`
|
||||||
// Executers perform the actual execution for the workflow template
|
// Executers perform the actual execution for the workflow template
|
||||||
Executers []*ProtocolExecuterPair `yaml:"-"`
|
Executers []*ProtocolExecuterPair `yaml:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProtocolExecuterPair is a pair of protocol executer and its options
|
// ProtocolExecuterPair is a pair of protocol executer and its options
|
||||||
@ -52,17 +52,17 @@ type ProtocolExecuterPair struct {
|
|||||||
type Matcher struct {
|
type Matcher struct {
|
||||||
// description: |
|
// description: |
|
||||||
// Name is the name of the items to match.
|
// Name is the name of the items to match.
|
||||||
Name stringslice.StringSlice `yaml:"name,omitempty" jsonschema:"title=name of items to match,description=Name of items to match"`
|
Name stringslice.StringSlice `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"title=name of items to match,description=Name of items to match"`
|
||||||
// description: |
|
// description: |
|
||||||
// Condition is the optional condition between names. By default,
|
// Condition is the optional condition between names. By default,
|
||||||
// the condition is assumed to be OR.
|
// the condition is assumed to be OR.
|
||||||
// values:
|
// values:
|
||||||
// - "and"
|
// - "and"
|
||||||
// - "or"
|
// - "or"
|
||||||
Condition string `yaml:"condition,omitempty" jsonschema:"title=condition between names,description=Condition between the names,enum=and,enum=or"`
|
Condition string `yaml:"condition,omitempty" json:"condition,omitempty" jsonschema:"title=condition between names,description=Condition between the names,enum=and,enum=or"`
|
||||||
// description: |
|
// description: |
|
||||||
// Subtemplates are run if the name of matcher matches.
|
// Subtemplates are run if the name of matcher matches.
|
||||||
Subtemplates []*WorkflowTemplate `yaml:"subtemplates,omitempty" jsonschema:"title=templates to run after match,description=Templates to run after match"`
|
Subtemplates []*WorkflowTemplate `yaml:"subtemplates,omitempty" json:"subtemplates,omitempty" jsonschema:"title=templates to run after match,description=Templates to run after match"`
|
||||||
|
|
||||||
condition ConditionType
|
condition ConditionType
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user