2022-08-10 23:35:58 +05:30
package disk
2020-12-29 18:02:45 +05:30
import (
2022-05-08 08:52:21 +02:00
"io/fs"
2020-12-29 18:02:45 +05:30
"os"
"path/filepath"
"strings"
2023-05-04 01:43:41 +05:30
"github.com/logrusorgru/aurora"
2020-12-29 18:02:45 +05:30
"github.com/pkg/errors"
2023-10-17 17:44:13 +05:30
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
2023-03-16 09:03:59 +01:00
stringsutil "github.com/projectdiscovery/utils/strings"
2023-05-04 01:43:41 +05:30
updateutils "github.com/projectdiscovery/utils/update"
urlutil "github.com/projectdiscovery/utils/url"
2020-12-29 18:02:45 +05:30
)
2023-05-04 01:43:41 +05:30
var deprecatedPathsCounter int
2020-12-29 18:02:45 +05:30
// GetTemplatesPath returns a list of absolute paths for the provided template list.
2022-12-21 22:48:43 +05:30
func ( c * DiskCatalog ) GetTemplatesPath ( definitions [ ] string ) ( [ ] string , map [ string ] error ) {
2020-12-29 18:02:45 +05:30
// keeps track of processed dirs and files
processed := make ( map [ string ] bool )
allTemplates := [ ] string { }
2022-12-21 22:48:43 +05:30
erred := make ( map [ string ] error )
2020-12-29 18:02:45 +05:30
for _ , t := range definitions {
2023-03-16 09:03:59 +01:00
if stringsutil . ContainsAny ( t , knownConfigFiles ... ) {
// TODO: this is a temporary fix to avoid treating these files as templates
// this should be replaced with more appropriate and robust logic
continue
}
2023-05-04 01:43:41 +05:30
if strings . Contains ( t , urlutil . SchemeSeparator ) && stringsutil . ContainsAny ( t , config . GetSupportTemplateFileExtensions ( ) ... ) {
2022-01-12 18:33:17 +05:30
if _ , ok := processed [ t ] ; ! ok {
processed [ t ] = true
allTemplates = append ( allTemplates , t )
}
} else {
paths , err := c . GetTemplatePath ( t )
if err != nil {
2022-12-21 22:48:43 +05:30
erred [ t ] = err
2022-01-12 18:33:17 +05:30
}
for _ , path := range paths {
if _ , ok := processed [ path ] ; ! ok {
processed [ path ] = true
allTemplates = append ( allTemplates , path )
}
2020-12-29 18:02:45 +05:30
}
}
}
2023-08-01 14:33:43 -04:00
// purge all false positives
2023-03-16 09:03:59 +01:00
filteredTemplates := [ ] string { }
for _ , v := range allTemplates {
// TODO: this is a temporary fix to avoid treating these files as templates
// this should be replaced with more appropriate and robust logic
if ! stringsutil . ContainsAny ( v , knownConfigFiles ... ) {
filteredTemplates = append ( filteredTemplates , v )
}
}
return filteredTemplates , erred
2020-12-29 18:02:45 +05:30
}
// GetTemplatePath parses the specified input template path and returns a compiled
// list of finished absolute paths to the templates evaluating any glob patterns
// or folders provided as in.
2022-08-10 23:35:58 +05:30
func ( c * DiskCatalog ) GetTemplatePath ( target string ) ( [ ] string , error ) {
2020-12-29 18:02:45 +05:30
processed := make ( map [ string ] struct { } )
// Template input includes a wildcard
2023-05-04 01:43:41 +05:30
if strings . Contains ( target , "*" ) {
matches , findErr := c . findGlobPathMatches ( target , processed )
2021-02-26 13:13:11 +05:30
if findErr != nil {
return nil , errors . Wrap ( findErr , "could not find glob matches" )
2020-12-29 18:02:45 +05:30
}
if len ( matches ) == 0 {
return nil , errors . Errorf ( "no templates found for path" )
}
return matches , nil
}
2023-05-04 01:43:41 +05:30
// try to handle deprecated template paths
2024-06-22 15:42:01 -04:00
absPath := target
if c . templatesFS == nil {
absPath = BackwardsCompatiblePaths ( c . templatesDirectory , target )
if absPath != target && strings . TrimPrefix ( absPath , c . templatesDirectory + string ( filepath . Separator ) ) != target {
if config . DefaultConfig . LogAllEvents {
2025-07-09 14:47:26 -05:00
config . DefaultConfig . Logger . Print ( ) . Msgf ( "[%v] requested Template path %s is deprecated, please update to %s\n" , aurora . Yellow ( "WRN" ) . String ( ) , target , absPath )
2024-06-22 15:42:01 -04:00
}
deprecatedPathsCounter ++
2023-10-16 14:34:52 +05:30
}
2023-05-04 01:43:41 +05:30
2024-06-22 15:42:01 -04:00
var err error
absPath , err = c . convertPathToAbsolute ( absPath )
if err != nil {
return nil , errors . Wrapf ( err , "could not find template file" )
}
2023-05-04 01:43:41 +05:30
}
2020-12-29 18:02:45 +05:30
// Template input is either a file or a directory
match , file , err := c . findFileMatches ( absPath , processed )
if err != nil {
return nil , errors . Wrap ( err , "could not find file" )
}
if file {
if match != "" {
return [ ] string { match } , nil
}
return nil , nil
}
// Recursively walk down the Templates directory and run all
// the template file checks
matches , err := c . findDirectoryMatches ( absPath , processed )
if err != nil {
return nil , errors . Wrap ( err , "could not find directory matches" )
}
if len ( matches ) == 0 {
2022-04-18 17:21:33 -05:00
return nil , errors . Errorf ( "no templates found in path %s" , absPath )
2020-12-29 18:02:45 +05:30
}
return matches , nil
}
// convertPathToAbsolute resolves the paths provided to absolute paths
2021-11-25 18:54:16 +02:00
// before doing any operations on them regardless of them being BLOB, folders, files, etc.
2022-08-10 23:35:58 +05:30
func ( c * DiskCatalog ) convertPathToAbsolute ( t string ) ( string , error ) {
2020-12-29 18:02:45 +05:30
if strings . Contains ( t , "*" ) {
2021-08-23 14:53:37 +03:00
file := filepath . Base ( t )
absPath , err := c . ResolvePath ( filepath . Dir ( t ) , "" )
2020-12-29 18:02:45 +05:30
if err != nil {
return "" , err
}
2021-08-23 14:53:37 +03:00
return filepath . Join ( absPath , file ) , nil
2020-12-29 18:02:45 +05:30
}
return c . ResolvePath ( t , "" )
}
// findGlobPathMatches returns the matched files from a glob path
2022-08-10 23:35:58 +05:30
func ( c * DiskCatalog ) findGlobPathMatches ( absPath string , processed map [ string ] struct { } ) ( [ ] string , error ) {
2023-08-01 14:33:43 -04:00
// to support globbing on old paths we use brute force to find matches with exit on first match
2023-05-04 01:43:41 +05:30
// trim templateDir if any
relPath := strings . TrimPrefix ( absPath , c . templatesDirectory )
// trim leading slash if any
relPath = strings . TrimPrefix ( relPath , string ( os . PathSeparator ) )
OldPathsResolver := func ( inputGlob string ) [ ] string {
templateDir := c . templatesDirectory
if c . templatesDirectory == "" {
templateDir = "./"
}
2024-06-22 15:42:01 -04:00
if c . templatesFS == nil {
matches , _ := fs . Glob ( os . DirFS ( filepath . Join ( templateDir , "http" ) ) , inputGlob )
if len ( matches ) != 0 {
return matches
}
// condition to support network cve related globs
matches , _ = fs . Glob ( os . DirFS ( filepath . Join ( templateDir , "network" ) ) , inputGlob )
return matches
} else {
sub , err := fs . Sub ( c . templatesFS , filepath . Join ( templateDir , "http" ) )
if err != nil {
return nil
}
matches , _ := fs . Glob ( sub , inputGlob )
if len ( matches ) != 0 {
return matches
}
// condition to support network cve related globs
sub , err = fs . Sub ( c . templatesFS , filepath . Join ( templateDir , "network" ) )
if err != nil {
return nil
}
matches , _ = fs . Glob ( sub , inputGlob )
2023-05-04 01:43:41 +05:30
return matches
}
}
var matched [ ] string
2024-06-22 15:42:01 -04:00
var matches [ ] string
if c . templatesFS == nil {
var err error
matches , err = filepath . Glob ( relPath )
if len ( matches ) != 0 {
matched = append ( matched , matches ... )
} else {
matched = append ( matched , OldPathsResolver ( relPath ) ... )
}
if err != nil && len ( matched ) == 0 {
return nil , errors . Errorf ( "wildcard found, but unable to glob: %s\n" , err )
}
2023-05-04 01:43:41 +05:30
} else {
2024-06-22 15:42:01 -04:00
var err error
matches , err = fs . Glob ( c . templatesFS , relPath )
if len ( matches ) != 0 {
matched = append ( matched , matches ... )
} else {
matched = append ( matched , OldPathsResolver ( relPath ) ... )
}
if err != nil && len ( matched ) == 0 {
return nil , errors . Errorf ( "wildcard found, but unable to glob: %s\n" , err )
}
2020-12-29 18:02:45 +05:30
}
results := make ( [ ] string , 0 , len ( matches ) )
for _ , match := range matches {
if _ , ok := processed [ match ] ; ! ok {
processed [ match ] = struct { } { }
results = append ( results , match )
}
}
return results , nil
}
// findFileMatches finds if a path is an absolute file. If the path
// is a file, it returns true otherwise false with no errors.
2022-08-10 23:35:58 +05:30
func ( c * DiskCatalog ) findFileMatches ( absPath string , processed map [ string ] struct { } ) ( match string , matched bool , err error ) {
2024-06-22 15:42:01 -04:00
if c . templatesFS != nil {
2024-09-20 17:11:22 -04:00
absPath = strings . Trim ( absPath , "/" )
2024-06-22 15:42:01 -04:00
}
var info fs . File
if c . templatesFS == nil {
info , err = os . Open ( absPath )
} else {
2024-09-20 17:11:22 -04:00
// If we were given no path, then it's not a file, it's the root, and we can quietly return.
if absPath == "" {
return "" , false , nil
}
2024-06-22 15:42:01 -04:00
info , err = c . templatesFS . Open ( absPath )
}
if err != nil {
return "" , false , err
}
stat , err := info . Stat ( )
2020-12-29 18:02:45 +05:30
if err != nil {
return "" , false , err
}
2024-06-22 15:42:01 -04:00
if ! stat . Mode ( ) . IsRegular ( ) {
2020-12-29 18:02:45 +05:30
return "" , false , nil
}
if _ , ok := processed [ absPath ] ; ! ok {
processed [ absPath ] = struct { } { }
return absPath , true , nil
}
return "" , true , nil
}
// findDirectoryMatches finds matches for templates from a directory
2022-08-10 23:35:58 +05:30
func ( c * DiskCatalog ) findDirectoryMatches ( absPath string , processed map [ string ] struct { } ) ( [ ] string , error ) {
2020-12-29 18:02:45 +05:30
var results [ ] string
2024-06-22 15:42:01 -04:00
var err error
if c . templatesFS == nil {
err = filepath . WalkDir (
absPath ,
func ( path string , d fs . DirEntry , err error ) error {
// continue on errors
if err != nil {
return nil
}
2025-09-27 15:21:21 +07:00
if ! d . IsDir ( ) && config . IsTemplate ( path ) {
2024-06-22 15:42:01 -04:00
if _ , ok := processed [ path ] ; ! ok {
results = append ( results , path )
processed [ path ] = struct { } { }
}
}
2022-05-08 08:52:21 +02:00
return nil
2024-06-22 15:42:01 -04:00
} ,
)
} else {
2024-09-20 17:11:22 -04:00
// For the special case of the root directory, we need to pass "." to `fs.WalkDir`.
if absPath == "" {
absPath = "."
}
absPath = strings . TrimSuffix ( absPath , "/" )
2024-06-22 15:42:01 -04:00
err = fs . WalkDir (
c . templatesFS ,
absPath ,
func ( path string , d fs . DirEntry , err error ) error {
// continue on errors
if err != nil {
return nil
2020-12-29 18:02:45 +05:30
}
2025-09-27 15:21:21 +07:00
if ! d . IsDir ( ) && config . IsTemplate ( path ) {
2024-06-22 15:42:01 -04:00
if _ , ok := processed [ path ] ; ! ok {
results = append ( results , path )
processed [ path ] = struct { } { }
}
}
return nil
} ,
)
}
2020-12-29 18:02:45 +05:30
return results , err
}
2023-05-04 01:43:41 +05:30
// PrintDeprecatedPathsMsgIfApplicable prints a warning message if any deprecated paths are found
// Unless mode is silent warning message is printed
func PrintDeprecatedPathsMsgIfApplicable ( isSilent bool ) {
if ! updateutils . IsOutdated ( "v9.4.3" , config . DefaultConfig . TemplateVersion ) {
return
}
if deprecatedPathsCounter > 0 && ! isSilent {
2025-07-09 14:47:26 -05:00
config . DefaultConfig . Logger . Print ( ) . Msgf ( "[%v] Found %v template[s] loaded with deprecated paths, update before v3 for continued support.\n" , aurora . Yellow ( "WRN" ) . String ( ) , deprecatedPathsCounter )
2023-05-04 01:43:41 +05:30
}
}