github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/file/file.go (about) 1 package file 2 3 import ( 4 "path/filepath" 5 "strings" 6 7 "github.com/docker/go-units" 8 "github.com/h2non/filetype" 9 "github.com/pkg/errors" 10 11 "github.com/projectdiscovery/nuclei/v2/pkg/operators" 12 "github.com/projectdiscovery/nuclei/v2/pkg/protocols" 13 ) 14 15 var ( 16 defaultMaxReadSize, _ = units.FromHumanSize("1Gb") 17 chunkSize, _ = units.FromHumanSize("100Mb") 18 ) 19 20 // Request contains a File matching mechanism for local disk operations. 21 type Request struct { 22 // Operators for the current request go here. 23 operators.Operators `yaml:",inline"` 24 // description: | 25 // Extensions is the list of extensions or mime types to perform matching on. 26 // examples: 27 // - value: '[]string{".txt", ".go", ".json"}' 28 Extensions []string `yaml:"extensions,omitempty" json:"extensions,omitempty" jsonschema:"title=extensions to match,description=List of extensions to perform matching on"` 29 // description: | 30 // DenyList is the list of file, directories, mime types or extensions to deny during matching. 31 // 32 // By default, it contains some non-interesting extensions that are hardcoded 33 // in nuclei. 34 // examples: 35 // - value: '[]string{".avi", ".mov", ".mp3"}' 36 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"` 37 38 // ID is the optional id of the request 39 ID string `yaml:"id,omitempty" json:"id,omitempty" jsonschema:"title=id of the request,description=ID is the optional ID for the request"` 40 41 // description: | 42 // MaxSize is the maximum size of the file to run request on. 43 // 44 // By default, nuclei will process 1 GB of content and not go more than that. 45 // It can be set to much lower or higher depending on use. 46 // If set to "no" then all content will be processed 47 // examples: 48 // - value: "\"5Mb\"" 49 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"` 50 maxSize int64 51 52 // description: | 53 // elaborates archives 54 Archive bool `yaml:"archive,omitempty" json:"archive,omitempty" jsonschema:"title=enable archives,description=Process compressed archives without unpacking"` 55 56 // description: | 57 // enables mime types check 58 MimeType bool `yaml:"mime-type,omitempty" json:"mime-type,omitempty" jsonschema:"title=enable filtering by mime-type,description=Filter files by mime-type"` 59 60 CompiledOperators *operators.Operators `yaml:"-" json:"-"` 61 62 // cache any variables that may be needed for operation. 63 options *protocols.ExecutorOptions 64 mimeTypesChecks []string 65 extensions map[string]struct{} 66 denyList map[string]struct{} 67 denyMimeTypesChecks []string 68 69 // description: | 70 // NoRecursive specifies whether to not do recursive checks if folders are provided. 71 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"` 72 73 allExtensions bool 74 } 75 76 // RequestPartDefinitions contains a mapping of request part definitions and their 77 // description. Multiple definitions are separated by commas. 78 // Definitions not having a name (generated on runtime) are prefixed & suffixed by <>. 79 var RequestPartDefinitions = map[string]string{ 80 "template-id": "ID of the template executed", 81 "template-info": "Info Block of the template executed", 82 "template-path": "Path of the template executed", 83 "matched": "Matched is the input which was matched upon", 84 "path": "Path is the path of file on local filesystem", 85 "type": "Type is the type of request made", 86 "raw,body,all,data": "Raw contains the raw file contents", 87 } 88 89 // defaultDenylist contains common extensions to exclude 90 var defaultDenylist = []string{".3g2", ".3gp", ".arj", ".avi", ".axd", ".bmp", ".css", ".csv", ".deb", ".dll", ".doc", ".drv", ".eot", ".exe", ".flv", ".gif", ".gifv", ".h264", ".ico", ".iso", ".jar", ".jpeg", ".jpg", ".lock", ".m4a", ".m4v", ".map", ".mkv", ".mov", ".mp3", ".mp4", ".mpeg", ".mpg", ".msi", ".ogg", ".ogm", ".ogv", ".otf", ".pdf", ".pkg", ".png", ".ppt", ".psd", ".rm", ".rpm", ".svg", ".swf", ".sys", ".tif", ".tiff", ".ttf", ".vob", ".wav", ".webm", ".wmv", ".woff", ".woff2", ".xcf", ".xls", ".xlsx"} 91 92 // defaultArchiveDenyList contains common archive extensions to exclude 93 var defaultArchiveDenyList = []string{".7z", ".apk", ".gz", ".rar", ".tar.gz", ".tar", ".zip"} 94 95 // GetID returns the unique ID of the request if any. 96 func (request *Request) GetID() string { 97 return request.ID 98 } 99 100 // Compile compiles the protocol request for further execution. 101 func (request *Request) Compile(options *protocols.ExecutorOptions) error { 102 // if there are no matchers/extractors, we trigger an error as no operation would be performed on the template 103 if request.Operators.IsEmpty() { 104 return errors.New("empty operators") 105 } 106 compiled := &request.Operators 107 compiled.ExcludeMatchers = options.ExcludeMatchers 108 compiled.TemplateID = options.TemplateID 109 if err := compiled.Compile(); err != nil { 110 return errors.Wrap(err, "could not compile operators") 111 } 112 request.CompiledOperators = compiled 113 114 // By default, use default max size if not defined 115 switch { 116 case request.MaxSize != "": 117 maxSize, err := units.FromHumanSize(request.MaxSize) 118 if err != nil { 119 return errors.Wrap(err, "could not compile operators") 120 } 121 request.maxSize = maxSize 122 case request.MaxSize == "no": 123 request.maxSize = -1 124 default: 125 request.maxSize = defaultMaxReadSize 126 } 127 128 request.options = options 129 130 request.extensions = make(map[string]struct{}) 131 request.denyList = make(map[string]struct{}) 132 133 for _, extension := range request.Extensions { 134 switch { 135 case extension == "all": 136 request.allExtensions = true 137 case request.MimeType && filetype.IsMIMESupported(extension): 138 continue 139 default: 140 if !strings.HasPrefix(extension, ".") { 141 extension = "." + extension 142 } 143 request.extensions[extension] = struct{}{} 144 } 145 } 146 request.mimeTypesChecks = extractMimeTypes(request.Extensions) 147 148 // process default denylist (extensions) 149 var denyList []string 150 if !request.Archive { 151 denyList = append(defaultDenylist, defaultArchiveDenyList...) 152 } else { 153 denyList = defaultDenylist 154 } 155 for _, excludeItem := range denyList { 156 if !strings.HasPrefix(excludeItem, ".") { 157 excludeItem = "." + excludeItem 158 } 159 request.denyList[excludeItem] = struct{}{} 160 } 161 for _, excludeItem := range request.DenyList { 162 request.denyList[excludeItem] = struct{}{} 163 // also add a cleaned version as the exclusion path can be dirty (eg. /a/b/c, /a/b/c/, a///b///c/../d) 164 request.denyList[filepath.Clean(excludeItem)] = struct{}{} 165 } 166 request.denyMimeTypesChecks = extractMimeTypes(request.DenyList) 167 return nil 168 } 169 170 func matchAnyMimeTypes(data []byte, mimeTypes []string) bool { 171 for _, mimeType := range mimeTypes { 172 if filetype.Is(data, mimeType) { 173 return true 174 } 175 } 176 return false 177 } 178 179 func extractMimeTypes(m []string) []string { 180 var mimeTypes []string 181 for _, mm := range m { 182 if !filetype.IsMIMESupported(mm) { 183 continue 184 } 185 mimeTypes = append(mimeTypes, mm) 186 } 187 return mimeTypes 188 } 189 190 // Requests returns the total number of requests the YAML rule will perform 191 func (request *Request) Requests() int { 192 return 0 193 }