package yaml import ( "bytes" "errors" "os" "regexp" "strings" "github.com/projectdiscovery/nuclei/v3/pkg/templates/extensions" fileutil "github.com/projectdiscovery/utils/file" stringsutil "github.com/projectdiscovery/utils/strings" ) var reImportsPattern = regexp.MustCompile(`(?m)# !include:(.+.yaml)`) // StrictSyntax determines if pre-processing directives should be observed var StrictSyntax bool // PreProcess all include directives func PreProcess(data []byte) ([]byte, error) { // find all matches like !include:path\n importMatches := reImportsPattern.FindAllSubmatch(data, -1) hasImportDirectives := len(importMatches) > 0 if hasImportDirectives && StrictSyntax { return data, errors.New("include directive preprocessing is disabled") } var replaceItems []string for _, match := range importMatches { var ( matchString string includeFileName string ) matchBytes := match[0] matchString = string(matchBytes) if len(match) > 0 { includeFileName = string(match[1]) } // gets the number of tabs/spaces between the last \n and the beginning of the match matchIndex := bytes.Index(data, matchBytes) lastNewLineIndex := bytes.LastIndex(data[:matchIndex], []byte("\n")) padBytes := data[lastNewLineIndex:matchIndex] // check if the file exists if fileutil.FileExists(includeFileName) { // and in case replace the comment with it includeFileContent, err := os.ReadFile(includeFileName) if err != nil { return nil, err } // if it's yaml, tries to preprocess that too recursively if stringsutil.HasSuffixAny(includeFileName, extensions.YAML) { if subIncludedFileContent, err := PreProcess(includeFileContent); err == nil { includeFileContent = subIncludedFileContent } else { return nil, err } } // pad each line of file content with padBytes includeFileContent = bytes.ReplaceAll(includeFileContent, []byte("\n"), padBytes) replaceItems = append(replaceItems, matchString) replaceItems = append(replaceItems, string(includeFileContent)) } } replacer := strings.NewReplacer(replaceItems...) return []byte(replacer.Replace(string(data))), nil }