mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-17 19:45:28 +00:00
* feat: added initial live DAST server implementation * feat: more logging + misc additions * feat: auth file support enhancements for more complex scenarios + misc * feat: added io.Reader support to input providers for http * feat: added stats db to fuzzing + use sdk for dast server + misc * feat: more additions and enhancements * misc changes to live server * misc * use utils pprof server * feat: added simpler stats tracking system * feat: fixed analyzer timeout issue + missing case fix * misc changes fix * feat: changed the logics a bit + misc changes and additions * feat: re-added slope checks + misc * feat: added baseline measurements for time based checks * chore(server): fix typos Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix(templates): potential DOM XSS Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix(authx): potential NIL deref Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * feat: misc review changes * removed debug logging * feat: remove existing cookies only * feat: lint fixes * misc * misc text update * request endpoint update * feat: added tracking for status code, waf-detection & grouped errors (#6028) * feat: added tracking for status code, waf-detection & grouped errors * lint error fixes * feat: review changes + moving to package + misc --------- Co-authored-by: sandeep <8293321+ehsandeep@users.noreply.github.com> * fix var dump (#5921) * fix var dump * fix dump test * Added filename length restriction for debug mode (-srd flag) (#5931) Co-authored-by: Andrey Matveenko <an.matveenko@vkteam.ru> * more updates * Update pkg/output/stats/waf/waf.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: sandeep <8293321+ehsandeep@users.noreply.github.com> Co-authored-by: Dwi Siswanto <25837540+dwisiswant0@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Dogan Can Bakir <65292895+dogancanbakir@users.noreply.github.com> Co-authored-by: 9flowers <51699499+Lercas@users.noreply.github.com> Co-authored-by: Andrey Matveenko <an.matveenko@vkteam.ru> Co-authored-by: Sandeep Singh <sandeep@projectdiscovery.io>
123 lines
2.6 KiB
Go
123 lines
2.6 KiB
Go
package server
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"net/url"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/input/types"
|
|
mapsutil "github.com/projectdiscovery/utils/maps"
|
|
)
|
|
|
|
var dynamicHeaders = map[string]bool{
|
|
"date": true,
|
|
"if-modified-since": true,
|
|
"if-unmodified-since": true,
|
|
"cache-control": true,
|
|
"if-none-match": true,
|
|
"if-match": true,
|
|
"authorization": true,
|
|
"cookie": true,
|
|
"x-csrf-token": true,
|
|
"content-length": true,
|
|
"content-md5": true,
|
|
"host": true,
|
|
"x-request-id": true,
|
|
"x-correlation-id": true,
|
|
"user-agent": true,
|
|
"referer": true,
|
|
}
|
|
|
|
type requestDeduplicator struct {
|
|
hashes map[string]struct{}
|
|
lock *sync.RWMutex
|
|
}
|
|
|
|
func newRequestDeduplicator() *requestDeduplicator {
|
|
return &requestDeduplicator{
|
|
hashes: make(map[string]struct{}),
|
|
lock: &sync.RWMutex{},
|
|
}
|
|
}
|
|
|
|
func (r *requestDeduplicator) isDuplicate(req *types.RequestResponse) bool {
|
|
hash, err := hashRequest(req)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
r.lock.RLock()
|
|
_, ok := r.hashes[hash]
|
|
r.lock.RUnlock()
|
|
if ok {
|
|
return true
|
|
}
|
|
|
|
r.lock.Lock()
|
|
r.hashes[hash] = struct{}{}
|
|
r.lock.Unlock()
|
|
return false
|
|
}
|
|
|
|
func hashRequest(req *types.RequestResponse) (string, error) {
|
|
normalizedURL, err := normalizeURL(req.URL.URL)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
var hashContent strings.Builder
|
|
hashContent.WriteString(req.Request.Method)
|
|
hashContent.WriteString(normalizedURL)
|
|
|
|
headers := sortedNonDynamicHeaders(req.Request.Headers)
|
|
for _, header := range headers {
|
|
hashContent.WriteString(header.Key)
|
|
hashContent.WriteString(header.Value)
|
|
}
|
|
|
|
if len(req.Request.Body) > 0 {
|
|
hashContent.Write([]byte(req.Request.Body))
|
|
}
|
|
|
|
// Calculate the SHA256 hash
|
|
hash := sha256.Sum256([]byte(hashContent.String()))
|
|
return hex.EncodeToString(hash[:]), nil
|
|
}
|
|
|
|
func normalizeURL(u *url.URL) (string, error) {
|
|
query := u.Query()
|
|
sortedQuery := make(url.Values)
|
|
for k, v := range query {
|
|
sort.Strings(v)
|
|
sortedQuery[k] = v
|
|
}
|
|
u.RawQuery = sortedQuery.Encode()
|
|
|
|
if u.Path == "" {
|
|
u.Path = "/"
|
|
}
|
|
return u.String(), nil
|
|
}
|
|
|
|
type header struct {
|
|
Key string
|
|
Value string
|
|
}
|
|
|
|
func sortedNonDynamicHeaders(headers mapsutil.OrderedMap[string, string]) []header {
|
|
var result []header
|
|
headers.Iterate(func(k, v string) bool {
|
|
if !dynamicHeaders[strings.ToLower(k)] {
|
|
result = append(result, header{Key: k, Value: v})
|
|
}
|
|
return true
|
|
})
|
|
sort.Slice(result, func(i, j int) bool {
|
|
return result[i].Key < result[j].Key
|
|
})
|
|
return result
|
|
}
|