Merge branch 'dev' into issue-3081-retry-gh

This commit is contained in:
shubhamrasal 2023-03-01 19:07:15 +05:30
commit 409393b64b
63 changed files with 1095 additions and 299 deletions

View 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"
]
}
]
}
]
}

View File

@ -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{}

View 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)
}

View File

@ -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

View File

@ -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=

View File

@ -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

View File

@ -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) {

View File

@ -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())
} }

View File

@ -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) {

View File

@ -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"`

View File

@ -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:

View File

@ -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())
} }

View File

@ -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

View 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
}

View File

@ -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())
} }

View File

@ -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...)
}, },

View File

@ -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{}")

View File

@ -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())
} }

View File

@ -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"`
} }

View File

@ -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

View File

@ -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())
} }

View File

@ -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

View File

@ -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
} }

View File

@ -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) {

View File

@ -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())
} }

View File

@ -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.

View File

@ -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,

View File

@ -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())

View File

@ -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")
} }

View File

@ -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

View File

@ -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())
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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())
} }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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())
} }

View File

@ -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())
} }

View File

@ -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.

View File

@ -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())
} }

View File

@ -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.

View File

@ -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

View File

@ -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 (

View File

@ -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

View 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
}

View File

@ -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()

View File

@ -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",

View 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:"-"`
}

View File

@ -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()
}

View File

@ -0,0 +1,7 @@
package signer
var DefaultVerifier *Signer
func init() {
DefaultVerifier, _ = NewVerifier(&Options{PublicKeyData: ecdsaPublicKey, Algorithm: ECDSA})
}

View File

@ -0,0 +1,8 @@
package signer
import (
_ "embed"
)
//go:embed ecdsa_public_key.pem
var ecdsaPublicKey []byte

View File

@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END PUBLIC KEY-----

View 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")
)

View 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)
}

View 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)
}

View File

@ -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

View File

@ -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{

View File

@ -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")
}

View 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"
]
}
]
}
]
}

View File

@ -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"
) )

View File

@ -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) {

View File

@ -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
} }