diff --git a/README.md b/README.md index 1722084..9575e99 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,11 @@ Scan file for vulnerability and remove the vulnerable class. ./bin/log4fix fix --overwrite ``` +Scan directory for vulnerability. +``` +./bin/log4fix scan +``` + We recommend taking a backup of the files prior to overwriting them. On Windows, it may be necessary to stop the service prior to applying the fix. Once the fix has been applied, the service should be restarted. diff --git a/finder/scan.go b/finder/scan.go new file mode 100644 index 0000000..de85ddb --- /dev/null +++ b/finder/scan.go @@ -0,0 +1,29 @@ +package finder + +import ( + "os" + "path/filepath" + "regexp" +) + +// Finds war, ear and jar files and returns their absolute paths. +func Scan(dirPath string) ([]string, error) { + compressedFiles := []string{} + r, _ := regexp.Compile(`.*\.(jar|war|ear)`) + err := filepath.Walk(dirPath, + func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + filename := filepath.Base(path) + + if r.MatchString(filename) { + compressedFiles = append(compressedFiles, path) + } + return nil + }) + if err != nil { + return compressedFiles, err + } + return compressedFiles, nil +} diff --git a/main.go b/main.go index 90f807d..43264e0 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "os" "github.com/nanitor/log4fix/finder" @@ -16,7 +17,7 @@ func main() { { Name: "detect", - Usage: "Scan file system for log4j vulnerability", + Usage: "Scan compressed file for log4j vulnerability", Action: func(c *cli.Context) { finder.LoggerInit() if len(c.Args()) == 0 { @@ -47,7 +48,7 @@ func main() { }, { Name: "fix", - Usage: "Scan file system for log4j vulnerability and delete the vulnerable class. Note, this command overwrites the given file.", + Usage: "Scan compressed file for log4j vulnerability and delete the vulnerable class. Note, this command overwrites the given file.", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "overwrite", @@ -89,6 +90,70 @@ func main() { } }, }, + { + Name: "scan", + Usage: "Scan file system for log4j vulnerability.", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "fix", + Usage: "If this flag is present the command scans the directory and removes all instances of the vulnerable class.", + }, + }, + Action: func(c *cli.Context) { + finder.LoggerInit() + if len(c.Args()) == 0 { + finder.ErrorLogger.Fatalf("Please specify path to directory as first argument.") + } + rootDir := c.Args()[0] + finder.InfoLogger.Printf("scanning...\n") + paths, err := finder.Scan(rootDir) + if err != nil { + finder.ErrorLogger.Fatalf("%v\n", err) + } + + vulnFiles := []string{} + for _, path := range paths { + finder.InfoLogger.Printf("Scanning %s for log4j\n", path) + hasLog4Jar, isVuln, vulnPath, err := finder.ArchiveVulnerableLog4shell(path) + if err != nil { + finder.ErrorLogger.Fatalf("err: %v\n", err) + } + + if hasLog4Jar { + if isVuln { + finder.InfoLogger.Printf("Log4 jar file found - Vulnerable - has JndiLookup.class\n") + vulnFiles = append(vulnFiles, path) + + if c.Bool("fix") { + finder.FixFile(path, []string{vulnPath}) + } + + } else { + finder.InfoLogger.Printf("Log4 jar file found - NOT Vulnerable - missing JndiLookup.class\n") + } + } else { + finder.InfoLogger.Printf("Not vulnerable\n") + } + + fmt.Println() + } + + finder.InfoLogger.Printf("Number of war/jar/ear files: %d\n", len(paths)) + finder.InfoLogger.Printf("Number of war/jar/ear files containing log4j vulnerability: %d\n", len(vulnFiles)) + + fmt.Println() + + if len(vulnFiles) > 0 && !c.Bool("fix") { + finder.InfoLogger.Println("Run the following to fix the files:") + for _, filepath := range vulnFiles { + fmt.Printf("\t log4fix fix %s --overwrite\n", filepath) + } + + finder.InfoLogger.Println("Or run this command with flag --fix") + } + + }, + }, } app.Run(os.Args)