Merge pull request #1004 from projectdiscovery/new-info-fields

Added new info block fields classification and remediation
This commit is contained in:
Ice3man 2021-09-10 15:03:14 +05:30 committed by GitHub
commit 8f62da84c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 286 additions and 45 deletions

160
v2/cmd/cve-annotate/main.go Normal file
View 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 ""
}

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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