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  }