mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-17 18:15:28 +00:00
Merge pull request #1004 from projectdiscovery/new-info-fields
Added new info block fields classification and remediation
This commit is contained in:
commit
8f62da84c0
160
v2/cmd/cve-annotate/main.go
Normal file
160
v2/cmd/cve-annotate/main.go
Normal file
@ -0,0 +1,160 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/daehee/nvd"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog"
|
||||
)
|
||||
|
||||
var (
|
||||
input = flag.String("i", "", "Templates to annotate")
|
||||
templateDir = flag.String("d", "", "Custom template directory for update")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *input == "" || *templateDir == "" {
|
||||
log.Fatalf("invalid input, see -h\n")
|
||||
}
|
||||
|
||||
if err := process(); err != nil {
|
||||
log.Fatalf("could not process: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func process() error {
|
||||
tempDir, err := ioutil.TempDir("", "nuclei-nvd-%s")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
client, err := nvd.NewClient(tempDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
catalog := catalog.New(*templateDir)
|
||||
|
||||
paths, err := catalog.GetTemplatePath(*input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, path := range paths {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
getCVEData(client, path, string(data))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
idRegex = regexp.MustCompile("id: ([C|c][V|v][E|e]-[0-9]+-[0-9]+)")
|
||||
severityRegex = regexp.MustCompile(`severity: ([a-z]+)`)
|
||||
)
|
||||
|
||||
func getCVEData(client *nvd.Client, filePath, data string) {
|
||||
matches := idRegex.FindAllStringSubmatch(data, 1)
|
||||
if len(matches) == 0 {
|
||||
return
|
||||
}
|
||||
cveName := matches[0][1]
|
||||
|
||||
severityMatches := severityRegex.FindAllStringSubmatch(data, 1)
|
||||
if len(matches) == 0 {
|
||||
return
|
||||
}
|
||||
severityValue := severityMatches[0][1]
|
||||
|
||||
cveItem, err := client.FetchCVE(cveName)
|
||||
if err != nil {
|
||||
log.Printf("Could not fetch cve %s: %s\n", cveName, err)
|
||||
return
|
||||
}
|
||||
var cweID []string
|
||||
for _, problemData := range cveItem.CVE.Problemtype.ProblemtypeData {
|
||||
for _, description := range problemData.Description {
|
||||
cweID = append(cweID, description.Value)
|
||||
}
|
||||
}
|
||||
cvssScore := cveItem.Impact.BaseMetricV3.CvssV3.BaseScore
|
||||
cvssMetrics := cveItem.Impact.BaseMetricV3.CvssV3.VectorString
|
||||
|
||||
// Perform some hacky string replacement to place the metadata in templates
|
||||
infoBlockIndexData := data[strings.Index(data, "info:"):]
|
||||
requestsIndex := strings.Index(infoBlockIndexData, "requests:")
|
||||
networkIndex := strings.Index(infoBlockIndexData, "network:")
|
||||
if requestsIndex == -1 && networkIndex == -1 {
|
||||
return
|
||||
}
|
||||
if networkIndex != -1 {
|
||||
requestsIndex = networkIndex
|
||||
}
|
||||
infoBlockData := infoBlockIndexData[:requestsIndex]
|
||||
infoBlockClean := strings.TrimRight(infoBlockData, "\n")
|
||||
|
||||
newInfoBlock := infoBlockClean
|
||||
var changed bool
|
||||
|
||||
if newSeverity := isSeverityMatchingCvssScore(severityValue, cvssScore); newSeverity != "" {
|
||||
changed = true
|
||||
newInfoBlock = strings.ReplaceAll(newInfoBlock, severityMatches[0][0], "severity: "+newSeverity)
|
||||
fmt.Printf("Adjusting severity for %s from %s=>%s (%.2f)\n", filePath, severityValue, newSeverity, cvssScore)
|
||||
}
|
||||
// Start with additional-fields as that is the one most likely to break stuff.
|
||||
if !strings.Contains(infoBlockClean, "classification") && (cvssScore != 0 && cvssMetrics != "") {
|
||||
changed = true
|
||||
newInfoBlock = newInfoBlock + fmt.Sprintf("\n classification:\n cvss-metrics: %s\n cvss-score: %.2f\n cve-id: %s", cvssMetrics, cvssScore, cveName)
|
||||
if len(cweID) > 0 && (cweID[0] != "NVD-CWE-Other" && cweID[0] != "NVD-CWE-noinfo") {
|
||||
newInfoBlock = newInfoBlock + fmt.Sprintf("\n cwe-id: %s", strings.Join(cweID, ","))
|
||||
}
|
||||
}
|
||||
// If there is no description field, fill the description from CVE information
|
||||
if !strings.Contains(infoBlockClean, "description:") {
|
||||
changed = true
|
||||
newInfoBlock = newInfoBlock + fmt.Sprintf("\n description: %s", fmt.Sprintf("%q", cveItem.CVE.Description.DescriptionData[0].Value))
|
||||
}
|
||||
if !strings.Contains(infoBlockClean, "reference:") && len(cveItem.CVE.References.ReferenceData) > 0 {
|
||||
changed = true
|
||||
newInfoBlock = newInfoBlock + "\n reference:"
|
||||
for _, reference := range cveItem.CVE.References.ReferenceData {
|
||||
newInfoBlock = newInfoBlock + fmt.Sprintf("\n - %s", reference.URL)
|
||||
}
|
||||
}
|
||||
newTemplate := strings.ReplaceAll(data, infoBlockClean, newInfoBlock)
|
||||
if changed {
|
||||
_ = ioutil.WriteFile(filePath, []byte(newTemplate), 0777)
|
||||
fmt.Printf("Wrote updated template to %s\n", filePath)
|
||||
}
|
||||
}
|
||||
|
||||
func isSeverityMatchingCvssScore(severity string, score float64) string {
|
||||
if score == 0.0 {
|
||||
return ""
|
||||
}
|
||||
var expected string
|
||||
|
||||
if score >= 0.1 && score <= 3.9 {
|
||||
expected = "low"
|
||||
} else if score >= 4.0 && score <= 6.9 {
|
||||
expected = "medium"
|
||||
} else if score >= 7.0 && score <= 8.9 {
|
||||
expected = "high"
|
||||
} else if score >= 9.0 && score <= 10.0 {
|
||||
expected = "critical"
|
||||
}
|
||||
if expected != "" && expected != severity {
|
||||
return expected
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@ -13,7 +13,7 @@ require (
|
||||
github.com/bluele/gcache v0.0.2
|
||||
github.com/c4milo/unpackit v0.1.0 // indirect
|
||||
github.com/corpix/uarand v0.1.1
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/daehee/nvd v1.0.4
|
||||
github.com/go-rod/rod v0.91.1
|
||||
github.com/google/go-github v17.0.0+incompatible
|
||||
github.com/gosuri/uilive v0.0.4 // indirect
|
||||
|
||||
17
v2/go.sum
17
v2/go.sum
@ -45,9 +45,10 @@ github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHS
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
|
||||
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||
github.com/PuerkitoBio/goquery v1.6.0 h1:j7taAbelrdcsOlGeMenZxc2AWXD5fieT1/znArdnx94=
|
||||
github.com/PuerkitoBio/goquery v1.6.0/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
|
||||
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
|
||||
github.com/akrylysov/pogreb v0.10.0 h1:pVKi+uf3EzZUmiwr9bZnPk4W379KP8QsFzAa9IUuOog=
|
||||
github.com/akrylysov/pogreb v0.10.0/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI=
|
||||
github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w=
|
||||
github.com/akrylysov/pogreb v0.10.1/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI=
|
||||
@ -55,6 +56,8 @@ github.com/alecthomas/jsonschema v0.0.0-20210818095345-1014919a589c h1:oJsq4z4xK
|
||||
github.com/alecthomas/jsonschema v0.0.0-20210818095345-1014919a589c/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60=
|
||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI=
|
||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
|
||||
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/andygrunwald/go-jira v1.14.0 h1:7GT/3qhar2dGJ0kq8w0d63liNyHOnxZsUZ9Pe4+AKBI=
|
||||
github.com/andygrunwald/go-jira v1.14.0/go.mod h1:KMo2f4DgMZA1C9FdImuLc04x4WQhn5derQpnsuBFgqE=
|
||||
github.com/antchfx/htmlquery v1.2.3 h1:sP3NFDneHx2stfNXCKbhHFo8XgNjCACnU/4AO5gWz6M=
|
||||
@ -96,7 +99,8 @@ github.com/corpix/uarand v0.1.1 h1:RMr1TWc9F4n5jiPDzFHtmaUXLKLNUFK0SgCLo4BhX/U=
|
||||
github.com/corpix/uarand v0.1.1/go.mod h1:SFKZvkcRoLqVRFZ4u25xPmp6m9ktANfbpXZ7SJ0/FNU=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/dave/dst v0.26.2 h1:lnxLAKI3tx7MgLNVDirFCsDTlTG9nKTk7GcptKcWSwY=
|
||||
github.com/daehee/nvd v1.0.4 h1:qC0kJ68vAYS86v8GwBORReBhyC5yUaUzsBokxjlsT98=
|
||||
github.com/daehee/nvd v1.0.4/go.mod h1:iBRJHIdIs+ylfq8630my2eMw8kwzH4Z7qsetjJZxCzs=
|
||||
github.com/dave/dst v0.26.2/go.mod h1:UMDJuIRPfyUCC78eFuB+SV/WI8oDeyFDvM/JR6NI3IU=
|
||||
github.com/dave/gopackages v0.0.0-20170318123100-46e7023ec56e/go.mod h1:i00+b/gKdIDIxuLDFob7ustLAVqhsZRk2qVZrArELGQ=
|
||||
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
|
||||
@ -229,7 +233,6 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
|
||||
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk=
|
||||
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/itchyny/go-flags v1.5.0/go.mod h1:lenkYuCobuxLBAd/HGFE4LRoW8D3B6iXRQfWYJ+MNbA=
|
||||
github.com/itchyny/gojq v0.12.4 h1:8zgOZWMejEWCLjbF/1mWY7hY7QEARm7dtuhC6Bp4R8o=
|
||||
@ -276,13 +279,11 @@ github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczG
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
|
||||
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
@ -508,8 +509,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -614,7 +615,6 @@ golang.org/x/sys v0.0.0-20210415045647-66c3f260301c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -676,7 +676,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -705,7 +704,6 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
@ -792,7 +790,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
mvdan.cc/gofumpt v0.1.1 h1:bi/1aS/5W00E2ny5q65w9SnKpWEF/UIOqDYBILpo9rA=
|
||||
mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
|
||||
@ -189,7 +189,7 @@ func New(options *types.Options) (*Runner, error) {
|
||||
}
|
||||
|
||||
// Create the output file if asked
|
||||
outputWriter, err := output.NewStandardWriter(!options.NoColor, options.NoMeta, options.NoTimestamp, options.JSON, options.Output, options.TraceLogFile)
|
||||
outputWriter, err := output.NewStandardWriter(!options.NoColor, options.NoMeta, options.NoTimestamp, options.JSON, options.JSONRequests, options.Output, options.TraceLogFile)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not create output file")
|
||||
}
|
||||
|
||||
@ -65,4 +65,41 @@ type Info struct {
|
||||
// - value: >
|
||||
// map[string]string{"customField1":"customValue1"}
|
||||
AdditionalFields map[string]string `json:"additional-fields,omitempty" yaml:"additional-fields,omitempty" jsonschema:"title=additional metadata for the template,description=Additional metadata fields for the template"`
|
||||
|
||||
// description: |
|
||||
// Classification contains classification information about the template.
|
||||
Classification *Classification `json:"classification,omitempty" yaml:"classification,omitempty" jsonschema:"title=classification info for the template,description=Classification information for the template"`
|
||||
|
||||
// description: |
|
||||
// Remediation steps for the template.
|
||||
//
|
||||
// You can go in-depth here on how to mitigate the problem found by this template.
|
||||
//
|
||||
// examples:
|
||||
// - value: "\"Change the default administrative username and password of Apache ActiveMQ by editing the file jetty-realm.properties\""
|
||||
Remediation string `json:"remediation,omitempty" yaml:"remediation,omitempty" jsonschema:"title=remediation steps for the template,description=In-depth explanation on how to fix the issues found by the template,example=Change the default administrative username and password of Apache ActiveMQ by editing the file jetty-realm.properties"`
|
||||
}
|
||||
|
||||
// Classification contains the vulnerability classification data for a template.
|
||||
type Classification struct {
|
||||
// description: |
|
||||
// CVE ID for the template
|
||||
// examples:
|
||||
// - value: "\"CVE-2020-14420\""
|
||||
CVEID stringslice.StringSlice `json:"cve-id,omitempty" yaml:"cve-id,omitempty" jsonschema:"title=cve ids for the template,description=CVE IDs for the template,example=CVE-2020-14420"`
|
||||
// description: |
|
||||
// CWE ID for the template.
|
||||
// examples:
|
||||
// - value: "\"CWE-22\""
|
||||
CWEID stringslice.StringSlice `json:"cwe-id,omitempty" yaml:"cwe-id,omitempty" jsonschema:"title=cwe ids for the template,description=CWE IDs for the template,example=CWE-22"`
|
||||
// description: |
|
||||
// CVSS Metrics for the template.
|
||||
// examples:
|
||||
// - value: "\"3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H\""
|
||||
CVSSMetrics string `json:"cvss-metrics,omitempty" yaml:"cvss-metrics,omitempty" jsonschema:"title=cvss metrics for the template,description=CVSS Metrics for the template,example=3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"`
|
||||
// description: |
|
||||
// CVSS Score for the template.
|
||||
// examples:
|
||||
// - value: "\"9.8\""
|
||||
CVSSScore float64 `json:"cvss-score,omitempty" yaml:"cvss-score,omitempty" jsonschema:"title=cvss score for the template,description=CVSS Score for the template,example=9.8"`
|
||||
}
|
||||
|
||||
@ -6,5 +6,9 @@ import (
|
||||
|
||||
// formatJSON formats the output for json based formatting
|
||||
func (w *StandardWriter) formatJSON(output *ResultEvent) ([]byte, error) {
|
||||
if !w.jsonReqResp { // don't show request-response in json if not asked
|
||||
output.Request = ""
|
||||
output.Response = ""
|
||||
}
|
||||
return jsoniter.Marshal(output)
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ type Writer interface {
|
||||
// StandardWriter is a writer writing output to file and screen for results.
|
||||
type StandardWriter struct {
|
||||
json bool
|
||||
jsonReqResp bool
|
||||
noTimestamp bool
|
||||
noMetadata bool
|
||||
aurora aurora.Aurora
|
||||
@ -94,7 +95,7 @@ type ResultEvent struct {
|
||||
}
|
||||
|
||||
// NewStandardWriter creates a new output writer based on user configurations
|
||||
func NewStandardWriter(colors, noMetadata, noTimestamp, json bool, file, traceFile string) (*StandardWriter, error) {
|
||||
func NewStandardWriter(colors, noMetadata, noTimestamp, json, jsonReqResp bool, file, traceFile string) (*StandardWriter, error) {
|
||||
auroraColorizer := aurora.NewAurora(colors)
|
||||
|
||||
var outputFile *fileWriter
|
||||
@ -115,6 +116,7 @@ func NewStandardWriter(colors, noMetadata, noTimestamp, json bool, file, traceFi
|
||||
}
|
||||
writer := &StandardWriter{
|
||||
json: json,
|
||||
jsonReqResp: jsonReqResp,
|
||||
noMetadata: noMetadata,
|
||||
noTimestamp: noTimestamp,
|
||||
aurora: auroraColorizer,
|
||||
|
||||
@ -147,10 +147,8 @@ func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *out
|
||||
Matched: types.ToString(wrapped.InternalEvent["matched"]),
|
||||
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
if r.options.Options.JSONRequests {
|
||||
data.Request = types.ToString(wrapped.InternalEvent["request"])
|
||||
data.Response = types.ToString(wrapped.InternalEvent["raw"])
|
||||
Request: types.ToString(wrapped.InternalEvent["request"]),
|
||||
Response: types.ToString(wrapped.InternalEvent["raw"]),
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
@ -143,10 +143,8 @@ func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *out
|
||||
Matched: types.ToString(wrapped.InternalEvent["matched"]),
|
||||
Host: types.ToString(wrapped.InternalEvent["matched"]),
|
||||
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
|
||||
Response: types.ToString(wrapped.InternalEvent["raw"]),
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
if r.options.Options.JSONRequests {
|
||||
data.Response = types.ToString(wrapped.InternalEvent["raw"])
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
@ -116,10 +116,8 @@ func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *out
|
||||
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
|
||||
Timestamp: time.Now(),
|
||||
IP: types.ToString(wrapped.InternalEvent["ip"]),
|
||||
}
|
||||
if r.options.Options.JSONRequests {
|
||||
data.Request = types.ToString(wrapped.InternalEvent["request"])
|
||||
data.Response = types.ToString(wrapped.InternalEvent["data"])
|
||||
Request: types.ToString(wrapped.InternalEvent["request"]),
|
||||
Response: types.ToString(wrapped.InternalEvent["data"]),
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
@ -154,10 +154,8 @@ func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *out
|
||||
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
|
||||
Timestamp: time.Now(),
|
||||
IP: types.ToString(wrapped.InternalEvent["ip"]),
|
||||
}
|
||||
if r.options.Options.JSONRequests {
|
||||
data.Request = types.ToString(wrapped.InternalEvent["request"])
|
||||
data.Response = types.ToString(wrapped.InternalEvent["response"])
|
||||
Request: types.ToString(wrapped.InternalEvent["request"]),
|
||||
Response: types.ToString(wrapped.InternalEvent["response"]),
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
@ -118,10 +118,8 @@ func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *out
|
||||
Metadata: wrapped.OperatorsResult.PayloadValues,
|
||||
Timestamp: time.Now(),
|
||||
IP: types.ToString(wrapped.InternalEvent["ip"]),
|
||||
}
|
||||
if r.options.Options.JSONRequests {
|
||||
data.Request = types.ToString(wrapped.InternalEvent["request"])
|
||||
data.Response = types.ToString(wrapped.InternalEvent["data"])
|
||||
Request: types.ToString(wrapped.InternalEvent["request"]),
|
||||
Response: types.ToString(wrapped.InternalEvent["data"]),
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
@ -145,10 +145,8 @@ func (r *Request) makeResultEventItem(wrapped *output.InternalWrappedEvent) *out
|
||||
Metadata: wrapped.OperatorsResult.PayloadValues,
|
||||
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
|
||||
IP: types.ToString(wrapped.InternalEvent["ip"]),
|
||||
}
|
||||
if r.options.Options.JSONRequests {
|
||||
data.Request = types.ToString(wrapped.InternalEvent["request"])
|
||||
data.Response = types.ToString(wrapped.InternalEvent["raw"])
|
||||
Request: types.ToString(wrapped.InternalEvent["request"]),
|
||||
Response: types.ToString(wrapped.InternalEvent["raw"]),
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
@ -3,9 +3,12 @@ package format
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/utils"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/model"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
@ -16,13 +19,10 @@ func Summary(event *output.ResultEvent) string {
|
||||
template := GetMatchedTemplate(event)
|
||||
|
||||
builder := &strings.Builder{}
|
||||
builder.WriteString("[")
|
||||
builder.WriteString(template)
|
||||
builder.WriteString("] [")
|
||||
builder.WriteString(types.ToString(event.Info.SeverityHolder))
|
||||
builder.WriteString("] ")
|
||||
builder.WriteString(types.ToString(event.Info.Name))
|
||||
builder.WriteString(" found on ")
|
||||
builder.WriteString(" (")
|
||||
builder.WriteString(template)
|
||||
builder.WriteString(") found on ")
|
||||
builder.WriteString(event.Host)
|
||||
data := builder.String()
|
||||
return data
|
||||
@ -71,6 +71,7 @@ func MarkdownDescription(event *output.ResultEvent) string { // TODO remove the
|
||||
|
||||
if len(event.ExtractedResults) > 0 || len(event.Metadata) > 0 {
|
||||
builder.WriteString("\n**Extra Information**\n\n")
|
||||
|
||||
if len(event.ExtractedResults) > 0 {
|
||||
builder.WriteString("**Extracted results**:\n\n")
|
||||
for _, v := range event.ExtractedResults {
|
||||
@ -131,7 +132,7 @@ func MarkdownDescription(event *output.ResultEvent) string { // TODO remove the
|
||||
}
|
||||
}
|
||||
|
||||
builder.WriteString("\n---\nGenerated by [Nuclei](https://github.com/projectdiscovery/nuclei)")
|
||||
builder.WriteString(fmt.Sprintf("\n---\nGenerated by [Nuclei %s](https://github.com/projectdiscovery/nuclei)", config.Version))
|
||||
data := builder.String()
|
||||
return data
|
||||
}
|
||||
@ -159,6 +160,16 @@ func ToMarkdownTableString(templateInfo *model.Info) string {
|
||||
fields.Set("Tags", templateInfo.Tags.String())
|
||||
fields.Set("Severity", templateInfo.SeverityHolder.Severity.String())
|
||||
fields.Set("Description", templateInfo.Description)
|
||||
fields.Set("Remediation", templateInfo.Remediation)
|
||||
|
||||
classification := templateInfo.Classification
|
||||
if classification != nil {
|
||||
if classification.CVSSMetrics != "" {
|
||||
generateCVSSMetricsFromClassification(classification, fields)
|
||||
}
|
||||
generateCVECWEIDLinksFromClassification(classification, fields)
|
||||
fields.Set("CVSS-Score", strconv.FormatFloat(classification.CVSSScore, 'f', 2, 64))
|
||||
}
|
||||
|
||||
builder := &bytes.Buffer{}
|
||||
|
||||
@ -175,3 +186,44 @@ func ToMarkdownTableString(templateInfo *model.Info) string {
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func generateCVSSMetricsFromClassification(classification *model.Classification, fields *utils.InsertionOrderedStringMap) {
|
||||
// Generate cvss link
|
||||
var cvssLinkPrefix string
|
||||
if strings.Contains(classification.CVSSMetrics, "CVSS:3.0") {
|
||||
cvssLinkPrefix = "https://www.first.org/cvss/calculator/3.0#"
|
||||
} else if strings.Contains(classification.CVSSMetrics, "CVSS:3.1") {
|
||||
cvssLinkPrefix = "https://www.first.org/cvss/calculator/3.1#"
|
||||
}
|
||||
if cvssLinkPrefix != "" {
|
||||
fields.Set("CVSS-Metrics", fmt.Sprintf("[%s](%s%s)", classification.CVSSMetrics, cvssLinkPrefix, classification.CVSSMetrics))
|
||||
} else {
|
||||
fields.Set("CVSS-Metrics", classification.CVSSMetrics)
|
||||
}
|
||||
}
|
||||
|
||||
func generateCVECWEIDLinksFromClassification(classification *model.Classification, fields *utils.InsertionOrderedStringMap) {
|
||||
cwes := classification.CWEID.ToSlice()
|
||||
|
||||
cweIDs := make([]string, 0, len(cwes))
|
||||
for _, value := range cwes {
|
||||
parts := strings.Split(value, "-")
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
cweIDs = append(cweIDs, fmt.Sprintf("[%s](https://cwe.mitre.org/data/definitions/%s.html)", strings.ToUpper(value), parts[1]))
|
||||
}
|
||||
if len(cweIDs) > 0 {
|
||||
fields.Set("CWE-ID", strings.Join(cweIDs, ","))
|
||||
}
|
||||
|
||||
cves := classification.CVEID.ToSlice()
|
||||
|
||||
cveIDs := make([]string, 0, len(cves))
|
||||
for _, value := range cves {
|
||||
cveIDs = append(cveIDs, fmt.Sprintf("[%s](https://cve.mitre.org/cgi-bin/cvename.cgi?name=%s)", strings.ToUpper(value), value))
|
||||
}
|
||||
if len(cveIDs) > 0 {
|
||||
fields.Set("CVE-ID", strings.Join(cveIDs, ","))
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/andygrunwald/go-jira"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/format"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
@ -240,7 +241,7 @@ func jiraFormatDescription(event *output.ResultEvent) string { // TODO remove th
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.WriteString("\n---\nGenerated by [Nuclei|https://github.com/projectdiscovery/nuclei]")
|
||||
builder.WriteString(fmt.Sprintf("\n---\nGenerated by [Nuclei v%s](https://github.com/projectdiscovery/nuclei)", config.Version))
|
||||
data := builder.String()
|
||||
return data
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user