2021-01-01 15:28:28 +05:30
package file
import (
2022-02-23 13:54:46 +01:00
"bufio"
2021-10-30 13:17:47 +03:00
"encoding/hex"
2022-02-23 13:54:46 +01:00
"io"
2021-01-01 15:28:28 +05:30
"os"
2022-02-10 15:59:05 +05:30
"sort"
"strings"
2021-01-01 15:28:28 +05:30
2022-02-23 13:54:46 +01:00
"github.com/docker/go-units"
2021-01-01 15:28:28 +05:30
"github.com/pkg/errors"
2021-09-29 19:43:46 +03:00
"github.com/remeh/sizedwaitgroup"
2021-01-01 15:28:28 +05:30
"github.com/projectdiscovery/gologger"
2022-02-23 23:32:25 +01:00
"github.com/projectdiscovery/nuclei/v2/pkg/operators"
2021-01-01 15:28:28 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/output"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
2021-10-01 16:52:38 +03:00
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/eventcreator"
2021-10-01 14:24:45 +03:00
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/responsehighlighter"
2021-11-03 19:53:45 +05:30
templateTypes "github.com/projectdiscovery/nuclei/v2/pkg/templates/types"
2022-02-24 22:50:41 +01:00
"github.com/projectdiscovery/sliceutil"
2021-01-01 15:28:28 +05:30
)
var _ protocols . Request = & Request { }
2021-11-03 19:53:45 +05:30
// Type returns the type of the protocol request
func ( request * Request ) Type ( ) templateTypes . ProtocolType {
return templateTypes . FileProtocol
}
2022-02-25 00:55:05 +01:00
type FileMatch struct {
Data string
Line int
ByteIndex int
Match bool
Extract bool
Expr string
}
2021-01-01 15:28:28 +05:30
// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
2022-02-23 13:54:46 +01:00
func ( request * Request ) ExecuteWithResults ( input string , metadata , previous output . InternalEvent , callback protocols . OutputEventCallback ) error {
2021-10-01 14:30:04 +03:00
wg := sizedwaitgroup . New ( request . options . Options . BulkSize )
2021-01-14 22:43:08 +05:30
2021-10-01 14:30:04 +03:00
err := request . getInputPaths ( input , func ( data string ) {
2022-02-10 15:59:05 +05:30
request . options . Progress . AddToTotal ( 1 )
2021-01-14 22:43:08 +05:30
wg . Add ( )
2021-09-29 19:43:46 +03:00
go func ( filePath string ) {
2021-01-14 22:43:08 +05:30
defer wg . Done ( )
2021-09-29 19:43:46 +03:00
file , err := os . Open ( filePath )
2021-01-14 22:43:08 +05:30
if err != nil {
2021-09-29 19:43:46 +03:00
gologger . Error ( ) . Msgf ( "Could not open file path %s: %s\n" , filePath , err )
2021-01-14 22:43:08 +05:30
return
}
defer file . Close ( )
2021-01-01 15:28:28 +05:30
2021-01-14 22:43:08 +05:30
stat , err := file . Stat ( )
if err != nil {
2021-09-29 19:43:46 +03:00
gologger . Error ( ) . Msgf ( "Could not stat file path %s: %s\n" , filePath , err )
2021-01-14 22:43:08 +05:30
return
}
2022-02-23 13:54:46 +01:00
if stat . Size ( ) >= request . maxSize {
gologger . Verbose ( ) . Msgf ( "Limiting %s processed data to %s bytes: exceeded max size\n" , filePath , units . HumanSize ( float64 ( request . maxSize ) ) )
2021-01-16 14:10:24 +05:30
}
2022-02-23 13:54:46 +01:00
totalBytes := units . BytesSize ( float64 ( stat . Size ( ) ) )
fileReader := io . LimitReader ( file , request . maxSize )
var bytesCount , linesCount , wordsCount int
2022-02-23 23:32:25 +01:00
isResponseDebug := request . options . Options . Debug || request . options . Options . DebugResponse
2022-02-23 13:54:46 +01:00
scanner := bufio . NewScanner ( fileReader )
buffer := [ ] byte { }
scanner . Buffer ( buffer , int ( chunkSize ) )
2022-02-23 23:32:25 +01:00
2022-02-25 00:55:05 +01:00
var fileMatches [ ] FileMatch
2022-02-23 13:54:46 +01:00
for scanner . Scan ( ) {
fileContent := scanner . Text ( )
n := len ( fileContent )
// update counters
currentBytes := bytesCount + n
processedBytes := units . BytesSize ( float64 ( currentBytes ) )
gologger . Verbose ( ) . Msgf ( "[%s] Processing file %s chunk %s/%s" , request . options . TemplateID , filePath , processedBytes , totalBytes )
2022-02-25 00:55:05 +01:00
dslMap := request . responseToDSLMap ( & fileStatus {
2022-02-23 13:54:46 +01:00
raw : fileContent ,
inputFilePath : input ,
matchedFileName : filePath ,
lines : linesCount ,
words : wordsCount ,
bytes : bytesCount ,
} )
2021-01-01 15:28:28 +05:30
2022-02-25 00:55:05 +01:00
if parts , ok := request . CompiledOperators . Execute ( dslMap , request . Match , request . Extract , isResponseDebug ) ; parts != nil && ok {
if parts . Extracts != nil {
for expr , extracts := range parts . Extracts {
for _ , extract := range extracts {
fileMatches = append ( fileMatches , FileMatch {
Data : extract ,
Extract : true ,
Line : linesCount + 1 ,
ByteIndex : bytesCount ,
Expr : expr ,
} )
}
}
}
if parts . Matches != nil {
for expr , matches := range parts . Matches {
for _ , match := range matches {
fileMatches = append ( fileMatches , FileMatch {
Data : match ,
Match : true ,
Line : linesCount + 1 ,
ByteIndex : bytesCount ,
Expr : expr ,
} )
}
}
2022-02-23 23:32:25 +01:00
}
}
2021-10-01 14:24:45 +03:00
2022-02-23 13:54:46 +01:00
currentLinesCount := 1 + strings . Count ( fileContent , "\n" )
linesCount += currentLinesCount
wordsCount += strings . Count ( fileContent , " " )
bytesCount = currentBytes
2022-02-23 23:32:25 +01:00
2022-02-23 13:54:46 +01:00
}
2022-02-25 00:55:05 +01:00
// create a new event trying to adapt it for the architecture
dumpResponse ( request . options , fileMatches , filePath )
// build event to allow the internal logic to hopefully handle it
event := & output . InternalWrappedEvent { }
event . Results = append ( event . Results , & output . ResultEvent { } )
event := eventcreator . CreateEvent ( request , outputEvent , isResponseDebug )
2022-02-24 23:54:45 +01:00
callback ( event )
2022-02-25 00:55:05 +01:00
2022-02-23 23:32:25 +01:00
request . options . Progress . IncrementRequests ( )
2021-01-14 22:43:08 +05:30
} ( data )
2021-01-01 15:28:28 +05:30
} )
2021-01-14 22:43:08 +05:30
wg . Wait ( )
2021-01-01 15:28:28 +05:30
if err != nil {
2021-11-05 03:01:41 +05:30
request . options . Output . Request ( request . options . TemplatePath , input , request . Type ( ) . String ( ) , err )
2021-10-01 14:30:04 +03:00
request . options . Progress . IncrementFailedRequestsBy ( 1 )
2021-01-01 19:36:21 +05:30
return errors . Wrap ( err , "could not send file request" )
2021-01-01 15:28:28 +05:30
}
2021-01-01 19:36:21 +05:30
return nil
2021-01-01 15:28:28 +05:30
}
2021-10-30 13:17:47 +03:00
2022-02-25 00:55:05 +01:00
func dumpResponse ( requestOptions * protocols . ExecuterOptions , filematches [ ] FileMatch , filePath string ) {
2021-11-01 20:45:54 +02:00
cliOptions := requestOptions . Options
if cliOptions . Debug || cliOptions . DebugResponse {
2022-02-25 00:55:05 +01:00
for _ , fileMatch := range filematches {
data := fileMatch . Data
hexDump := false
if responsehighlighter . HasBinaryContent ( data ) {
hexDump = true
data = hex . Dump ( [ ] byte ( data ) )
}
highlightedResponse := responsehighlighter . HighlightAll ( data , cliOptions . NoColor , hexDump )
gologger . Debug ( ) . Msgf ( "[%s] Dumped match/extract file snippet for %s at line %d\n\n%s" , requestOptions . TemplateID , filePath , fileMatch . Line , highlightedResponse )
2021-10-30 13:17:47 +03:00
}
}
}
2022-02-10 15:59:05 +05:30
2022-02-24 22:41:33 +01:00
func calculateLineFunc ( allMatches [ ] * output . InternalEvent , words map [ string ] struct { } ) [ ] int {
2022-02-10 15:59:05 +05:30
var lines [ ] int
for word := range words {
2022-02-24 22:41:33 +01:00
for _ , match := range allMatches {
matchPt := * match
opResult := matchPt [ "results" ] . ( operators . Result )
if opResult . Matched {
for _ , matchedItems := range opResult . Matches {
for _ , matchedItem := range matchedItems {
if word == matchedItem {
lines = append ( lines , matchPt [ "lines" ] . ( int ) + 1 )
}
}
2022-02-23 23:32:25 +01:00
}
}
2022-02-24 22:41:33 +01:00
for _ , v := range opResult . OutputExtracts {
if word == v {
lines = append ( lines , matchPt [ "lines" ] . ( int ) + 1 )
}
2022-02-23 23:32:25 +01:00
}
2022-02-10 15:59:05 +05:30
}
2022-02-24 22:41:33 +01:00
_ = word
2022-02-10 15:59:05 +05:30
}
2022-02-24 22:50:41 +01:00
lines = sliceutil . DedupeInt ( lines )
2022-02-10 15:59:05 +05:30
sort . Ints ( lines )
return lines
}
2022-02-24 23:18:37 +01:00
func calculateFileIndexFunc ( allMatches [ ] * output . InternalEvent , extraction string ) int {
for _ , match := range allMatches {
matchPt := * match
opResult := matchPt [ "results" ] . ( operators . Result )
2022-02-24 23:35:51 +01:00
for _ , extracts := range opResult . Extracts {
for _ , extract := range extracts {
if extraction == extract {
return matchPt [ "bytes" ] . ( int )
2022-02-24 23:18:37 +01:00
}
}
}
}
return - 1
}