From a26303e3680d16fc3b137a912c381de9bc02003a Mon Sep 17 00:00:00 2001 From: phbreitbach Date: Tue, 11 Jan 2022 20:26:08 +0100 Subject: [PATCH 1/8] Gitignore for IDEA --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index eb5a316..c507849 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target +.idea From 30f8d4393fcf11d406a0167e3b54080d058e3515 Mon Sep 17 00:00:00 2001 From: phbreitbach Date: Wed, 12 Jan 2022 00:00:17 +0100 Subject: [PATCH 2/8] OOM Fix Catch OOM to cancel the processing of the offending file but continue to do the filewalk Introducing --oomThreshold Switch --- .../com/mergebase/log4j/Log4JDetector.java | 56 ++++++++++++++++--- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/mergebase/log4j/Log4JDetector.java b/src/main/java/com/mergebase/log4j/Log4JDetector.java index 764e149..1523c39 100644 --- a/src/main/java/com/mergebase/log4j/Log4JDetector.java +++ b/src/main/java/com/mergebase/log4j/Log4JDetector.java @@ -81,9 +81,13 @@ public class Log4JDetector { // This occurs in "DataSourceConnectionSource.class" in 2.17.1 and friends. private static final byte[] IS_CVE_2021_44832_SAFE = Bytes.fromString("JNDI must be enabled by setting log4j2.enableJndiJdbc=true"); + private static final String SWITCH_OOM_THRESHOLD = "--oomThreshold="; + private static boolean verbose = false; private static boolean debug = false; private static boolean json = false; + private static int oomThreshold = 10; + private static int oomCounter = 0; private static Set excludes = new TreeSet(); private static boolean foundHits = false; private static boolean foundLog4j1 = false; @@ -132,6 +136,19 @@ public static void main(String[] args) throws IOException { byte[] b = Bytes.streamToBytes(System.in); String s = new String(b, Bytes.UTF_8); stdinLines = Strings.intoLines(s); + } else if (argOrig.startsWith(SWITCH_OOM_THRESHOLD)) { + String thresholdString = argOrig.substring(SWITCH_OOM_THRESHOLD.length()); + it.remove(); + try { + oomThreshold = Integer.parseInt(thresholdString); + // don't need to check against negative numbers here - the simply the first OOM will lead to + // abort as we begin counting by 0; + } catch(NumberFormatException e) { + System.err.println("Illegal value for " + SWITCH_OOM_THRESHOLD + "<" + thresholdString + ">, you need to specify an int. " + + "Aborting ... Stack Trace Follows:"); + e.printStackTrace(); + System.exit(103); + } } else { File f; if (argOrig.length() == 2 && ':' == argOrig.charAt(1) && Character.isLetter(argOrig.charAt(0))) { @@ -149,26 +166,31 @@ public static void main(String[] args) throws IOException { if (argsList.isEmpty()) { System.out.println(); - System.out.println("Usage: java -jar log4j-detector-2021.12.29.jar [--verbose] [--json] [--stdin] [--exclude=X] [paths to scan...]"); + System.out.println("Usage: java -jar log4j-detector-.jar [--verbose] [--json] " + + "[--stdin] [--exclude=X] [--oomThreshold=Y] [paths to scan...]"); System.out.println(); - System.out.println(" --json - Output STDOUT results in JSON. (Errors/warning still emitted to STDERR)"); - System.out.println(" --stdin - Parse STDIN for paths to explore."); - System.out.println(" --exclude=X - Where X is a JSON list containing full paths to exclude. Must be valid JSON."); + System.out.println(" --json - Output STDOUT results in JSON. (Errors/warning still emitted to STDERR)"); + System.out.println(" --stdin - Parse STDIN for paths to explore."); + System.out.println(" --exclude=X - Where X is a JSON list containing full paths to exclude. Must be valid JSON."); System.out.println(); - System.out.println(" Example: --exclude='[\"/dev\", \"/media\", \"Z:\\TEMP\"]' "); + System.out.println(" Example: --exclude='[\"/dev\", \"/media\", \"Z:\\TEMP\"]' "); + System.out.println(); + System.out.println(" --oomThreshold - Specifies how many OutOfMemoryErrors should be catched during analyzing ZIP files before " + + "aborting the run as an int. If 0 or negative, no OutOfMemoryError will be catched. If omitted, defaults to 10."); System.out.println(); System.out.println("Exit codes: 0 = No vulnerable Log4J versions found."); System.out.println(" 1 = At least one legacy Log4J 1.x version found."); System.out.println(" 2 = At least one vulnerable Log4J 2.x version found."); System.out.println(); - System.out.println("About - MergeBase log4j detector (version 2021.12.29)"); + System.out.println("About - MergeBase log4j detector (version )"); System.out.println("Docs - https://github.com/mergebase/log4j-detector "); System.out.println("(C) Copyright 2021 Mergebase Software Inc. Licensed to you via GPLv3."); System.out.println(); System.exit(100); } - System.err.println("-- github.com/mergebase/log4j-detector v2021.12.29 (by mergebase.com) analyzing paths (could take a while)."); + System.err.println("-- github.com/mergebase/log4j-detector (by mergebase.com) analyzing " + + "paths (could take a while)."); System.err.println("-- Note: specify the '--verbose' flag to have every file examined printed to STDERR."); if (json) { System.out.println("{\"hits\":["); @@ -666,6 +688,26 @@ public void close() { } catch (Exception e) { System.err.println("-- Problem: " + zipFile.getPath() + " FAILED: " + e); e.printStackTrace(System.err); + } catch (OutOfMemoryError oom) { + // Safety Guard for multiple OutOfMemories on low free Heap + // Threshold oomThreshold is checked to limit this + // in some cases a new OutOfMemory might rise from within the catch block - not attempting to handle that + // though ... + if(oomCounter < oomThreshold) { + oomCounter++; + System.err.println("-- Problem: OutOfMemoryError for path: <" + zipFile.getPath() + ">"); + System.err.println("oomCounter now: <" + oomCounter + ">, Stack Trace follows:"); + oom.printStackTrace(System.err); + } else { + oomCounter++; + System.err.println("-- Problem: OutOfMemoryError for path: <" + zipFile.getPath() + ">"); + System.err.println("oom Threshold reached by oomCounter now: <" + oomCounter + ">, ABORTING, Stack " + + "Trace follows:"); + oom.printStackTrace(System.err); + System.err.flush(); + System.exit(104); + } + } finally { myZipper.close(); } From 9811dab6acc65803b5f225e1fd06b5a3524153fb Mon Sep 17 00:00:00 2001 From: phbreitbach Date: Wed, 12 Jan 2022 00:13:02 +0100 Subject: [PATCH 3/8] Named version acccording to named feature branch for OOM Fix --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dac724d..65c5af1 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.mergebase log4j-detector - latest + 20220112-0009-oom GPL-3.0-only From 0efe4bde393e3444a252a24b0017f6978e247b4b Mon Sep 17 00:00:00 2001 From: phbreitbach Date: Wed, 12 Jan 2022 00:27:02 +0100 Subject: [PATCH 4/8] Version back to latest for featire branch (for easy pr) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 65c5af1..dac724d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.mergebase log4j-detector - 20220112-0009-oom + latest GPL-3.0-only From c6d477934177d5f986e36b4721431ec4d47d7668 Mon Sep 17 00:00:00 2001 From: phbreitbach Date: Wed, 12 Jan 2022 00:59:24 +0100 Subject: [PATCH 5/8] --ignoreSymLinks and --ignoreReparsePoints Feature --- pom.xml | 4 +- .../com/mergebase/log4j/Log4JDetector.java | 104 +++++++++++++++++- 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index dac724d..d5dcd3d 100644 --- a/pom.xml +++ b/pom.xml @@ -16,8 +16,8 @@ UTF-8 - 1.6 - 1.6 + 1.7 + 1.7 diff --git a/src/main/java/com/mergebase/log4j/Log4JDetector.java b/src/main/java/com/mergebase/log4j/Log4JDetector.java index 1523c39..decc766 100644 --- a/src/main/java/com/mergebase/log4j/Log4JDetector.java +++ b/src/main/java/com/mergebase/log4j/Log4JDetector.java @@ -19,6 +19,11 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Method; +import java.nio.file.AccessDeniedException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.attribute.DosFileAttributes; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -82,10 +87,14 @@ public class Log4JDetector { private static final byte[] IS_CVE_2021_44832_SAFE = Bytes.fromString("JNDI must be enabled by setting log4j2.enableJndiJdbc=true"); private static final String SWITCH_OOM_THRESHOLD = "--oomThreshold="; + private static final String SWITCH_IGNORE_SYMLINKS = "--ignoreSymLinks"; + private static final String SWITCH_IGNORE_REPARSE_POINTS = "--ignoreReparsePoints"; private static boolean verbose = false; private static boolean debug = false; private static boolean json = false; + private static boolean ignoreSymLinks = false; + private static boolean ignoreReparsePoints = false; private static int oomThreshold = 10; private static int oomCounter = 0; private static Set excludes = new TreeSet(); @@ -96,6 +105,8 @@ public class Log4JDetector { private static String currentPath = null; private static boolean printFullPaths = false; + private static Method methodIsReparsePoint = null; + public static void main(String[] args) throws IOException { currentDir = canonicalize(new File(".")); currentPath = currentDir.getPath(); @@ -136,6 +147,12 @@ public static void main(String[] args) throws IOException { byte[] b = Bytes.streamToBytes(System.in); String s = new String(b, Bytes.UTF_8); stdinLines = Strings.intoLines(s); + } else if (SWITCH_IGNORE_SYMLINKS.equals(argOrig)) { + ignoreSymLinks = true; + it.remove(); + } else if (SWITCH_IGNORE_REPARSE_POINTS.equals(argOrig)) { + ignoreReparsePoints = true; + it.remove(); } else if (argOrig.startsWith(SWITCH_OOM_THRESHOLD)) { String thresholdString = argOrig.substring(SWITCH_OOM_THRESHOLD.length()); it.remove(); @@ -162,12 +179,21 @@ public static void main(String[] args) throws IOException { } } } + + if(!ignoreSymLinks && ignoreReparsePoints) { + // Only the addition of ignoreReparsePoints is allowed as the implementation of Reparse Points is + // considered experimental; this keeps unexpected results to a minimum + System.err.println("Illegal option mix: " + SWITCH_IGNORE_REPARSE_POINTS + " is only legal if " + SWITCH_IGNORE_SYMLINKS + "has been " + + "specified as well."); + System.exit(105); + } + argsList.addAll(stdinLines); if (argsList.isEmpty()) { System.out.println(); System.out.println("Usage: java -jar log4j-detector-.jar [--verbose] [--json] " + - "[--stdin] [--exclude=X] [--oomThreshold=Y] [paths to scan...]"); + "[--stdin] [--exclude=X] [--ignoreSymLinks] [--ignoreReparsePoints] [--oomThreshold=Y] [paths to scan...]"); System.out.println(); System.out.println(" --json - Output STDOUT results in JSON. (Errors/warning still emitted to STDERR)"); System.out.println(" --stdin - Parse STDIN for paths to explore."); @@ -175,7 +201,10 @@ public static void main(String[] args) throws IOException { System.out.println(); System.out.println(" Example: --exclude='[\"/dev\", \"/media\", \"Z:\\TEMP\"]' "); System.out.println(); - System.out.println(" --oomThreshold - Specifies how many OutOfMemoryErrors should be catched during analyzing ZIP files before " + + System.out.println(" --ignoreSymLinks - Use this to ignore symlinks. If not specified, symlinks are followed"); + System.out.println(" --ignoreReparsePoints - Use this only on Windows to ignore Reparse Points. If not specified, Reparse Points are " + + "followed. This option is experimental and only legal if --ignoreSymLinks was set."); + System.out.println(" --oomThreshold=Y - Specifies how many OutOfMemoryErrors should be catched during analyzing ZIP files before " + "aborting the run as an int. If 0 or negative, no OutOfMemoryError will be catched. If omitted, defaults to 10."); System.out.println(); System.out.println("Exit codes: 0 = No vulnerable Log4J versions found."); @@ -795,6 +824,77 @@ private static void analyze(File f) { visited.add(crc); } + if(ignoreSymLinks && Files.isSymbolicLink(f.toPath())){ + System.err.println("-- Info: Skipping symlink [" + path + "] because --ignoreSymLinks is specified."); + return; + } else if(ignoreSymLinks && ignoreReparsePoints){ + // ignoreReparsePoints is only legal on Windows so we can assume to be on Windows here + // The following is Windows specific to exclude Reparse Points as they are not considered symlinks by + // Files.isSymbolicLink + try { + DosFileAttributes attrs = Files.readAttributes(f.toPath(), DosFileAttributes.class, LinkOption.NOFOLLOW_LINKS); + if(methodIsReparsePoint == null) { + // attrs should be an instance of sun.nio.fs.WindowsFileAttributes on Windows, which has a + // method isReparsePoint + // The WindowsFileAttributes class is part of the jdk since 1.7 and still is in all Java 11 JVMs + // (Oracle, Open JDK, Zulu that have been tested). It is not by default importable (seems to be + // a Maven constraint) + // Don't want to import anyway as this would break every call, not only if this code snipped is + // executed + try { + methodIsReparsePoint = attrs.getClass().getDeclaredMethod("isReparsePoint"); + methodIsReparsePoint.setAccessible(true); + } catch ( Exception e) { + + // JVM seems to not support Reparse Points - might be --illegaƶ-access=permit needs to be set + System.err.println("-- Problem: Cannot determine Reparse Points in current setup. You can try" + + " one of the following: (1) Omit --ignoreReparsePoints (2) Try another JVM (3) set " + + "--illegal-access=permit JVM Option (4) do further analysis."); + + System.err.println("-- Java Home: <" + System.getProperty("java.home") + ">."); + System.err.println("-- Java Vendor: <" + System.getProperty("java.vendor") + ">."); + System.err.println("-- Java Version: <" + System.getProperty("java.version") + ">."); + System.err.println("-- OS Arch: <" + System.getProperty("os.arch") + ">."); + System.err.println("-- OS Name: <" + System.getProperty("os.name") + ">."); + System.err.println("-- OS Version: <" + System.getProperty("os.version") + ">."); + System.err.println("-- Security Manager: <" + System.getSecurityManager() + ">."); + System.err.println("-- Aborting due to Exception <" + e.getMessage () + ">, " + + "Stack Trace follows:"); + e.printStackTrace(); + System.exit(105); + } + + } + if(attrs != null) { + boolean isReparsePoint = (boolean) methodIsReparsePoint.invoke(attrs); + if(isReparsePoint) { + // Not symlink but Reparse Point should be a Junction or some old Windows Reparse Point not + // considered a symlink + System.err.println("-- Info: Skipping Junction/ReparsePoint [" + path + "] because " + + "--ignoreReparsePoints is specified."); + return; + } + } else { + // Don't ignore it but give out an info + System.err.println("-- Info: File Attributes could not be read, so no ReparsePoint possible for <" + + path + ">. Continuing the scan for this path."); + } + } catch (AccessDeniedException e) { + // Something is generally wrong, aborting here + System.err.println("-- Problem: Missing permissions for path: <" + f.getPath() + ">. " + + "Stack Trace follows:"); + e.printStackTrace(); + return; + } catch ( Exception e) { + // Something is generally wrong, aborting here + System.err.println("-- Problem: Aborting due to unexpected Exception <" + e.getMessage () + ">, " + + "Stack Trace follows:"); + e.printStackTrace(); + System.exit(106); + } + + } + if (f.isDirectory()) { if (!f.canRead()) { System.err.println("-- Problem: no permission to read directory - " + f.getPath()); From df36954f06364b5ec13f73aecf4515fcaae2a941 Mon Sep 17 00:00:00 2001 From: phbreitbach Date: Wed, 12 Jan 2022 01:39:27 +0100 Subject: [PATCH 6/8] Removed oom from symlink branch to have disjunct feature branches --- .../com/mergebase/log4j/Log4JDetector.java | 40 +------------------ 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/src/main/java/com/mergebase/log4j/Log4JDetector.java b/src/main/java/com/mergebase/log4j/Log4JDetector.java index decc766..f429c8c 100644 --- a/src/main/java/com/mergebase/log4j/Log4JDetector.java +++ b/src/main/java/com/mergebase/log4j/Log4JDetector.java @@ -86,7 +86,6 @@ public class Log4JDetector { // This occurs in "DataSourceConnectionSource.class" in 2.17.1 and friends. private static final byte[] IS_CVE_2021_44832_SAFE = Bytes.fromString("JNDI must be enabled by setting log4j2.enableJndiJdbc=true"); - private static final String SWITCH_OOM_THRESHOLD = "--oomThreshold="; private static final String SWITCH_IGNORE_SYMLINKS = "--ignoreSymLinks"; private static final String SWITCH_IGNORE_REPARSE_POINTS = "--ignoreReparsePoints"; @@ -95,8 +94,6 @@ public class Log4JDetector { private static boolean json = false; private static boolean ignoreSymLinks = false; private static boolean ignoreReparsePoints = false; - private static int oomThreshold = 10; - private static int oomCounter = 0; private static Set excludes = new TreeSet(); private static boolean foundHits = false; private static boolean foundLog4j1 = false; @@ -153,19 +150,6 @@ public static void main(String[] args) throws IOException { } else if (SWITCH_IGNORE_REPARSE_POINTS.equals(argOrig)) { ignoreReparsePoints = true; it.remove(); - } else if (argOrig.startsWith(SWITCH_OOM_THRESHOLD)) { - String thresholdString = argOrig.substring(SWITCH_OOM_THRESHOLD.length()); - it.remove(); - try { - oomThreshold = Integer.parseInt(thresholdString); - // don't need to check against negative numbers here - the simply the first OOM will lead to - // abort as we begin counting by 0; - } catch(NumberFormatException e) { - System.err.println("Illegal value for " + SWITCH_OOM_THRESHOLD + "<" + thresholdString + ">, you need to specify an int. " + - "Aborting ... Stack Trace Follows:"); - e.printStackTrace(); - System.exit(103); - } } else { File f; if (argOrig.length() == 2 && ':' == argOrig.charAt(1) && Character.isLetter(argOrig.charAt(0))) { @@ -193,7 +177,7 @@ public static void main(String[] args) throws IOException { if (argsList.isEmpty()) { System.out.println(); System.out.println("Usage: java -jar log4j-detector-.jar [--verbose] [--json] " + - "[--stdin] [--exclude=X] [--ignoreSymLinks] [--ignoreReparsePoints] [--oomThreshold=Y] [paths to scan...]"); + "[--stdin] [--exclude=X] [--ignoreSymLinks] [--ignoreReparsePoints] [paths to scan...]"); System.out.println(); System.out.println(" --json - Output STDOUT results in JSON. (Errors/warning still emitted to STDERR)"); System.out.println(" --stdin - Parse STDIN for paths to explore."); @@ -204,8 +188,6 @@ public static void main(String[] args) throws IOException { System.out.println(" --ignoreSymLinks - Use this to ignore symlinks. If not specified, symlinks are followed"); System.out.println(" --ignoreReparsePoints - Use this only on Windows to ignore Reparse Points. If not specified, Reparse Points are " + "followed. This option is experimental and only legal if --ignoreSymLinks was set."); - System.out.println(" --oomThreshold=Y - Specifies how many OutOfMemoryErrors should be catched during analyzing ZIP files before " + - "aborting the run as an int. If 0 or negative, no OutOfMemoryError will be catched. If omitted, defaults to 10."); System.out.println(); System.out.println("Exit codes: 0 = No vulnerable Log4J versions found."); System.out.println(" 1 = At least one legacy Log4J 1.x version found."); @@ -717,26 +699,6 @@ public void close() { } catch (Exception e) { System.err.println("-- Problem: " + zipFile.getPath() + " FAILED: " + e); e.printStackTrace(System.err); - } catch (OutOfMemoryError oom) { - // Safety Guard for multiple OutOfMemories on low free Heap - // Threshold oomThreshold is checked to limit this - // in some cases a new OutOfMemory might rise from within the catch block - not attempting to handle that - // though ... - if(oomCounter < oomThreshold) { - oomCounter++; - System.err.println("-- Problem: OutOfMemoryError for path: <" + zipFile.getPath() + ">"); - System.err.println("oomCounter now: <" + oomCounter + ">, Stack Trace follows:"); - oom.printStackTrace(System.err); - } else { - oomCounter++; - System.err.println("-- Problem: OutOfMemoryError for path: <" + zipFile.getPath() + ">"); - System.err.println("oom Threshold reached by oomCounter now: <" + oomCounter + ">, ABORTING, Stack " + - "Trace follows:"); - oom.printStackTrace(System.err); - System.err.flush(); - System.exit(104); - } - } finally { myZipper.close(); } From c3ef3115d4daca2739f00e4c996b8bd8fccc6e6a Mon Sep 17 00:00:00 2001 From: phbreitbach Date: Mon, 17 Jan 2022 20:23:53 +0100 Subject: [PATCH 7/8] Added usage for --ignoreSymLinks/ReparsePoints in README and cited Stackoverflow --- README.md | 11 +++++++---- src/main/java/com/mergebase/log4j/Log4JDetector.java | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 470ddf5..d7b3867 100644 --- a/README.md +++ b/README.md @@ -90,12 +90,15 @@ java -jar log4j-detector-2021.12.29.jar Usage: java -jar log4j-detector-2021.12.29.jar [--verbose] [--json] [--stdin] [--exclude=X] [paths to scan...] - --json - Output STDOUT results in JSON. (Errors/warning still emitted to STDERR) - --stdin - Read STDIN for paths to explore (one path per line) - --exclude=X - Where X is a JSON list containing full paths to exclude. Must be valid JSON. + --json - Output STDOUT results in JSON. (Errors/warning still emitted to STDERR) + --stdin - Read STDIN for paths to explore (one path per line) + --exclude=X - Where X is a JSON list containing full paths to exclude. Must be valid JSON. - Example: --exclude='["/dev", "/media", "Z:\TEMP"]' + Example: --exclude='["/dev", "/media", "Z:\TEMP"]' + --ignoreSymLinks - Use this to ignore symlinks. If not specified, symlinks are followed. + --ignoreReparsePoints - Use this only on Windows to ignore Reparse Points. If not specified, Reparse Points are followed. This option is + experimental and only legal if --ignoreSymLinks was set. Exit codes: 0 = No vulnerable Log4J versions found. 1 = At least one legacy Log4J 1.x version found. 2 = At least one vulnerable Log4J version found. diff --git a/src/main/java/com/mergebase/log4j/Log4JDetector.java b/src/main/java/com/mergebase/log4j/Log4JDetector.java index f429c8c..6e8e111 100644 --- a/src/main/java/com/mergebase/log4j/Log4JDetector.java +++ b/src/main/java/com/mergebase/log4j/Log4JDetector.java @@ -794,6 +794,7 @@ private static void analyze(File f) { // The following is Windows specific to exclude Reparse Points as they are not considered symlinks by // Files.isSymbolicLink try { + // Code adapted from Stackoverflow: https://stackoverflow.com/a/29647840 DosFileAttributes attrs = Files.readAttributes(f.toPath(), DosFileAttributes.class, LinkOption.NOFOLLOW_LINKS); if(methodIsReparsePoint == null) { // attrs should be an instance of sun.nio.fs.WindowsFileAttributes on Windows, which has a From aff5120f0e88e070cf25bde33d4efb1b52715b2e Mon Sep 17 00:00:00 2001 From: phbreitbach Date: Mon, 17 Jan 2022 20:29:10 +0100 Subject: [PATCH 8/8] Add syymlink/reparsePoint sitches in sample call --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d7b3867..a8c6d70 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,8 @@ old-hits/log4j-core-2.0-beta2.jar contains Log4J-2.x <= 2.0-beta8 _POTENTIALLY ``` java -jar log4j-detector-2021.12.29.jar -Usage: java -jar log4j-detector-2021.12.29.jar [--verbose] [--json] [--stdin] [--exclude=X] [paths to scan...] +Usage: java -jar log4j-detector-2021.12.29.jar [--verbose] [--json] [--stdin] [--ignoreSymLinks] [--ignoreReparsePoints] [--exclude=X] [paths to +scan...] --json - Output STDOUT results in JSON. (Errors/warning still emitted to STDERR) --stdin - Read STDIN for paths to explore (one path per line)