diff --git a/.gitignore b/.gitignore index 5fff7a394..b79573ae8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ # Eclipse project files .classpath .project +.settings +maven-eclipse.xml +.externalToolBuilders # Netbeans configuration nb-configuration.xml /target/ diff --git a/dependency-check-ant/pom.xml b/dependency-check-ant/pom.xml index 632805a00..19cd62adb 100644 --- a/dependency-check-ant/pom.xml +++ b/dependency-check-ant/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.2.5 + 1.2.6-SNAPSHOT dependency-check-ant diff --git a/dependency-check-ant/src/site/markdown/configuration.md b/dependency-check-ant/src/site/markdown/configuration.md index d09ea5334..c0065a7e8 100644 --- a/dependency-check-ant/src/site/markdown/configuration.md +++ b/dependency-check-ant/src/site/markdown/configuration.md @@ -46,17 +46,17 @@ Note, that specific analyzers will automatically disable themselves if no file types that they support are detected - so specifically disabling them may not be needed. -Property | Description | Default Value -------------------------|------------------------------------|------------------ -archiveAnalyzerEnabled | Sets whether the Archive Analyzer will be used. | true +Property | Description | Default Value +------------------------|---------------------------------------------------------------------------|------------------ +archiveAnalyzerEnabled | Sets whether the Archive Analyzer will be used. | true zipExtensions | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. |   -jarAnalyzer | Sets whether Jar Analyzer will be used. | true -nexusAnalyzerEnabled | Sets whether Nexus Analyzer will be used. | true -nexusUrl | Defines the Nexus URL. | https://repository.sonatype.org/service/local/ +jarAnalyzer | Sets whether Jar Analyzer will be used. | true +nexusAnalyzerEnabled | Sets whether Nexus Analyzer will be used. | true +nexusUrl | Defines the Nexus Pro URL. If not set the Nexus Analyzer will be disabled. |   nexusUsesProxy | Whether or not the defined proxy should be used when connecting to Nexus. | true -nuspecAnalyzerEnabled | Sets whether or not the .NET Nuget Nuspec Analyzer will be used. | true -assemblyAnalyzerEnabled | Sets whether or not the .NET Assembly Analyzer should be used. | true -pathToMono | The path to Mono for .NET assembly analysis on non-windows systems |   +nuspecAnalyzerEnabled | Sets whether or not the .NET Nuget Nuspec Analyzer will be used. | true +assemblyAnalyzerEnabled | Sets whether or not the .NET Assembly Analyzer should be used. | true +pathToMono | The path to Mono for .NET assembly analysis on non-windows systems. |   Advanced Configuration ==================== diff --git a/dependency-check-cli/pom.xml b/dependency-check-cli/pom.xml index fd2f6ad61..c89fbe155 100644 --- a/dependency-check-cli/pom.xml +++ b/dependency-check-cli/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.2.5 + 1.2.6-SNAPSHOT dependency-check-cli diff --git a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java index 25748e970..bf258397d 100644 --- a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java +++ b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java @@ -21,15 +21,19 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.cli.ParseException; -import org.owasp.dependencycheck.cli.CliParser; import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.org.apache.tools.ant.DirectoryScanner; import org.owasp.dependencycheck.reporting.ReportGenerator; import org.owasp.dependencycheck.utils.LogUtils; import org.owasp.dependencycheck.utils.Settings; @@ -93,7 +97,11 @@ public class App { cli.printVersionInfo(); } else if (cli.isRunScan()) { populateSettings(cli); - runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getApplicationName(), cli.getScanFiles()); + try { + runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getApplicationName(), cli.getScanFiles(), cli.getExcludeList()); + } catch (InvalidScanPathException ex) { + Logger.getLogger(App.class.getName()).log(Level.SEVERE, "An invalid scan path was detected; unable to scan '//*' paths"); + } } else { cli.printHelp(); } @@ -106,18 +114,71 @@ public class App { * @param outputFormat the output format of the report * @param applicationName the application name for the report * @param files the files/directories to scan + * @param excludes the patterns for files/directories to exclude + * + * @throws InvalidScanPathException thrown if the path to scan starts with "//" */ - private void runScan(String reportDirectory, String outputFormat, String applicationName, String[] files) { - Engine scanner = null; + private void runScan(String reportDirectory, String outputFormat, String applicationName, String[] files, + String[] excludes) throws InvalidScanPathException { + Engine engine = null; try { - scanner = new Engine(); - - for (String file : files) { - scanner.scan(file); + engine = new Engine(); + List antStylePaths = new ArrayList(); + if (excludes == null || excludes.length == 0) { + for (String file : files) { + if (file.contains("*") || file.contains("?")) { + antStylePaths.add(file); + } else { + engine.scan(file); + } + } + } else { + antStylePaths = Arrays.asList(files); } - scanner.analyzeDependencies(); - final List dependencies = scanner.getDependencies(); + final Set paths = new HashSet(); + for (String file : antStylePaths) { + final DirectoryScanner scanner = new DirectoryScanner(); + String include = file.replace('\\', '/'); + File baseDir; + + if (include.startsWith("//")) { + throw new InvalidScanPathException("Unable to scan paths specified by //"); + } else if (include.startsWith("./")) { + baseDir = new File("."); + include = include.substring(2); + } else if (include.startsWith("/")) { + baseDir = new File("/"); + include = include.substring(1); + } else if (include.contains("/")) { + final int pos = include.indexOf('/'); + final String tmp = include.substring(0, pos); + if (tmp.contains("*") || tmp.contains("?")) { + baseDir = new File("."); + } else { + baseDir = new File(tmp); + include = include.substring(pos + 1); + } + } else { //no path info - must just be a file in the working directory + baseDir = new File("."); + } + scanner.setBasedir(baseDir); + scanner.setIncludes(include); + if (excludes != null && excludes.length > 0) { + scanner.addExcludes(excludes); + } + scanner.scan(); + if (scanner.getIncludedFilesCount() > 0) { + for (String s : scanner.getIncludedFiles()) { + final File f = new File(baseDir, s); + paths.add(f); + } + } + } + engine.scan(paths); + + engine.analyzeDependencies(); + final List dependencies = engine.getDependencies(); DatabaseProperties prop = null; CveDB cve = null; try { @@ -131,7 +192,7 @@ public class App { cve.close(); } } - final ReportGenerator report = new ReportGenerator(applicationName, dependencies, scanner.getAnalyzers(), prop); + final ReportGenerator report = new ReportGenerator(applicationName, dependencies, engine.getAnalyzers(), prop); try { report.generateReports(reportDirectory, outputFormat); } catch (IOException ex) { @@ -145,8 +206,8 @@ public class App { LOGGER.log(Level.SEVERE, "Unable to connect to the dependency-check database; analysis has stopped"); LOGGER.log(Level.FINE, "", ex); } finally { - if (scanner != null) { - scanner.cleanup(); + if (engine != null) { + engine.cleanup(); } } } diff --git a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/cli/CliParser.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java similarity index 92% rename from dependency-check-cli/src/main/java/org/owasp/dependencycheck/cli/CliParser.java rename to dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java index b3069ab6d..e6018ab99 100644 --- a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/cli/CliParser.java +++ b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java @@ -15,7 +15,7 @@ * * Copyright (c) 2012 Jeremy Long. All Rights Reserved. */ -package org.owasp.dependencycheck.cli; +package org.owasp.dependencycheck; import java.io.File; import java.io.FileNotFoundException; @@ -134,14 +134,33 @@ public final class CliParser { * @throws FileNotFoundException is thrown if the path being validated does not exist. */ private void validatePathExists(String path, String argumentName) throws FileNotFoundException { - if (!path.contains("*.")) { + if (path == null) { + isValid = false; + final String msg = String.format("Invalid '%s' argument: null", argumentName); + throw new FileNotFoundException(msg); + } else if (!path.contains("*") && !path.contains("?")) { final File f = new File(path); - if (!f.exists()) { - isValid = false; - final String msg = String.format("Invalid '%s' argument: '%s'", argumentName, path); - throw new FileNotFoundException(msg); + if ("o".equals(argumentName.substring(0, 1).toLowerCase()) && !"ALL".equals(this.getReportFormat().toUpperCase())) { + final String checkPath = path.toLowerCase(); + if (checkPath.endsWith(".html") || checkPath.endsWith(".xml") || checkPath.endsWith(".htm")) { + if (!f.getParentFile().isDirectory()) { + isValid = false; + final String msg = String.format("Invalid '%s' argument: '%s'", argumentName, path); + throw new FileNotFoundException(msg); + } + } + } else { + if (!f.exists()) { + isValid = false; + final String msg = String.format("Invalid '%s' argument: '%s'", argumentName, path); + throw new FileNotFoundException(msg); + } } - } // else { // TODO add a validation for *.zip extensions rather then relying on the engine to validate it. + } else if (path.startsWith("//") || path.startsWith("\\\\")) { + isValid = false; + final String msg = String.format("Invalid '%s' argument: '%s'%nUnable to scan paths that start with '//'.", argumentName, path); + throw new FileNotFoundException(msg); + } } /** @@ -151,7 +170,6 @@ public final class CliParser { */ @SuppressWarnings("static-access") private Options createCommandLineOptions() { - final Options options = new Options(); addStandardOptions(options); addAdvancedOptions(options); @@ -184,16 +202,22 @@ public final class CliParser { .create(ARGUMENT.APP_NAME_SHORT); final Option path = OptionBuilder.withArgName("path").hasArg().withLongOpt(ARGUMENT.SCAN) - .withDescription("The path to scan - this option can be specified multiple times. To limit the scan" - + " to specific file types *.[ext] can be added to the end of the path.") + .withDescription("The path to scan - this option can be specified multiple times. Ant style" + + " paths are supported (e.g. path/**/*.jar).") .create(ARGUMENT.SCAN_SHORT); + final Option excludes = OptionBuilder.withArgName("pattern").hasArg().withLongOpt(ARGUMENT.EXCLUDE) + .withDescription("Specify and exclusion pattern. This option can be specified multiple times" + + " and it accepts Ant style excludsions.") + .create(); + final Option props = OptionBuilder.withArgName("file").hasArg().withLongOpt(ARGUMENT.PROP) .withDescription("A property file to load.") .create(ARGUMENT.PROP_SHORT); - final Option out = OptionBuilder.withArgName("folder").hasArg().withLongOpt(ARGUMENT.OUT) - .withDescription("The folder to write reports to. This defaults to the current directory.") + final Option out = OptionBuilder.withArgName("path").hasArg().withLongOpt(ARGUMENT.OUT) + .withDescription("The folder to write reports to. This defaults to the current directory. " + + "It is possible to set this to a specific file name if the format argument is not set to ALL.") .create(ARGUMENT.OUT_SHORT); final Option outputFormat = OptionBuilder.withArgName("format").hasArg().withLongOpt(ARGUMENT.OUTPUT_FORMAT) @@ -212,7 +236,11 @@ public final class CliParser { final OptionGroup og = new OptionGroup(); og.addOption(path); + final OptionGroup exog = new OptionGroup(); + exog.addOption(excludes); + options.addOptionGroup(og) + .addOptionGroup(exog) .addOption(out) .addOption(outputFormat) .addOption(appName) @@ -297,7 +325,7 @@ public final class CliParser { .create(); final Option nexusUrl = OptionBuilder.withArgName("url").hasArg().withLongOpt(ARGUMENT.NEXUS_URL) - .withDescription("The url to the Nexus Server.") + .withDescription("The url to the Nexus Pro Server. If not set the Nexus Analyzer will be disabled.") .create(); final Option nexusUsesProxy = OptionBuilder.withArgName("true/false").hasArg().withLongOpt(ARGUMENT.NEXUS_USES_PROXY) @@ -479,7 +507,6 @@ public final class CliParser { options, "", true); - } /** @@ -491,6 +518,15 @@ public final class CliParser { return line.getOptionValues(ARGUMENT.SCAN); } + /** + * Retrieves the list of excluded file patterns specified by the 'exclude' argument. + * + * @return the excluded file patterns + */ + public String[] getExcludeList() { + return line.getOptionValues(ARGUMENT.EXCLUDE); + } + /** * Returns the directory to write the reports to specified on the command line. * @@ -877,5 +913,9 @@ public final class CliParser { * The CLI argument name for setting extra extensions. */ public static final String ADDITIONAL_ZIP_EXTENSIONS = "zipExtensions"; + /** + * Exclude path argument. + */ + public static final String EXCLUDE = "exclude"; } } diff --git a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/InvalidScanPathException.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/InvalidScanPathException.java new file mode 100644 index 000000000..af1ca76d6 --- /dev/null +++ b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/InvalidScanPathException.java @@ -0,0 +1,59 @@ +/* + * Copyright 2014 OWASP. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.owasp.dependencycheck; + +/** + * Thrown if an invalid path is encountered. + * + * @author Jeremy Long + */ +class InvalidScanPathException extends Exception { + + /** + * Creates a new InvalidScanPathException. + */ + public InvalidScanPathException() { + super(); + } + + /** + * Creates a new InvalidScanPathException. + * + * @param msg a message for the exception + */ + public InvalidScanPathException(String msg) { + super(msg); + } + + /** + * Creates a new InvalidScanPathException. + * + * @param ex the cause of the exception + */ + public InvalidScanPathException(Throwable ex) { + super(ex); + } + + /** + * Creates a new InvalidScanPathException. + * + * @param msg a message for the exception + * @param ex the cause of the exception + */ + public InvalidScanPathException(String msg, Throwable ex) { + super(msg, ex); + } +} diff --git a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/cli/package-info.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/cli/package-info.java deleted file mode 100644 index 1d5265535..000000000 --- a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/cli/package-info.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * - * - * org.owasp.dependencycheck.cli - * - * - * Includes utility classes such as the CLI Parser, - * - * -*/ - -package org.owasp.dependencycheck.cli; diff --git a/dependency-check-cli/src/site/markdown/arguments.md b/dependency-check-cli/src/site/markdown/arguments.md index fb7455502..c4a3f4b7d 100644 --- a/dependency-check-cli/src/site/markdown/arguments.md +++ b/dependency-check-cli/src/site/markdown/arguments.md @@ -6,8 +6,9 @@ The following table lists the command line arguments: Short | Argument Name   | Parameter | Description | Requirement -------|-----------------------|-----------------|-------------|------------ \-a | \-\-app | \ | The name of the application being scanned. This is a required argument. | Required - \-s | \-\-scan | \ | The path to scan \- this option can be specified multiple times. It is also possible to specify specific file types that should be scanned by supplying a scan path of '[path]/[to]/[scan]/*.zip'. The wild card can only be used to denote any file-name with a specific extension. | Required - \-o | \-\-out | \ | The folder to write reports to. This defaults to the current directory. | Optional + \-s | \-\-scan | \ | The path to scan \- this option can be specified multiple times. It is also possible to specify Ant style paths (e.g. directory/**/*.jar). | Required + | \-\-exclude | \ | The path patterns to exclude from the scan \- this option can be specified multiple times. This accepts Ant style path patterns (e.g. **/exclude/**) . | Optional + \-o | \-\-out | \ | The folder to write reports to. This defaults to the current directory. If the format is not set to ALL one could specify a specific file name. | Optional \-f | \-\-format | \ | The output format to write to (XML, HTML, VULN, ALL). The default is HTML. | Required \-l | \-\-log | \ | The file path to write verbose logging information. | Optional \-n | \-\-noupdate | | Disables the automatic updating of the CPE data. | Optional @@ -18,26 +19,25 @@ Short | Argument Name   | Parameter | Description | Requir Advanced Options ================ -Short | Argument Name        | Parameter | Description | Default Value --------|-----------------------|-----------------|-------------|--------------- - | \-\-disableArchive | | Sets whether the Archive Analyzer will be used. | false +Short | Argument Name        | Parameter | Description | Default Value +-------|-----------------------|-----------------|-----------------------------------------------------------------------------|--------------- + | \-\-disableArchive | | Sets whether the Archive Analyzer will be used. | false | \-\-zipExtensions | \ | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. |   - | \-\-disableJar | | Sets whether Jar Analyzer will be used. | false - | \-\-disableNexus | | Sets whether Nexus Analyzer will be used. | false - | \-\-disableNexus | | Disable the Nexus Analyzer. |   - | \-\-nexus | \ | The url to the Nexus Server. | https://repository.sonatype.org/service/local/ - | \-\-nexusUsesProxy | \ | Whether or not the defined proxy should be used when connecting to Nexus. | true - | \-\-disableNuspec | | Sets whether or not the .NET Nuget Nuspec Analyzer will be used. | false - | \-\-disableAssembly | | Sets whether or not the .NET Assembly Analyzer should be used. | false - | \-\-pathToMono | \ | The path to Mono for .NET Assembly analysis on non-windows systems. |   - | \-\-proxyserver | \ | The proxy server to use when downloading resources. |   - | \-\-proxyport | \ | The proxy port to use when downloading resources. |   + | \-\-disableJar | | Sets whether Jar Analyzer will be used. | false + | \-\-disableNexus | | Sets whether Nexus Analyzer will be used. | false + | \-\-nexus | \ | The url to the Nexus Pro Server. If not set the Nexus Analyzer will be disabled. |   + | \-\-nexusUsesProxy | \ | Whether or not the defined proxy should be used when connecting to Nexus. | true + | \-\-disableNuspec | | Sets whether or not the .NET Nuget Nuspec Analyzer will be used. | false + | \-\-disableAssembly | | Sets whether or not the .NET Assembly Analyzer should be used. | false + | \-\-pathToMono | \ | The path to Mono for .NET Assembly analysis on non-windows systems. |   + | \-\-proxyserver | \ | The proxy server to use when downloading resources. |   + | \-\-proxyport | \ | The proxy port to use when downloading resources. |   | \-\-connectiontimeout | \ | The connection timeout (in milliseconds) to use when downloading resources. |   - | \-\-proxypass | \ | The proxy password to use when downloading resources. |   - | \-\-proxyuser | \ | The proxy username to use when downloading resources. |   - | \-\-connectionString | \ | The connection string to the database. |   - | \-\-dbDriverName | \ | The database driver name. |   + | \-\-proxypass | \ | The proxy password to use when downloading resources. |   + | \-\-proxyuser | \ | The proxy username to use when downloading resources. |   + | \-\-connectionString | \ | The connection string to the database. |   + | \-\-dbDriverName | \ | The database driver name. |   | \-\-dbDriverPath | \ | The path to the database driver; note, this does not need to be set unless the JAR is outside of the class path. |   - | \-\-dbPassword | \ | The password for connecting to the database. |   - | \-\-dbUser | \ | The username used to connect to the database. |   + | \-\-dbPassword | \ | The password for connecting to the database. |   + | \-\-dbUser | \ | The username used to connect to the database. |   \-d | \-\-data | \ | The location of the data directory used to store persistent data. This option should generally not be set. |   diff --git a/dependency-check-cli/src/test/java/org/owasp/dependencycheck/cli/CliParserTest.java b/dependency-check-cli/src/test/java/org/owasp/dependencycheck/CliParserTest.java similarity index 99% rename from dependency-check-cli/src/test/java/org/owasp/dependencycheck/cli/CliParserTest.java rename to dependency-check-cli/src/test/java/org/owasp/dependencycheck/CliParserTest.java index 1c62ed81e..119397278 100644 --- a/dependency-check-cli/src/test/java/org/owasp/dependencycheck/cli/CliParserTest.java +++ b/dependency-check-cli/src/test/java/org/owasp/dependencycheck/CliParserTest.java @@ -15,8 +15,9 @@ * * Copyright (c) 2012 Jeremy Long. All Rights Reserved. */ -package org.owasp.dependencycheck.cli; +package org.owasp.dependencycheck; +import org.owasp.dependencycheck.CliParser; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; diff --git a/dependency-check-core/pom.xml b/dependency-check-core/pom.xml index 5b38375a4..8512ae327 100644 --- a/dependency-check-core/pom.xml +++ b/dependency-check-core/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.2.5 + 1.2.6-SNAPSHOT dependency-check-core @@ -400,6 +400,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. 3.1 false + -Xlint:unchecked 1.6 1.6 @@ -464,50 +465,6 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. velocity 1.7 - - org.apache.velocity - velocity-tools - 2.0 - - - - commons-chain - commons-chain - - - javax.servlet - servlet-api - - - commons-validator - commons-validator - - - dom4j - dom4j - - - sslext - sslext - - - org.apache.struts - struts-core - - - antlr - antlr - - - org.apache.struts - struts-taglib - - - org.apache.struts - struts-tiles - - - com.h2database h2 @@ -751,6 +708,21 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. provided true + + com.sun.jersey + jersey-client + 1.11.1 + provided + true + + + com.sun.faces + jsf-impl + 2.2.8-02 + provided + true + + diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java index 3dfb16489..e2a73d8e9 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java @@ -62,11 +62,11 @@ public class Engine implements Serializable { /** * A Map of analyzers grouped by Analysis phase. */ - private transient final EnumMap> analyzers; + private final transient EnumMap> analyzers; /** * A Map of analyzers grouped by Analysis phase. */ - private transient final Set fileTypeAnalyzers; + private final transient Set fileTypeAnalyzers; /** * The ClassLoader to use when dynamically loading Analyzer and Update services. */ @@ -74,7 +74,7 @@ public class Engine implements Serializable { /** * The Logger for use throughout the class. */ - private transient static final Logger LOGGER = Logger.getLogger(Engine.class.getName()); + private static final transient Logger LOGGER = Logger.getLogger(Engine.class.getName()); /** * Creates a new Engine. @@ -168,142 +168,171 @@ public class Engine implements Serializable { * Scans an array of files or directories. If a directory is specified, it will be scanned recursively. Any * dependencies identified are added to the dependency collection. * - * @since v0.3.2.5 + * @param paths an array of paths to files or directories to be analyzed + * @return the list of dependencies scanned * - * @param paths an array of paths to files or directories to be analyzed. + * @since v0.3.2.5 */ - public void scan(String[] paths) { + public List scan(String[] paths) { + final List deps = new ArrayList(); for (String path : paths) { final File file = new File(path); - scan(file); + final List d = scan(file); + if (d != null) { + deps.addAll(d); + } } + return deps; } /** * Scans a given file or directory. If a directory is specified, it will be scanned recursively. Any dependencies * identified are added to the dependency collection. * - * @param path the path to a file or directory to be analyzed. + * @param path the path to a file or directory to be analyzed + * @return the list of dependencies scanned */ - public void scan(String path) { - if (path.matches("^.*[\\/]\\*\\.[^\\/:*|?<>\"]+$")) { - final String[] parts = path.split("\\*\\."); - final String[] ext = new String[]{parts[parts.length - 1]}; - final File dir = new File(path.substring(0, path.length() - ext[0].length() - 2)); - if (dir.isDirectory()) { - final List files = (List) org.apache.commons.io.FileUtils.listFiles(dir, ext, true); - scan(files); - } else { - final String msg = String.format("Invalid file path provided to scan '%s'", path); - LOGGER.log(Level.SEVERE, msg); - } - } else { - final File file = new File(path); - scan(file); - } + public List scan(String path) { + final File file = new File(path); + return scan(file); } /** * Scans an array of files or directories. If a directory is specified, it will be scanned recursively. Any * dependencies identified are added to the dependency collection. * - * @since v0.3.2.5 - * * @param files an array of paths to files or directories to be analyzed. + * @return the list of dependencies + * + * @since v0.3.2.5 */ - public void scan(File[] files) { + public List scan(File[] files) { + final List deps = new ArrayList(); for (File file : files) { - scan(file); + final List d = scan(file); + if (d != null) { + deps.addAll(d); + } } + return deps; } /** * Scans a list of files or directories. If a directory is specified, it will be scanned recursively. Any * dependencies identified are added to the dependency collection. * - * @since v0.3.2.5 + * @param files a set of paths to files or directories to be analyzed + * @return the list of dependencies scanned * - * @param files a set of paths to files or directories to be analyzed. + * @since v0.3.2.5 */ - public void scan(Set files) { + public List scan(Set files) { + final List deps = new ArrayList(); for (File file : files) { - scan(file); + final List d = scan(file); + if (d != null) { + deps.addAll(d); + } } + return deps; } /** * Scans a list of files or directories. If a directory is specified, it will be scanned recursively. Any * dependencies identified are added to the dependency collection. * - * @since v0.3.2.5 + * @param files a set of paths to files or directories to be analyzed + * @return the list of dependencies scanned * - * @param files a set of paths to files or directories to be analyzed. + * @since v0.3.2.5 */ - public void scan(List files) { + public List scan(List files) { + final List deps = new ArrayList(); for (File file : files) { - scan(file); + final List d = scan(file); + if (d != null) { + deps.addAll(d); + } } + return deps; } /** * Scans a given file or directory. If a directory is specified, it will be scanned recursively. Any dependencies * identified are added to the dependency collection. * + * @param file the path to a file or directory to be analyzed + * @return the list of dependencies scanned + * * @since v0.3.2.4 * - * @param file the path to a file or directory to be analyzed. */ - public void scan(File file) { + public List scan(File file) { if (file.exists()) { if (file.isDirectory()) { - scanDirectory(file); + return scanDirectory(file); } else { - scanFile(file); + final Dependency d = scanFile(file); + if (d != null) { + final List deps = new ArrayList(); + deps.add(d); + return deps; + } } } + return null; } /** * Recursively scans files and directories. Any dependencies identified are added to the dependency collection. * - * @param dir the directory to scan. + * @param dir the directory to scan + * @return the list of Dependency objects scanned */ - protected void scanDirectory(File dir) { + protected List scanDirectory(File dir) { final File[] files = dir.listFiles(); + final List deps = new ArrayList(); if (files != null) { for (File f : files) { if (f.isDirectory()) { - scanDirectory(f); + final List d = scanDirectory(f); + if (d != null) { + deps.addAll(d); + } } else { - scanFile(f); + final Dependency d = scanFile(f); + deps.add(d); } } } + return deps; } /** * Scans a specified file. If a dependency is identified it is added to the dependency collection. * - * @param file The file to scan. + * @param file The file to scan + * @return the scanned dependency */ - protected void scanFile(File file) { + protected Dependency scanFile(File file) { if (!file.isFile()) { final String msg = String.format("Path passed to scanFile(File) is not a file: %s. Skipping the file.", file.toString()); LOGGER.log(Level.FINE, msg); - return; + return null; } final String fileName = file.getName(); final String extension = FileUtils.getFileExtension(fileName); + Dependency dependency = null; if (extension != null) { if (supportsExtension(extension)) { - final Dependency dependency = new Dependency(file); + dependency = new Dependency(file); dependencies.add(dependency); } } else { - final String msg = String.format("No file extension found on file '%s'. The file was not analyzed.", - file.toString()); + final String msg = String.format("No file extension found on file '%s'. The file was not analyzed.", file.toString()); LOGGER.log(Level.FINEST, msg); } + return dependency; } /** @@ -439,8 +468,7 @@ public class Engine implements Serializable { } catch (UpdateException ex) { LOGGER.log(Level.WARNING, "Unable to update Cached Web DataSource, using local data instead. Results may not include recent vulnerabilities."); - LOGGER.log(Level.FINE, - String.format("Unable to update details for %s", source.getClass().getName()), ex); + LOGGER.log(Level.FINE, String.format("Unable to update details for %s", source.getClass().getName()), ex); } } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java index 9cab329ee..c406185db 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java @@ -110,7 +110,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer { static { final String additionalZipExt = Settings.getString(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS); if (additionalZipExt != null) { - final HashSet ext = new HashSet(Arrays.asList(additionalZipExt)); + final HashSet ext = new HashSet(Arrays.asList(additionalZipExt)); ZIPPABLES.addAll(ext); } EXTENSIONS.addAll(ZIPPABLES); @@ -186,7 +186,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer { if (tempFileLocation != null && tempFileLocation.exists()) { LOGGER.log(Level.FINE, "Attempting to delete temporary files"); final boolean success = FileUtils.delete(tempFileLocation); - if (!success && tempFileLocation != null & tempFileLocation.exists()) { + if (!success && tempFileLocation != null && tempFileLocation.exists() && tempFileLocation.list().length > 0) { LOGGER.log(Level.WARNING, "Failed to delete some temporary files, see the log for more details"); } } @@ -221,9 +221,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer { final String displayPath = String.format("%s%s", dependency.getFilePath(), d.getActualFilePath().substring(tmpDir.getAbsolutePath().length())); - final String displayName = String.format("%s%s%s", + final String displayName = String.format("%s: %s", dependency.getFileName(), - File.separator, d.getFileName()); d.setFilePath(displayPath); d.setFileName(displayName); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CPEAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CPEAnalyzer.java index 97779a515..ea2475539 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CPEAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CPEAnalyzer.java @@ -188,7 +188,9 @@ public class CPEAnalyzer implements Analyzer { if (!vendors.isEmpty() && !products.isEmpty()) { final List entries = searchCPE(vendors, products, dependency.getProductEvidence().getWeighting(), dependency.getVendorEvidence().getWeighting()); - + if (entries == null) { + continue; + } boolean identifierAdded = false; for (IndexEntry e : entries) { if (verifyEntry(e, dependency)) { @@ -250,27 +252,24 @@ public class CPEAnalyzer implements Analyzer { * @param vendorWeightings a list of strings to use to add weighting factors to the vendor field * @param productWeightings Adds a list of strings that will be used to add weighting factors to the product search * @return a list of possible CPE values - * @throws CorruptIndexException when the Lucene index is corrupt - * @throws IOException when the Lucene index is not found - * @throws ParseException when the generated query is not valid */ protected List searchCPE(String vendor, String product, - Set vendorWeightings, Set productWeightings) - throws CorruptIndexException, IOException, ParseException { + Set vendorWeightings, Set productWeightings) { + final ArrayList ret = new ArrayList(MAX_QUERY_RESULTS); final String searchString = buildSearch(vendor, product, vendorWeightings, productWeightings); if (searchString == null) { return ret; } - - final TopDocs docs = cpe.search(searchString, MAX_QUERY_RESULTS); - for (ScoreDoc d : docs.scoreDocs) { - if (d.score >= 0.08) { - final Document doc = cpe.getDocument(d.doc); - final IndexEntry entry = new IndexEntry(); - entry.setVendor(doc.get(Fields.VENDOR)); - entry.setProduct(doc.get(Fields.PRODUCT)); + try { + final TopDocs docs = cpe.search(searchString, MAX_QUERY_RESULTS); + for (ScoreDoc d : docs.scoreDocs) { + if (d.score >= 0.08) { + final Document doc = cpe.getDocument(d.doc); + final IndexEntry entry = new IndexEntry(); + entry.setVendor(doc.get(Fields.VENDOR)); + entry.setProduct(doc.get(Fields.PRODUCT)); // if (d.score < 0.08) { // System.out.print(entry.getVendor()); // System.out.print(":"); @@ -278,13 +277,23 @@ public class CPEAnalyzer implements Analyzer { // System.out.print(":"); // System.out.println(d.score); // } - entry.setSearchScore(d.score); - if (!ret.contains(entry)) { - ret.add(entry); + entry.setSearchScore(d.score); + if (!ret.contains(entry)) { + ret.add(entry); + } } } + return ret; + } catch (ParseException ex) { + final String msg = String.format("Unable to parse: %s", searchString); + LOGGER.log(Level.WARNING, "An error occured querying the CPE data. See the log for more details."); + LOGGER.log(Level.INFO, msg, ex); + } catch (IOException ex) { + final String msg = String.format("IO Error with search string: %s", searchString); + LOGGER.log(Level.WARNING, "An error occured reading CPE data. See the log for more details."); + LOGGER.log(Level.INFO, msg, ex); } - return ret; + return null; } /** @@ -489,10 +498,12 @@ public class CPEAnalyzer implements Analyzer { * @param dependency the Dependency being analyzed * @param vendor the vendor for the CPE being analyzed * @param product the product for the CPE being analyzed + * @param currentConfidence the current confidence being used during analysis * @return true if an identifier was added to the dependency; otherwise false * @throws UnsupportedEncodingException is thrown if UTF-8 is not supported */ - private boolean determineIdentifiers(Dependency dependency, String vendor, String product, Confidence currentConfidence) throws UnsupportedEncodingException { + protected boolean determineIdentifiers(Dependency dependency, String vendor, String product, + Confidence currentConfidence) throws UnsupportedEncodingException { final Set cpes = cve.getCPEs(vendor, product); DependencyVersion bestGuess = new DependencyVersion("-"); Confidence bestGuessConf = null; diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CentralAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CentralAnalyzer.java new file mode 100644 index 000000000..ade039960 --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CentralAnalyzer.java @@ -0,0 +1,167 @@ +package org.owasp.dependencycheck.analyzer; + +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.data.nexus.MavenArtifact; +import org.owasp.dependencycheck.data.central.CentralSearch; +import org.owasp.dependencycheck.dependency.Confidence; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.utils.InvalidSettingException; +import org.owasp.dependencycheck.utils.Settings; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Created by colezlaw on 10/9/14. + */ +public class CentralAnalyzer extends AbstractFileTypeAnalyzer { + /** + * The logger. + */ + private static final Logger LOGGER = Logger.getLogger(CentralAnalyzer.class.getName()); + + /** + * The name of the analyzer. + */ + private static final String ANALYZER_NAME = "Central Analyzer"; + + /** + * The phase in which this analyzer runs. + */ + private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION; + + /** + * The types of files on which this will work. + */ + private static final Set SUPPORTED_EXTENSIONS = newHashSet("jar"); + + /** + * The analyzer should be disabled if there are errors, so this is a flag + * to determine if such an error has occurred. + */ + protected boolean errorFlag = false; + + /** + * The searcher itself. + */ + private CentralSearch searcher; + + /** + * Determine whether to enable this analyzer or not. + * + * @return whether the analyzer should be enabled + */ + @Override + public boolean isEnabled() { + boolean retval = false; + + try { + if (Settings.getBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED)) { + if (!Settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED) + || NexusAnalyzer.DEFAULT_URL.equals(Settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL))) { + LOGGER.info("Enabling the Central analyzer"); + retval = true; + } else { + LOGGER.info("Nexus analyzer is enabled, disabling Central"); + } + } else { + LOGGER.info("Central analyzer disabled"); + } + } catch (InvalidSettingException ise) { + LOGGER.warning("Invalid setting. Disabling the Central analyzer"); + } + + return retval; + } + + /** + * Initializes the analyzer once before any analysis is performed. + * + * @throws Exception if there's an error during initalization + */ + @Override + public void initializeFileTypeAnalyzer() throws Exception { + LOGGER.fine("Initializing Central analyzer"); + LOGGER.fine(String.format("Central analyzer enabled: %s", isEnabled())); + if (isEnabled()) { + final String searchUrl = Settings.getString(Settings.KEYS.ANALYZER_CENTRAL_URL); + LOGGER.fine(String.format("Central Analyzer URL: %s", searchUrl)); + searcher = new CentralSearch(new URL(searchUrl)); + } + } + + /** + * Returns the analyzer's name. + * + * @return the name of the analyzer + */ + @Override + public String getName() { + return ANALYZER_NAME; + } + + /** Returns the key used in the properties file to to reference the analyzer's enabled property. + * + * @return the analyzer's enabled property setting key. + */ + @Override + protected String getAnalyzerEnabledSettingKey() { + return Settings.KEYS.ANALYZER_CENTRAL_ENABLED; + } + + /** + * Returns the analysis phase under which the analyzer runs. + * + * @return the phase under which the analyzer runs + */ + @Override + public AnalysisPhase getAnalysisPhase() { + return ANALYSIS_PHASE; + } + + /** + * Returns the extensions for which this Analyzer runs. + * + * @return the extensions for which this Analyzer runs + */ + @Override + public Set getSupportedExtensions() { + return SUPPORTED_EXTENSIONS; + } + + /** + * Performs the analysis. + * + * @param dependency the dependency to analyze + * @param engine the engine + * @throws AnalysisException when there's an exception during analysis + */ + @Override + public void analyzeFileType(Dependency dependency, Engine engine) throws AnalysisException { + if (errorFlag || !isEnabled()) { + return; + } + + try { + final List mas = searcher.searchSha1(dependency.getSha1sum()); + final Confidence confidence = mas.size() > 1 ? Confidence.HIGH : Confidence.HIGHEST; + for (MavenArtifact ma : mas) { + LOGGER.fine(String.format("Central analyzer found artifact (%s) for dependency (%s)", ma.toString(), dependency.getFileName())); + dependency.addAsEvidence("central", ma, confidence); + } + } catch (IllegalArgumentException iae) { + LOGGER.info(String.format("invalid sha1-hash on %s", dependency.getFileName())); + } catch (FileNotFoundException fnfe) { + LOGGER.fine(String.format("Artifact not found in repository: '%s", dependency.getFileName())); + } catch (IOException ioe) { + LOGGER.log(Level.FINE, "Could not connect to Central search", ioe); + errorFlag = true; + } + } +} diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java index 3c8a67471..f71960cff 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java @@ -112,7 +112,7 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal while (subIterator.hasNext()) { final Dependency nextDependency = subIterator.next(); if (hashesMatch(dependency, nextDependency)) { - if (isCore(dependency, nextDependency)) { + if (firstPathIsShortest(dependency.getFilePath(), nextDependency.getFilePath())) { mergeDependencies(dependency, nextDependency, dependenciesToRemove); } else { mergeDependencies(nextDependency, dependency, dependenciesToRemove); @@ -390,4 +390,43 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal } return false; } + + /** + * Determines which path is shortest; if path lengths are equal then we use compareTo of the string method to + * determine if the first path is smaller. + * + * @param left the first path to compare + * @param right the second path to compare + * @return true if the leftPath is the shortest; otherwise false + */ + protected boolean firstPathIsShortest(String left, String right) { + final String leftPath = left.replace('\\', '/'); + final String rightPath = right.replace('\\', '/'); + + final int leftCount = countChar(leftPath, '/'); + final int rightCount = countChar(rightPath, '/'); + if (leftCount == rightCount) { + return leftPath.compareTo(rightPath) <= 0; + } else { + return leftCount < rightCount; + } + } + + /** + * Counts the number of times the character is present in the string. + * + * @param string the string to count the characters in + * @param c the character to count + * @return the number of times the character is present in the string + */ + private int countChar(String string, char c) { + int count = 0; + final int max = string.length(); + for (int i = 0; i < max; i++) { + if (c == string.charAt(i)) { + count++; + } + } + return count; + } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FalsePositiveAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FalsePositiveAnalyzer.java index 3eb5d46c3..725e32ede 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FalsePositiveAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FalsePositiveAnalyzer.java @@ -93,12 +93,17 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { addFalseNegativeCPEs(dependency); } + /** + * Removes inaccurate matches on springframework CPEs. + * + * @param dependency the dependency to test for and remove known inaccurate CPE matches + */ private void removeBadSpringMatches(Dependency dependency) { String mustContain = null; for (Identifier i : dependency.getIdentifiers()) { if ("maven".contains(i.getType())) { if (i.getValue() != null && i.getValue().startsWith("org.springframework.")) { - int endPoint = i.getValue().indexOf(":", 19); + final int endPoint = i.getValue().indexOf(":", 19); if (endPoint >= 0) { mustContain = i.getValue().substring(19, endPoint).toLowerCase(); break; @@ -107,9 +112,9 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { } } if (mustContain != null) { - Iterator itr = dependency.getIdentifiers().iterator(); + final Iterator itr = dependency.getIdentifiers().iterator(); while (itr.hasNext()) { - Identifier i = itr.next(); + final Identifier i = itr.next(); if ("cpe".contains(i.getType()) && i.getValue() != null && i.getValue().startsWith("cpe:/a:springsource:") @@ -117,7 +122,6 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { itr.remove(); //dependency.getIdentifiers().remove(i); } - } } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java index e4bc0a6b1..8910f704d 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java @@ -73,7 +73,7 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer { public void analyze(Dependency dependency, Engine engine) throws AnalysisException { //strip any path information that may get added by ArchiveAnalyzer, etc. - final File f = new File(dependency.getFileName()); + final File f = dependency.getActualFile(); String fileName = f.getName(); //remove file extension diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java index 97e934840..c7fc30a0d 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java @@ -293,13 +293,27 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { LOGGER.log(Level.FINE, msg, ex); return false; } + File externalPom = null; if (pomEntries.isEmpty()) { - return false; + if (dependency.getActualFilePath().matches(".*\\.m2.repository\\b.*")) { + String pomPath = dependency.getActualFilePath(); + pomPath = pomPath.substring(0, pomPath.lastIndexOf('.')) + ".pom"; + externalPom = new File(pomPath); + if (externalPom.isFile()) { + pomEntries.add(pomPath); + } else { + return false; + } + } else { + return false; + } } for (String path : pomEntries) { Properties pomProperties = null; try { - pomProperties = retrievePomProperties(path, jar); + if (externalPom == null) { + pomProperties = retrievePomProperties(path, jar); + } } catch (IOException ex) { LOGGER.log(Level.FINEST, "ignore this, failed reading a non-existent pom.properties", ex); } @@ -313,11 +327,11 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { final String displayPath = String.format("%s%s%s", dependency.getFilePath(), File.separator, - path); //.replaceAll("[\\/]", File.separator)); + path); final String displayName = String.format("%s%s%s", dependency.getFileName(), File.separator, - path); //.replaceAll("[\\/]", File.separator)); + path); newDependency.setFileName(displayName); newDependency.setFilePath(displayPath); @@ -325,7 +339,11 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { engine.getDependencies().add(newDependency); Collections.sort(engine.getDependencies()); } else { - pom = retrievePom(path, jar); + if (externalPom == null) { + pom = retrievePom(path, jar); + } else { + pom = retrievePom(externalPom); + } foundSomething |= setPomEvidence(dependency, pom, pomProperties, classes); } } catch (AnalysisException ex) { @@ -523,6 +541,41 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { return model; } + /** + * Reads in the specified POM and converts it to a Model. + * + * @param file the pom.xml file + * @return returns a + * @throws AnalysisException is thrown if there is an exception extracting or parsing the POM + * {@link org.owasp.dependencycheck.jaxb.pom.generated.Model} object + */ + private Model retrievePom(File file) throws AnalysisException { + Model model = null; + try { + final FileInputStream stream = new FileInputStream(file); + final InputStreamReader reader = new InputStreamReader(stream, "UTF-8"); + final InputSource xml = new InputSource(reader); + final SAXSource source = new SAXSource(xml); + model = readPom(source); + } catch (SecurityException ex) { + final String msg = String.format("Unable to parse pom '%s'; invalid signature", file.getPath()); + LOGGER.log(Level.WARNING, msg); + LOGGER.log(Level.FINE, null, ex); + throw new AnalysisException(ex); + } catch (IOException ex) { + final String msg = String.format("Unable to parse pom '%s'(IO Exception)", file.getPath()); + LOGGER.log(Level.WARNING, msg); + LOGGER.log(Level.FINE, "", ex); + throw new AnalysisException(ex); + } catch (Throwable ex) { + final String msg = String.format("Unexpected error during parsing of the pom '%s'", file.getPath()); + LOGGER.log(Level.WARNING, msg); + LOGGER.log(Level.FINE, "", ex); + throw new AnalysisException(ex); + } + return model; + } + /** * Retrieves the specified POM from a jar file and converts it to a Model. * @@ -583,9 +636,6 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { final String originalGroupID = groupid; if (groupid != null && !groupid.isEmpty()) { - if (groupid.startsWith("org.") || groupid.startsWith("com.")) { - groupid = groupid.substring(4); - } foundSomething = true; dependency.getVendorEvidence().addEvidence("pom", "groupid", groupid, Confidence.HIGHEST); dependency.getProductEvidence().addEvidence("pom", "groupid", groupid, Confidence.LOW); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NexusAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NexusAnalyzer.java index dbee6a5d7..22db369ad 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NexusAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NexusAnalyzer.java @@ -24,15 +24,18 @@ import java.net.URL; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; + import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.data.nexus.MavenArtifact; import org.owasp.dependencycheck.data.nexus.NexusSearch; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.dependency.Identifier; +import org.owasp.dependencycheck.utils.InvalidSettingException; import org.owasp.dependencycheck.utils.Settings; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + /** * Analyzer which will attempt to locate a dependency on a Nexus service by SHA-1 digest of the dependency. * @@ -48,6 +51,10 @@ import org.owasp.dependencycheck.utils.Settings; * @author colezlaw */ public class NexusAnalyzer extends AbstractFileTypeAnalyzer { + /** + * The default URL - this will be used by the CentralAnalyzer to determine whether to enable this. + */ + public static final String DEFAULT_URL = "https://repository.sonatype.org/service/local/"; /** * The logger. @@ -74,6 +81,33 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer { */ private NexusSearch searcher; + /** + * Determine whether to enable this analyzer or not. + * + * @return whether the analyzer should be enabled + */ + @Override + public boolean isEnabled() { + /* Enable this analyzer ONLY if the Nexus URL has been set to something + other than the default one (if it's the default one, we'll use the + central one) and it's enabled by the user. + */ + boolean retval = false; + try { + if ((! DEFAULT_URL.equals(Settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL))) + && Settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED)) { + LOGGER.info("Enabling Nexus analyzer"); + retval = true; + } else { + LOGGER.info("Nexus analyzer disabled"); + } + } catch (InvalidSettingException ise) { + LOGGER.warning("Invalid setting. Disabling Nexus analyzer"); + } + + return retval; + } + /** * Initializes the analyzer once before any analysis is performed. * @@ -150,31 +184,12 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer { */ @Override public void analyzeFileType(Dependency dependency, Engine engine) throws AnalysisException { + if (! isEnabled()) { + return; + } try { final MavenArtifact ma = searcher.searchSha1(dependency.getSha1sum()); - if (ma.getGroupId() != null && !"".equals(ma.getGroupId())) { - dependency.getVendorEvidence().addEvidence("nexus", "groupid", ma.getGroupId(), Confidence.HIGH); - } - if (ma.getArtifactId() != null && !"".equals(ma.getArtifactId())) { - dependency.getProductEvidence().addEvidence("nexus", "artifactid", ma.getArtifactId(), Confidence.HIGH); - } - if (ma.getVersion() != null && !"".equals(ma.getVersion())) { - dependency.getVersionEvidence().addEvidence("nexus", "version", ma.getVersion(), Confidence.HIGH); - } - if (ma.getArtifactUrl() != null && !"".equals(ma.getArtifactUrl())) { - boolean found = false; - for (Identifier i : dependency.getIdentifiers()) { - if ("maven".equals(i.getType()) && i.getValue().equals(ma.toString())) { - found = true; - i.setConfidence(Confidence.HIGHEST); - i.setUrl(ma.getArtifactUrl()); - break; - } - } - if (!found) { - dependency.addIdentifier("maven", ma.toString(), ma.getArtifactUrl(), Confidence.HIGHEST); - } - } + dependency.addAsEvidence("nexus", ma, Confidence.HIGH); } catch (IllegalArgumentException iae) { //dependency.addAnalysisException(new AnalysisException("Invalid SHA-1")); LOGGER.info(String.format("invalid sha-1 hash on %s", dependency.getFileName())); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/central/CentralSearch.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/central/CentralSearch.java new file mode 100644 index 000000000..8977d13e7 --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/central/CentralSearch.java @@ -0,0 +1,141 @@ +package org.owasp.dependencycheck.data.central; + +import org.owasp.dependencycheck.data.nexus.MavenArtifact; +import org.owasp.dependencycheck.utils.InvalidSettingException; +import org.owasp.dependencycheck.utils.Settings; +import org.owasp.dependencycheck.utils.URLConnectionFactory; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +/** + * Class of methods to search Maven Central via Central. + * + * @author colezlaw + */ +public class CentralSearch { + /** + * The URL for the Central service + */ + private final URL rootURL; + + /** + * Whether to use the Proxy when making requests + */ + private boolean useProxy; + + /** + * Used for logging. + */ + private static final Logger LOGGER = Logger.getLogger(CentralSearch.class.getName()); + + /** + * Determines whether we'll continue using the analyzer. If there's some sort + * of HTTP failure, we'll disable the analyzer. + */ + private boolean isEnabled = true; + + /** + * Creates a NexusSearch for the given repository URL. + * + * @param rootURL the URL of the repository on which searches should execute. + * Only parameters are added to this (so it should end in /select) + */ + public CentralSearch(URL rootURL) { + this.rootURL = rootURL; + if (null != Settings.getString(Settings.KEYS.PROXY_SERVER)) { + useProxy = true; + LOGGER.fine("Using proxy"); + } else { + useProxy = false; + LOGGER.fine("Not using proxy"); + } + } + + /** + * Searches the configured Central URL for the given sha1 hash. If the artifact is found, a + * MavenArtifact is populated with the GAV. + * + * @param sha1 the SHA-1 hash string for which to search + * @return the populated Maven GAV. + * @throws IOException if it's unable to connect to the specified repository or if + * the specified artifact is not found. + */ + public List searchSha1(String sha1) throws IOException { + if (null == sha1 || !sha1.matches("^[0-9A-Fa-f]{40}$")) { + throw new IllegalArgumentException("Invalid SHA1 format"); + } + + final URL url = new URL(rootURL + String.format("?q=1:\"%s\"&wt=xml", sha1)); + + LOGGER.info(String.format("Searching Central url %s", url.toString())); + + // Determine if we need to use a proxy. The rules: + // 1) If the proxy is set, AND the setting is set to true, use the proxy + // 2) Otherwise, don't use the proxy (either the proxy isn't configured, + // or proxy is specifically set to false) + final HttpURLConnection conn = URLConnectionFactory.createHttpURLConnection(url, useProxy); + + conn.setDoOutput(true); + + // JSON would be more elegant, but there's not currently a dependency + // on JSON, so don't want to add one just for this + conn.addRequestProperty("Accept", "application/xml"); + conn.connect(); + + if (conn.getResponseCode() == 200) { + boolean missing = false; + try { + final DocumentBuilder builder = DocumentBuilderFactory + .newInstance().newDocumentBuilder(); + final Document doc = builder.parse(conn.getInputStream()); + final XPath xpath = XPathFactory.newInstance().newXPath(); + final String numFound = xpath.evaluate("/response/result/@numFound", doc); + if ("0".equals(numFound)) { + missing = true; + } else { + ArrayList result = new ArrayList(); + NodeList docs = (NodeList)xpath.evaluate("/response/result/doc", doc, XPathConstants.NODESET); + for (int i = 0; i < docs.getLength(); i++) { + final String g = xpath.evaluate("./str[@name='g']", docs.item(i)); + LOGGER.finest(String.format("GroupId: %s", g)); + final String a = xpath.evaluate("./str[@name='a']", docs.item(i)); + LOGGER.finest(String.format("ArtifactId: %s", a)); + final String v = xpath.evaluate("./str[@name='v']", docs.item(i)); + LOGGER.finest(String.format("Version: %s", v)); + result.add(new MavenArtifact(g, a, v, url.toString())); + } + + return result; + } + } catch (Throwable e) { + // Anything else is jacked up XML stuff that we really can't recover + // from well + throw new IOException(e.getMessage(), e); + } + + if (missing) { + throw new FileNotFoundException("Artifact not found in Central"); + } + } else { + final String msg = String.format("Could not connect to Central received response code: %d %s", + conn.getResponseCode(), conn.getResponseMessage()); + LOGGER.fine(msg); + throw new IOException(msg); + } + + return null; + } +} diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cwe/CweDB.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cwe/CweDB.java index ce2410ec5..ca67107f3 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cwe/CweDB.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cwe/CweDB.java @@ -29,10 +29,12 @@ import java.util.logging.Logger; * @author Jeremy Long */ public final class CweDB { + /** * The Logger. */ private static final Logger LOGGER = Logger.getLogger(CweDB.class.getName()); + /** * Empty private constructor as this is a utility class. */ @@ -55,7 +57,9 @@ public final class CweDB { final String filePath = "data/cwe.hashmap.serialized"; final InputStream input = CweDB.class.getClassLoader().getResourceAsStream(filePath); oin = new ObjectInputStream(input); - return (HashMap) oin.readObject(); + @SuppressWarnings("unchecked") + final HashMap ret = (HashMap) oin.readObject(); + return ret; } catch (ClassNotFoundException ex) { LOGGER.log(Level.WARNING, "Unable to load CWE data. This should not be an issue."); LOGGER.log(Level.FINE, null, ex); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Dependency.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Dependency.java index 9ce32d38d..2f72b3d84 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Dependency.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Dependency.java @@ -26,6 +26,7 @@ import java.util.SortedSet; import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; +import org.owasp.dependencycheck.data.nexus.MavenArtifact; import org.owasp.dependencycheck.utils.Checksum; import org.owasp.dependencycheck.utils.FileUtils; @@ -316,6 +317,41 @@ public class Dependency implements Serializable, Comparable { this.identifiers.add(i); } + /** + * Adds the maven artifact as evidence. + * + * @param source The source of the evidence + * @param mavenArtifact The maven artifact + * @param confidence The confidence level of this evidence + */ + public void addAsEvidence(String source, MavenArtifact mavenArtifact, Confidence confidence) { + if (mavenArtifact.getGroupId() != null && !mavenArtifact.getGroupId().isEmpty()) { + this.getVendorEvidence().addEvidence(source, "groupid", mavenArtifact.getGroupId(), confidence); + } + if (mavenArtifact.getArtifactId() != null && !mavenArtifact.getArtifactId().isEmpty()) { + this.getProductEvidence().addEvidence(source, "artifactid", mavenArtifact.getArtifactId(), confidence); + } + if (mavenArtifact.getVersion() != null && !mavenArtifact.getVersion().isEmpty()) { + this.getVersionEvidence().addEvidence(source, "version", mavenArtifact.getVersion(), confidence); + } + if (mavenArtifact.getArtifactUrl() != null && !mavenArtifact.getArtifactUrl().isEmpty()) { + boolean found = false; + for (Identifier i : this.getIdentifiers()) { + if ("maven".equals(i.getType()) && i.getValue().equals(mavenArtifact.toString())) { + found = true; + i.setConfidence(Confidence.HIGHEST); + i.setUrl(mavenArtifact.getArtifactUrl()); + LOGGER.fine(String.format("Already found identifier %s. Confidence set to highest", i.getValue())); + break; + } + } + if (!found) { + LOGGER.fine(String.format("Adding new maven identifier %s", mavenArtifact.toString())); + this.addIdentifier("maven", mavenArtifact.toString(), mavenArtifact.getArtifactUrl(), Confidence.HIGHEST); + } + } + } + /** * Adds an entry to the list of detected Identifiers for the dependency file. * @@ -324,6 +360,7 @@ public class Dependency implements Serializable, Comparable { public void addIdentifier(Identifier identifier) { this.identifiers.add(identifier); } + /** * A set of identifiers that have been suppressed. */ @@ -441,6 +478,7 @@ public class Dependency implements Serializable, Comparable { public EvidenceCollection getVersionEvidence() { return this.versionEvidence; } + /** * The description of the JAR file. */ @@ -463,6 +501,7 @@ public class Dependency implements Serializable, Comparable { public void setDescription(String description) { this.description = description; } + /** * The license that this dependency uses. */ @@ -485,6 +524,7 @@ public class Dependency implements Serializable, Comparable { public void setLicense(String license) { this.license = license; } + /** * A list of vulnerabilities for this dependency. */ @@ -540,6 +580,7 @@ public class Dependency implements Serializable, Comparable { public void addVulnerability(Vulnerability vulnerability) { this.vulnerabilities.add(vulnerability); } + /** * A collection of related dependencies. */ @@ -579,7 +620,7 @@ public class Dependency implements Serializable, Comparable { * @return an integer representing the natural ordering */ public int compareTo(Dependency o) { - return this.getFileName().compareToIgnoreCase(o.getFileName()); + return this.getFilePath().compareToIgnoreCase(o.getFilePath()); } /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java index d437ebd1f..851ad7826 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java @@ -167,15 +167,28 @@ public class ReportGenerator { */ public void generateReports(String outputDir, String outputFormat) throws IOException, Exception { final String format = outputFormat.toUpperCase(); + final String pathToCheck = outputDir.toLowerCase(); if (format.matches("^(XML|HTML|VULN|ALL)$")) { if ("XML".equalsIgnoreCase(format)) { - generateReports(outputDir, Format.XML); + if (pathToCheck.endsWith(".xml")) { + generateReport("XmlReport", outputDir); + } else { + generateReports(outputDir, Format.XML); + } } if ("HTML".equalsIgnoreCase(format)) { - generateReports(outputDir, Format.HTML); + if (pathToCheck.endsWith(".html") || pathToCheck.endsWith(".htm")) { + generateReport("HtmlReport", outputDir); + } else { + generateReports(outputDir, Format.HTML); + } } if ("VULN".equalsIgnoreCase(format)) { - generateReports(outputDir, Format.VULN); + if (pathToCheck.endsWith(".html") || pathToCheck.endsWith(".htm")) { + generateReport("VulnReport", outputDir); + } else { + generateReports(outputDir, Format.VULN); + } } if ("ALL".equalsIgnoreCase(format)) { generateReports(outputDir, Format.ALL); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionParseException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionParseException.java index 411ac9662..8bf546e28 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionParseException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionParseException.java @@ -26,11 +26,6 @@ import java.io.IOException; */ public class SuppressionParseException extends IOException { - /** - * The serial version UID. - */ - private static final long serialVersionUID = 1L; - /** * Creates a new SuppressionParseException. */ @@ -50,7 +45,7 @@ public class SuppressionParseException extends IOException { /** * Creates a new SuppressionParseException. * - * @param ex the cause of the download failure. + * @param ex the cause of the parse exception */ public SuppressionParseException(Throwable ex) { super(ex); @@ -60,7 +55,7 @@ public class SuppressionParseException extends IOException { * Creates a new SuppressionParseException. * * @param msg a message for the exception. - * @param ex the cause of the download failure. + * @param ex the cause of the parse exception */ public SuppressionParseException(String msg, Throwable ex) { super(msg, ex); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionRule.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionRule.java index 1254d5ea5..90abfdec0 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionRule.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionRule.java @@ -266,10 +266,14 @@ public class SuppressionRule { return gav != null; } + /** + * A flag indicating whether or not the suppression rule is a core/base rule that should not be included in the + * resulting report in the "suppressed" section. + */ private boolean base; /** - * Get the value of base + * Get the value of base. * * @return the value of base */ @@ -278,7 +282,7 @@ public class SuppressionRule { } /** - * Set the value of base + * Set the value of base. * * @param base new value of base */ diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DBUtils.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DBUtils.java index 819e7a798..4ee930733 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DBUtils.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DBUtils.java @@ -54,7 +54,9 @@ public final class DBUtils { int id = 0; try { rs = statement.getGeneratedKeys(); - rs.next(); + if (!rs.next()) { + throw new DatabaseException("Unable to get primary key for inserted row"); + } id = rs.getInt(1); } catch (SQLException ex) { throw new DatabaseException("Unable to get primary key for inserted row"); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersion.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersion.java index b0ba88311..1201c7ca0 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersion.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersion.java @@ -65,7 +65,7 @@ public class DependencyVersion implements Iterable, Comparable(); if (version != null) { - final Pattern rx = Pattern.compile("(\\d+|[a-z]+\\d+|(release|beta|alpha)$)"); + final Pattern rx = Pattern.compile("(\\d+[a-z]{1,3}$|[a-z]+\\d+|\\d+|(release|beta|alpha)$)"); final Matcher matcher = rx.matcher(version.toLowerCase()); while (matcher.find()) { versionParts.add(matcher.group()); @@ -198,8 +198,8 @@ public class DependencyVersion implements Iterable, Comparable= 3) { if (thisVersion.compareToIgnoreCase(otherVersion) >= 0) { ret = false; diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java index b67a5ac05..a938434be 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java @@ -32,7 +32,7 @@ public final class DependencyVersionUtil { /** * Regular expression to extract version numbers from file names. */ - private static final Pattern RX_VERSION = Pattern.compile("\\d+(\\.\\d{1,6})+(\\.?([_-](release|beta|alpha)|[a-zA-Z_-]{1,3}\\d{1,8}))?"); + private static final Pattern RX_VERSION = Pattern.compile("\\d+(\\.\\d{1,6})+(\\.?([_-](release|beta|alpha|\\d+)|[a-zA-Z_-]{1,3}\\d{0,8}))?"); /** * Regular expression to extract a single version number without periods. This is a last ditch effort just to check * in case we are missing a version number using the previous regex. diff --git a/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer b/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer index d752f4294..f2c4509c0 100644 --- a/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer +++ b/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer @@ -8,6 +8,7 @@ org.owasp.dependencycheck.analyzer.CpeSuppressionAnalyzer org.owasp.dependencycheck.analyzer.DependencyBundlingAnalyzer org.owasp.dependencycheck.analyzer.NvdCveAnalyzer org.owasp.dependencycheck.analyzer.VulnerabilitySuppressionAnalyzer +org.owasp.dependencycheck.analyzer.CentralAnalyzer org.owasp.dependencycheck.analyzer.NexusAnalyzer org.owasp.dependencycheck.analyzer.NuspecAnalyzer org.owasp.dependencycheck.analyzer.AssemblyAnalyzer \ No newline at end of file diff --git a/dependency-check-core/src/main/resources/dependencycheck-base-suppression.xml b/dependency-check-core/src/main/resources/dependencycheck-base-suppression.xml index 0b1518d1d..87c465451 100644 --- a/dependency-check-core/src/main/resources/dependencycheck-base-suppression.xml +++ b/dependency-check-core/src/main/resources/dependencycheck-base-suppression.xml @@ -9,6 +9,15 @@ cpe:/a:springsource:spring_framework cpe:/a:vmware:springsource_spring_framework + + + .*spring-security-[^\\/]*\.jar$ + cpe:/a:mod_security:mod_security + cpe:/a:springsource:spring_framework + cpe:/a:vmware:springsource_spring_framework + - org\.glassfish\.jersey\.core:jersey-(client|common):.* + (com\.sun\.jersey|org\.glassfish\.jersey\.core):jersey-(client|common):.* + cpe:/a:oracle:glassfish + cpe:/a:oracle:oracle_client + + + + org\.glassfish\.grizzly:grizzly-framework:.* cpe:/a:oracle:glassfish + + + org\.forgerock\.opendj:opendj-ldap-sdk:.* + cpe:/a:ldap_project:ldap + \ No newline at end of file diff --git a/dependency-check-core/src/main/resources/dependencycheck.properties b/dependency-check-core/src/main/resources/dependencycheck.properties index 0090e0c50..9f764fd0a 100644 --- a/dependency-check-core/src/main/resources/dependencycheck.properties +++ b/dependency-check-core/src/main/resources/dependencycheck.properties @@ -58,3 +58,7 @@ analyzer.nexus.url=https://repository.sonatype.org/service/local/ # If set to true, the proxy will still ONLY be used if the proxy properties (proxy.url, proxy.port) # are configured analyzer.nexus.proxy=true + +# the URL for searching search.maven.org for SHA-1 and whether it's enabled +analyzer.central.enabled=true +analyzer.central.url=http://search.maven.org/solrsearch/select diff --git a/dependency-check-core/src/main/resources/templates/HtmlReport.vsl b/dependency-check-core/src/main/resources/templates/HtmlReport.vsl index 6c6d88120..54d88da56 100644 --- a/dependency-check-core/src/main/resources/templates/HtmlReport.vsl +++ b/dependency-check-core/src/main/resources/templates/HtmlReport.vsl @@ -906,6 +906,6 @@ arising out of or in connection with the use of this tool, the analysis performe ## END SUPPRESSED VULNERABILITIES -


This report contains data retrieved from the National Vulnerability Database.
+


This report contains data retrieved from the National Vulnerability Database.
diff --git a/dependency-check-core/src/main/resources/templates/VulnerabilityReport.vsl b/dependency-check-core/src/main/resources/templates/VulnerabilityReport.vsl index 44ee05e77..9070cc7a8 100644 --- a/dependency-check-core/src/main/resources/templates/VulnerabilityReport.vsl +++ b/dependency-check-core/src/main/resources/templates/VulnerabilityReport.vsl @@ -236,6 +236,6 @@ arising out of or in connection with the use of this tool, the analysis performe -



This report contains data retrieved from the National Vulnerability Database.

+



This report contains data retrieved from the National Vulnerability Database.

diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIntegrationTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIntegrationTest.java index c986ae487..86f1db682 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIntegrationTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIntegrationTest.java @@ -23,7 +23,6 @@ import org.junit.Before; import org.junit.Test; import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; -import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.reporting.ReportGenerator; import org.owasp.dependencycheck.utils.Settings; @@ -42,26 +41,6 @@ public class EngineIntegrationTest extends BaseTest { public void tearDown() { } - /** - * Test of scan method, of class Engine. - * - * @throws Exception is thrown when an exception occurs. - */ - @Test - public void testScan() throws Exception { - String testClasses = "target/test-classes/*.zip"; - boolean autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Engine instance = new Engine(); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate); - instance.scan(testClasses); - assertTrue(instance.getDependencies().size() > 0); - for (Dependency d : instance.getDependencies()) { - assertTrue("non-zip file collected " + d.getFileName(), d.getFileName().toLowerCase().endsWith(".zip")); - } - instance.cleanup(); - } - /** * Test running the entire engine. * @@ -81,8 +60,7 @@ public class EngineIntegrationTest extends BaseTest { cveDB.open(); DatabaseProperties dbProp = cveDB.getDatabaseProperties(); cveDB.close(); - ReportGenerator rg = new ReportGenerator("DependencyCheck", - instance.getDependencies(), instance.getAnalyzers(), dbProp); + ReportGenerator rg = new ReportGenerator("DependencyCheck", instance.getDependencies(), instance.getAnalyzers(), dbProp); rg.generateReports("./target/", "ALL"); instance.cleanup(); } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CPEAnalyzerIntegrationTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CPEAnalyzerIntegrationTest.java index b79d49739..b52cb6139 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CPEAnalyzerIntegrationTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CPEAnalyzerIntegrationTest.java @@ -25,9 +25,11 @@ import java.util.Set; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.queryparser.classic.ParseException; import org.junit.Assert; +import static org.junit.Assert.assertTrue; import org.junit.Test; import org.owasp.dependencycheck.data.cpe.AbstractDatabaseTestCase; import org.owasp.dependencycheck.data.cpe.IndexEntry; +import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Identifier; @@ -183,6 +185,30 @@ public class CPEAnalyzerIntegrationTest extends AbstractDatabaseTestCase { //Assert.assertTrue("Incorrect match - spring", spring.getIdentifiers().get(0).getValue().equals(expResultSpring)); } + /** + * Test of determineIdentifiers method, of class CPEAnalyzer. + * + * @throws Exception is thrown when an exception occurs + */ + @Test + public void testDetermineIdentifiers() throws Exception { + Dependency openssl = new Dependency(); + openssl.getVendorEvidence().addEvidence("test", "vendor", "openssl", Confidence.HIGHEST); + openssl.getProductEvidence().addEvidence("test", "product", "openssl", Confidence.HIGHEST); + openssl.getVersionEvidence().addEvidence("test", "version", "1.0.1c", Confidence.HIGHEST); + + CPEAnalyzer instance = new CPEAnalyzer(); + instance.open(); + instance.determineIdentifiers(openssl, "openssl", "openssl", Confidence.HIGHEST); + instance.close(); + + String expResult = "cpe:/a:openssl:openssl:1.0.1c"; + Identifier expIdentifier = new Identifier("cpe", expResult, expResult); + + assertTrue(openssl.getIdentifiers().contains(expIdentifier)); + + } + /** * Test of searchCPE method, of class CPEAnalyzer. * @@ -193,12 +219,12 @@ public class CPEAnalyzerIntegrationTest extends AbstractDatabaseTestCase { String vendor = "apache software foundation"; String product = "struts 2 core"; String version = "2.1.2"; - String expResult = "cpe:/a:apache:struts:2.1.2"; + String expVendor = "apache"; + String expProduct = "struts"; CPEAnalyzer instance = new CPEAnalyzer(); instance.open(); - //TODO - yeah, not a very good test as the results are the same with or without weighting... Set productWeightings = new HashSet(1); productWeightings.add("struts2"); @@ -206,9 +232,16 @@ public class CPEAnalyzerIntegrationTest extends AbstractDatabaseTestCase { vendorWeightings.add("apache"); List result = instance.searchCPE(vendor, product, productWeightings, vendorWeightings); - //TODO fix this assert - //Assert.assertEquals(expResult, result.get(0).getName()); - instance.close(); + + boolean found = false; + for (IndexEntry entry : result) { + if (expVendor.equals(entry.getVendor()) && expProduct.equals(entry.getProduct())) { + found = true; + break; + } + } + assertTrue("apache:struts was not identified", found); + } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzerTest.java index 897ad6b3e..4978eb694 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzerTest.java @@ -86,4 +86,40 @@ public class DependencyBundlingAnalyzerTest extends BaseTest { assertEquals(expResult, result); } + @Test + public void testFirstPathIsShortest() { + DependencyBundlingAnalyzer instance = new DependencyBundlingAnalyzer(); + + String left = "./a/c.jar"; + String right = "./d/e/f.jar"; + boolean expResult = true; + boolean result = instance.firstPathIsShortest(left, right); + assertEquals(expResult, result); + + left = "./a/b/c.jar"; + right = "./d/e/f.jar"; + expResult = true; + result = instance.firstPathIsShortest(left, right); + assertEquals(expResult, result); + + left = "./d/b/c.jar"; + right = "./a/e/f.jar"; + expResult = false; + result = instance.firstPathIsShortest(left, right); + assertEquals(expResult, result); + + left = "./a/b/c.jar"; + right = "./d/f.jar"; + expResult = false; + result = instance.firstPathIsShortest(left, right); + assertEquals(expResult, result); + + left = "./a/b/c.jar"; + right = "./a/b/c.jar"; + expResult = true; + result = instance.firstPathIsShortest(left, right); + assertEquals(expResult, result); + + } + } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/central/CentralSearchTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/central/CentralSearchTest.java new file mode 100644 index 000000000..d75ef404a --- /dev/null +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/central/CentralSearchTest.java @@ -0,0 +1,63 @@ +package org.owasp.dependencycheck.data.central; + +import org.junit.Before; +import org.junit.Test; +import org.owasp.dependencycheck.BaseTest; +import org.owasp.dependencycheck.data.nexus.MavenArtifact; +import org.owasp.dependencycheck.utils.Settings; + +import java.io.FileNotFoundException; +import java.net.URL; +import java.util.List; +import java.util.logging.Logger; + +import static org.junit.Assert.*; + +/** + * Created by colezlaw on 10/13/14. + */ +public class CentralSearchTest extends BaseTest { + private static final Logger LOGGER = Logger.getLogger(CentralSearchTest.class.getName()); + private CentralSearch searcher; + + @Before + public void setUp() throws Exception { + String centralUrl = Settings.getString(Settings.KEYS.ANALYZER_CENTRAL_URL); + LOGGER.fine(centralUrl); + searcher = new CentralSearch(new URL(centralUrl)); + } + + @Test(expected = IllegalArgumentException.class) + public void testNullSha1() throws Exception { searcher.searchSha1(null); } + + @Test(expected = IllegalArgumentException.class) + public void testMalformedSha1() throws Exception { + searcher.searchSha1("invalid"); + } + + // This test does generate network traffic and communicates with a host + // you may not be able to reach. Remove the @Ignore annotation if you want to + // test it anyway + @Test + public void testValidSha1() throws Exception { + List ma = searcher.searchSha1("9977a8d04e75609cf01badc4eb6a9c7198c4c5ea"); + assertEquals("Incorrect group", "org.apache.maven.plugins", ma.get(0).getGroupId()); + assertEquals("Incorrect artifact", "maven-compiler-plugin", ma.get(0).getArtifactId()); + assertEquals("Incorrect version", "3.1", ma.get(0).getVersion()); + } + + // This test does generate network traffic and communicates with a host + // you may not be able to reach. Remove the @Ignore annotation if you want to + // test it anyway + @Test(expected = FileNotFoundException.class) + public void testMissingSha1() throws Exception { + searcher.searchSha1("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + } + + // This test should give us multiple results back from Central + @Test + public void testMultipleReturns() throws Exception { + List ma = searcher.searchSha1("94A9CE681A42D0352B3AD22659F67835E560D107"); + assertTrue(ma.size() > 1); + } +} diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/dependency/DependencyTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/dependency/DependencyTest.java index 8062ddb11..a3062ac0a 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/dependency/DependencyTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/dependency/DependencyTest.java @@ -23,10 +23,12 @@ import java.util.Set; import org.junit.After; import org.junit.AfterClass; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.owasp.dependencycheck.data.nexus.MavenArtifact; /** * @@ -152,7 +154,7 @@ public class DependencyTest { public void testGetMd5sum() { File file = new File(this.getClass().getClassLoader().getResource("struts2-core-2.1.2.jar").getPath()); Dependency instance = new Dependency(file); -// assertEquals("89CE9E36AA9A9E03F1450936D2F4F8DD0F961F8B", result.getSha1sum()); + //assertEquals("89CE9E36AA9A9E03F1450936D2F4F8DD0F961F8B", result.getSha1sum()); String expResult = "C30B57142E1CCBC1EFD5CD15F307358F"; String result = instance.getMd5sum(); assertEquals(expResult, result); @@ -294,4 +296,34 @@ public class DependencyTest { EvidenceCollection result = instance.getVersionEvidence(); assertTrue(true); //this is just a getter setter pair. } + + /** + * Test of addAsEvidence method, of class Dependency. + */ + @Test + public void testAddAsEvidence() { + Dependency instance = new Dependency(); + MavenArtifact mavenArtifact = new MavenArtifact("group", "artifact", "version", "url"); + instance.addAsEvidence("pom", mavenArtifact, Confidence.HIGH); + assertTrue(instance.getEvidence().contains(Confidence.HIGH)); + assertFalse(instance.getEvidence().getEvidence("pom", "groupid").isEmpty()); + assertFalse(instance.getEvidence().getEvidence("pom", "artifactid").isEmpty()); + assertFalse(instance.getEvidence().getEvidence("pom", "version").isEmpty()); + assertFalse(instance.getIdentifiers().isEmpty()); + } + + /** + * Test of addAsEvidence method, of class Dependency. + */ + @Test + public void testAddAsEvidenceWithEmptyArtefact() { + Dependency instance = new Dependency(); + MavenArtifact mavenArtifact = new MavenArtifact(null, null, null, null); + instance.addAsEvidence("pom", mavenArtifact, Confidence.HIGH); + assertFalse(instance.getEvidence().contains(Confidence.HIGH)); + assertTrue(instance.getEvidence().getEvidence("pom", "groupid").isEmpty()); + assertTrue(instance.getEvidence().getEvidence("pom", "artifactid").isEmpty()); + assertTrue(instance.getEvidence().getEvidence("pom", "version").isEmpty()); + assertTrue(instance.getIdentifiers().isEmpty()); + } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/utils/DependencyVersionUtilTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/utils/DependencyVersionUtilTest.java index 9cb022a06..766e61f7e 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/utils/DependencyVersionUtilTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/utils/DependencyVersionUtilTest.java @@ -54,13 +54,13 @@ public class DependencyVersionUtilTest { * Test of parseVersion method, of class DependencyVersionUtil. */ @Test - public void testParseVersionFromFileName() { + public void testParseVersion() { final String[] fileName = {"something-0.9.5.jar", "lib2-1.1.jar", "lib1.5r4-someflag-R26.jar", "lib-1.2.5-dev-20050313.jar", "testlib_V4.4.0.jar", "lib-core-2.0.0-RC1-SNAPSHOT.jar", "lib-jsp-2.0.1_R114940.jar", "dev-api-2.3.11_R121413.jar", "lib-api-3.7-SNAPSHOT.jar", - "-", "", "1.3-beta", "6"}; + "-", "", "1.3-beta", "6", "openssl1.0.1c", "jsf-impl-2.2.8-02.jar"}; final String[] expResult = {"0.9.5", "1.1", "1.5.r4", "1.2.5", "4.4.0", "2.0.0.rc1", - "2.0.1.r114940", "2.3.11.r121413", "3.7", "-", null, "1.3.beta", "6"}; + "2.0.1.r114940", "2.3.11.r121413", "3.7", "-", null, "1.3.beta", "6", "1.0.1c", "2.2.8.02"}; for (int i = 0; i < fileName.length; i++) { final DependencyVersion version = DependencyVersionUtil.parseVersion(fileName[i]); diff --git a/dependency-check-core/src/test/resources/dependencycheck.properties b/dependency-check-core/src/test/resources/dependencycheck.properties index 61efb2407..ae48d04c2 100644 --- a/dependency-check-core/src/test/resources/dependencycheck.properties +++ b/dependency-check-core/src/test/resources/dependencycheck.properties @@ -59,3 +59,7 @@ analyzer.nexus.url=https://repository.sonatype.org/service/local/ # If set to true, the proxy will still ONLY be used if the proxy properties (proxy.url, proxy.port) # are configured analyzer.nexus.proxy=true + +# the URL for searching search.maven.org for SHA-1 and whether it's enabled +analyzer.central.enabled=true +analyzer.central.url=http://search.maven.org/solrsearch/select diff --git a/dependency-check-jenkins/pom.xml b/dependency-check-jenkins/pom.xml index c656eb1f8..7c87b0e1d 100644 --- a/dependency-check-jenkins/pom.xml +++ b/dependency-check-jenkins/pom.xml @@ -3,7 +3,7 @@ org.owasp dependency-check-parent - 1.2.5 + 1.2.6-SNAPSHOT org.owasp dependency-check-jenkins diff --git a/dependency-check-maven/pom.xml b/dependency-check-maven/pom.xml index 9e024e153..dfbdb8e37 100644 --- a/dependency-check-maven/pom.xml +++ b/dependency-check-maven/pom.xml @@ -22,7 +22,7 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.2.5 + 1.2.6-SNAPSHOT dependency-check-maven diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/DependencyCheckMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/DependencyCheckMojo.java index 26b9ef4c5..b82a69277 100644 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/DependencyCheckMojo.java +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/DependencyCheckMojo.java @@ -46,7 +46,9 @@ import org.apache.maven.settings.Proxy; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.DependencyBundlingAnalyzer; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.data.nexus.MavenArtifact; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; +import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Vulnerability; @@ -58,9 +60,13 @@ import org.owasp.dependencycheck.utils.Settings; * * @author Jeremy Long */ -@Mojo(name = "check", defaultPhase = LifecyclePhase.COMPILE, threadSafe = true, +@Mojo( + name = "check", + defaultPhase = LifecyclePhase.COMPILE, + threadSafe = true, requiresDependencyResolution = ResolutionScope.RUNTIME_PLUS_SYSTEM, - requiresOnline = true) + requiresOnline = true +) public class DependencyCheckMojo extends ReportAggregationMojo { // @@ -292,6 +298,7 @@ public class DependencyCheckMojo extends ReportAggregationMojo { @Parameter(property = "externalReport") @Deprecated private String externalReport = null; + // /** * Constructs a new dependency-check-mojo. @@ -326,8 +333,20 @@ public class DependencyCheckMojo extends ReportAggregationMojo { if (excludeFromScan(a)) { continue; } - - localEngine.scan(a.getFile().getAbsolutePath()); + final List deps = localEngine.scan(a.getFile().getAbsoluteFile()); + if (deps != null) { + if (deps.size() == 1) { + final Dependency d = deps.get(0); + if (d != null) { + final MavenArtifact ma = new MavenArtifact(a.getGroupId(), a.getArtifactId(), a.getVersion()); + d.addAsEvidence("pom", ma, Confidence.HIGHEST); + } + } else { + final String msg = String.format("More then 1 dependency was identified in first pass scan of '%s:%s:%s'", + a.getGroupId(), a.getArtifactId(), a.getVersion()); + LOGGER.info(msg); + } + } } localEngine.analyzeDependencies(); @@ -396,8 +415,7 @@ public class DependencyCheckMojo extends ReportAggregationMojo { } if (proxyUrl != null && !proxyUrl.isEmpty()) { - LOGGER.warning("Deprecated configuration detected, proxyUrl will be ignored; use the maven settings " - + "to configure the proxy instead"); + LOGGER.warning("Deprecated configuration detected, proxyUrl will be ignored; use the maven settings " + "to configure the proxy instead"); } final Proxy proxy = getMavenProxy(); if (proxy != null) { @@ -510,8 +528,8 @@ public class DependencyCheckMojo extends ReportAggregationMojo { } return null; } - // + // /** * Executes the dependency-check and generates the report. * @@ -530,8 +548,7 @@ public class DependencyCheckMojo extends ReportAggregationMojo { checkForFailure(engine.getDependencies()); } } catch (DatabaseException ex) { - LOGGER.log(Level.SEVERE, - "Unable to connect to the dependency-check database; analysis has stopped"); + LOGGER.log(Level.SEVERE, "Unable to connect to the dependency-check database; analysis has stopped"); LOGGER.log(Level.FINE, "", ex); } } @@ -620,7 +637,8 @@ public class DependencyCheckMojo extends ReportAggregationMojo { for (MavenProject child : getAllChildren(project)) { deps = readDataFile(child); if (deps == null) { - final String msg = String.format("Unable to include information on %s in the dependency-check aggregate report", child.getName()); + final String msg = String.format("Unable to include information on %s in the dependency-check aggregate report", + child.getName()); LOGGER.severe(msg); } else { engine.getDependencies().addAll(deps); @@ -646,8 +664,7 @@ public class DependencyCheckMojo extends ReportAggregationMojo { * @return the output name */ public String getOutputName() { - if ("HTML".equalsIgnoreCase(this.format) - || "ALL".equalsIgnoreCase(this.format)) { + if ("HTML".equalsIgnoreCase(this.format) || "ALL".equalsIgnoreCase(this.format)) { return "dependency-check-report"; } else if ("XML".equalsIgnoreCase(this.format)) { return "dependency-check-report.xml#"; @@ -740,8 +757,8 @@ public class DependencyCheckMojo extends ReportAggregationMojo { protected boolean canGenerateAggregateReport() { return isAggregate() && isLastProject(); } - // + // // /** * Checks to see if a vulnerability has been identified with a CVSS score that is above the threshold set in the @@ -807,14 +824,13 @@ public class DependencyCheckMojo extends ReportAggregationMojo { } } if (summary.length() > 0) { - final String msg = String.format("%n%n" - + "One or more dependencies were identified with known vulnerabilities:%n%n%s" + final String msg = String.format("%n%n" + "One or more dependencies were identified with known vulnerabilities:%n%n%s" + "%n%nSee the dependency-check report for more details.%n%n", summary.toString()); LOGGER.log(Level.WARNING, msg); } } - // + // // /** * Writes the scan data to disk. This is used to serialize the scan data between the "check" and "aggregate" phase. diff --git a/dependency-check-maven/src/site/markdown/configuration.md b/dependency-check-maven/src/site/markdown/configuration.md index cccc733fb..da7710d80 100644 --- a/dependency-check-maven/src/site/markdown/configuration.md +++ b/dependency-check-maven/src/site/markdown/configuration.md @@ -23,17 +23,17 @@ Note, that specific analyzers will automatically disable themselves if no file types that they support are detected - so specifically disabling them may not be needed. -Property | Description | Default Value -------------------------|------------------------------------|------------------ -archiveAnalyzerEnabled | Sets whether the Archive Analyzer will be used. | true +Property | Description | Default Value +------------------------|---------------------------------------------------------------------------|------------------ +archiveAnalyzerEnabled | Sets whether the Archive Analyzer will be used. | true zipExtensions | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. |   -jarAnalyzer | Sets whether Jar Analyzer will be used. | true -nexusAnalyzerEnabled | Sets whether Nexus Analyzer will be used. | true -nexusUrl | Defines the Nexus URL. | https://repository.sonatype.org/service/local/ +jarAnalyzer | Sets whether Jar Analyzer will be used. | true +nexusAnalyzerEnabled | Sets whether Nexus Analyzer will be used. | true +nexusUrl | Defines the Nexus Pro Server URL. If not set the Nexus Analyzer will be disabled. |   nexusUsesProxy | Whether or not the defined proxy should be used when connecting to Nexus. | true -nuspecAnalyzerEnabled | Sets whether or not the .NET Nuget Nuspec Analyzer will be used. | true -assemblyAnalyzerEnabled | Sets whether or not the .NET Assembly Analyzer should be used. | true -pathToMono | The path to Mono for .NET assembly analysis on non-windows systems |   +nuspecAnalyzerEnabled | Sets whether or not the .NET Nuget Nuspec Analyzer will be used. | true +assemblyAnalyzerEnabled | Sets whether or not the .NET Assembly Analyzer should be used. | true +pathToMono | The path to Mono for .NET assembly analysis on non-windows systems. |   Advanced Configuration ==================== diff --git a/dependency-check-utils/pom.xml b/dependency-check-utils/pom.xml index fce777241..9dad1ea6f 100644 --- a/dependency-check-utils/pom.xml +++ b/dependency-check-utils/pom.xml @@ -21,7 +21,7 @@ Copyright (c) 2014 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.2.5 + 1.2.6-SNAPSHOT dependency-check-utils @@ -249,6 +249,9 @@ Copyright (c) 2014 - Jeremy Long. All Rights Reserved. 1.6 true utf-8 + + **/org/owasp/dependencycheck/org/apache/**/*.java + ../src/main/config/dcrules.xml /rulesets/java/basic.xml @@ -261,6 +264,9 @@ Copyright (c) 2014 - Jeremy Long. All Rights Reserved. org.codehaus.mojo findbugs-maven-plugin 2.5.3 + + org.owasp.dependencycheck.utils.* + diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/BuildException.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/BuildException.java new file mode 100644 index 000000000..59446d3ce --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/BuildException.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant; + +/** + * Signals an error condition during a build + */ +public class BuildException extends RuntimeException { + + private static final long serialVersionUID = -5419014565354664240L; + + /** Location in the build file where the exception occurred */ + private Location location = Location.UNKNOWN_LOCATION; + + /** + * Constructs a build exception with no descriptive information. + */ + public BuildException() { + super(); + } + + /** + * Constructs an exception with the given descriptive message. + * + * @param message A description of or information about the exception. + * Should not be null. + */ + public BuildException(String message) { + super(message); + } + + /** + * Constructs an exception with the given message and exception as + * a root cause. + * + * @param message A description of or information about the exception. + * Should not be null unless a cause is specified. + * @param cause The exception that might have caused this one. + * May be null. + */ + public BuildException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs an exception with the given message and exception as + * a root cause and a location in a file. + * + * @param msg A description of or information about the exception. + * Should not be null unless a cause is specified. + * @param cause The exception that might have caused this one. + * May be null. + * @param location The location in the project file where the error + * occurred. Must not be null. + */ + public BuildException(String msg, Throwable cause, Location location) { + this(msg, cause); + this.location = location; + } + + /** + * Constructs an exception with the given exception as a root cause. + * + * @param cause The exception that might have caused this one. + * Should not be null. + */ + public BuildException(Throwable cause) { + super(cause); + } + + /** + * Constructs an exception with the given descriptive message and a + * location in a file. + * + * @param message A description of or information about the exception. + * Should not be null. + * @param location The location in the project file where the error + * occurred. Must not be null. + */ + public BuildException(String message, Location location) { + super(message); + this.location = location; + } + + /** + * Constructs an exception with the given exception as + * a root cause and a location in a file. + * + * @param cause The exception that might have caused this one. + * Should not be null. + * @param location The location in the project file where the error + * occurred. Must not be null. + */ + public BuildException(Throwable cause, Location location) { + this(cause); + this.location = location; + } + + /** + * Returns the nested exception, if any. + * + * @return the nested exception, or null if no + * exception is associated with this one + * @deprecated Use {@link #getCause} instead. + */ + public Throwable getException() { + return getCause(); + } + + /** + * Returns the location of the error and the error message. + * + * @return the location of the error and the error message + */ + public String toString() { + return location.toString() + getMessage(); + } + + /** + * Sets the file location where the error occurred. + * + * @param location The file location where the error occurred. + * Must not be null. + */ + public void setLocation(Location location) { + this.location = location; + } + + /** + * Returns the file location where the error occurred. + * + * @return the file location where the error occurred. + */ + public Location getLocation() { + return location; + } + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScanner.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScanner.java new file mode 100644 index 000000000..e65d994a0 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScanner.java @@ -0,0 +1,1816 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; +import java.util.Vector; +import org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition.Os; +import org.owasp.dependencycheck.org.apache.tools.ant.types.Resource; +import org.owasp.dependencycheck.org.apache.tools.ant.types.ResourceFactory; +import org.owasp.dependencycheck.org.apache.tools.ant.types.resources.FileResource; +import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.FileSelector; +import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.SelectorScanner; +import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.SelectorUtils; +import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.TokenizedPath; +import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.TokenizedPattern; +import org.owasp.dependencycheck.org.apache.tools.ant.util.CollectionUtils; +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; +import org.owasp.dependencycheck.org.apache.tools.ant.util.SymbolicLinkUtils; +import org.owasp.dependencycheck.org.apache.tools.ant.util.VectorSet; + +/** + * Class for scanning a directory for files/directories which match certain criteria. + *

+ * These criteria consist of selectors and patterns which have been specified. With the selectors you can select which + * files you want to have included. Files which are not selected are excluded. With patterns you can include or exclude + * files based on their filename. + *

+ * The idea is simple. A given directory is recursively scanned for all files and directories. Each file/directory is + * matched against a set of selectors, including special support for matching against filenames with include and and + * exclude patterns. Only files/directories which match at least one pattern of the include pattern list or other file + * selector, and don't match any pattern of the exclude pattern list or fail to match against a required selector will + * be placed in the list of files/directories found. + *

+ * When no list of include patterns is supplied, "**" will be used, which means that everything will be matched. When no + * list of exclude patterns is supplied, an empty list is used, such that nothing will be excluded. When no selectors + * are supplied, none are applied. + *

+ * The filename pattern matching is done as follows: The name to be matched is split up in path segments. A path segment + * is the name of a directory or file, which is bounded by File.separator ('/' under UNIX, '\' under + * Windows). For example, "abc/def/ghi/xyz.java" is split up in the segments "abc", "def","ghi" and "xyz.java". The same + * is done for the pattern against which should be matched. + *

+ * The segments of the name and the pattern are then matched against each other. When '**' is used for a path segment in + * the pattern, it matches zero or more path segments of the name. + *

+ * There is a special case regarding the use of File.separators at the beginning of the pattern and the + * string to match:
+ * When a pattern starts with a File.separator, the string to match must also start with a + * File.separator. When a pattern does not start with a File.separator, the string to match + * may not start with a File.separator. When one of these rules is not obeyed, the string will not match. + *

+ * When a name path segment is matched against a pattern path segment, the following special characters can be used:
+ * '*' matches zero or more characters
+ * '?' matches one character. + *

+ * Examples: + *

+ * "**\*.class" matches all .class files/dirs in a directory tree. + *

+ * "test\a??.java" matches all files/dirs which start with an 'a', then two more characters and then ".java", in a + * directory called test. + *

+ * "**" matches everything in a directory tree. + *

+ * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where there is a parent directory called test + * (e.g. "abc\test\def\ghi\XYZ123"). + *

+ * Case sensitivity may be turned off if necessary. By default, it is turned on. + *

+ * Example of usage: + *

+ *   String[] includes = {"**\\*.class"};
+ *   String[] excludes = {"modules\\*\\**"};
+ *   ds.setIncludes(includes);
+ *   ds.setExcludes(excludes);
+ *   ds.setBasedir(new File("test"));
+ *   ds.setCaseSensitive(true);
+ *   ds.scan();
+ *
+ *   System.out.println("FILES:");
+ *   String[] files = ds.getIncludedFiles();
+ *   for (int i = 0; i < files.length; i++) {
+ *     System.out.println(files[i]);
+ *   }
+ * 
This will scan a directory called test for .class files, but excludes all files in all proper subdirectories + * of a directory called "modules". + * + */ +public class DirectoryScanner + implements FileScanner, SelectorScanner, ResourceFactory { + + /** + * Is OpenVMS the operating system we're running on? + */ + private static final boolean ON_VMS = Os.isFamily("openvms"); + + /** + * Patterns which should be excluded by default. + * + *

+ * Note that you can now add patterns to the list of default excludes. Added patterns will not become part of this + * array that has only been kept around for backwards compatibility reasons.

+ * + * @deprecated since 1.6.x. Use the {@link #getDefaultExcludes getDefaultExcludes} method instead. + */ + protected static final String[] DEFAULTEXCLUDES = { + // Miscellaneous typical temporary files + SelectorUtils.DEEP_TREE_MATCH + "/*~", + SelectorUtils.DEEP_TREE_MATCH + "/#*#", + SelectorUtils.DEEP_TREE_MATCH + "/.#*", + SelectorUtils.DEEP_TREE_MATCH + "/%*%", + SelectorUtils.DEEP_TREE_MATCH + "/._*", + // CVS + SelectorUtils.DEEP_TREE_MATCH + "/CVS", + SelectorUtils.DEEP_TREE_MATCH + "/CVS/" + SelectorUtils.DEEP_TREE_MATCH, + SelectorUtils.DEEP_TREE_MATCH + "/.cvsignore", + // SCCS + SelectorUtils.DEEP_TREE_MATCH + "/SCCS", + SelectorUtils.DEEP_TREE_MATCH + "/SCCS/" + SelectorUtils.DEEP_TREE_MATCH, + // Visual SourceSafe + SelectorUtils.DEEP_TREE_MATCH + "/vssver.scc", + // Subversion + SelectorUtils.DEEP_TREE_MATCH + "/.svn", + SelectorUtils.DEEP_TREE_MATCH + "/.svn/" + SelectorUtils.DEEP_TREE_MATCH, + // Git + SelectorUtils.DEEP_TREE_MATCH + "/.git", + SelectorUtils.DEEP_TREE_MATCH + "/.git/" + SelectorUtils.DEEP_TREE_MATCH, + SelectorUtils.DEEP_TREE_MATCH + "/.gitattributes", + SelectorUtils.DEEP_TREE_MATCH + "/.gitignore", + SelectorUtils.DEEP_TREE_MATCH + "/.gitmodules", + // Mercurial + SelectorUtils.DEEP_TREE_MATCH + "/.hg", + SelectorUtils.DEEP_TREE_MATCH + "/.hg/" + SelectorUtils.DEEP_TREE_MATCH, + SelectorUtils.DEEP_TREE_MATCH + "/.hgignore", + SelectorUtils.DEEP_TREE_MATCH + "/.hgsub", + SelectorUtils.DEEP_TREE_MATCH + "/.hgsubstate", + SelectorUtils.DEEP_TREE_MATCH + "/.hgtags", + // Bazaar + SelectorUtils.DEEP_TREE_MATCH + "/.bzr", + SelectorUtils.DEEP_TREE_MATCH + "/.bzr/" + SelectorUtils.DEEP_TREE_MATCH, + SelectorUtils.DEEP_TREE_MATCH + "/.bzrignore", + // Mac + SelectorUtils.DEEP_TREE_MATCH + "/.DS_Store" + }; + + /** + * default value for {@link #maxLevelsOfSymlinks maxLevelsOfSymlinks} + * + * @since Ant 1.8.0 + */ + public static final int MAX_LEVELS_OF_SYMLINKS = 5; + /** + * The end of the exception message if something that should be there doesn't exist. + */ + public static final String DOES_NOT_EXIST_POSTFIX = " does not exist."; + + /** + * Helper. + */ + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * Helper. + */ + private static final SymbolicLinkUtils SYMLINK_UTILS + = SymbolicLinkUtils.getSymbolicLinkUtils(); + + /** + * Patterns which should be excluded by default. + * + * @see #addDefaultExcludes() + */ + private static final Set defaultExcludes = new HashSet(); + + static { + resetDefaultExcludes(); + } + + // CheckStyle:VisibilityModifier OFF - bc + /** + * The base directory to be scanned. + */ + protected File basedir; + + /** + * The patterns for the files to be included. + */ + protected String[] includes; + + /** + * The patterns for the files to be excluded. + */ + protected String[] excludes; + + /** + * Selectors that will filter which files are in our candidate list. + */ + protected FileSelector[] selectors = null; + + /** + * The files which matched at least one include and no excludes and were selected. + */ + protected Vector filesIncluded; + + /** + * The files which did not match any includes or selectors. + */ + protected Vector filesNotIncluded; + + /** + * The files which matched at least one include and at least one exclude. + */ + protected Vector filesExcluded; + + /** + * The directories which matched at least one include and no excludes and were selected. + */ + protected Vector dirsIncluded; + + /** + * The directories which were found and did not match any includes. + */ + protected Vector dirsNotIncluded; + + /** + * The directories which matched at least one include and at least one exclude. + */ + protected Vector dirsExcluded; + + /** + * The files which matched at least one include and no excludes and which a selector discarded. + */ + protected Vector filesDeselected; + + /** + * The directories which matched at least one include and no excludes but which a selector discarded. + */ + protected Vector dirsDeselected; + + /** + * Whether or not our results were built by a slow scan. + */ + protected boolean haveSlowResults = false; + + /** + * Whether or not the file system should be treated as a case sensitive one. + */ + protected boolean isCaseSensitive = true; + + /** + * Whether a missing base directory is an error. + * + * @since Ant 1.7.1 + */ + protected boolean errorOnMissingDir = true; + + /** + * Whether or not symbolic links should be followed. + * + * @since Ant 1.5 + */ + private boolean followSymlinks = true; + + /** + * Whether or not everything tested so far has been included. + */ + protected boolean everythingIncluded = true; + + // CheckStyle:VisibilityModifier ON + /** + * List of all scanned directories. + * + * @since Ant 1.6 + */ + private Set scannedDirs = new HashSet(); + + /** + * Map of all include patterns that are full file names and don't contain any wildcards. + * + *

+ * Maps pattern string to TokenizedPath.

+ * + *

+ * If this instance is not case sensitive, the file names get turned to upper case.

+ * + *

+ * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan + * method (cleared in clearCaches, actually).

+ * + * @since Ant 1.8.0 + */ + private Map includeNonPatterns = new HashMap(); + + /** + * Map of all exclude patterns that are full file names and don't contain any wildcards. + * + *

+ * Maps pattern string to TokenizedPath.

+ * + *

+ * If this instance is not case sensitive, the file names get turned to upper case.

+ * + *

+ * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan + * method (cleared in clearCaches, actually).

+ * + * @since Ant 1.8.0 + */ + private Map excludeNonPatterns = new HashMap(); + + /** + * Array of all include patterns that contain wildcards. + * + *

+ * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan + * method (cleared in clearCaches, actually).

+ */ + private TokenizedPattern[] includePatterns; + + /** + * Array of all exclude patterns that contain wildcards. + * + *

+ * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan + * method (cleared in clearCaches, actually).

+ */ + private TokenizedPattern[] excludePatterns; + + /** + * Have the non-pattern sets and pattern arrays for in- and excludes been initialized? + * + * @since Ant 1.6.3 + */ + private boolean areNonPatternSetsReady = false; + + /** + * Scanning flag. + * + * @since Ant 1.6.3 + */ + private boolean scanning = false; + + /** + * Scanning lock. + * + * @since Ant 1.6.3 + */ + private Object scanLock = new Object(); + + /** + * Slow scanning flag. + * + * @since Ant 1.6.3 + */ + private boolean slowScanning = false; + + /** + * Slow scanning lock. + * + * @since Ant 1.6.3 + */ + private Object slowScanLock = new Object(); + + /** + * Exception thrown during scan. + * + * @since Ant 1.6.3 + */ + private IllegalStateException illegal = null; + + /** + * The maximum number of times a symbolic link may be followed during a scan. + * + * @since Ant 1.8.0 + */ + private int maxLevelsOfSymlinks = MAX_LEVELS_OF_SYMLINKS; + + /** + * Absolute paths of all symlinks that haven't been followed but would have been if followsymlinks had been true or + * maxLevelsOfSymlinks had been higher. + * + * @since Ant 1.8.0 + */ + private Set notFollowedSymlinks = new HashSet(); + + /** + * Sole constructor. + */ + public DirectoryScanner() { + } + + /** + * Test whether or not a given path matches the start of a given pattern up to the first "**". + *

+ * This is not a general purpose test and should only be used if you can live with false positives. For example, + * pattern=**\a and str=b will yield true. + * + * @param pattern The pattern to match against. Must not be null. + * @param str The path to match, as a String. Must not be null. + * + * @return whether or not a given path matches the start of a given pattern up to the first "**". + */ + protected static boolean matchPatternStart(String pattern, String str) { + return SelectorUtils.matchPatternStart(pattern, str); + } + + /** + * Test whether or not a given path matches the start of a given pattern up to the first "**". + *

+ * This is not a general purpose test and should only be used if you can live with false positives. For example, + * pattern=**\a and str=b will yield true. + * + * @param pattern The pattern to match against. Must not be null. + * @param str The path to match, as a String. Must not be null. + * @param isCaseSensitive Whether or not matching should be performed case sensitively. + * + * @return whether or not a given path matches the start of a given pattern up to the first "**". + */ + protected static boolean matchPatternStart(String pattern, String str, + boolean isCaseSensitive) { + return SelectorUtils.matchPatternStart(pattern, str, isCaseSensitive); + } + + /** + * Test whether or not a given path matches a given pattern. + * + * @param pattern The pattern to match against. Must not be null. + * @param str The path to match, as a String. Must not be null. + * + * @return true if the pattern matches against the string, or false otherwise. + */ + protected static boolean matchPath(String pattern, String str) { + return SelectorUtils.matchPath(pattern, str); + } + + /** + * Test whether or not a given path matches a given pattern. + * + * @param pattern The pattern to match against. Must not be null. + * @param str The path to match, as a String. Must not be null. + * @param isCaseSensitive Whether or not matching should be performed case sensitively. + * + * @return true if the pattern matches against the string, or false otherwise. + */ + protected static boolean matchPath(String pattern, String str, + boolean isCaseSensitive) { + return SelectorUtils.matchPath(pattern, str, isCaseSensitive); + } + + /** + * Test whether or not a string matches against a pattern. The pattern may contain two special characters:
+ * '*' means zero or more characters
+ * '?' means one and only one character + * + * @param pattern The pattern to match against. Must not be null. + * @param str The string which must be matched against the pattern. Must not be null. + * + * @return true if the string matches against the pattern, or false otherwise. + */ + public static boolean match(String pattern, String str) { + return SelectorUtils.match(pattern, str); + } + + /** + * Test whether or not a string matches against a pattern. The pattern may contain two special characters:
+ * '*' means zero or more characters
+ * '?' means one and only one character + * + * @param pattern The pattern to match against. Must not be null. + * @param str The string which must be matched against the pattern. Must not be null. + * @param isCaseSensitive Whether or not matching should be performed case sensitively. + * + * + * @return true if the string matches against the pattern, or false otherwise. + */ + protected static boolean match(String pattern, String str, + boolean isCaseSensitive) { + return SelectorUtils.match(pattern, str, isCaseSensitive); + } + + /** + * Get the list of patterns that should be excluded by default. + * + * @return An array of String based on the current contents of the defaultExcludes + * Set. + * + * @since Ant 1.6 + */ + public static String[] getDefaultExcludes() { + synchronized (defaultExcludes) { + return (String[]) defaultExcludes.toArray(new String[defaultExcludes + .size()]); + } + } + + /** + * Add a pattern to the default excludes unless it is already a default exclude. + * + * @param s A string to add as an exclude pattern. + * @return true if the string was added; false if it already existed. + * + * @since Ant 1.6 + */ + public static boolean addDefaultExclude(String s) { + synchronized (defaultExcludes) { + return defaultExcludes.add(s); + } + } + + /** + * Remove a string if it is a default exclude. + * + * @param s The string to attempt to remove. + * @return true if s was a default exclude (and thus was removed); false if + * s was not in the default excludes list to begin with. + * + * @since Ant 1.6 + */ + public static boolean removeDefaultExclude(String s) { + synchronized (defaultExcludes) { + return defaultExcludes.remove(s); + } + } + + /** + * Go back to the hardwired default exclude patterns. + * + * @since Ant 1.6 + */ + public static void resetDefaultExcludes() { + synchronized (defaultExcludes) { + defaultExcludes.clear(); + for (int i = 0; i < DEFAULTEXCLUDES.length; i++) { + defaultExcludes.add(DEFAULTEXCLUDES[i]); + } + } + } + + /** + * Set the base directory to be scanned. This is the directory which is scanned recursively. All '/' and '\' + * characters are replaced by File.separatorChar, so the separator used need not match + * File.separatorChar. + * + * @param basedir The base directory to scan. + */ + public void setBasedir(String basedir) { + setBasedir(basedir == null ? (File) null + : new File(basedir.replace('/', File.separatorChar).replace( + '\\', File.separatorChar))); + } + + /** + * Set the base directory to be scanned. This is the directory which is scanned recursively. + * + * @param basedir The base directory for scanning. + */ + public synchronized void setBasedir(File basedir) { + this.basedir = basedir; + } + + /** + * Return the base directory to be scanned. This is the directory which is scanned recursively. + * + * @return the base directory to be scanned. + */ + public synchronized File getBasedir() { + return basedir; + } + + /** + * Find out whether include exclude patterns are matched in a case sensitive way. + * + * @return whether or not the scanning is case sensitive. + * @since Ant 1.6 + */ + public synchronized boolean isCaseSensitive() { + return isCaseSensitive; + } + + /** + * Set whether or not include and exclude patterns are matched in a case sensitive way. + * + * @param isCaseSensitive whether or not the file system should be regarded as a case sensitive one. + */ + public synchronized void setCaseSensitive(boolean isCaseSensitive) { + this.isCaseSensitive = isCaseSensitive; + } + + /** + * Sets whether or not a missing base directory is an error + * + * @param errorOnMissingDir whether or not a missing base directory is an error + * @since Ant 1.7.1 + */ + public void setErrorOnMissingDir(boolean errorOnMissingDir) { + this.errorOnMissingDir = errorOnMissingDir; + } + + /** + * Get whether or not a DirectoryScanner follows symbolic links. + * + * @return flag indicating whether symbolic links should be followed. + * + * @since Ant 1.6 + */ + public synchronized boolean isFollowSymlinks() { + return followSymlinks; + } + + /** + * Set whether or not symbolic links should be followed. + * + * @param followSymlinks whether or not symbolic links should be followed. + */ + public synchronized void setFollowSymlinks(boolean followSymlinks) { + this.followSymlinks = followSymlinks; + } + + /** + * The maximum number of times a symbolic link may be followed during a scan. + * + * @since Ant 1.8.0 + */ + public void setMaxLevelsOfSymlinks(int max) { + maxLevelsOfSymlinks = max; + } + + /** + * Set the list of include patterns to use. All '/' and '\' characters are replaced by + * File.separatorChar, so the separator used need not match File.separatorChar. + *

+ * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param includes A list of include patterns. May be null, indicating that all files should be + * included. If a non-null list is given, all elements must be non-null. + */ + public synchronized void setIncludes(String[] includes) { + if (includes == null) { + this.includes = null; + } else { + this.includes = new String[includes.length]; + for (int i = 0; i < includes.length; i++) { + this.includes[i] = normalizePattern(includes[i]); + } + } + } + + public synchronized void setIncludes(String include) { + if (include == null) { + this.includes = null; + } else { + this.includes = new String[1]; + this.includes[0] = normalizePattern(include); + } + } + + /** + * Set the list of exclude patterns to use. All '/' and '\' characters are replaced by + * File.separatorChar, so the separator used need not match File.separatorChar. + *

+ * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param excludes A list of exclude patterns. May be null, indicating that no files should be + * excluded. If a non-null list is given, all elements must be non-null. + */ + public synchronized void setExcludes(String[] excludes) { + if (excludes == null) { + this.excludes = null; + } else { + this.excludes = new String[excludes.length]; + for (int i = 0; i < excludes.length; i++) { + this.excludes[i] = normalizePattern(excludes[i]); + } + } + } + + /** + * Add to the list of exclude patterns to use. All '/' and '\' characters are replaced by + * File.separatorChar, so the separator used need not match File.separatorChar. + *

+ * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param excludes A list of exclude patterns. May be null, in which case the exclude patterns don't + * get changed at all. + * + * @since Ant 1.6.3 + */ + public synchronized void addExcludes(String[] excludes) { + if (excludes != null && excludes.length > 0) { + if (this.excludes != null && this.excludes.length > 0) { + String[] tmp = new String[excludes.length + + this.excludes.length]; + System.arraycopy(this.excludes, 0, tmp, 0, + this.excludes.length); + for (int i = 0; i < excludes.length; i++) { + tmp[this.excludes.length + i] + = normalizePattern(excludes[i]); + } + this.excludes = tmp; + } else { + setExcludes(excludes); + } + } + } + + /** + * All '/' and '\' characters are replaced by File.separatorChar, so the separator used need not match + * File.separatorChar. + * + *

+ * When a pattern ends with a '/' or '\', "**" is appended. + * + * @since Ant 1.6.3 + */ + private static String normalizePattern(String p) { + String pattern = p.replace('/', File.separatorChar) + .replace('\\', File.separatorChar); + if (pattern.endsWith(File.separator)) { + pattern += SelectorUtils.DEEP_TREE_MATCH; + } + return pattern; + } + + /** + * Set the selectors that will select the filelist. + * + * @param selectors specifies the selectors to be invoked on a scan. + */ + public synchronized void setSelectors(FileSelector[] selectors) { + this.selectors = selectors; + } + + /** + * Return whether or not the scanner has included all the files or directories it has come across so far. + * + * @return true if all files and directories which have been found so far have been included. + */ + public synchronized boolean isEverythingIncluded() { + return everythingIncluded; + } + + /** + * Scan for files which match at least one include pattern and don't match any exclude patterns. If there are + * selectors then the files must pass muster there, as well. Scans under basedir, if set; otherwise the include + * patterns without leading wildcards specify the absolute paths of the files that may be included. + * + * @exception IllegalStateException if the base directory was set incorrectly (i.e. if it doesn't exist or isn't a + * directory). + */ + public void scan() throws IllegalStateException { + synchronized (scanLock) { + if (scanning) { + while (scanning) { + try { + scanLock.wait(); + } catch (InterruptedException e) { + continue; + } + } + if (illegal != null) { + throw illegal; + } + return; + } + scanning = true; + } + File savedBase = basedir; + try { + synchronized (this) { + illegal = null; + clearResults(); + + // set in/excludes to reasonable defaults if needed: + boolean nullIncludes = (includes == null); + includes = nullIncludes + ? new String[]{SelectorUtils.DEEP_TREE_MATCH} : includes; + boolean nullExcludes = (excludes == null); + excludes = nullExcludes ? new String[0] : excludes; + + if (basedir != null && !followSymlinks + && SYMLINK_UTILS.isSymbolicLink(basedir)) { + notFollowedSymlinks.add(basedir.getAbsolutePath()); + basedir = null; + } + + if (basedir == null) { + // if no basedir and no includes, nothing to do: + if (nullIncludes) { + return; + } + } else { + if (!basedir.exists()) { + if (errorOnMissingDir) { + illegal = new IllegalStateException("basedir " + + basedir + + DOES_NOT_EXIST_POSTFIX); + } else { + // Nothing to do - basedir does not exist + return; + } + } else if (!basedir.isDirectory()) { + illegal = new IllegalStateException("basedir " + + basedir + + " is not a" + + " directory."); + } + if (illegal != null) { + throw illegal; + } + } + if (isIncluded(TokenizedPath.EMPTY_PATH)) { + if (!isExcluded(TokenizedPath.EMPTY_PATH)) { + if (isSelected("", basedir)) { + dirsIncluded.addElement(""); + } else { + dirsDeselected.addElement(""); + } + } else { + dirsExcluded.addElement(""); + } + } else { + dirsNotIncluded.addElement(""); + } + checkIncludePatterns(); + clearCaches(); + includes = nullIncludes ? null : includes; + excludes = nullExcludes ? null : excludes; + } + } catch (IOException ex) { + throw new BuildException(ex); + } finally { + basedir = savedBase; + synchronized (scanLock) { + scanning = false; + scanLock.notifyAll(); + } + } + } + + /** + * This routine is actually checking all the include patterns in order to avoid scanning everything under base dir. + * + * @since Ant 1.6 + */ + private void checkIncludePatterns() { + ensureNonPatternSetsReady(); + Map newroots = new HashMap(); + + // put in the newroots map the include patterns without + // wildcard tokens + for (int i = 0; i < includePatterns.length; i++) { + String pattern = includePatterns[i].toString(); + if (!shouldSkipPattern(pattern)) { + newroots.put(includePatterns[i].rtrimWildcardTokens(), + pattern); + } + } + for (Map.Entry entry : includeNonPatterns.entrySet()) { + String pattern = entry.getKey(); + if (!shouldSkipPattern(pattern)) { + newroots.put(entry.getValue(), pattern); + } + } + + if (newroots.containsKey(TokenizedPath.EMPTY_PATH) + && basedir != null) { + // we are going to scan everything anyway + scandir(basedir, "", true); + } else { + File canonBase = null; + if (basedir != null) { + try { + canonBase = basedir.getCanonicalFile(); + } catch (IOException ex) { + throw new BuildException(ex); + } + } + // only scan directories that can include matched files or + // directories + for (Map.Entry entry : newroots.entrySet()) { + TokenizedPath currentPath = entry.getKey(); + String currentelement = currentPath.toString(); + if (basedir == null + && !FileUtils.isAbsolutePath(currentelement)) { + continue; + } + File myfile = new File(basedir, currentelement); + + if (myfile.exists()) { + // may be on a case insensitive file system. We want + // the results to show what's really on the disk, so + // we need to double check. + try { + String path = (basedir == null) + ? myfile.getCanonicalPath() + : FILE_UTILS.removeLeadingPath(canonBase, + myfile.getCanonicalFile()); + if (!path.equals(currentelement) || ON_VMS) { + myfile = currentPath.findFile(basedir, true); + if (myfile != null && basedir != null) { + currentelement = FILE_UTILS.removeLeadingPath( + basedir, myfile); + if (!currentPath.toString() + .equals(currentelement)) { + currentPath + = new TokenizedPath(currentelement); + } + } + } + } catch (IOException ex) { + throw new BuildException(ex); + } + } + + if ((myfile == null || !myfile.exists()) && !isCaseSensitive()) { + File f = currentPath.findFile(basedir, false); + if (f != null && f.exists()) { + // adapt currentelement to the case we've + // actually found + currentelement = (basedir == null) + ? f.getAbsolutePath() + : FILE_UTILS.removeLeadingPath(basedir, f); + myfile = f; + currentPath = new TokenizedPath(currentelement); + } + } + + if (myfile != null && myfile.exists()) { + if (!followSymlinks && currentPath.isSymlink(basedir)) { + accountForNotFollowedSymlink(currentPath, myfile); + continue; + } + if (myfile.isDirectory()) { + if (isIncluded(currentPath) + && currentelement.length() > 0) { + accountForIncludedDir(currentPath, myfile, true); + } else { + scandir(myfile, currentPath, true); + } + } else if (myfile.isFile()) { + String originalpattern = (String) entry.getValue(); + boolean included = isCaseSensitive() + ? originalpattern.equals(currentelement) + : originalpattern.equalsIgnoreCase(currentelement); + if (included) { + accountForIncludedFile(currentPath, myfile); + } + } + } + } + } + } + + /** + * true if the pattern specifies a relative path without basedir or an absolute path not inside basedir. + * + * @since Ant 1.8.0 + */ + private boolean shouldSkipPattern(String pattern) { + if (FileUtils.isAbsolutePath(pattern)) { + //skip abs. paths not under basedir, if set: + if (basedir != null + && !SelectorUtils.matchPatternStart(pattern, + basedir.getAbsolutePath(), + isCaseSensitive())) { + return true; + } + } else if (basedir == null) { + //skip non-abs. paths if basedir == null: + return true; + } + return false; + } + + /** + * Clear the result caches for a scan. + */ + protected synchronized void clearResults() { + filesIncluded = new VectorSet(); + filesNotIncluded = new VectorSet(); + filesExcluded = new VectorSet(); + filesDeselected = new VectorSet(); + dirsIncluded = new VectorSet(); + dirsNotIncluded = new VectorSet(); + dirsExcluded = new VectorSet(); + dirsDeselected = new VectorSet(); + everythingIncluded = (basedir != null); + scannedDirs.clear(); + notFollowedSymlinks.clear(); + } + + /** + * Top level invocation for a slow scan. A slow scan builds up a full list of excluded/included files/directories, + * whereas a fast scan will only have full results for included files, as it ignores directories which can't + * possibly hold any included files/directories. + *

+ * Returns immediately if a slow scan has already been completed. + */ + protected void slowScan() { + synchronized (slowScanLock) { + if (haveSlowResults) { + return; + } + if (slowScanning) { + while (slowScanning) { + try { + slowScanLock.wait(); + } catch (InterruptedException e) { + // Empty + } + } + return; + } + slowScanning = true; + } + try { + synchronized (this) { + + // set in/excludes to reasonable defaults if needed: + boolean nullIncludes = (includes == null); + includes = nullIncludes + ? new String[]{SelectorUtils.DEEP_TREE_MATCH} : includes; + boolean nullExcludes = (excludes == null); + excludes = nullExcludes ? new String[0] : excludes; + + String[] excl = new String[dirsExcluded.size()]; + dirsExcluded.copyInto(excl); + + String[] notIncl = new String[dirsNotIncluded.size()]; + dirsNotIncluded.copyInto(notIncl); + + ensureNonPatternSetsReady(); + + processSlowScan(excl); + processSlowScan(notIncl); + clearCaches(); + includes = nullIncludes ? null : includes; + excludes = nullExcludes ? null : excludes; + } + } finally { + synchronized (slowScanLock) { + haveSlowResults = true; + slowScanning = false; + slowScanLock.notifyAll(); + } + } + } + + private void processSlowScan(String[] arr) { + for (int i = 0; i < arr.length; i++) { + TokenizedPath path = new TokenizedPath(arr[i]); + if (!couldHoldIncluded(path) || contentsExcluded(path)) { + scandir(new File(basedir, arr[i]), path, false); + } + } + } + + /** + * Scan the given directory for files and directories. Found files and directories are placed in their respective + * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is + * scanned recursively. + * + * @param dir The directory to scan. Must not be null. + * @param vpath The path relative to the base directory (needed to prevent problems with an absolute path when using + * dir). Must not be null. + * @param fast Whether or not this call is part of a fast scan. + * + * @see #filesIncluded + * @see #filesNotIncluded + * @see #filesExcluded + * @see #dirsIncluded + * @see #dirsNotIncluded + * @see #dirsExcluded + * @see #slowScan + */ + protected void scandir(File dir, String vpath, boolean fast) { + scandir(dir, new TokenizedPath(vpath), fast); + } + + /** + * Scan the given directory for files and directories. Found files and directories are placed in their respective + * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is + * scanned recursively. + * + * @param dir The directory to scan. Must not be null. + * @param path The path relative to the base directory (needed to prevent problems with an absolute path when using + * dir). Must not be null. + * @param fast Whether or not this call is part of a fast scan. + * + * @see #filesIncluded + * @see #filesNotIncluded + * @see #filesExcluded + * @see #dirsIncluded + * @see #dirsNotIncluded + * @see #dirsExcluded + * @see #slowScan + */ + private void scandir(File dir, TokenizedPath path, boolean fast) { + if (dir == null) { + throw new BuildException("dir must not be null."); + } + String[] newfiles = dir.list(); + if (newfiles == null) { + if (!dir.exists()) { + throw new BuildException(dir + DOES_NOT_EXIST_POSTFIX); + } else if (!dir.isDirectory()) { + throw new BuildException(dir + " is not a directory."); + } else { + throw new BuildException("IO error scanning directory '" + + dir.getAbsolutePath() + "'"); + } + } + scandir(dir, path, fast, newfiles, new LinkedList()); + } + + private void scandir(File dir, TokenizedPath path, boolean fast, + String[] newfiles, LinkedList directoryNamesFollowed) { + String vpath = path.toString(); + if (vpath.length() > 0 && !vpath.endsWith(File.separator)) { + vpath += File.separator; + } + + // avoid double scanning of directories, can only happen in fast mode + if (fast && hasBeenScanned(vpath)) { + return; + } + if (!followSymlinks) { + ArrayList noLinks = new ArrayList(); + for (int i = 0; i < newfiles.length; i++) { + try { + if (SYMLINK_UTILS.isSymbolicLink(dir, newfiles[i])) { + String name = vpath + newfiles[i]; + File file = new File(dir, newfiles[i]); + if (file.isDirectory()) { + dirsExcluded.addElement(name); + } else if (file.isFile()) { + filesExcluded.addElement(name); + } + accountForNotFollowedSymlink(name, file); + } else { + noLinks.add(newfiles[i]); + } + } catch (IOException ioe) { + String msg = "IOException caught while checking " + + "for links, couldn't get canonical path!"; + // will be caught and redirected to Ant's logging system + System.err.println(msg); + noLinks.add(newfiles[i]); + } + } + newfiles = (String[]) (noLinks.toArray(new String[noLinks.size()])); + } else { + directoryNamesFollowed.addFirst(dir.getName()); + } + + for (int i = 0; i < newfiles.length; i++) { + String name = vpath + newfiles[i]; + TokenizedPath newPath = new TokenizedPath(path, newfiles[i]); + File file = new File(dir, newfiles[i]); + String[] children = file.list(); + if (children == null || (children.length == 0 && file.isFile())) { + if (isIncluded(newPath)) { + accountForIncludedFile(newPath, file); + } else { + everythingIncluded = false; + filesNotIncluded.addElement(name); + } + } else if (file.isDirectory()) { // dir + + if (followSymlinks + && causesIllegalSymlinkLoop(newfiles[i], dir, + directoryNamesFollowed)) { + // will be caught and redirected to Ant's logging system + System.err.println("skipping symbolic link " + + file.getAbsolutePath() + + " -- too many levels of symbolic" + + " links."); + notFollowedSymlinks.add(file.getAbsolutePath()); + continue; + } + + if (isIncluded(newPath)) { + accountForIncludedDir(newPath, file, fast, children, + directoryNamesFollowed); + } else { + everythingIncluded = false; + dirsNotIncluded.addElement(name); + if (fast && couldHoldIncluded(newPath) + && !contentsExcluded(newPath)) { + scandir(file, newPath, fast, children, + directoryNamesFollowed); + } + } + if (!fast) { + scandir(file, newPath, fast, children, directoryNamesFollowed); + } + } + } + + if (followSymlinks) { + directoryNamesFollowed.removeFirst(); + } + } + + /** + * Process included file. + * + * @param name path of the file relative to the directory of the FileSet. + * @param file included File. + */ + private void accountForIncludedFile(TokenizedPath name, File file) { + processIncluded(name, file, filesIncluded, filesExcluded, + filesDeselected); + } + + /** + * Process included directory. + * + * @param name path of the directory relative to the directory of the FileSet. + * @param file directory as File. + * @param fast whether to perform fast scans. + */ + private void accountForIncludedDir(TokenizedPath name, File file, + boolean fast) { + processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected); + if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) { + scandir(file, name, fast); + } + } + + private void accountForIncludedDir(TokenizedPath name, + File file, boolean fast, + String[] children, + LinkedList directoryNamesFollowed) { + processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected); + if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) { + scandir(file, name, fast, children, directoryNamesFollowed); + } + } + + private void accountForNotFollowedSymlink(String name, File file) { + accountForNotFollowedSymlink(new TokenizedPath(name), file); + } + + private void accountForNotFollowedSymlink(TokenizedPath name, File file) { + if (!isExcluded(name) + && (isIncluded(name) + || (file.isDirectory() && couldHoldIncluded(name) + && !contentsExcluded(name)))) { + notFollowedSymlinks.add(file.getAbsolutePath()); + } + } + + private void processIncluded(TokenizedPath path, + File file, Vector inc, Vector exc, + Vector des) { + String name = path.toString(); + if (inc.contains(name) || exc.contains(name) || des.contains(name)) { + return; + } + + boolean included = false; + if (isExcluded(path)) { + exc.add(name); + } else if (isSelected(name, file)) { + included = true; + inc.add(name); + } else { + des.add(name); + } + everythingIncluded &= included; + } + + /** + * Test whether or not a name matches against at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one include pattern, or false + * otherwise. + */ + protected boolean isIncluded(String name) { + return isIncluded(new TokenizedPath(name)); + } + + /** + * Test whether or not a name matches against at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one include pattern, or false + * otherwise. + */ + private boolean isIncluded(TokenizedPath path) { + ensureNonPatternSetsReady(); + + if (isCaseSensitive() + ? includeNonPatterns.containsKey(path.toString()) + : includeNonPatterns.containsKey(path.toString().toUpperCase())) { + return true; + } + for (int i = 0; i < includePatterns.length; i++) { + if (includePatterns[i].matchPath(path, isCaseSensitive())) { + return true; + } + } + return false; + } + + /** + * Test whether or not a name matches the start of at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against the start of at least one include pattern, or + * false otherwise. + */ + protected boolean couldHoldIncluded(String name) { + return couldHoldIncluded(new TokenizedPath(name)); + } + + /** + * Test whether or not a name matches the start of at least one include pattern. + * + * @param tokenizedName The name to match. Must not be null. + * @return true when the name matches against the start of at least one include pattern, or + * false otherwise. + */ + private boolean couldHoldIncluded(TokenizedPath tokenizedName) { + for (int i = 0; i < includePatterns.length; i++) { + if (couldHoldIncluded(tokenizedName, includePatterns[i])) { + return true; + } + } + for (Iterator iter = includeNonPatterns.values().iterator(); + iter.hasNext();) { + if (couldHoldIncluded(tokenizedName, + iter.next().toPattern())) { + return true; + } + } + return false; + } + + /** + * Test whether or not a name matches the start of the given include pattern. + * + * @param tokenizedName The name to match. Must not be null. + * @return true when the name matches against the start of the include pattern, or false + * otherwise. + */ + private boolean couldHoldIncluded(TokenizedPath tokenizedName, + TokenizedPattern tokenizedInclude) { + return tokenizedInclude.matchStartOf(tokenizedName, isCaseSensitive()) + && isMorePowerfulThanExcludes(tokenizedName.toString()) + && isDeeper(tokenizedInclude, tokenizedName); + } + + /** + * Verify that a pattern specifies files deeper than the level of the specified file. + * + * @param pattern the pattern to check. + * @param name the name to check. + * @return whether the pattern is deeper than the name. + * @since Ant 1.6.3 + */ + private boolean isDeeper(TokenizedPattern pattern, TokenizedPath name) { + return pattern.containsPattern(SelectorUtils.DEEP_TREE_MATCH) + || pattern.depth() > name.depth(); + } + + /** + * Find out whether one particular include pattern is more powerful than all the excludes. Note: the power + * comparison is based on the length of the include pattern and of the exclude patterns without the wildcards. + * Ideally the comparison should be done based on the depth of the match; that is to say how many file separators + * have been matched before the first ** or the end of the pattern. + * + * IMPORTANT : this function should return false "with care". + * + * @param name the relative path to test. + * @return true if there is no exclude pattern more powerful than this include pattern. + * @since Ant 1.6 + */ + private boolean isMorePowerfulThanExcludes(String name) { + final String soughtexclude + = name + File.separatorChar + SelectorUtils.DEEP_TREE_MATCH; + for (int counter = 0; counter < excludePatterns.length; counter++) { + if (excludePatterns[counter].toString().equals(soughtexclude)) { + return false; + } + } + return true; + } + + /** + * Test whether all contents of the specified directory must be excluded. + * + * @param path the path to check. + * @return whether all the specified directory's contents are excluded. + */ + /* package */ boolean contentsExcluded(TokenizedPath path) { + for (int i = 0; i < excludePatterns.length; i++) { + if (excludePatterns[i].endsWith(SelectorUtils.DEEP_TREE_MATCH) + && excludePatterns[i].withoutLastToken() + .matchPath(path, isCaseSensitive())) { + return true; + } + } + return false; + } + + /** + * Test whether or not a name matches against at least one exclude pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one exclude pattern, or false + * otherwise. + */ + protected boolean isExcluded(String name) { + return isExcluded(new TokenizedPath(name)); + } + + /** + * Test whether or not a name matches against at least one exclude pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one exclude pattern, or false + * otherwise. + */ + private boolean isExcluded(TokenizedPath name) { + ensureNonPatternSetsReady(); + + if (isCaseSensitive() + ? excludeNonPatterns.containsKey(name.toString()) + : excludeNonPatterns.containsKey(name.toString().toUpperCase())) { + return true; + } + for (int i = 0; i < excludePatterns.length; i++) { + if (excludePatterns[i].matchPath(name, isCaseSensitive())) { + return true; + } + } + return false; + } + + /** + * Test whether a file should be selected. + * + * @param name the filename to check for selecting. + * @param file the java.io.File object for this filename. + * @return false when the selectors says that the file should not be selected, true + * otherwise. + */ + protected boolean isSelected(String name, File file) { + if (selectors != null) { + for (int i = 0; i < selectors.length; i++) { + if (!selectors[i].isSelected(basedir, name, file)) { + return false; + } + } + } + return true; + } + + /** + * Return the names of the files which matched at least one of the include patterns and none of the exclude + * patterns. The names are relative to the base directory. + * + * @return the names of the files which matched at least one of the include patterns and none of the exclude + * patterns. + */ + public String[] getIncludedFiles() { + String[] files; + synchronized (this) { + if (filesIncluded == null) { + throw new IllegalStateException("Must call scan() first"); + } + files = new String[filesIncluded.size()]; + filesIncluded.copyInto(files); + } + Arrays.sort(files); + return files; + } + + /** + * Return the count of included files. + * + * @return int. + * @since Ant 1.6.3 + */ + public synchronized int getIncludedFilesCount() { + if (filesIncluded == null) { + throw new IllegalStateException("Must call scan() first"); + } + return filesIncluded.size(); + } + + /** + * Return the names of the files which matched none of the include patterns. The names are relative to the base + * directory. This involves performing a slow scan if one has not already been completed. + * + * @return the names of the files which matched none of the include patterns. + * + * @see #slowScan + */ + public synchronized String[] getNotIncludedFiles() { + slowScan(); + String[] files = new String[filesNotIncluded.size()]; + filesNotIncluded.copyInto(files); + return files; + } + + /** + * Return the names of the files which matched at least one of the include patterns and at least one of the exclude + * patterns. The names are relative to the base directory. This involves performing a slow scan if one has not + * already been completed. + * + * @return the names of the files which matched at least one of the include patterns and at least one of the exclude + * patterns. + * + * @see #slowScan + */ + public synchronized String[] getExcludedFiles() { + slowScan(); + String[] files = new String[filesExcluded.size()]; + filesExcluded.copyInto(files); + return files; + } + + /** + *

+ * Return the names of the files which were selected out and therefore not ultimately included.

+ * + *

+ * The names are relative to the base directory. This involves performing a slow scan if one has not already been + * completed.

+ * + * @return the names of the files which were deselected. + * + * @see #slowScan + */ + public synchronized String[] getDeselectedFiles() { + slowScan(); + String[] files = new String[filesDeselected.size()]; + filesDeselected.copyInto(files); + return files; + } + + /** + * Return the names of the directories which matched at least one of the include patterns and none of the exclude + * patterns. The names are relative to the base directory. + * + * @return the names of the directories which matched at least one of the include patterns and none of the exclude + * patterns. + */ + public String[] getIncludedDirectories() { + String[] directories; + synchronized (this) { + if (dirsIncluded == null) { + throw new IllegalStateException("Must call scan() first"); + } + directories = new String[dirsIncluded.size()]; + dirsIncluded.copyInto(directories); + } + Arrays.sort(directories); + return directories; + } + + /** + * Return the count of included directories. + * + * @return int. + * @since Ant 1.6.3 + */ + public synchronized int getIncludedDirsCount() { + if (dirsIncluded == null) { + throw new IllegalStateException("Must call scan() first"); + } + return dirsIncluded.size(); + } + + /** + * Return the names of the directories which matched none of the include patterns. The names are relative to the + * base directory. This involves performing a slow scan if one has not already been completed. + * + * @return the names of the directories which matched none of the include patterns. + * + * @see #slowScan + */ + public synchronized String[] getNotIncludedDirectories() { + slowScan(); + String[] directories = new String[dirsNotIncluded.size()]; + dirsNotIncluded.copyInto(directories); + return directories; + } + + /** + * Return the names of the directories which matched at least one of the include patterns and at least one of the + * exclude patterns. The names are relative to the base directory. This involves performing a slow scan if one has + * not already been completed. + * + * @return the names of the directories which matched at least one of the include patterns and at least one of the + * exclude patterns. + * + * @see #slowScan + */ + public synchronized String[] getExcludedDirectories() { + slowScan(); + String[] directories = new String[dirsExcluded.size()]; + dirsExcluded.copyInto(directories); + return directories; + } + + /** + *

+ * Return the names of the directories which were selected out and therefore not ultimately included.

+ * + *

+ * The names are relative to the base directory. This involves performing a slow scan if one has not already been + * completed.

+ * + * @return the names of the directories which were deselected. + * + * @see #slowScan + */ + public synchronized String[] getDeselectedDirectories() { + slowScan(); + String[] directories = new String[dirsDeselected.size()]; + dirsDeselected.copyInto(directories); + return directories; + } + + /** + * Absolute paths of all symbolic links that haven't been followed but would have been followed had followsymlinks + * been true or maxLevelsOfSymlinks been bigger. + * + * @return sorted array of not followed symlinks + * @since Ant 1.8.0 + * @see #notFollowedSymlinks + */ + public synchronized String[] getNotFollowedSymlinks() { + String[] links; + synchronized (this) { + links = (String[]) notFollowedSymlinks + .toArray(new String[notFollowedSymlinks.size()]); + } + Arrays.sort(links); + return links; + } + + /** + * Add default exclusions to the current exclusions set. + */ + public synchronized void addDefaultExcludes() { + int excludesLength = excludes == null ? 0 : excludes.length; + String[] newExcludes; + String[] defaultExcludesTemp = getDefaultExcludes(); + newExcludes = new String[excludesLength + defaultExcludesTemp.length]; + if (excludesLength > 0) { + System.arraycopy(excludes, 0, newExcludes, 0, excludesLength); + } + for (int i = 0; i < defaultExcludesTemp.length; i++) { + newExcludes[i + excludesLength] + = defaultExcludesTemp[i].replace('/', File.separatorChar) + .replace('\\', File.separatorChar); + } + excludes = newExcludes; + } + + /** + * Get the named resource. + * + * @param name path name of the file relative to the dir attribute. + * + * @return the resource with the given name. + * @since Ant 1.5.2 + */ + public synchronized Resource getResource(String name) { + return new FileResource(basedir, name); + } + + /** + * Has the directory with the given path relative to the base directory already been scanned? + * + *

+ * Registers the given directory as scanned as a side effect.

+ * + * @since Ant 1.6 + */ + private boolean hasBeenScanned(String vpath) { + return !scannedDirs.add(vpath); + } + + /** + * This method is of interest for testing purposes. The returned Set is live and should not be modified. + * + * @return the Set of relative directory names that have been scanned. + */ + /* package-private */ Set getScannedDirs() { + return scannedDirs; + } + + /** + * Clear internal caches. + * + * @since Ant 1.6 + */ + private synchronized void clearCaches() { + includeNonPatterns.clear(); + excludeNonPatterns.clear(); + includePatterns = null; + excludePatterns = null; + areNonPatternSetsReady = false; + } + + /** + * Ensure that the in|exclude "patterns" have been properly divided up. + * + * @since Ant 1.6.3 + */ + /* package */ synchronized void ensureNonPatternSetsReady() { + if (!areNonPatternSetsReady) { + includePatterns = fillNonPatternSet(includeNonPatterns, includes); + excludePatterns = fillNonPatternSet(excludeNonPatterns, excludes); + areNonPatternSetsReady = true; + } + } + + /** + * Add all patterns that are not real patterns (do not contain wildcards) to the set and returns the real patterns. + * + * @param map Map to populate. + * @param patterns String[] of patterns. + * @since Ant 1.8.0 + */ + private TokenizedPattern[] fillNonPatternSet(Map map, String[] patterns) { + ArrayList al = new ArrayList(patterns.length); + for (int i = 0; i < patterns.length; i++) { + if (!SelectorUtils.hasWildcards(patterns[i])) { + String s = isCaseSensitive() + ? patterns[i] : patterns[i].toUpperCase(); + map.put(s, new TokenizedPath(s)); + } else { + al.add(new TokenizedPattern(patterns[i])); + } + } + return (TokenizedPattern[]) al.toArray(new TokenizedPattern[al.size()]); + } + + /** + * Would following the given directory cause a loop of symbolic links deeper than allowed? + * + *

+ * Can only happen if the given directory has been seen at least more often than allowed during the current scan and + * it is a symbolic link and enough other occurrences of the same name higher up are symbolic links that point to + * the same place.

+ * + * @since Ant 1.8.0 + */ + private boolean causesIllegalSymlinkLoop(String dirName, File parent, + LinkedList directoryNamesFollowed) { + try { + if (directoryNamesFollowed.size() >= maxLevelsOfSymlinks + && CollectionUtils.frequency(directoryNamesFollowed, dirName) + >= maxLevelsOfSymlinks + && SYMLINK_UTILS.isSymbolicLink(parent, dirName)) { + + ArrayList files = new ArrayList(); + File f = FILE_UTILS.resolveFile(parent, dirName); + String target = f.getCanonicalPath(); + files.add(target); + + String relPath = ""; + for (String dir : directoryNamesFollowed) { + relPath += "../"; + if (dirName.equals(dir)) { + f = FILE_UTILS.resolveFile(parent, relPath + dir); + files.add(f.getCanonicalPath()); + if (files.size() > maxLevelsOfSymlinks + && CollectionUtils.frequency(files, target) + > maxLevelsOfSymlinks) { + return true; + } + } + } + + } + return false; + } catch (IOException ex) { + throw new BuildException("Caught error while checking for" + + " symbolic links", ex); + } + } + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/FileScanner.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/FileScanner.java new file mode 100644 index 000000000..2fdd654f5 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/FileScanner.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant; + +import java.io.File; + +/** + * An interface used to describe the actions required of any type of + * directory scanner. + * + */ +public interface FileScanner { + /** + * Adds default exclusions to the current exclusions set. + */ + void addDefaultExcludes(); + + /** + * Returns the base directory to be scanned. + * This is the directory which is scanned recursively. + * + * @return the base directory to be scanned + */ + File getBasedir(); + + /** + * Returns the names of the directories which matched at least one of the + * include patterns and at least one of the exclude patterns. + * The names are relative to the base directory. + * + * @return the names of the directories which matched at least one of the + * include patterns and at least one of the exclude patterns. + */ + String[] getExcludedDirectories(); + + /** + * Returns the names of the files which matched at least one of the + * include patterns and at least one of the exclude patterns. + * The names are relative to the base directory. + * + * @return the names of the files which matched at least one of the + * include patterns and at least one of the exclude patterns. + * + */ + String[] getExcludedFiles(); + + /** + * Returns the names of the directories which matched at least one of the + * include patterns and none of the exclude patterns. + * The names are relative to the base directory. + * + * @return the names of the directories which matched at least one of the + * include patterns and none of the exclude patterns. + */ + String[] getIncludedDirectories(); + + /** + * Returns the names of the files which matched at least one of the + * include patterns and none of the exclude patterns. + * The names are relative to the base directory. + * + * @return the names of the files which matched at least one of the + * include patterns and none of the exclude patterns. + */ + String[] getIncludedFiles(); + + /** + * Returns the names of the directories which matched none of the include + * patterns. The names are relative to the base directory. + * + * @return the names of the directories which matched none of the include + * patterns. + */ + String[] getNotIncludedDirectories(); + + /** + * Returns the names of the files which matched none of the include + * patterns. The names are relative to the base directory. + * + * @return the names of the files which matched none of the include + * patterns. + */ + String[] getNotIncludedFiles(); + + /** + * Scans the base directory for files which match at least one include + * pattern and don't match any exclude patterns. + * + * @exception IllegalStateException if the base directory was set + * incorrectly (i.e. if it is null, doesn't exist, + * or isn't a directory). + */ + void scan() throws IllegalStateException; + + /** + * Sets the base directory to be scanned. This is the directory which is + * scanned recursively. All '/' and '\' characters should be replaced by + * File.separatorChar, so the separator used need not match + * File.separatorChar. + * + * @param basedir The base directory to scan. + * Must not be null. + */ + void setBasedir(String basedir); + + /** + * Sets the base directory to be scanned. This is the directory which is + * scanned recursively. + * + * @param basedir The base directory for scanning. + * Should not be null. + */ + void setBasedir(File basedir); + + /** + * Sets the list of exclude patterns to use. + * + * @param excludes A list of exclude patterns. + * May be null, indicating that no files + * should be excluded. If a non-null list is + * given, all elements must be non-null. + */ + void setExcludes(String[] excludes); + + /** + * Sets the list of include patterns to use. + * + * @param includes A list of include patterns. + * May be null, indicating that all files + * should be included. If a non-null + * list is given, all elements must be + * non-null. + */ + void setIncludes(String[] includes); + + /** + * Sets whether or not the file system should be regarded as case sensitive. + * + * @param isCaseSensitive whether or not the file system should be + * regarded as a case sensitive one + */ + void setCaseSensitive(boolean isCaseSensitive); +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/Location.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/Location.java new file mode 100644 index 000000000..44137ab5b --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/Location.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant; + +import java.io.Serializable; +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; +import org.xml.sax.Locator; + +/** + * Stores the location of a piece of text within a file (file name, + * line number and column number). Note that the column number is + * currently ignored. + * + */ +public class Location implements Serializable { + private static final long serialVersionUID = 1L; + + /** Name of the file. */ + private final String fileName; + /** Line number within the file. */ + private final int lineNumber; + /** Column number within the file. */ + private final int columnNumber; + + /** Location to use when one is needed but no information is available */ + public static final Location UNKNOWN_LOCATION = new Location(); + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * Creates an "unknown" location. + */ + private Location() { + this(null, 0, 0); + } + + /** + * Creates a location consisting of a file name but no line number or + * column number. + * + * @param fileName The name of the file. May be null, + * in which case the location is equivalent to + * {@link #UNKNOWN_LOCATION UNKNOWN_LOCATION}. + */ + public Location(String fileName) { + this(fileName, 0, 0); + } + + /** + * Creates a location from the SAX locator using the system ID as + * the filename. + * + * @param loc Must not be null. + * + * @since Ant 1.6 + */ + public Location(Locator loc) { + this(loc.getSystemId(), loc.getLineNumber(), loc.getColumnNumber()); + } + + /** + * Creates a location consisting of a file name, line number and + * column number. + * + * @param fileName The name of the file. May be null, + * in which case the location is equivalent to + * {@link #UNKNOWN_LOCATION UNKNOWN_LOCATION}. + * + * @param lineNumber Line number within the file. Use 0 for unknown + * positions within a file. + * @param columnNumber Column number within the line. + */ + public Location(String fileName, int lineNumber, int columnNumber) { + if (fileName != null && fileName.startsWith("file:")) { + this.fileName = FILE_UTILS.fromURI(fileName); + } else { + this.fileName = fileName; + } + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + } + + /** + * @return the filename portion of the location + * @since Ant 1.6 + */ + public String getFileName() { + return fileName; + } + + /** + * @return the line number + * @since Ant 1.6 + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * @return the column number + * @since Ant 1.7 + */ + public int getColumnNumber() { + return columnNumber; + } + + /** + * Returns the file name, line number, a colon and a trailing space. + * An error message can be appended easily. For unknown locations, an + * empty string is returned. + * + * @return a String of the form "fileName:lineNumber: " + * if both file name and line number are known, + * "fileName: " if only the file name is known, + * and the empty string for unknown locations. + */ + public String toString() { + StringBuffer buf = new StringBuffer(); + + if (fileName != null) { + buf.append(fileName); + + if (lineNumber != 0) { + buf.append(":"); + buf.append(lineNumber); + } + + buf.append(": "); + } + + return buf.toString(); + } + + /** + * Equality operation. + * @param other the object to compare to. + * @return true if the other object contains the same information + * as this object. + * @since Ant 1.6.3 + */ + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null) { + return false; + } + if (!(other.getClass() == getClass())) { + return false; + } + return toString().equals(other.toString()); + } + + /** + * Hash operation. + * @return a hash code value for this location. + * @since Ant 1.6.3 + */ + public int hashCode() { + return toString().hashCode(); + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/PathTokenizer.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/PathTokenizer.java new file mode 100644 index 000000000..5d1a9a47b --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/PathTokenizer.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant; + +import java.io.File; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; +import org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition.Os; + +/** + * A Path tokenizer takes a path and returns the components that make up + * that path. + * + * The path can use path separators of either ':' or ';' and file separators + * of either '/' or '\'. + * + */ +public class PathTokenizer { + /** + * A tokenizer to break the string up based on the ':' or ';' separators. + */ + private StringTokenizer tokenizer; + + /** + * A String which stores any path components which have been read ahead + * due to DOS filesystem compensation. + */ + private String lookahead = null; + + /** + * A boolean that determines if we are running on Novell NetWare, which + * exhibits slightly different path name characteristics (multi-character + * volume / drive names) + */ + private boolean onNetWare = Os.isFamily("netware"); + + /** + * Flag to indicate whether or not we are running on a platform with a + * DOS style filesystem + */ + private boolean dosStyleFilesystem; + + /** + * Constructs a path tokenizer for the specified path. + * + * @param path The path to tokenize. Must not be null. + */ + public PathTokenizer(String path) { + if (onNetWare) { + // For NetWare, use the boolean=true mode, so we can use delimiter + // information to make a better decision later. + tokenizer = new StringTokenizer(path, ":;", true); + } else { + // on Windows and Unix, we can ignore delimiters and still have + // enough information to tokenize correctly. + tokenizer = new StringTokenizer(path, ":;", false); + } + dosStyleFilesystem = File.pathSeparatorChar == ';'; + } + + /** + * Tests if there are more path elements available from this tokenizer's + * path. If this method returns true, then a subsequent call + * to nextToken will successfully return a token. + * + * @return true if and only if there is at least one token + * in the string after the current position; false otherwise. + */ + public boolean hasMoreTokens() { + if (lookahead != null) { + return true; + } + + return tokenizer.hasMoreTokens(); + } + + /** + * Returns the next path element from this tokenizer. + * + * @return the next path element from this tokenizer. + * + * @exception NoSuchElementException if there are no more elements in this + * tokenizer's path. + */ + public String nextToken() throws NoSuchElementException { + String token = null; + if (lookahead != null) { + token = lookahead; + lookahead = null; + } else { + token = tokenizer.nextToken().trim(); + } + + if (!onNetWare) { + if (token.length() == 1 && Character.isLetter(token.charAt(0)) + && dosStyleFilesystem + && tokenizer.hasMoreTokens()) { + // we are on a dos style system so this path could be a drive + // spec. We look at the next token + String nextToken = tokenizer.nextToken().trim(); + if (nextToken.startsWith("\\") || nextToken.startsWith("/")) { + // we know we are on a DOS style platform and the next path + // starts with a slash or backslash, so we know this is a + // drive spec + token += ":" + nextToken; + } else { + // store the token just read for next time + lookahead = nextToken; + } + } + } else { + // we are on NetWare, tokenizing is handled a little differently, + // due to the fact that NetWare has multiple-character volume names. + if (token.equals(File.pathSeparator) || token.equals(":")) { + // ignore ";" and get the next token + token = tokenizer.nextToken().trim(); + } + + if (tokenizer.hasMoreTokens()) { + // this path could be a drive spec, so look at the next token + String nextToken = tokenizer.nextToken().trim(); + + // make sure we aren't going to get the path separator next + if (!nextToken.equals(File.pathSeparator)) { + if (nextToken.equals(":")) { + if (!token.startsWith("/") && !token.startsWith("\\") + && !token.startsWith(".") + && !token.startsWith("..")) { + // it indeed is a drive spec, get the next bit + String oneMore = tokenizer.nextToken().trim(); + if (!oneMore.equals(File.pathSeparator)) { + token += ":" + oneMore; + } else { + token += ":"; + lookahead = oneMore; + } + } + // implicit else: ignore the ':' since we have either a + // UNIX or a relative path + } else { + // store the token just read for next time + lookahead = nextToken; + } + } + } + } + return token; + } +} + diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/ProjectComponent.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/ProjectComponent.java new file mode 100644 index 000000000..7b57e4956 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/ProjectComponent.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant; + +/** + * Base class for components of a project, including tasks and data types. Provides common facilities. + * + */ +public abstract class ProjectComponent implements Cloneable { + +// // CheckStyle:VisibilityModifier OFF - bc +// /** +// * Project object of this component. +// * @deprecated since 1.6.x. +// * You should not be directly accessing this variable directly. +// * You should access project object via the getProject() +// * or setProject() accessor/mutators. +// */ +// protected Project project; + /** + * Location within the build file of this task definition. + * + * @deprecated since 1.6.x. You should not be accessing this variable directly. Please use the + * {@link #getLocation()} method. + */ + protected Location location = Location.UNKNOWN_LOCATION; + + /** + * Description of this component, if any. + * + * @deprecated since 1.6.x. You should not be accessing this variable directly. + */ + protected String description; + // CheckStyle:VisibilityModifier ON + + /** + * Sole constructor. + */ + public ProjectComponent() { + } + +// /** +// * Sets the project object of this component. This method is used by +// * Project when a component is added to it so that the component has +// * access to the functions of the project. It should not be used +// * for any other purpose. +// * +// * @param project Project in whose scope this component belongs. +// * Must not be null. +// */ +// public void setProject(Project project) { +// this.project = project; +// } +// +// /** +// * Returns the project to which this component belongs. +// * +// * @return the components's project. +// */ +// public Project getProject() { +// return project; +// } + /** + * Returns the file/location where this task was defined. + * + * @return the file/location where this task was defined. Should not return null. + * Location.UNKNOWN_LOCATION is used for unknown locations. + * + * @see Location#UNKNOWN_LOCATION + */ + public Location getLocation() { + return location; + } + + /** + * Sets the file/location where this task was defined. + * + * @param location The file/location where this task was defined. Should not be null--use + * Location.UNKNOWN_LOCATION if the location isn't known. + * + * @see Location#UNKNOWN_LOCATION + */ + public void setLocation(Location location) { + this.location = location; + } + + /** + * Sets a description of the current action. This may be used for logging purposes. + * + * @param desc Description of the current action. May be null, indicating that no description is + * available. + * + */ + public void setDescription(String desc) { + description = desc; + } + + /** + * Returns the description of the current action. + * + * @return the description of the current action, or null if no description is available. + */ + public String getDescription() { + return description; + } + + /** + * Logs a message with the default (INFO) priority. + * + * @param msg The message to be logged. Should not be null. + */ + public void log(String msg) { +// log(msg, Project.MSG_INFO); + } + + /** + * Logs a message with the given priority. + * + * @param msg The message to be logged. Should not be null. + * @param msgLevel the message priority at which this message is to be logged. + */ + public void log(String msg, int msgLevel) { +// if (getProject() != null) { +// getProject().log(msg, msgLevel); +// } else { +// // 'reasonable' default, if the component is used without +// // a Project ( for example as a standalone Bean ). +// // Most ant components can be used this way. +// if (msgLevel <= Project.MSG_INFO) { +// System.err.println(msg); +// } +// } + } + + /** + * @since Ant 1.7 + * @return a shallow copy of this projectcomponent. + * @throws CloneNotSupportedException does not happen, but is declared to allow subclasses to do so. + */ + public Object clone() throws CloneNotSupportedException { + ProjectComponent pc = (ProjectComponent) super.clone(); + pc.setLocation(getLocation()); + //pc.setProject(getProject()); + return pc; + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/Locator.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/Locator.java new file mode 100644 index 000000000..dca690640 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/Locator.java @@ -0,0 +1,530 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.launch; + +import java.net.MalformedURLException; +import java.net.URL; +import java.io.File; +import java.io.FilenameFilter; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.Locale; + +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; + +// CheckStyle:LineLengthCheck OFF - urls are long! +/** + * The Locator is a utility class which is used to find certain items + * in the environment. + * + * It is used at boot time in the launcher, and cannot make use of any of Ant's other classes. + * + * This is a surprisingly brittle piece of code, and has had lots of bugs filed against it. + * {@link running ant off a network share can cause Ant to fail} + * {@link use File.toURI().toURL().toExternalForm()} + * {@link Locator implementation not encoding URI strings properly: spaces in paths} + * It also breaks Eclipse 3.3 Betas + * {@link Exception if installation path has spaces} + * + * Be very careful when making changes to this class, as a break will upset a lot of people. + * @since Ant 1.6 + */ +// CheckStyle:LineLengthCheck ON - urls are long! +public final class Locator { + + private static final int NIBBLE = 4; + private static final int NIBBLE_MASK = 0xF; + + private static final int ASCII_SIZE = 128; + + private static final int BYTE_SIZE = 256; + + private static final int WORD = 16; + + private static final int SPACE = 0x20; + private static final int DEL = 0x7F; + + /** + * encoding used to represent URIs + */ + public static final String URI_ENCODING = "UTF-8"; + // stolen from org.apache.xerces.impl.XMLEntityManager#getUserDir() + // of the Xerces-J team + // which ASCII characters need to be escaped + private static boolean[] gNeedEscaping = new boolean[ASCII_SIZE]; + // the first hex character if a character needs to be escaped + private static char[] gAfterEscaping1 = new char[ASCII_SIZE]; + // the second hex character if a character needs to be escaped + private static char[] gAfterEscaping2 = new char[ASCII_SIZE]; + private static char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + /** Error string used when an invalid uri is seen */ + public static final String ERROR_NOT_FILE_URI + = "Can only handle valid file: URIs, not "; + + // initialize the above 3 arrays + static { + for (int i = 0; i < SPACE; i++) { + gNeedEscaping[i] = true; + gAfterEscaping1[i] = gHexChs[i >> NIBBLE]; + gAfterEscaping2[i] = gHexChs[i & NIBBLE_MASK]; + } + gNeedEscaping[DEL] = true; + gAfterEscaping1[DEL] = '7'; + gAfterEscaping2[DEL] = 'F'; + char[] escChs = {' ', '<', '>', '#', '%', '"', '{', '}', + '|', '\\', '^', '~', '[', ']', '`'}; + int len = escChs.length; + char ch; + for (int i = 0; i < len; i++) { + ch = escChs[i]; + gNeedEscaping[ch] = true; + gAfterEscaping1[ch] = gHexChs[ch >> NIBBLE]; + gAfterEscaping2[ch] = gHexChs[ch & NIBBLE_MASK]; + } + } + /** + * Not instantiable + */ + private Locator() { + } + + /** + * Find the directory or jar file the class has been loaded from. + * + * @param c the class whose location is required. + * @return the file or jar with the class or null if we cannot + * determine the location. + * + * @since Ant 1.6 + */ + public static File getClassSource(Class c) { + String classResource = c.getName().replace('.', '/') + ".class"; + return getResourceSource(c.getClassLoader(), classResource); + } + + /** + * Find the directory or jar a given resource has been loaded from. + * + * @param c the classloader to be consulted for the source. + * @param resource the resource whose location is required. + * + * @return the file with the resource source or null if + * we cannot determine the location. + * + * @since Ant 1.6 + */ + public static File getResourceSource(ClassLoader c, String resource) { + if (c == null) { + c = Locator.class.getClassLoader(); + } + URL url = null; + if (c == null) { + url = ClassLoader.getSystemResource(resource); + } else { + url = c.getResource(resource); + } + if (url != null) { + String u = url.toString(); + try { + if (u.startsWith("jar:file:")) { + return new File(fromJarURI(u)); + } else if (u.startsWith("file:")) { + int tail = u.indexOf(resource); + String dirName = u.substring(0, tail); + return new File(fromURI(dirName)); + } + } catch (IllegalArgumentException e) { + //unable to determine the URI for reasons unknown. + return null; + } + } + return null; + } + + + + /** + * Constructs a file path from a file: URI. + * + *

Will be an absolute path if the given URI is absolute.

+ * + *

Prior to Java 1.4, + * swallows '%' that are not followed by two characters.

+ * + * See dt-sysid + * which makes some mention of how + * characters not supported by URI Reference syntax should be escaped. + * + * @param uri the URI designating a file in the local filesystem. + * @return the local file system path for the file. + * @throws IllegalArgumentException if the URI is malformed or not a legal file: URL + * @since Ant 1.6 + */ + public static String fromURI(String uri) { + return fromURIJava13(uri); + // #buzilla8031: first try Java 1.4. + // TODO should use java.net.URI now that we can rely on 1.4... + // but check for UNC-related regressions, e.g. #42275 + // (and remember that \\server\share\file -> file:////server/share/file + // rather than -> file://server/share/file as it should; + // fixed only in JDK 7's java.nio.file.Path.toUri) + // return fromUriJava14(uri); + } + + /** + * Java1.4+ code to extract the path from the URI. + * @param uri + * @return null if a conversion was not possible + */ + /* currently unused: + private static String fromUriJava14(String uri) { + // Also check for properly formed URIs. Ant formerly recommended using + // nonsense URIs such as "file:./foo.xml" in XML includes. You shouldn't + // do that (just "foo.xml" is correct) but for compatibility we special-case + // things when the path is not absolute, and fall back to the old parsing behavior. + if (uri.startsWith("file:/")) { + try { + File f = new File(URI.create(encodeURI(uri))); + //bug #42227 forgot to decode before returning + return decodeUri(f.getAbsolutePath()); + } catch (IllegalArgumentException e) { + // Bad URI, pass this on. + // no, this is downgraded to a warning after various + // JRE bugs surfaced. Hand off + // to our built in code on a failure + //throw new IllegalArgumentException( + // "Bad URI " + uri + ":" + e.getMessage(), e); + e.printStackTrace(); + } catch (Exception e) { + // Unexpected exception? Should not happen. + e.printStackTrace(); + } + } + return null; + } + */ + + /** + * @param uri uri to expand + * @return the decoded URI + * @since Ant1.7.1 + */ + private static String fromURIJava13(String uri) { + // Fallback method for Java 1.3 or earlier. + + URL url = null; + try { + url = new URL(uri); + } catch (MalformedURLException emYouEarlEx) { + // Ignore malformed exception + } + if (url == null || !("file".equals(url.getProtocol()))) { + throw new IllegalArgumentException(ERROR_NOT_FILE_URI + uri); + } + StringBuffer buf = new StringBuffer(url.getHost()); + if (buf.length() > 0) { + buf.insert(0, File.separatorChar).insert(0, File.separatorChar); + } + String file = url.getFile(); + int queryPos = file.indexOf('?'); + buf.append((queryPos < 0) ? file : file.substring(0, queryPos)); + + uri = buf.toString().replace('/', File.separatorChar); + + if (File.pathSeparatorChar == ';' && uri.startsWith("\\") && uri.length() > 2 + && Character.isLetter(uri.charAt(1)) && uri.lastIndexOf(':') > -1) { + uri = uri.substring(1); + } + String path = null; + try { + path = decodeUri(uri); + //consider adding the current directory. This is not done when + //the path is a UNC name + String cwd = System.getProperty("user.dir"); + int posi = cwd.indexOf(':'); + boolean pathStartsWithFileSeparator = path.startsWith(File.separator); + boolean pathStartsWithUNC = path.startsWith("" + File.separator + File.separator); + if ((posi > 0) && pathStartsWithFileSeparator && !pathStartsWithUNC) { + path = cwd.substring(0, posi + 1) + path; + } + } catch (UnsupportedEncodingException exc) { + // not sure whether this is clean, but this method is + // declared not to throw exceptions. + throw new IllegalStateException( + "Could not convert URI " + uri + " to path: " + + exc.getMessage()); + } + return path; + } + + /** + * Crack a JAR URI. + * This method is public for testing; we may delete it without any warning -it is not part of Ant's stable API. + * @param uri uri to expand; contains jar: somewhere in it + * @return the decoded URI + * @since Ant1.7.1 + */ + public static String fromJarURI(String uri) { + int pling = uri.indexOf("!/"); + String jarName = uri.substring("jar:".length(), pling); + return fromURI(jarName); + } + + /** + * Decodes an Uri with % characters. + * The URI is escaped + * @param uri String with the uri possibly containing % characters. + * @return The decoded Uri + * @throws UnsupportedEncodingException if UTF-8 is not available + * @since Ant 1.7 + */ + public static String decodeUri(String uri) throws UnsupportedEncodingException { + if (uri.indexOf('%') == -1) { + return uri; + } + ByteArrayOutputStream sb = new ByteArrayOutputStream(uri.length()); + CharacterIterator iter = new StringCharacterIterator(uri); + for (char c = iter.first(); c != CharacterIterator.DONE; + c = iter.next()) { + if (c == '%') { + char c1 = iter.next(); + if (c1 != CharacterIterator.DONE) { + int i1 = Character.digit(c1, WORD); + char c2 = iter.next(); + if (c2 != CharacterIterator.DONE) { + int i2 = Character.digit(c2, WORD); + sb.write((char) ((i1 << NIBBLE) + i2)); + } + } + } else if (c >= 0x0000 && c < 0x0080) { + sb.write(c); + } else { // #50543 + byte[] bytes = String.valueOf(c).getBytes(URI_ENCODING); + sb.write(bytes, 0, bytes.length); + } + } + return sb.toString(URI_ENCODING); + } + + /** + * Encodes an Uri with % characters. + * The URI is escaped + * @param path String to encode. + * @return The encoded string, according to URI norms + * @throws UnsupportedEncodingException if UTF-8 is not available + * @since Ant 1.7 + */ + public static String encodeURI(String path) throws UnsupportedEncodingException { + int i = 0; + int len = path.length(); + int ch = 0; + StringBuffer sb = null; + for (; i < len; i++) { + ch = path.charAt(i); + // if it's not an ASCII character, break here, and use UTF-8 encoding + if (ch >= ASCII_SIZE) { + break; + } + if (gNeedEscaping[ch]) { + if (sb == null) { + sb = new StringBuffer(path.substring(0, i)); + } + sb.append('%'); + sb.append(gAfterEscaping1[ch]); + sb.append(gAfterEscaping2[ch]); + // record the fact that it's escaped + } else if (sb != null) { + sb.append((char) ch); + } + } + + // we saw some non-ascii character + if (i < len) { + if (sb == null) { + sb = new StringBuffer(path.substring(0, i)); + } + // get UTF-8 bytes for the remaining sub-string + byte[] bytes = null; + byte b; + bytes = path.substring(i).getBytes(URI_ENCODING); + len = bytes.length; + + // for each byte + for (i = 0; i < len; i++) { + b = bytes[i]; + // for non-ascii character: make it positive, then escape + if (b < 0) { + ch = b + BYTE_SIZE; + sb.append('%'); + sb.append(gHexChs[ch >> NIBBLE]); + sb.append(gHexChs[ch & NIBBLE_MASK]); + } else if (gNeedEscaping[b]) { + sb.append('%'); + sb.append(gAfterEscaping1[b]); + sb.append(gAfterEscaping2[b]); + } else { + sb.append((char) b); + } + } + } + return sb == null ? path : sb.toString(); + } + + /** + * Convert a File to a URL. + * File.toURL() does not encode characters like #. + * File.toURI() has been introduced in java 1.4, so + * Ant cannot use it (except by reflection) + * FileUtils.toURI() cannot be used by Locator.java + * Implemented this way. + * File.toURL() adds file: and changes '\' to '/' for dos OSes + * encodeURI converts characters like ' ' and '#' to %DD + * @param file the file to convert + * @return URL the converted File + * @throws MalformedURLException on error + * @deprecated since 1.9, use {@link FileUtils#getFileURL(File)} + */ + @Deprecated + public static URL fileToURL(File file) + throws MalformedURLException { + return new URL(file.toURI().toASCIIString()); + } + + /** + * Get the File necessary to load the Sun compiler tools. If the classes + * are available to this class, then no additional URL is required and + * null is returned. This may be because the classes are explicitly in the + * class path or provided by the JVM directly. + * + * @return the tools jar as a File if required, null otherwise. + */ + public static File getToolsJar() { + // firstly check if the tools jar is already in the classpath + boolean toolsJarAvailable = false; + try { + // just check whether this throws an exception + Class.forName("com.sun.tools.javac.Main"); + toolsJarAvailable = true; + } catch (Exception e) { + try { + Class.forName("sun.tools.javac.Main"); + toolsJarAvailable = true; + } catch (Exception e2) { + // ignore + } + } + if (toolsJarAvailable) { + return null; + } + // couldn't find compiler - try to find tools.jar + // based on java.home setting + String libToolsJar + = File.separator + "lib" + File.separator + "tools.jar"; + String javaHome = System.getProperty("java.home"); + File toolsJar = new File(javaHome + libToolsJar); + if (toolsJar.exists()) { + // Found in java.home as given + return toolsJar; + } + if (javaHome.toLowerCase(Locale.ENGLISH).endsWith(File.separator + "jre")) { + javaHome = javaHome.substring( + 0, javaHome.length() - "/jre".length()); + toolsJar = new File(javaHome + libToolsJar); + } + if (!toolsJar.exists()) { + System.out.println("Unable to locate tools.jar. " + + "Expected to find it in " + toolsJar.getPath()); + return null; + } + return toolsJar; + } + + /** + * Get an array of URLs representing all of the jar files in the + * given location. If the location is a file, it is returned as the only + * element of the array. If the location is a directory, it is scanned for + * jar files. + * + * @param location the location to scan for Jars. + * + * @return an array of URLs for all jars in the given location. + * + * @exception MalformedURLException if the URLs for the jars cannot be + * formed. + */ + public static URL[] getLocationURLs(File location) + throws MalformedURLException { + return getLocationURLs(location, new String[]{".jar"}); + } + + /** + * Get an array of URLs representing all of the files of a given set of + * extensions in the given location. If the location is a file, it is + * returned as the only element of the array. If the location is a + * directory, it is scanned for matching files. + * + * @param location the location to scan for files. + * @param extensions an array of extension that are to match in the + * directory search. + * + * @return an array of URLs of matching files. + * @exception MalformedURLException if the URLs for the files cannot be + * formed. + */ + public static URL[] getLocationURLs(File location, + final String[] extensions) + throws MalformedURLException { + URL[] urls = new URL[0]; + + if (!location.exists()) { + return urls; + } + if (!location.isDirectory()) { + urls = new URL[1]; + String path = location.getPath(); + String littlePath = path.toLowerCase(Locale.ENGLISH); + for (int i = 0; i < extensions.length; ++i) { + if (littlePath.endsWith(extensions[i])) { + urls[0] = fileToURL(location); + break; + } + } + return urls; + } + File[] matches = location.listFiles( + new FilenameFilter() { + public boolean accept(File dir, String name) { + String littleName = name.toLowerCase(Locale.ENGLISH); + for (int i = 0; i < extensions.length; ++i) { + if (littleName.endsWith(extensions[i])) { + return true; + } + } + return false; + } + }); + urls = new URL[matches.length]; + for (int i = 0; i < matches.length; ++i) { + urls[i] = fileToURL(matches[i]); + } + return urls; + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/package-info.java new file mode 100644 index 000000000..115f3b29e --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/package-info.java @@ -0,0 +1,14 @@ +/** + * + * + * org.owasp.dependencycheck.org.apache.tools.ant.launch + * + * + * This is a copy of classes within Apache Ant. The DirectoryScanner + * is needed by dependency-check. However, we did not want to make + * Ant a dependency. As such, a few files were copied and slightly + * modified to remove any references to the Ant Project class. + * + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.launch; diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/package-info.java new file mode 100644 index 000000000..cb3c12962 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/package-info.java @@ -0,0 +1,14 @@ +/** + * + * + * org.owasp.dependencycheck.org.apache.tools.ant + * + * + * This is a copy of classes within Apache Ant. The DirectoryScanner + * is needed by dependency-check. However, we did not want to make + * Ant a dependency. As such, a few files were copied and slightly + * modified to remove any references to the Ant Project class. + * + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant; diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Condition.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Condition.java new file mode 100644 index 000000000..d6e0351d3 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Condition.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition; + +import org.owasp.dependencycheck.org.apache.tools.ant.BuildException; + +/** + * Interface for conditions to use inside the <condition> task. + * + */ +public interface Condition { + /** + * Is this condition true? + * @return true if the condition is true + * @exception BuildException if an error occurs + */ + boolean eval() throws BuildException; +} + diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Os.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Os.java new file mode 100644 index 000000000..4ea20554e --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Os.java @@ -0,0 +1,321 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition; + +import java.util.Locale; + +import org.owasp.dependencycheck.org.apache.tools.ant.BuildException; + +/** + * Condition that tests the OS type. + * + * @since Ant 1.4 + */ +public class Os implements Condition { + private static final String OS_NAME = + System.getProperty("os.name").toLowerCase(Locale.ENGLISH); + private static final String OS_ARCH = + System.getProperty("os.arch").toLowerCase(Locale.ENGLISH); + private static final String OS_VERSION = + System.getProperty("os.version").toLowerCase(Locale.ENGLISH); + private static final String PATH_SEP = + System.getProperty("path.separator"); + + /** + * OS family to look for + */ + private String family; + /** + * Name of OS + */ + private String name; + /** + * version of OS + */ + private String version; + /** + * OS architecture + */ + private String arch; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_WINDOWS = "windows"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_9X = "win9x"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NT = "winnt"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OS2 = "os/2"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NETWARE = "netware"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_DOS = "dos"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_MAC = "mac"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_TANDEM = "tandem"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_UNIX = "unix"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_VMS = "openvms"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_ZOS = "z/os"; + /** OS family that can be tested for. {@value} */ + public static final String FAMILY_OS400 = "os/400"; + + /** + * OpenJDK is reported to call MacOS X "Darwin" + * @see https://issues.apache.org/bugzilla/show_bug.cgi?id=44889 + * @see https://issues.apache.org/jira/browse/HADOOP-3318 + */ + private static final String DARWIN = "darwin"; + + /** + * Default constructor + * + */ + public Os() { + //default + } + + /** + * Constructor that sets the family attribute + * @param family a String value + */ + public Os(String family) { + setFamily(family); + } + + /** + * Sets the desired OS family type + * + * @param f The OS family type desired
+ * Possible values:
+ *
    + *
  • dos
  • + *
  • mac
  • + *
  • netware
  • + *
  • os/2
  • + *
  • tandem
  • + *
  • unix
  • + *
  • windows
  • + *
  • win9x
  • + *
  • z/os
  • + *
  • os/400
  • + *
+ */ + public void setFamily(String f) { + family = f.toLowerCase(Locale.ENGLISH); + } + + /** + * Sets the desired OS name + * + * @param name The OS name + */ + public void setName(String name) { + this.name = name.toLowerCase(Locale.ENGLISH); + } + + /** + * Sets the desired OS architecture + * + * @param arch The OS architecture + */ + public void setArch(String arch) { + this.arch = arch.toLowerCase(Locale.ENGLISH); + } + + /** + * Sets the desired OS version + * + * @param version The OS version + */ + public void setVersion(String version) { + this.version = version.toLowerCase(Locale.ENGLISH); + } + + /** + * Determines if the OS on which Ant is executing matches the type of + * that set in setFamily. + * @return true if the os matches. + * @throws BuildException if there is an error. + * @see Os#setFamily(String) + */ + public boolean eval() throws BuildException { + return isOs(family, name, arch, version); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family. + * @param family the family to check for + * @return true if the OS matches + * @since 1.5 + */ + public static boolean isFamily(String family) { + return isOs(family, null, null, null); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS name. + * + * @param name the OS name to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isName(String name) { + return isOs(null, name, null, null); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS architecture. + * + * @param arch the OS architecture to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isArch(String arch) { + return isOs(null, null, arch, null); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS version. + * + * @param version the OS version to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isVersion(String version) { + return isOs(null, null, null, version); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family, name, architecture and version + * + * @param family The OS family + * @param name The OS name + * @param arch The OS architecture + * @param version The OS version + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isOs(String family, String name, String arch, + String version) { + boolean retValue = false; + + if (family != null || name != null || arch != null + || version != null) { + + boolean isFamily = true; + boolean isName = true; + boolean isArch = true; + boolean isVersion = true; + + if (family != null) { + + //windows probing logic relies on the word 'windows' in + //the OS + boolean isWindows = OS_NAME.indexOf(FAMILY_WINDOWS) > -1; + boolean is9x = false; + boolean isNT = false; + if (isWindows) { + //there are only four 9x platforms that we look for + is9x = (OS_NAME.indexOf("95") >= 0 + || OS_NAME.indexOf("98") >= 0 + || OS_NAME.indexOf("me") >= 0 + //wince isn't really 9x, but crippled enough to + //be a muchness. Ant doesnt run on CE, anyway. + || OS_NAME.indexOf("ce") >= 0); + isNT = !is9x; + } + if (family.equals(FAMILY_WINDOWS)) { + isFamily = isWindows; + } else if (family.equals(FAMILY_9X)) { + isFamily = isWindows && is9x; + } else if (family.equals(FAMILY_NT)) { + isFamily = isWindows && isNT; + } else if (family.equals(FAMILY_OS2)) { + isFamily = OS_NAME.indexOf(FAMILY_OS2) > -1; + } else if (family.equals(FAMILY_NETWARE)) { + isFamily = OS_NAME.indexOf(FAMILY_NETWARE) > -1; + } else if (family.equals(FAMILY_DOS)) { + isFamily = PATH_SEP.equals(";") && !isFamily(FAMILY_NETWARE); + } else if (family.equals(FAMILY_MAC)) { + isFamily = OS_NAME.indexOf(FAMILY_MAC) > -1 + || OS_NAME.indexOf(DARWIN) > -1; + } else if (family.equals(FAMILY_TANDEM)) { + isFamily = OS_NAME.indexOf("nonstop_kernel") > -1; + } else if (family.equals(FAMILY_UNIX)) { + isFamily = PATH_SEP.equals(":") + && !isFamily(FAMILY_VMS) + && (!isFamily(FAMILY_MAC) || OS_NAME.endsWith("x") + || OS_NAME.indexOf(DARWIN) > -1); + } else if (family.equals(FAMILY_ZOS)) { + isFamily = OS_NAME.indexOf(FAMILY_ZOS) > -1 + || OS_NAME.indexOf("os/390") > -1; + } else if (family.equals(FAMILY_OS400)) { + isFamily = OS_NAME.indexOf(FAMILY_OS400) > -1; + } else if (family.equals(FAMILY_VMS)) { + isFamily = OS_NAME.indexOf(FAMILY_VMS) > -1; + } else { + throw new BuildException( + "Don\'t know how to detect os family \"" + + family + "\""); + } + } + if (name != null) { + isName = name.equals(OS_NAME); + } + if (arch != null) { + isArch = arch.equals(OS_ARCH); + } + if (version != null) { + isVersion = version.equals(OS_VERSION); + } + retValue = isFamily && isName && isArch && isVersion; + } + return retValue; + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/package-info.java new file mode 100644 index 000000000..58f73450b --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/package-info.java @@ -0,0 +1,14 @@ +/** + * + * + * org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition + * + * + * This is a copy of classes within Apache Ant. The DirectoryScanner + * is needed by dependency-check. However, we did not want to make + * Ant a dependency. As such, a few files were copied and slightly + * modified to remove any references to the Ant Project class. + * + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition; diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/DataType.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/DataType.java new file mode 100644 index 000000000..d97367c90 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/DataType.java @@ -0,0 +1,353 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types; + +import org.owasp.dependencycheck.org.apache.tools.ant.BuildException; +import org.owasp.dependencycheck.org.apache.tools.ant.ProjectComponent; + +/** + * Base class for those classes that can appear inside the build file as stand alone data types. + * + *

+ * This class handles the common description attribute and provides a default implementation for reference handling and + * checking for circular references that is appropriate for types that can not be nested inside elements of the same + * type (i.e. <patternset> but not <path>).

+ * + */ +public abstract class DataType extends ProjectComponent implements Cloneable { + // CheckStyle:VisibilityModifier OFF + + /** + * Value to the refid attribute. + * + * @deprecated since 1.7. The user should not be directly referencing variable. Please use {@link #getRefid} + * instead. + */ + protected Reference ref; + + /** + * Are we sure we don't hold circular references? + * + *

+ * Subclasses are responsible for setting this value to false if we'd need to investigate this condition (usually + * because a child element has been added that is a subclass of DataType).

+ * + * @deprecated since 1.7. The user should not be directly referencing variable. Please use {@link #setChecked} or + * {@link #isChecked} instead. + */ + protected boolean checked = true; + // CheckStyle:VisibilityModifier ON + + /** + * Has the refid attribute of this element been set? + * + * @return true if the refid attribute has been set + */ + public boolean isReference() { + return ref != null; + } + + /** + * Set the value of the refid attribute. + * + *

+ * Subclasses may need to check whether any other attributes have been set as well or child elements have been + * created and thus override this method. if they do the must call super.setRefid.

+ * + * @param ref the reference to use + */ + public void setRefid(final Reference ref) { + this.ref = ref; + checked = false; + } + +// /** +// * Gets as descriptive as possible a name used for this datatype instance. +// * +// * @return String name. +// */ +// protected String getDataTypeName() { +// return ComponentHelper.getElementName(getProject(), this, true); +// } +// /** +// * Convenience method. +// * @since Ant 1.7 +// */ +// protected void dieOnCircularReference() { +// dieOnCircularReference(getProject()); +// } +// +// /** +// * Convenience method. +// * @param p the Ant Project instance against which to resolve references. +// * @since Ant 1.7 +// */ +// protected void dieOnCircularReference(Project p) { +// if (checked || !isReference()) { +// return; +// } +// dieOnCircularReference(new IdentityStack(this), p); +// } +// +// /** +// * Check to see whether any DataType we hold references to is +// * included in the Stack (which holds all DataType instances that +// * directly or indirectly reference this instance, including this +// * instance itself). +// * +// *

If one is included, throw a BuildException created by {@link +// * #circularReference circularReference}.

+// * +// *

This implementation is appropriate only for a DataType that +// * cannot hold other DataTypes as children.

+// * +// *

The general contract of this method is that it shouldn't do +// * anything if {@link #checked checked} is true and +// * set it to true on exit.

+// * @param stack the stack of references to check. +// * @param project the project to use to dereference the references. +// * @throws BuildException on error. +// */ +// protected void dieOnCircularReference(final Stack stack, +// final Project project) +// throws BuildException { +// +// if (checked || !isReference()) { +// return; +// } +// Object o = ref.getReferencedObject(project); +// +// if (o instanceof DataType) { +// IdentityStack id = IdentityStack.getInstance(stack); +// +// if (id.contains(o)) { +// throw circularReference(); +// } else { +// id.push(o); +// ((DataType) o).dieOnCircularReference(id, project); +// id.pop(); +// } +// } +// checked = true; +// } +// /** +// * Allow DataTypes outside org.apache.tools.ant.types to indirectly call dieOnCircularReference on nested DataTypes. +// * +// * @param dt the DataType to check. +// * @param stk the stack of references to check. +// * @param p the project to use to dereference the references. +// * @throws BuildException on error. +// * @since Ant 1.7 +// */ +// public static void invokeCircularReferenceCheck(DataType dt, Stack stk, +// Project p) { +// dt.dieOnCircularReference(stk, p); +// } +// +// /** +// * Allow DataTypes outside org.apache.tools.ant.types to indirectly call dieOnCircularReference on nested DataTypes. +// * +// *

+// * Pushes dt on the stack, runs dieOnCircularReference and pops it again.

+// * +// * @param dt the DataType to check. +// * @param stk the stack of references to check. +// * @param p the project to use to dereference the references. +// * @throws BuildException on error. +// * @since Ant 1.8.0 +// */ +// public static void pushAndInvokeCircularReferenceCheck(DataType dt, +// Stack stk, +// Project p) { +// stk.push(dt); +// dt.dieOnCircularReference(stk, p); +// stk.pop(); +// } +// /** +// * Performs the check for circular references and returns the referenced object. +// * +// * @return the dereferenced object. +// * @throws BuildException if the reference is invalid (circular ref, wrong class, etc). +// * @since Ant 1.7 +// */ +// protected Object getCheckedRef() { +// return getCheckedRef(getProject()); +// } +// +// /** +// * Performs the check for circular references and returns the referenced object. +// * +// * @param p the Ant Project instance against which to resolve references. +// * @return the dereferenced object. +// * @throws BuildException if the reference is invalid (circular ref, wrong class, etc). +// * @since Ant 1.7 +// */ +// protected Object getCheckedRef(Project p) { +// return getCheckedRef(getClass(), getDataTypeName(), p); +// } +// +// /** +// * Performs the check for circular references and returns the referenced object. +// * +// * @param requiredClass the class that this reference should be a subclass of. +// * @param dataTypeName the name of the datatype that the reference should be (error message use only). +// * @return the dereferenced object. +// * @throws BuildException if the reference is invalid (circular ref, wrong class, etc). +// */ +// protected T getCheckedRef(final Class requiredClass, +// final String dataTypeName) { +// return getCheckedRef(requiredClass, dataTypeName, getProject()); +// } +// +// /** +// * Performs the check for circular references and returns the referenced object. This version allows the fallback +// * Project instance to be specified. +// * +// * @param requiredClass the class that this reference should be a subclass of. +// * @param dataTypeName the name of the datatype that the reference should be (error message use only). +// * @param project the fallback Project instance for dereferencing. +// * @return the dereferenced object. +// * @throws BuildException if the reference is invalid (circular ref, wrong class, etc), or if project +// * is null. +// * @since Ant 1.7 +// */ +// protected T getCheckedRef(final Class requiredClass, +// final String dataTypeName, final Project project) { +// if (project == null) { +// throw new BuildException("No Project specified"); +// } +// dieOnCircularReference(project); +// Object o = ref.getReferencedObject(project); +// if (!(requiredClass.isAssignableFrom(o.getClass()))) { +// log("Class " + o.getClass() + " is not a subclass of " + requiredClass, +// Project.MSG_VERBOSE); +// String msg = ref.getRefId() + " doesn\'t denote a " + dataTypeName; +// throw new BuildException(msg); +// } +// @SuppressWarnings("unchecked") +// final T result = (T) o; +// return result; +// } + /** + * Creates an exception that indicates that refid has to be the only attribute if it is set. + * + * @return the exception to throw + */ + protected BuildException tooManyAttributes() { + return new BuildException("You must not specify more than one " + + "attribute when using refid"); + } + + /** + * Creates an exception that indicates that this XML element must not have child elements if the refid attribute is + * set. + * + * @return the exception to throw + */ + protected BuildException noChildrenAllowed() { + return new BuildException("You must not specify nested elements " + + "when using refid"); + } + + /** + * Creates an exception that indicates the user has generated a loop of data types referencing each other. + * + * @return the exception to throw + */ + protected BuildException circularReference() { + return new BuildException("This data type contains a circular " + + "reference."); + } + + /** + * The flag that is used to indicate that circular references have been checked. + * + * @return true if circular references have been checked + */ + protected boolean isChecked() { + return checked; + } + + /** + * Set the flag that is used to indicate that circular references have been checked. + * + * @param checked if true, if circular references have been checked + */ + protected void setChecked(final boolean checked) { + this.checked = checked; + } + + /** + * get the reference set on this object + * + * @return the reference or null + */ + public Reference getRefid() { + return ref; + } + + /** + * check that it is ok to set attributes, i.e that no reference is defined + * + * @since Ant 1.6 + * @throws BuildException if not allowed + */ + protected void checkAttributesAllowed() { + if (isReference()) { + throw tooManyAttributes(); + } + } + + /** + * check that it is ok to add children, i.e that no reference is defined + * + * @since Ant 1.6 + * @throws BuildException if not allowed + */ + protected void checkChildrenAllowed() { + if (isReference()) { + throw noChildrenAllowed(); + } + } + + /** + * Basic DataType toString(). + * + * @return this DataType formatted as a String. + */ + public String toString() { + String d = getDescription(); + //return d == null ? getDataTypeName() : getDataTypeName() + " " + d; + return d == null ? "DataType" : d; + } + + /** + * @since Ant 1.7 + * @return a shallow copy of this DataType. + * @throws CloneNotSupportedException if there is a problem. + */ + public Object clone() throws CloneNotSupportedException { + DataType dt = (DataType) super.clone(); + dt.setDescription(getDescription()); + if (getRefid() != null) { + dt.setRefid(getRefid()); + } + dt.setChecked(isChecked()); + return dt; + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/Reference.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/Reference.java new file mode 100644 index 000000000..1150a38f5 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/Reference.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types; + +/** + * Class to hold a reference to another object in the project. + * + */ +public class Reference { + + private String refid; + //private Project project; + + /** + * Create a reference. + * + * @deprecated since 1.7. Please use {@link Reference#Reference(Project,String)} instead. + */ + public Reference() { + } + + /** + * Create a reference to a named ID. + * + * @param id the name of this reference + * @deprecated since 1.7. Please use {@link Reference#Reference(Project,String)} instead. + */ + public Reference(String id) { + setRefId(id); + } +// +// /** +// * Create a reference to a named ID in a particular project. +// * @param p the project this reference is associated with +// * @param id the name of this reference +// * @since Ant 1.6.3 +// */ +// public Reference(Project p, String id) { +// setRefId(id); +// setProject(p); +// } + + /** + * Set the reference id. Should not normally be necessary; use {@link Reference#Reference(Project, String)}. + * + * @param id the reference id to use + */ + public void setRefId(String id) { + refid = id; + } + + /** + * Get the reference id of this reference. + * + * @return the reference id + */ + public String getRefId() { + return refid; + } + +// /** +// * Set the associated project. Should not normally be necessary; +// * use {@link Reference#Reference(Project,String)}. +// * @param p the project to use +// * @since Ant 1.6.3 +// */ +// public void setProject(Project p) { +// this.project = p; +// } +// +// /** +// * Get the associated project, if any; may be null. +// * @return the associated project +// * @since Ant 1.6.3 +// */ +// public Project getProject() { +// return project; +// } +// /** +// * Resolve the reference, using the associated project if +// * it set, otherwise use the passed in project. +// * @param fallback the fallback project to use if the project attribute of +// * reference is not set. +// * @return the dereferenced object. +// * @throws BuildException if the reference cannot be dereferenced. +// */ +// public Object getReferencedObject(Project fallback) throws BuildException { +// if (refid == null) { +// throw new BuildException("No reference specified"); +// } +// +// Object o = project == null ? fallback.getReference(refid) : project.getReference(refid); +// if (o == null) { +// throw new BuildException("Reference " + refid + " not found."); +// } +// return o; +// } +// /** +// * Resolve the reference, looking in the associated project. +// * @see Project#getReference +// * @return the dereferenced object. +// * @throws BuildException if the project is null or the reference cannot be dereferenced +// * @since Ant 1.6.3 +// */ +// public Object getReferencedObject() throws BuildException { +// if (project == null) { +// throw new BuildException("No project set on reference to " + refid); +// } +// return getReferencedObject(project); +// } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/Resource.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/Resource.java new file mode 100644 index 000000000..f8379619a --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/Resource.java @@ -0,0 +1,462 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.util.Iterator; +import java.util.NoSuchElementException; +import org.owasp.dependencycheck.org.apache.tools.ant.types.resources.FileProvider; + +/** + * Describes a "File-like" resource (File, ZipEntry, etc.). + * + * This class is meant to be used by classes needing to record path and date/time information about a file, a zip entry + * or some similar resource (URL, archive in a version control repository, ...). + * + * @since Ant 1.5.2 + * @see org.apache.tools.ant.types.resources.Touchable + */ +public class Resource extends DataType implements Comparable, ResourceCollection { + + /** + * Constant unknown size + */ + public static final long UNKNOWN_SIZE = -1; + + /** + * Constant unknown datetime for getLastModified + */ + public static final long UNKNOWN_DATETIME = 0L; + + /** + * Magic number + */ + protected static final int MAGIC = getMagicNumber("Resource".getBytes()); + + private static final int NULL_NAME = getMagicNumber("null name".getBytes()); + + /** + * Create a "magic number" for use in hashCode calculations. + * + * @param seed byte[] to seed with. + * @return a magic number as int. + */ + protected static int getMagicNumber(byte[] seed) { + return new BigInteger(seed).intValue(); + } + + private String name = null; + private Boolean exists = null; + private Long lastmodified = null; + private Boolean directory = null; + private Long size = null; + + /** + * Default constructor. + */ + public Resource() { + } + + /** + * Only sets the name. + * + *

+ * This is a dummy, used for not existing resources.

+ * + * @param name relative path of the resource. Expects "/" to be used as the directory separator. + */ + public Resource(String name) { + this(name, false, 0, false); + } + + /** + * Sets the name, lastmodified flag, and exists flag. + * + * @param name relative path of the resource. Expects "/" to be used as the directory separator. + * @param exists if true, this resource exists. + * @param lastmodified the last modification time of this resource. + */ + public Resource(String name, boolean exists, long lastmodified) { + this(name, exists, lastmodified, false); + } + + /** + * Sets the name, lastmodified flag, exists flag, and directory flag. + * + * @param name relative path of the resource. Expects "/" to be used as the directory separator. + * @param exists if true the resource exists + * @param lastmodified the last modification time of the resource + * @param directory if true, this resource is a directory + */ + public Resource(String name, boolean exists, long lastmodified, boolean directory) { + this(name, exists, lastmodified, directory, UNKNOWN_SIZE); + } + + /** + * Sets the name, lastmodified flag, exists flag, directory flag, and size. + * + * @param name relative path of the resource. Expects "/" to be used as the directory separator. + * @param exists if true the resource exists + * @param lastmodified the last modification time of the resource + * @param directory if true, this resource is a directory + * @param size the size of this resource. + */ + public Resource(String name, boolean exists, long lastmodified, boolean directory, long size) { + this.name = name; + setName(name); + setExists(exists); + setLastModified(lastmodified); + setDirectory(directory); + setSize(size); + } + + /** + * Name attribute will contain the path of a file relative to the root directory of its fileset or the recorded path + * of a zip entry. + * + *

+ * example for a file with fullpath /var/opt/adm/resource.txt in a file set with root dir /var/opt it will be + * adm/resource.txt.

+ * + *

+ * "/" will be used as the directory separator.

+ * + * @return the name of this resource. + */ + public String getName() { + //return isReference() ? ((Resource) getCheckedRef()).getName() : name; + return name; + } + + /** + * Set the name of this Resource. + * + * @param name relative path of the resource. Expects "/" to be used as the directory separator. + */ + public void setName(String name) { + checkAttributesAllowed(); + this.name = name; + } + + /** + * The exists attribute tells whether a resource exists. + * + * @return true if this resource exists. + */ + public boolean isExists() { +// if (isReference()) { +// return ((Resource) getCheckedRef()).isExists(); +// } + //default true: + return exists == null || exists.booleanValue(); + } + + /** + * Set the exists attribute. + * + * @param exists if true, this resource exists. + */ + public void setExists(boolean exists) { + checkAttributesAllowed(); + this.exists = exists ? Boolean.TRUE : Boolean.FALSE; + } + + /** + * Tells the modification time in milliseconds since 01.01.1970 (the "epoch"). + * + * @return the modification time, if that is meaningful (e.g. for a file resource which exists); 0 if the resource + * does not exist, to mirror the behavior of {@link java.io.File#lastModified}; or 0 if the notion of modification + * time is meaningless for this class of resource (e.g. an inline string) + */ + public long getLastModified() { +// if (isReference()) { +// return ((Resource) getCheckedRef()).getLastModified(); +// } + if (!isExists() || lastmodified == null) { + return UNKNOWN_DATETIME; + } + long result = lastmodified.longValue(); + return result < UNKNOWN_DATETIME ? UNKNOWN_DATETIME : result; + } + + /** + * Set the last modification attribute. + * + * @param lastmodified the modification time in milliseconds since 01.01.1970. + */ + public void setLastModified(long lastmodified) { + checkAttributesAllowed(); + this.lastmodified = new Long(lastmodified); + } + + /** + * Tells if the resource is a directory. + * + * @return boolean flag indicating if the resource is a directory. + */ + public boolean isDirectory() { +// if (isReference()) { +// return ((Resource) getCheckedRef()).isDirectory(); +// } + //default false: + return directory != null && directory.booleanValue(); + } + + /** + * Set the directory attribute. + * + * @param directory if true, this resource is a directory. + */ + public void setDirectory(boolean directory) { + checkAttributesAllowed(); + this.directory = directory ? Boolean.TRUE : Boolean.FALSE; + } + + /** + * Set the size of this Resource. + * + * @param size the size, as a long. + * @since Ant 1.6.3 + */ + public void setSize(long size) { + checkAttributesAllowed(); + this.size = new Long(size > UNKNOWN_SIZE ? size : UNKNOWN_SIZE); + } + + /** + * Get the size of this Resource. + * + * @return the size, as a long, 0 if the Resource does not exist (for compatibility with java.io.File), or + * UNKNOWN_SIZE if not known. + * @since Ant 1.6.3 + */ + public long getSize() { +// if (isReference()) { +// return ((Resource) getCheckedRef()).getSize(); +// } + return isExists() + ? (size != null ? size.longValue() : UNKNOWN_SIZE) + : 0L; + } + + /** + * Clone this Resource. + * + * @return copy of this. + */ + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new UnsupportedOperationException( + "CloneNotSupportedException for a Resource caught. " + + "Derived classes must support cloning."); + } + } + + /** + * Delegates to a comparison of names. + * + * @param other the object to compare to. + * @return a negative integer, zero, or a positive integer as this Resource is less than, equal to, or greater than + * the specified Resource. + * @since Ant 1.6 + */ + public int compareTo(Resource other) { +// if (isReference()) { +// return ((Resource) getCheckedRef()).compareTo(other); +// } + return toString().compareTo(other.toString()); + } + + /** + * Implement basic Resource equality. + * + * @param other the object to check against. + * @return true if the specified Object is equal to this Resource. + * @since Ant 1.7 + */ + public boolean equals(Object other) { +// if (isReference()) { +// return getCheckedRef().equals(other); +// } + return other != null && other.getClass().equals(getClass()) + && compareTo((Resource) other) == 0; + } + + /** + * Get the hash code for this Resource. + * + * @return hash code as int. + * @since Ant 1.7 + */ + public int hashCode() { +// if (isReference()) { +// return getCheckedRef().hashCode(); +// } + String name = getName(); + return MAGIC * (name == null ? NULL_NAME : name.hashCode()); + } + + /** + * Get an InputStream for the Resource. + * + * @return an InputStream containing this Resource's content. + * @throws IOException if unable to provide the content of this Resource as a stream. + * @throws UnsupportedOperationException if InputStreams are not supported for this Resource type. + * @since Ant 1.7 + */ + public InputStream getInputStream() throws IOException { +// if (isReference()) { +// return ((Resource) getCheckedRef()).getInputStream(); +// } + throw new UnsupportedOperationException(); + } + + /** + * Get an OutputStream for the Resource. + * + * @return an OutputStream to which content can be written. + * @throws IOException if unable to provide the content of this Resource as a stream. + * @throws UnsupportedOperationException if OutputStreams are not supported for this Resource type. + * @since Ant 1.7 + */ + public OutputStream getOutputStream() throws IOException { +// if (isReference()) { +// return ((Resource) getCheckedRef()).getOutputStream(); +// } + throw new UnsupportedOperationException(); + } + + /** + * Fulfill the ResourceCollection contract. + * + * @return an Iterator of Resources. + * @since Ant 1.7 + */ + public Iterator iterator() { + //return isReference() ? ((Resource) getCheckedRef()).iterator() + // : new Iterator() { + return new Iterator() { + private boolean done = false; + + public boolean hasNext() { + return !done; + } + + public Resource next() { + if (done) { + throw new NoSuchElementException(); + } + done = true; + return Resource.this; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Fulfill the ResourceCollection contract. + * + * @return the size of this ResourceCollection. + * @since Ant 1.7 + */ + public int size() { + //return isReference() ? ((Resource) getCheckedRef()).size() : 1; + return 1; + } + + /** + * Fulfill the ResourceCollection contract. + * + * @return whether this Resource is a FileProvider. + * @since Ant 1.7 + */ + public boolean isFilesystemOnly() { +// return (isReference() && ((Resource) getCheckedRef()).isFilesystemOnly()) +// || this.as(FileProvider.class) != null; + return this.as(FileProvider.class) != null; + } + + /** + * Get the string representation of this Resource. + * + * @return this Resource formatted as a String. + * @since Ant 1.7 + */ + public String toString() { +// if (isReference()) { +// return getCheckedRef().toString(); +// } + String n = getName(); + return n == null ? "(anonymous)" : n; + } + + /** + * Get a long String representation of this Resource. This typically should be the value of toString() + * prefixed by a type description. + * + * @return this Resource formatted as a long String. + * @since Ant 1.7 + */ + public final String toLongString() { +// return isReference() ? ((Resource) getCheckedRef()).toLongString() +// : getDataTypeName() + " \"" + toString() + '"'; + return toString(); + } + + /** + * Overrides the base version. + * + * @param r the Reference to set. + */ + public void setRefid(Reference r) { + if (name != null + || exists != null + || lastmodified != null + || directory != null + || size != null) { + throw tooManyAttributes(); + } + super.setRefid(r); + } + + /** + * Returns a view of this resource that implements the interface given as the argument or null if there is no such + * view. + * + *

+ * This allows extension interfaces to be added to resources without growing the number of permutations of + * interfaces decorators/adapters need to implement.

+ * + *

+ * This implementation of the method will return the current instance itself if it can be assigned to the given + * class.

+ * + * @since Ant 1.8.0 + */ + public T as(Class clazz) { + return clazz.isAssignableFrom(getClass()) ? clazz.cast(this) : null; + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/ResourceCollection.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/ResourceCollection.java new file mode 100644 index 000000000..3aed793fe --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/ResourceCollection.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types; + +import java.util.Iterator; +import org.owasp.dependencycheck.org.apache.tools.ant.types.resources.FileProvider; + +/** + * Interface describing a collection of Resources. + * @since Ant 1.7 + */ +public interface ResourceCollection extends Iterable { + + /** + * Gets the contents of this collection. + * @return all resources in the collection + */ + Iterator iterator(); + + /** + * Learn the number of contained Resources. + * @return number of elements as int. + */ + int size(); + + /** + * Indicate whether this ResourceCollection is composed entirely of + * Resources accessible via local filesystem conventions. If true, + * all resources returned from this collection should + * respond with a {@link FileProvider} when asked via {@link Resource#as}. + * @return whether this is a filesystem-only resource collection. + */ + boolean isFilesystemOnly(); + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/ResourceFactory.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/ResourceFactory.java new file mode 100644 index 000000000..552e2ee73 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/ResourceFactory.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types; + +/** + * this interface should be implemented by classes (Scanners) needing + * to deliver information about resources. + * + * @since Ant 1.5.2 + */ +public interface ResourceFactory { + + /** + * Query a resource (file, zipentry, ...) by name + * + * @param name relative path of the resource about which + * information is sought. Expects "/" to be used as the + * directory separator. + * @return instance of Resource; the exists attribute of Resource + * will tell whether the sought resource exists + */ + Resource getResource(String name); +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/package-info.java new file mode 100644 index 000000000..6834fbfc2 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/package-info.java @@ -0,0 +1,14 @@ +/** + * + * + * org.owasp.dependencycheck.org.apache.tools.ant.types + * + * + * This is a copy of classes within Apache Ant. The DirectoryScanner + * is needed by dependency-check. However, we did not want to make + * Ant a dependency. As such, a few files were copied and slightly + * modified to remove any references to the Ant Project class. + * + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types; diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/Appendable.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/Appendable.java new file mode 100644 index 000000000..076912049 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/Appendable.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types.resources; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Interface to be implemented by "appendable" resources. + * @since Ant 1.8 + */ +public interface Appendable { + + /** + * Get an appending OutputStream. + * @return OutputStream + * @throws IOException if anything goes wrong + */ + OutputStream getAppendOutputStream() throws IOException; +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/FileProvider.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/FileProvider.java new file mode 100644 index 000000000..fdfac2598 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/FileProvider.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.types.resources; + +import java.io.File; + +/** + * This is an interface that resources that can provide a file should implement. + * This is a refactoring of {@link FileResource}, to allow other resources + * to act as sources of files (and to make components that only support + * file-based resources from only support FileResource resources. + * @since Ant 1.8 + */ +public interface FileProvider { + /** + * Get the file represented by this Resource. + * @return the file. + */ + File getFile(); +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/FileResource.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/FileResource.java new file mode 100644 index 000000000..430da35c4 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/FileResource.java @@ -0,0 +1,414 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types.resources; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import org.owasp.dependencycheck.org.apache.tools.ant.BuildException; +import org.owasp.dependencycheck.org.apache.tools.ant.types.Reference; +import org.owasp.dependencycheck.org.apache.tools.ant.types.Resource; +import org.owasp.dependencycheck.org.apache.tools.ant.types.ResourceFactory; +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; + +/** + * A Resource representation of a File. + * + * @since Ant 1.7 + */ +public class FileResource extends Resource implements Touchable, FileProvider, + ResourceFactory, Appendable { + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + private static final int NULL_FILE + = Resource.getMagicNumber("null file".getBytes()); + + private File file; + private File baseDir; + + /** + * Default constructor. + */ + public FileResource() { + } + + /** + * Construct a new FileResource using the specified basedir and relative name. + * + * @param b the basedir as File. + * @param name the relative filename. + */ + public FileResource(File b, String name) { + this.baseDir = b; + this.file = FILE_UTILS.resolveFile(b, name); + } + + /** + * Construct a new FileResource from a File. + * + * @param f the File represented. + */ + public FileResource(File f) { + setFile(f); + } + +// /** +// * Create a new FileResource. +// * @param p Project +// * @param f File represented +// * @since Ant 1.8 +// */ +// public FileResource(Project p, File f) { +// this(f); +// setProject(p); +// } +// +// /** +// * Constructor for Ant attribute introspection. +// * @param p the Project against which to resolve s. +// * @param s the absolute or Project-relative filename as a String. +// * @see org.apache.tools.ant.IntrospectionHelper +// */ +// public FileResource(Project p, String s) { +// this(p, p.resolveFile(s)); +// } + /** + * Set the File for this FileResource. + * + * @param f the File to be represented. + */ + public void setFile(File f) { + checkAttributesAllowed(); + file = f; + if (f != null && (getBaseDir() == null || !FILE_UTILS.isLeadingPath(getBaseDir(), f))) { + setBaseDir(f.getParentFile()); + } + } + + /** + * Get the file represented by this FileResource. + * + * @return the File. + */ + public File getFile() { +// if (isReference()) { +// return ((FileResource) getCheckedRef()).getFile(); +// } +// dieOnCircularReference(); + synchronized (this) { + if (file == null) { + //try to resolve file set via basedir/name property setters: + File d = getBaseDir(); + String n = super.getName(); + if (n != null) { + setFile(FILE_UTILS.resolveFile(d, n)); + } + } + } + return file; + } + + /** + * Set the basedir for this FileResource. + * + * @param b the basedir as File. + */ + public void setBaseDir(File b) { + checkAttributesAllowed(); + baseDir = b; + } + + /** + * Return the basedir to which the name is relative. + * + * @return the basedir as File. + */ + public File getBaseDir() { +// if (isReference()) { +// return ((FileResource) getCheckedRef()).getBaseDir(); +// } +// dieOnCircularReference(); + return baseDir; + } + + /** + * Overrides the super version. + * + * @param r the Reference to set. + */ + public void setRefid(Reference r) { + if (file != null || baseDir != null) { + throw tooManyAttributes(); + } + super.setRefid(r); + } + + /** + * Get the name of this FileResource. If the basedir is set, the name will be relative to that. Otherwise the + * basename only will be returned. + * + * @return the name of this resource. + */ + public String getName() { +// if (isReference()) { +// return ((Resource) getCheckedRef()).getName(); +// } + File b = getBaseDir(); + return b == null ? getNotNullFile().getName() + : FILE_UTILS.removeLeadingPath(b, getNotNullFile()); + } + + /** + * Learn whether this file exists. + * + * @return true if this resource exists. + */ + public boolean isExists() { +// return isReference() ? ((Resource) getCheckedRef()).isExists() +// : getNotNullFile().exists(); + return getNotNullFile().exists(); + } + + /** + * Get the modification time in milliseconds since 01.01.1970 . + * + * @return 0 if the resource does not exist. + */ + public long getLastModified() { +// return isReference() +// ? ((Resource) getCheckedRef()).getLastModified() +// : getNotNullFile().lastModified(); + return getNotNullFile().lastModified(); + } + + /** + * Learn whether the resource is a directory. + * + * @return boolean flag indicating if the resource is a directory. + */ + public boolean isDirectory() { +// return isReference() ? ((Resource) getCheckedRef()).isDirectory() +// : getNotNullFile().isDirectory(); + return getNotNullFile().isDirectory(); + } + + /** + * Get the size of this Resource. + * + * @return the size, as a long, 0 if the Resource does not exist. + */ + public long getSize() { +// return isReference() ? ((Resource) getCheckedRef()).getSize() +// : getNotNullFile().length(); + return getNotNullFile().length(); + } + + /** + * Return an InputStream for reading the contents of this Resource. + * + * @return an InputStream object. + * @throws IOException if an error occurs. + */ + public InputStream getInputStream() throws IOException { +// return isReference() +// ? ((Resource) getCheckedRef()).getInputStream() +// : new FileInputStream(getNotNullFile()); + return new FileInputStream(getNotNullFile()); + } + + /** + * Get an OutputStream for the Resource. + * + * @return an OutputStream to which content can be written. + * @throws IOException if unable to provide the content of this Resource as a stream. + * @throws UnsupportedOperationException if OutputStreams are not supported for this Resource type. + */ + public OutputStream getOutputStream() throws IOException { +// if (isReference()) { +// return ((FileResource) getCheckedRef()).getOutputStream(); +// } + return getOutputStream(false); + } + + /** + * {@inheritDoc} + */ + public OutputStream getAppendOutputStream() throws IOException { +// if (isReference()) { +// return ((FileResource) getCheckedRef()).getAppendOutputStream(); +// } + return getOutputStream(true); + } + + private OutputStream getOutputStream(boolean append) throws IOException { + File f = getNotNullFile(); + if (f.exists()) { + if (f.isFile() && !append) { + f.delete(); + } + } else { + File p = f.getParentFile(); + if (p != null && !(p.exists())) { + p.mkdirs(); + } + } + return append ? new FileOutputStream(f.getAbsolutePath(), true) : new FileOutputStream(f); + } + + /** + * Compare this FileResource to another Resource. + * + * @param another the other Resource against which to compare. + * @return a negative integer, zero, or a positive integer as this FileResource is less than, equal to, or greater + * than the specified Resource. + */ + public int compareTo(Resource another) { +// if (isReference()) { +// return ((Resource) getCheckedRef()).compareTo(another); +// } + if (this.equals(another)) { + return 0; + } + FileProvider otherFP = another.as(FileProvider.class); + if (otherFP != null) { + File f = getFile(); + if (f == null) { + return -1; + } + File of = otherFP.getFile(); + if (of == null) { + return 1; + } + return f.compareTo(of); + } + return super.compareTo(another); + } + + /** + * Compare another Object to this FileResource for equality. + * + * @param another the other Object to compare. + * @return true if another is a FileResource representing the same file. + */ + public boolean equals(Object another) { + if (this == another) { + return true; + } +// if (isReference()) { +// return getCheckedRef().equals(another); +// } + if (another == null || !(another.getClass().equals(getClass()))) { + return false; + } + FileResource otherfr = (FileResource) another; + return getFile() == null + ? otherfr.getFile() == null + : getFile().equals(otherfr.getFile()); + } + + /** + * Get the hash code for this Resource. + * + * @return hash code as int. + */ + public int hashCode() { +// if (isReference()) { +// return getCheckedRef().hashCode(); +// } + return MAGIC * (getFile() == null ? NULL_FILE : getFile().hashCode()); + } + + /** + * Get the string representation of this Resource. + * + * @return this FileResource formatted as a String. + */ + public String toString() { +// if (isReference()) { +// return getCheckedRef().toString(); +// } + if (file == null) { + return "(unbound file resource)"; + } + String absolutePath = file.getAbsolutePath(); + return FILE_UTILS.normalize(absolutePath).getAbsolutePath(); + } + + /** + * Fulfill the ResourceCollection contract. + * + * @return whether this Resource is a FileResource. + */ + public boolean isFilesystemOnly() { +// if (isReference()) { +// return ((FileResource) getCheckedRef()).isFilesystemOnly(); +// } +// dieOnCircularReference(); + return true; + } + + /** + * Implement the Touchable interface. + * + * @param modTime new last modification time. + */ + public void touch(long modTime) { +// if (isReference()) { +// ((FileResource) getCheckedRef()).touch(modTime); +// return; +// } + if (!getNotNullFile().setLastModified(modTime)) { + //log("Failed to change file modification time", Project.MSG_WARN); + } + } + + /** + * Get the file represented by this FileResource, ensuring it is not null. + * + * @return the not-null File. + * @throws BuildException if file is null. + */ + protected File getNotNullFile() { + if (getFile() == null) { + throw new BuildException("file attribute is null!"); + } +// dieOnCircularReference(); + return getFile(); + } + + /** + * Create a new resource that matches a relative or absolute path. If the current instance has a compatible baseDir + * attribute, it is copied. + * + * @param path relative/absolute path to a resource + * @return a new resource of type FileResource + * @throws BuildException if desired + * @since Ant1.8 + */ + public Resource getResource(String path) { + File newfile = FILE_UTILS.resolveFile(getFile(), path); + FileResource fileResource = new FileResource(newfile); + if (FILE_UTILS.isLeadingPath(getBaseDir(), newfile)) { + fileResource.setBaseDir(getBaseDir()); + } + return fileResource; + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/Touchable.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/Touchable.java new file mode 100644 index 000000000..fdb81f9bf --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/Touchable.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types.resources; + +/** + * Interface to be implemented by "touchable" resources; + * that is, those whose modification time can be altered. + * @since Ant 1.7 + */ +public interface Touchable { + /** + * Method called to "touch" the resource. + * @param modTime the time to set the modified "field" of the resource, + * measured in milliseconds since the epoch. + */ + void touch(long modTime); +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/package-info.java new file mode 100644 index 000000000..2e1e26783 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/package-info.java @@ -0,0 +1,14 @@ +/** + * + * + * org.owasp.dependencycheck.org.apache.tools.ant.types.resources + * + * + * This is a copy of classes within Apache Ant. The DirectoryScanner + * is needed by dependency-check. However, we did not want to make + * Ant a dependency. As such, a few files were copied and slightly + * modified to remove any references to the Ant Project class. + * + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types.resources; diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/FileSelector.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/FileSelector.java new file mode 100644 index 000000000..787e5e928 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/FileSelector.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.types.selectors; + +import java.io.File; + +import org.owasp.dependencycheck.org.apache.tools.ant.BuildException; + +/** + * This is the interface to be used by all selectors. + * + * @since 1.5 + */ +public interface FileSelector { + + /** + * Method that each selector will implement to create their + * selection behaviour. If there is a problem with the setup + * of a selector, it can throw a BuildException to indicate + * the problem. + * + * @param basedir A java.io.File object for the base directory + * @param filename The name of the file to check + * @param file A File object for this filename + * @return whether the file should be selected or not + * @exception BuildException if the selector was not configured correctly + */ + boolean isSelected(File basedir, String filename, File file) + throws BuildException; + +} + diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorScanner.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorScanner.java new file mode 100644 index 000000000..6597bfd16 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorScanner.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types.selectors; + +/** + * An interface used to describe the actions required by any type of + * directory scanner that supports Selectors. + * + * @since 1.5 + */ +public interface SelectorScanner { + /** + * Sets the selectors the scanner should use. + * + * @param selectors the list of selectors + */ + void setSelectors(FileSelector[] selectors); + + /** + * Directories which were selected out of a scan. + * + * @return list of directories not selected + */ + String[] getDeselectedDirectories(); + + /** + * Files which were selected out of a scan. + * + * @return list of files not selected + */ + String[] getDeselectedFiles(); + + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorUtils.java new file mode 100644 index 000000000..6cbcd1fbf --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorUtils.java @@ -0,0 +1,695 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.types.selectors; + +import java.io.File; +import java.util.StringTokenizer; +import java.util.Vector; + +import org.owasp.dependencycheck.org.apache.tools.ant.types.Resource; +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; + +/** + *

This is a utility class used by selectors and DirectoryScanner. The + * functionality more properly belongs just to selectors, but unfortunately + * DirectoryScanner exposed these as protected methods. Thus we have to + * support any subclasses of DirectoryScanner that may access these methods. + *

+ *

This is a Singleton.

+ * + * @since 1.5 + */ +public final class SelectorUtils { + + /** + * The pattern that matches an arbitrary number of directories. + * @since Ant 1.8.0 + */ + public static final String DEEP_TREE_MATCH = "**"; + + private static final SelectorUtils instance = new SelectorUtils(); + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * Private Constructor + */ + private SelectorUtils() { + } + + /** + * Retrieves the instance of the Singleton. + * @return singleton instance + */ + public static SelectorUtils getInstance() { + return instance; + } + + /** + * Tests whether or not a given path matches the start of a given + * pattern up to the first "**". + *

+ * This is not a general purpose test and should only be used if you + * can live with false positives. For example, pattern=**\a + * and str=b will yield true. + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + public static boolean matchPatternStart(String pattern, String str) { + return matchPatternStart(pattern, str, true); + } + + /** + * Tests whether or not a given path matches the start of a given + * pattern up to the first "**". + *

+ * This is not a general purpose test and should only be used if you + * can live with false positives. For example, pattern=**\a + * and str=b will yield true. + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + public static boolean matchPatternStart(String pattern, String str, + boolean isCaseSensitive) { + // When str starts with a File.separator, pattern has to start with a + // File.separator. + // When pattern starts with a File.separator, str has to start with a + // File.separator. + if (str.startsWith(File.separator) + != pattern.startsWith(File.separator)) { + return false; + } + + String[] patDirs = tokenizePathAsArray(pattern); + String[] strDirs = tokenizePathAsArray(str); + return matchPatternStart(patDirs, strDirs, isCaseSensitive); + } + + + /** + * Tests whether or not a given path matches the start of a given + * pattern up to the first "**". + *

+ * This is not a general purpose test and should only be used if you + * can live with false positives. For example, pattern=**\a + * and str=b will yield true. + * + * @param patDirs The tokenized pattern to match against. Must not be + * null. + * @param strDirs The tokenized path to match. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + static boolean matchPatternStart(String[] patDirs, String[] strDirs, + boolean isCaseSensitive) { + int patIdxStart = 0; + int patIdxEnd = patDirs.length - 1; + int strIdxStart = 0; + int strIdxEnd = strDirs.length - 1; + + // up to first '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = patDirs[patIdxStart]; + if (patDir.equals(DEEP_TREE_MATCH)) { + break; + } + if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) { + return false; + } + patIdxStart++; + strIdxStart++; + } + + // CheckStyle:SimplifyBooleanReturnCheck OFF + // Check turned off as the code needs the comments for the various + // code paths. + if (strIdxStart > strIdxEnd) { + // String is exhausted + return true; + } else if (patIdxStart > patIdxEnd) { + // String not exhausted, but pattern is. Failure. + return false; + } else { + // pattern now holds ** while string is not exhausted + // this will generate false positives but we can live with that. + return true; + } + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * If you need to call this method multiple times with the same + * pattern you should rather use TokenizedPath + * + * @see TokenizedPath + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * + * @return true if the pattern matches against the string, + * or false otherwise. + */ + public static boolean matchPath(String pattern, String str) { + String[] patDirs = tokenizePathAsArray(pattern); + return matchPath(patDirs, tokenizePathAsArray(str), true); + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * If you need to call this method multiple times with the same + * pattern you should rather use TokenizedPattern + * + * @see TokenizedPattern + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return true if the pattern matches against the string, + * or false otherwise. + */ + public static boolean matchPath(String pattern, String str, + boolean isCaseSensitive) { + String[] patDirs = tokenizePathAsArray(pattern); + return matchPath(patDirs, tokenizePathAsArray(str), isCaseSensitive); + } + + /** + * Core implementation of matchPath. It is isolated so that it + * can be called from TokenizedPattern. + */ + static boolean matchPath(String[] tokenizedPattern, String[] strDirs, + boolean isCaseSensitive) { + int patIdxStart = 0; + int patIdxEnd = tokenizedPattern.length - 1; + int strIdxStart = 0; + int strIdxEnd = strDirs.length - 1; + + // up to first '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = tokenizedPattern[patIdxStart]; + if (patDir.equals(DEEP_TREE_MATCH)) { + break; + } + if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) { + return false; + } + patIdxStart++; + strIdxStart++; + } + if (strIdxStart > strIdxEnd) { + // String is exhausted + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + return false; + } + } + return true; + } else { + if (patIdxStart > patIdxEnd) { + // String not exhausted, but pattern is. Failure. + return false; + } + } + + // up to last '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = tokenizedPattern[patIdxEnd]; + if (patDir.equals(DEEP_TREE_MATCH)) { + break; + } + if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) { + return false; + } + patIdxEnd--; + strIdxEnd--; + } + if (strIdxStart > strIdxEnd) { + // String is exhausted + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + return false; + } + } + return true; + } + + while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { + int patIdxTmp = -1; + for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { + if (tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + patIdxTmp = i; + break; + } + } + if (patIdxTmp == patIdxStart + 1) { + // '**/**' situation, so skip one + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = (patIdxTmp - patIdxStart - 1); + int strLength = (strIdxEnd - strIdxStart + 1); + int foundIdx = -1; + strLoop: + for (int i = 0; i <= strLength - patLength; i++) { + for (int j = 0; j < patLength; j++) { + String subPat = tokenizedPattern[patIdxStart + j + 1]; + String subStr = strDirs[strIdxStart + i + j]; + if (!match(subPat, subStr, isCaseSensitive)) { + continue strLoop; + } + } + + foundIdx = strIdxStart + i; + break; + } + + if (foundIdx == -1) { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx + patLength; + } + + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + return false; + } + } + + return true; + } + + /** + * Tests whether or not a string matches against a pattern. + * The pattern may contain two special characters:
+ * '*' means zero or more characters
+ * '?' means one and only one character + * + * @param pattern The pattern to match against. + * Must not be null. + * @param str The string which must be matched against the pattern. + * Must not be null. + * + * @return true if the string matches against the pattern, + * or false otherwise. + */ + public static boolean match(String pattern, String str) { + return match(pattern, str, true); + } + + /** + * Tests whether or not a string matches against a pattern. + * The pattern may contain two special characters:
+ * '*' means zero or more characters
+ * '?' means one and only one character + * + * @param pattern The pattern to match against. + * Must not be null. + * @param str The string which must be matched against the pattern. + * Must not be null. + * @param caseSensitive Whether or not matching should be performed + * case sensitively. + * + * + * @return true if the string matches against the pattern, + * or false otherwise. + */ + public static boolean match(String pattern, String str, + boolean caseSensitive) { + char[] patArr = pattern.toCharArray(); + char[] strArr = str.toCharArray(); + int patIdxStart = 0; + int patIdxEnd = patArr.length - 1; + int strIdxStart = 0; + int strIdxEnd = strArr.length - 1; + char ch; + + boolean containsStar = false; + for (int i = 0; i < patArr.length; i++) { + if (patArr[i] == '*') { + containsStar = true; + break; + } + } + + if (!containsStar) { + // No '*'s, so we make a shortcut + if (patIdxEnd != strIdxEnd) { + return false; // Pattern and string do not have the same size + } + for (int i = 0; i <= patIdxEnd; i++) { + ch = patArr[i]; + if (ch != '?') { + if (different(caseSensitive, ch, strArr[i])) { + return false; // Character mismatch + } + } + } + return true; // String matches against pattern + } + + if (patIdxEnd == 0) { + return true; // Pattern contains only '*', which matches anything + } + + // Process characters before first star + while (true) { + ch = patArr[patIdxStart]; + if (ch == '*' || strIdxStart > strIdxEnd) { + break; + } + if (ch != '?') { + if (different(caseSensitive, ch, strArr[strIdxStart])) { + return false; // Character mismatch + } + } + patIdxStart++; + strIdxStart++; + } + if (strIdxStart > strIdxEnd) { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + // Process characters after last star + while (true) { + ch = patArr[patIdxEnd]; + if (ch == '*' || strIdxStart > strIdxEnd) { + break; + } + if (ch != '?') { + if (different(caseSensitive, ch, strArr[strIdxEnd])) { + return false; // Character mismatch + } + } + patIdxEnd--; + strIdxEnd--; + } + if (strIdxStart > strIdxEnd) { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + // process pattern between stars. padIdxStart and patIdxEnd point + // always to a '*'. + while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { + int patIdxTmp = -1; + for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { + if (patArr[i] == '*') { + patIdxTmp = i; + break; + } + } + if (patIdxTmp == patIdxStart + 1) { + // Two stars next to each other, skip the first one. + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = (patIdxTmp - patIdxStart - 1); + int strLength = (strIdxEnd - strIdxStart + 1); + int foundIdx = -1; + strLoop: + for (int i = 0; i <= strLength - patLength; i++) { + for (int j = 0; j < patLength; j++) { + ch = patArr[patIdxStart + j + 1]; + if (ch != '?') { + if (different(caseSensitive, ch, + strArr[strIdxStart + i + j])) { + continue strLoop; + } + } + } + + foundIdx = strIdxStart + i; + break; + } + + if (foundIdx == -1) { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx + patLength; + } + + // All characters in the string are used. Check if only '*'s are left + // in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + private static boolean allStars(char[] chars, int start, int end) { + for (int i = start; i <= end; ++i) { + if (chars[i] != '*') { + return false; + } + } + return true; + } + + private static boolean different( + boolean caseSensitive, char ch, char other) { + return caseSensitive + ? ch != other + : Character.toUpperCase(ch) != Character.toUpperCase(other); + } + + /** + * Breaks a path up into a Vector of path elements, tokenizing on + * File.separator. + * + * @param path Path to tokenize. Must not be null. + * + * @return a Vector of path elements from the tokenized path + */ + public static Vector tokenizePath(String path) { + return tokenizePath(path, File.separator); + } + + /** + * Breaks a path up into a Vector of path elements, tokenizing on + * + * @param path Path to tokenize. Must not be null. + * @param separator the separator against which to tokenize. + * + * @return a Vector of path elements from the tokenized path + * @since Ant 1.6 + */ + public static Vector tokenizePath(String path, String separator) { + Vector ret = new Vector(); + if (FileUtils.isAbsolutePath(path)) { + String[] s = FILE_UTILS.dissect(path); + ret.add(s[0]); + path = s[1]; + } + StringTokenizer st = new StringTokenizer(path, separator); + while (st.hasMoreTokens()) { + ret.addElement(st.nextToken()); + } + return ret; + } + + /** + * Same as {@link #tokenizePath tokenizePath} but hopefully faster. + */ + /*package*/ static String[] tokenizePathAsArray(String path) { + String root = null; + if (FileUtils.isAbsolutePath(path)) { + String[] s = FILE_UTILS.dissect(path); + root = s[0]; + path = s[1]; + } + char sep = File.separatorChar; + int start = 0; + int len = path.length(); + int count = 0; + for (int pos = 0; pos < len; pos++) { + if (path.charAt(pos) == sep) { + if (pos != start) { + count++; + } + start = pos + 1; + } + } + if (len != start) { + count++; + } + String[] l = new String[count + ((root == null) ? 0 : 1)]; + + if (root != null) { + l[0] = root; + count = 1; + } else { + count = 0; + } + start = 0; + for (int pos = 0; pos < len; pos++) { + if (path.charAt(pos) == sep) { + if (pos != start) { + String tok = path.substring(start, pos); + l[count++] = tok; + } + start = pos + 1; + } + } + if (len != start) { + String tok = path.substring(start); + l[count/*++*/] = tok; + } + return l; + } + + /** + * Returns dependency information on these two files. If src has been + * modified later than target, it returns true. If target doesn't exist, + * it likewise returns true. Otherwise, target is newer than src and + * is not out of date, thus the method returns false. It also returns + * false if the src file doesn't even exist, since how could the + * target then be out of date. + * + * @param src the original file + * @param target the file being compared against + * @param granularity the amount in seconds of slack we will give in + * determining out of dateness + * @return whether the target is out of date + */ + public static boolean isOutOfDate(File src, File target, int granularity) { + if (!src.exists()) { + return false; + } + if (!target.exists()) { + return true; + } + if ((src.lastModified() - granularity) > target.lastModified()) { + return true; + } + return false; + } + + /** + * Returns dependency information on these two resources. If src has been + * modified later than target, it returns true. If target doesn't exist, + * it likewise returns true. Otherwise, target is newer than src and + * is not out of date, thus the method returns false. It also returns + * false if the src file doesn't even exist, since how could the + * target then be out of date. + * + * @param src the original resource + * @param target the resource being compared against + * @param granularity the int amount in seconds of slack we will give in + * determining out of dateness + * @return whether the target is out of date + */ + public static boolean isOutOfDate(Resource src, Resource target, + int granularity) { + return isOutOfDate(src, target, (long) granularity); + } + + /** + * Returns dependency information on these two resources. If src has been + * modified later than target, it returns true. If target doesn't exist, + * it likewise returns true. Otherwise, target is newer than src and + * is not out of date, thus the method returns false. It also returns + * false if the src file doesn't even exist, since how could the + * target then be out of date. + * + * @param src the original resource + * @param target the resource being compared against + * @param granularity the long amount in seconds of slack we will give in + * determining out of dateness + * @return whether the target is out of date + */ + public static boolean isOutOfDate(Resource src, Resource target, long granularity) { + long sourceLastModified = src.getLastModified(); + long targetLastModified = target.getLastModified(); + return src.isExists() + && (sourceLastModified == Resource.UNKNOWN_DATETIME + || targetLastModified == Resource.UNKNOWN_DATETIME + || (sourceLastModified - granularity) > targetLastModified); + } + + /** + * "Flattens" a string by removing all whitespace (space, tab, linefeed, + * carriage return, and formfeed). This uses StringTokenizer and the + * default set of tokens as documented in the single argument constructor. + * + * @param input a String to remove all whitespace. + * @return a String that has had all whitespace removed. + */ + public static String removeWhitespace(String input) { + StringBuffer result = new StringBuffer(); + if (input != null) { + StringTokenizer st = new StringTokenizer(input); + while (st.hasMoreTokens()) { + result.append(st.nextToken()); + } + } + return result.toString(); + } + + /** + * Tests if a string contains stars or question marks + * @param input a String which one wants to test for containing wildcard + * @return true if the string contains at least a star or a question mark + */ + public static boolean hasWildcards(String input) { + return (input.indexOf('*') != -1 || input.indexOf('?') != -1); + } + + /** + * removes from a pattern all tokens to the right containing wildcards + * @param input the input string + * @return the leftmost part of the pattern without wildcards + */ + public static String rtrimWildcardTokens(String input) { + return new TokenizedPattern(input).rtrimWildcardTokens().toString(); + } +} + diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/TokenizedPath.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/TokenizedPath.java new file mode 100644 index 000000000..d3c19b037 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/TokenizedPath.java @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.types.selectors; + +import java.io.File; + +import org.owasp.dependencycheck.org.apache.tools.ant.BuildException; +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; +import org.owasp.dependencycheck.org.apache.tools.ant.util.SymbolicLinkUtils; + +/** + * Container for a path that has been split into its components. + * @since 1.8.0 + */ +public class TokenizedPath { + + /** + * Instance that holds no tokens at all. + */ + public static final TokenizedPath EMPTY_PATH = + new TokenizedPath("", new String[0]); + + /** Helper. */ + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + /** Helper. */ + private static final SymbolicLinkUtils SYMLINK_UTILS = + SymbolicLinkUtils.getSymbolicLinkUtils(); + /** iterations for case-sensitive scanning. */ + private static final boolean[] CS_SCAN_ONLY = new boolean[] {true}; + /** iterations for non-case-sensitive scanning. */ + private static final boolean[] CS_THEN_NON_CS = new boolean[] {true, false}; + + private final String path; + private final String[] tokenizedPath; + + /** + * Initialize the TokenizedPath by parsing it. + * @param path The path to tokenize. Must not be + * null. + */ + public TokenizedPath(String path) { + this(path, SelectorUtils.tokenizePathAsArray(path)); + } + + /** + * Creates a new path as a child of another path. + * + * @param parent the parent path + * @param child the child, must not contain the file separator + */ + public TokenizedPath(TokenizedPath parent, String child) { + if (parent.path.length() > 0 + && parent.path.charAt(parent.path.length() - 1) + != File.separatorChar) { + path = parent.path + File.separatorChar + child; + } else { + path = parent.path + child; + } + tokenizedPath = new String[parent.tokenizedPath.length + 1]; + System.arraycopy(parent.tokenizedPath, 0, tokenizedPath, 0, + parent.tokenizedPath.length); + tokenizedPath[parent.tokenizedPath.length] = child; + } + + /* package */ TokenizedPath(String path, String[] tokens) { + this.path = path; + this.tokenizedPath = tokens; + } + + /** + * @return The original path String + */ + public String toString() { + return path; + } + + /** + * The depth (or length) of a path. + */ + public int depth() { + return tokenizedPath.length; + } + + /* package */ String[] getTokens() { + return tokenizedPath; + } + + /** + * From base traverse the filesystem in order to find + * a file that matches the given name. + * + * @param base base File (dir). + * @param cs whether to scan case-sensitively. + * @return File object that points to the file in question or null. + */ + public File findFile(File base, final boolean cs) { + String[] tokens = tokenizedPath; + if (FileUtils.isAbsolutePath(path)) { + if (base == null) { + String[] s = FILE_UTILS.dissect(path); + base = new File(s[0]); + tokens = SelectorUtils.tokenizePathAsArray(s[1]); + } else { + File f = FILE_UTILS.normalize(path); + String s = FILE_UTILS.removeLeadingPath(base, f); + if (s.equals(f.getAbsolutePath())) { + //removing base from path yields no change; path + //not child of base + return null; + } + tokens = SelectorUtils.tokenizePathAsArray(s); + } + } + return findFile(base, tokens, cs); + } + + /** + * Do we have to traverse a symlink when trying to reach path from + * basedir? + * @param base base File (dir). + */ + public boolean isSymlink(File base) { + for (int i = 0; i < tokenizedPath.length; i++) { + try { + if ((base != null + && SYMLINK_UTILS.isSymbolicLink(base, tokenizedPath[i])) + || + (base == null + && SYMLINK_UTILS.isSymbolicLink(tokenizedPath[i])) + ) { + return true; + } + base = new File(base, tokenizedPath[i]); + } catch (java.io.IOException ioe) { + String msg = "IOException caught while checking " + + "for links, couldn't get canonical path!"; + // will be caught and redirected to Ant's logging system + System.err.println(msg); + } + } + return false; + } + + /** + * true if the original paths are equal. + */ + public boolean equals(Object o) { + return o instanceof TokenizedPath + && path.equals(((TokenizedPath) o).path); + } + + public int hashCode() { + return path.hashCode(); + } + + /** + * From base traverse the filesystem in order to find + * a file that matches the given stack of names. + * + * @param base base File (dir) - must not be null. + * @param pathElements array of path elements (dirs...file). + * @param cs whether to scan case-sensitively. + * @return File object that points to the file in question or null. + */ + private static File findFile(File base, final String[] pathElements, + final boolean cs) { + for (int current = 0; current < pathElements.length; current++) { + if (!base.isDirectory()) { + return null; + } + String[] files = base.list(); + if (files == null) { + throw new BuildException("IO error scanning directory " + + base.getAbsolutePath()); + } + boolean found = false; + boolean[] matchCase = cs ? CS_SCAN_ONLY : CS_THEN_NON_CS; + for (int i = 0; !found && i < matchCase.length; i++) { + for (int j = 0; !found && j < files.length; j++) { + if (matchCase[i] + ? files[j].equals(pathElements[current]) + : files[j].equalsIgnoreCase(pathElements[current])) { + base = new File(base, files[j]); + found = true; + } + } + } + if (!found) { + return null; + } + } + return pathElements.length == 0 && !base.isDirectory() ? null : base; + } + + /** + * Creates a TokenizedPattern from the same tokens that make up + * this path. + */ + public TokenizedPattern toPattern() { + return new TokenizedPattern(path, tokenizedPath); + } + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/TokenizedPattern.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/TokenizedPattern.java new file mode 100644 index 000000000..bfb666db6 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/TokenizedPattern.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.types.selectors; + +import java.io.File; + +/** + * Provides reusable path pattern matching. PathPattern is preferable + * to equivalent SelectorUtils methods if you need to execute multiple + * matching with the same pattern because here the pattern itself will + * be parsed only once. + * @see SelectorUtils#matchPath(String, String) + * @see SelectorUtils#matchPath(String, String, boolean) + * @since 1.8.0 + */ +public class TokenizedPattern { + + /** + * Instance that holds no tokens at all. + */ + public static final TokenizedPattern EMPTY_PATTERN = + new TokenizedPattern("", new String[0]); + + private final String pattern; + private final String[] tokenizedPattern; + + /** + * Initialize the PathPattern by parsing it. + * @param pattern The pattern to match against. Must not be + * null. + */ + public TokenizedPattern(String pattern) { + this(pattern, SelectorUtils.tokenizePathAsArray(pattern)); + } + + TokenizedPattern(String pattern, String[] tokens) { + this.pattern = pattern; + this.tokenizedPattern = tokens; + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * @param path The path to match, as a String. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return true if the pattern matches against the string, + * or false otherwise. + */ + public boolean matchPath(TokenizedPath path, boolean isCaseSensitive) { + return SelectorUtils.matchPath(tokenizedPattern, path.getTokens(), + isCaseSensitive); + } + + /** + * Tests whether or not this pattern matches the start of + * a path. + */ + public boolean matchStartOf(TokenizedPath path, + boolean caseSensitive) { + return SelectorUtils.matchPatternStart(tokenizedPattern, + path.getTokens(), caseSensitive); + } + + /** + * @return The pattern String + */ + public String toString() { + return pattern; + } + + public String getPattern() { + return pattern; + } + + /** + * true if the original patterns are equal. + */ + public boolean equals(Object o) { + return o instanceof TokenizedPattern + && pattern.equals(((TokenizedPattern) o).pattern); + } + + public int hashCode() { + return pattern.hashCode(); + } + + /** + * The depth (or length) of a pattern. + */ + public int depth() { + return tokenizedPattern.length; + } + + /** + * Does the tokenized pattern contain the given string? + */ + public boolean containsPattern(String pat) { + for (int i = 0; i < tokenizedPattern.length; i++) { + if (tokenizedPattern[i].equals(pat)) { + return true; + } + } + return false; + } + + /** + * Returns a new TokenizedPath where all tokens of this pattern to + * the right containing wildcards have been removed + * @return the leftmost part of the pattern without wildcards + */ + public TokenizedPath rtrimWildcardTokens() { + StringBuilder sb = new StringBuilder(); + int newLen = 0; + for (; newLen < tokenizedPattern.length; newLen++) { + if (SelectorUtils.hasWildcards(tokenizedPattern[newLen])) { + break; + } + if (newLen > 0 + && sb.charAt(sb.length() - 1) != File.separatorChar) { + sb.append(File.separator); + } + sb.append(tokenizedPattern[newLen]); + } + if (newLen == 0) { + return TokenizedPath.EMPTY_PATH; + } + String[] newPats = new String[newLen]; + System.arraycopy(tokenizedPattern, 0, newPats, 0, newLen); + return new TokenizedPath(sb.toString(), newPats); + } + + /** + * true if the last token equals the given string. + */ + public boolean endsWith(String s) { + return tokenizedPattern.length > 0 + && tokenizedPattern[tokenizedPattern.length - 1].equals(s); + } + + /** + * Returns a new pattern without the last token of this pattern. + */ + public TokenizedPattern withoutLastToken() { + if (tokenizedPattern.length == 0) { + throw new IllegalStateException("cant strip a token from nothing"); + } else if (tokenizedPattern.length == 1) { + return EMPTY_PATTERN; + } else { + String toStrip = tokenizedPattern[tokenizedPattern.length - 1]; + int index = pattern.lastIndexOf(toStrip); + String[] tokens = new String[tokenizedPattern.length - 1]; + System.arraycopy(tokenizedPattern, 0, tokens, 0, + tokenizedPattern.length - 1); + return new TokenizedPattern(pattern.substring(0, index), tokens); + } + } + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/package-info.java new file mode 100644 index 000000000..b5a6ae6d6 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/package-info.java @@ -0,0 +1,14 @@ +/** + * + * + * org.owasp.dependencycheck.org.apache.tools.ant.types.selectors + * + * + * This is a copy of classes within Apache Ant. The DirectoryScanner + * is needed by dependency-check. However, we did not want to make + * Ant a dependency. As such, a few files were copied and slightly + * modified to remove any references to the Ant Project class. + * + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types.selectors; diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/CollectionUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/CollectionUtils.java new file mode 100644 index 000000000..fe334e346 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/CollectionUtils.java @@ -0,0 +1,265 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Vector; + +// CheckStyle:HideUtilityClassConstructorCheck OFF - bc + +/** + * A set of helper methods related to collection manipulation. + * + * @since Ant 1.5 + */ +public class CollectionUtils { + + /** + * Collections.emptyList() is Java5+. + */ + @SuppressWarnings("rawtypes") + @Deprecated + public static final List EMPTY_LIST = Collections.EMPTY_LIST; + + /** + * Please use Vector.equals() or List.equals(). + * @param v1 the first vector. + * @param v2 the second vector. + * @return true if the vectors are equal. + * @since Ant 1.5 + * @deprecated since 1.6.x. + */ + public static boolean equals(Vector v1, Vector v2) { + if (v1 == v2) { + return true; + } + + if (v1 == null || v2 == null) { + return false; + } + + return v1.equals(v2); + } + + /** + * Dictionary does not have an equals. + * Please use Map.equals(). + * + *

Follows the equals contract of Java 2's Map.

+ * @param d1 the first directory. + * @param d2 the second directory. + * @return true if the directories are equal. + * @since Ant 1.5 + * @deprecated since 1.6.x. + */ + public static boolean equals(Dictionary d1, Dictionary d2) { + if (d1 == d2) { + return true; + } + + if (d1 == null || d2 == null) { + return false; + } + + if (d1.size() != d2.size()) { + return false; + } + + Enumeration e1 = d1.keys(); + while (e1.hasMoreElements()) { + Object key = e1.nextElement(); + Object value1 = d1.get(key); + Object value2 = d2.get(key); + if (value2 == null || !value1.equals(value2)) { + return false; + } + } + + // don't need the opposite check as the Dictionaries have the + // same size, so we've also covered all keys of d2 already. + + return true; + } + + /** + * Creates a comma separated list of all values held in the given + * collection. + * + * @since Ant 1.8.0 + */ + public static String flattenToString(Collection c) { + final StringBuilder sb = new StringBuilder(); + for (Object o : c) { + if (sb.length() != 0) { + sb.append(","); + } + sb.append(o); + } + return sb.toString(); + } + + /** + * Dictionary does not know the putAll method. Please use Map.putAll(). + * @param m1 the to directory. + * @param m2 the from directory. + * @since Ant 1.6 + * @deprecated since 1.6.x. + */ + public static void putAll(Dictionary m1, Dictionary m2) { + for (Enumeration it = m2.keys(); it.hasMoreElements();) { + K key = it.nextElement(); + m1.put(key, m2.get(key)); + } + } + + /** + * An empty enumeration. + * @since Ant 1.6 + */ + public static final class EmptyEnumeration implements Enumeration { + /** Constructor for the EmptyEnumeration */ + public EmptyEnumeration() { + } + + /** + * @return false always. + */ + public boolean hasMoreElements() { + return false; + } + + /** + * @return nothing. + * @throws NoSuchElementException always. + */ + public E nextElement() throws NoSuchElementException { + throw new NoSuchElementException(); + } + } + + /** + * Append one enumeration to another. + * Elements are evaluated lazily. + * @param e1 the first enumeration. + * @param e2 the subsequent enumeration. + * @return an enumeration representing e1 followed by e2. + * @since Ant 1.6.3 + */ + public static Enumeration append(Enumeration e1, Enumeration e2) { + return new CompoundEnumeration(e1, e2); + } + + /** + * Adapt the specified Iterator to the Enumeration interface. + * @param iter the Iterator to adapt. + * @return an Enumeration. + */ + public static Enumeration asEnumeration(final Iterator iter) { + return new Enumeration() { + public boolean hasMoreElements() { + return iter.hasNext(); + } + public E nextElement() { + return iter.next(); + } + }; + } + + /** + * Adapt the specified Enumeration to the Iterator interface. + * @param e the Enumeration to adapt. + * @return an Iterator. + */ + public static Iterator asIterator(final Enumeration e) { + return new Iterator() { + public boolean hasNext() { + return e.hasMoreElements(); + } + public E next() { + return e.nextElement(); + } + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Returns a collection containing all elements of the iterator. + * + * @since Ant 1.8.0 + */ + public static Collection asCollection(final Iterator iter) { + List l = new ArrayList(); + while (iter.hasNext()) { + l.add(iter.next()); + } + return l; + } + + private static final class CompoundEnumeration implements Enumeration { + + private final Enumeration e1, e2; + + public CompoundEnumeration(Enumeration e1, Enumeration e2) { + this.e1 = e1; + this.e2 = e2; + } + + public boolean hasMoreElements() { + return e1.hasMoreElements() || e2.hasMoreElements(); + } + + public E nextElement() throws NoSuchElementException { + if (e1.hasMoreElements()) { + return e1.nextElement(); + } else { + return e2.nextElement(); + } + } + + } + + /** + * Counts how often the given Object occurs in the given + * collection using equals() for comparison. + * + * @since Ant 1.8.0 + */ + public static int frequency(Collection c, Object o) { + // same as Collections.frequency introduced with JDK 1.5 + int freq = 0; + if (c != null) { + for (Iterator i = c.iterator(); i.hasNext(); ) { + Object test = i.next(); + if (o == null ? test == null : o.equals(test)) { + freq++; + } + } + } + return freq; + } + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/FileUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/FileUtils.java new file mode 100644 index 000000000..a097c7991 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/FileUtils.java @@ -0,0 +1,1667 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.util; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.net.HttpURLConnection; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.channels.Channel; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Stack; +import java.util.StringTokenizer; +import java.util.jar.JarFile; +import org.owasp.dependencycheck.org.apache.tools.ant.BuildException; +import org.owasp.dependencycheck.org.apache.tools.ant.PathTokenizer; +import org.owasp.dependencycheck.org.apache.tools.ant.launch.Locator; +import org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition.Os; + +/** + * This class also encapsulates methods which allow Files to be referred to using abstract path names which are + * translated to native system file paths at runtime as well as copying files or setting their last modification time. + * + */ +public class FileUtils { + + private static final int DELETE_RETRY_SLEEP_MILLIS = 10; + private static final int EXPAND_SPACE = 50; + private static final FileUtils PRIMARY_INSTANCE = new FileUtils(); + + //get some non-crypto-grade randomness from various places. + private static Random rand = new Random(System.currentTimeMillis() + + Runtime.getRuntime().freeMemory()); + + private static final boolean ON_NETWARE = Os.isFamily("netware"); + private static final boolean ON_DOS = Os.isFamily("dos"); + private static final boolean ON_WIN9X = Os.isFamily("win9x"); + private static final boolean ON_WINDOWS = Os.isFamily("windows"); + + static final int BUF_SIZE = 8192; + + /** + * The granularity of timestamps under FAT. + */ + public static final long FAT_FILE_TIMESTAMP_GRANULARITY = 2000; + + /** + * The granularity of timestamps under Unix. + */ + public static final long UNIX_FILE_TIMESTAMP_GRANULARITY = 1000; + + /** + * The granularity of timestamps under the NT File System. NTFS has a granularity of 100 nanoseconds, which is less + * than 1 millisecond, so we round this up to 1 millisecond. + */ + public static final long NTFS_FILE_TIMESTAMP_GRANULARITY = 1; + + /** + * A one item cache for fromUri. fromUri is called for each element when parseing ant build files. It is a costly + * operation. This just caches the result of the last call. + */ + private Object cacheFromUriLock = new Object(); + private String cacheFromUriRequest = null; + private String cacheFromUriResponse = null; + + /** + * Factory method. + * + * @return a new instance of FileUtils. + * @deprecated since 1.7. Use getFileUtils instead, FileUtils do not have state. + */ + public static FileUtils newFileUtils() { + return new FileUtils(); + } + + /** + * Method to retrieve The FileUtils, which is shared by all users of this method. + * + * @return an instance of FileUtils. + * @since Ant 1.6.3 + */ + public static FileUtils getFileUtils() { + return PRIMARY_INSTANCE; + } + + /** + * Empty constructor. + */ + protected FileUtils() { + } + + /** + * Get the URL for a file taking into account # characters. + * + * @param file the file whose URL representation is required. + * @return The FileURL value. + * @throws MalformedURLException if the URL representation cannot be formed. + */ + public URL getFileURL(File file) throws MalformedURLException { + return new URL(file.toURI().toASCIIString()); + } + +// /** +// * Convenience method to copy a file from a source to a destination. +// * No filtering is performed. +// * +// * @param sourceFile Name of file to copy from. +// * Must not be null. +// * @param destFile Name of file to copy to. +// * Must not be null. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(String sourceFile, String destFile) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), null, false, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination +// * specifying if token filtering must be used. +// * +// * @param sourceFile Name of file to copy from. +// * Must not be null. +// * @param destFile Name of file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(String sourceFile, String destFile, FilterSetCollection filters) +// throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, false, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination specifying if token +// * filtering must be used and if source files may overwrite newer destination files. +// * +// * @param sourceFile Name of file to copy from. Must not be null. +// * @param destFile Name of file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be overwritten if it already +// * exists. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(String sourceFile, String destFile, FilterSetCollection filters, +// boolean overwrite) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, overwrite, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination +// * specifying if token +// * filtering must be used, if source files may overwrite newer destination +// * files and the last +// * modified time of destFile file should be made equal to +// * the last modified time +// * of sourceFile. +// * +// * @param sourceFile Name of file to copy from. Must not be null. +// * @param destFile Name of file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file +// * should be set to that of the source file. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(String sourceFile, String destFile, +// FilterSetCollection filters, +// boolean overwrite, boolean preserveLastModified) +// throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, overwrite, +// preserveLastModified); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination specifying if token +// * filtering must be used, if source files may overwrite newer destination files and the last +// * modified time of destFile file should be made equal to the last modified time +// * of sourceFile. +// * +// * @param sourceFile Name of file to copy from. Must not be null. +// * @param destFile Name of file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be overwritten if it already +// * exists. +// * @param preserveLastModified Whether or not the last modified time of the resulting file +// * should be set to that of the source file. +// * @param encoding the encoding used to read and write the files. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.5 +// */ +// public void copyFile(String sourceFile, String destFile, +// FilterSetCollection filters, boolean overwrite, +// boolean preserveLastModified, String encoding) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, +// overwrite, preserveLastModified, encoding); +// } +// // CheckStyle:ParameterNumberCheck OFF - bc +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile Name of file to copy from. +// * Must not be null. +// * @param destFile Name of file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param encoding the encoding used to read and write the files. +// * @param project the project instance. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.5 +// */ +// public void copyFile(String sourceFile, String destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// String encoding, Project project) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, filterChains, overwrite, +// preserveLastModified, encoding, project); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination specifying if token +// * filtering must be used, if filter chains must be used, if source files may overwrite newer +// * destination files and the last modified time of destFile file should be made +// * equal to the last modified time of sourceFile. +// * +// * @param sourceFile Name of file to copy from. Must not be null. +// * @param destFile Name of file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be overwritten if it already +// * exists. +// * @param preserveLastModified Whether or not the last modified time of the resulting file +// * should be set to that of the source file. +// * @param inputEncoding the encoding used to read the files. +// * @param outputEncoding the encoding used to write the files. +// * @param project the project instance. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.6 +// */ +// public void copyFile(String sourceFile, String destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// String inputEncoding, String outputEncoding, +// Project project) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, filterChains, overwrite, +// preserveLastModified, inputEncoding, outputEncoding, project); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination. No filtering is performed. +// * +// * @param sourceFile the file to copy from. Must not be null. +// * @param destFile the file to copy to. Must not be null. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(File sourceFile, File destFile) throws IOException { +// copyFile(sourceFile, destFile, null, false, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination +// * specifying if token filtering must be used. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(File sourceFile, File destFile, FilterSetCollection filters) +// throws IOException { +// copyFile(sourceFile, destFile, filters, false, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used and if +// * source files may overwrite newer destination files. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(File sourceFile, File destFile, FilterSetCollection filters, +// boolean overwrite) throws IOException { +// copyFile(sourceFile, destFile, filters, overwrite, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * source files may overwrite newer destination files and the +// * last modified time of destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(File sourceFile, File destFile, FilterSetCollection filters, +// boolean overwrite, boolean preserveLastModified) throws IOException { +// copyFile(sourceFile, destFile, filters, overwrite, preserveLastModified, null); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination specifying if token +// * filtering must be used, if source files may overwrite newer destination files, the last +// * modified time of destFile file should be made equal to the last modified time +// * of sourceFile and which character encoding to assume. +// * +// * @param sourceFile the file to copy from. Must not be null. +// * @param destFile the file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be overwritten if it already +// * exists. +// * @param preserveLastModified Whether or not the last modified time of the resulting file +// * should be set to that of the source file. +// * @param encoding the encoding used to read and write the files. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.5 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, boolean overwrite, +// boolean preserveLastModified, String encoding) throws IOException { +// copyFile(sourceFile, destFile, filters, null, overwrite, +// preserveLastModified, encoding, null); +// } +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param encoding the encoding used to read and write the files. +// * @param project the project instance. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.5 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// String encoding, Project project) throws IOException { +// copyFile(sourceFile, destFile, filters, filterChains, +// overwrite, preserveLastModified, encoding, encoding, project); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param inputEncoding the encoding used to read the files. +// * @param outputEncoding the encoding used to write the files. +// * @param project the project instance. +// * +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.6 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// String inputEncoding, String outputEncoding, +// Project project) throws IOException { +// copyFile(sourceFile, destFile, filters, filterChains, overwrite, preserveLastModified, +// false, inputEncoding, outputEncoding, project); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param append whether to append to the destination file. +// * @param inputEncoding the encoding used to read the files. +// * @param outputEncoding the encoding used to write the files. +// * @param project the project instance. +// * +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.8 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// boolean append, +// String inputEncoding, String outputEncoding, +// Project project) throws IOException { +// copyFile(sourceFile, destFile, filters, filterChains, overwrite, +// preserveLastModified, append, inputEncoding, outputEncoding, +// project, /* force: */ false); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param append whether to append to the destination file. +// * @param inputEncoding the encoding used to read the files. +// * @param outputEncoding the encoding used to write the files. +// * @param project the project instance. +// * @param force whether to overwrite read-only destination files. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.8.2 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// boolean append, +// String inputEncoding, String outputEncoding, +// Project project, boolean force) throws IOException { +// ResourceUtils.copyResource(new FileResource(sourceFile), +// new FileResource(destFile), +// filters, filterChains, overwrite, +// preserveLastModified, append, inputEncoding, +// outputEncoding, project, force); +// } +// +// // CheckStyle:ParameterNumberCheck ON +// +// /** +// * Calls File.setLastModified(long time). Originally written to +// * to dynamically bind to that call on Java1.2+. +// * +// * @param file the file whose modified time is to be set +// * @param time the time to which the last modified time is to be set. +// * if this is -1, the current time is used. +// */ +// public void setFileLastModified(File file, long time) { +// ResourceUtils.setLastModified(new FileResource(file), time); +// } + /** + * Interpret the filename as a file relative to the given file unless the filename already represents an absolute + * filename. Differs from new File(file, filename) in that the resulting File's path will always be a + * normalized, absolute pathname. Also, if it is determined that filename is context-relative, + * file will be discarded and the reference will be resolved using available context/state information + * about the filesystem. + * + * @param file the "reference" file for relative paths. This instance must be an absolute file and must not contain + * "./" or "../" sequences (same for \ instead of /). If it is null, this call is equivalent to + * new java.io.File(filename).getAbsoluteFile(). + * + * @param filename a file name. + * + * @return an absolute file. + * @throws java.lang.NullPointerException if filename is null. + */ + public File resolveFile(File file, String filename) { + if (!isAbsolutePath(filename)) { + char sep = File.separatorChar; + filename = filename.replace('/', sep).replace('\\', sep); + if (isContextRelativePath(filename)) { + file = null; + // on cygwin, our current directory can be a UNC; + // assume user.dir is absolute or all hell breaks loose... + String udir = System.getProperty("user.dir"); + if (filename.charAt(0) == sep && udir.charAt(0) == sep) { + filename = dissect(udir)[0] + filename.substring(1); + } + } + filename = new File(file, filename).getAbsolutePath(); + } + return normalize(filename); + } + + /** + * On DOS and NetWare, the evaluation of certain file specifications is context-dependent. These are filenames + * beginning with a single separator (relative to current root directory) and filenames with a drive specification + * and no intervening separator (relative to current directory of the specified root). + * + * @param filename the filename to evaluate. + * @return true if the filename is relative to system context. + * @throws java.lang.NullPointerException if filename is null. + * @since Ant 1.7 + */ + public static boolean isContextRelativePath(String filename) { + if (!(ON_DOS || ON_NETWARE) || filename.length() == 0) { + return false; + } + char sep = File.separatorChar; + filename = filename.replace('/', sep).replace('\\', sep); + char c = filename.charAt(0); + int len = filename.length(); + return (c == sep && (len == 1 || filename.charAt(1) != sep)) + || (Character.isLetter(c) && len > 1 + && filename.charAt(1) == ':' + && (len == 2 || filename.charAt(2) != sep)); + } + + /** + * Verifies that the specified filename represents an absolute path. Differs from new + * java.io.File("filename").isAbsolute() in that a path beginning with a double file separator--signifying a Windows + * UNC--must at minimum match "\\a\b" to be considered an absolute path. + * + * @param filename the filename to be checked. + * @return true if the filename represents an absolute path. + * @throws java.lang.NullPointerException if filename is null. + * @since Ant 1.6.3 + */ + public static boolean isAbsolutePath(String filename) { + int len = filename.length(); + if (len == 0) { + return false; + } + char sep = File.separatorChar; + filename = filename.replace('/', sep).replace('\\', sep); + char c = filename.charAt(0); + if (!(ON_DOS || ON_NETWARE)) { + return (c == sep); + } + if (c == sep) { + // CheckStyle:MagicNumber OFF + if (!(ON_DOS && len > 4 && filename.charAt(1) == sep)) { + return false; + } + // CheckStyle:MagicNumber ON + int nextsep = filename.indexOf(sep, 2); + return nextsep > 2 && nextsep + 1 < len; + } + int colon = filename.indexOf(':'); + return (Character.isLetter(c) && colon == 1 + && filename.length() > 2 && filename.charAt(2) == sep) + || (ON_NETWARE && colon > 0); + } + + /** + * Translate a path into its native (platform specific) format. + *

+ * This method uses PathTokenizer to separate the input path into its components. This handles DOS style paths in a + * relatively sensible way. The file separators are then converted to their platform specific versions. + * + * @param toProcess The path to be translated. May be null. + * + * @return the native version of the specified path or an empty string if the path is null or empty. + * + * @since ant 1.7 + * @see PathTokenizer + */ + public static String translatePath(String toProcess) { + if (toProcess == null || toProcess.length() == 0) { + return ""; + } + StringBuffer path = new StringBuffer(toProcess.length() + EXPAND_SPACE); + PathTokenizer tokenizer = new PathTokenizer(toProcess); + while (tokenizer.hasMoreTokens()) { + String pathComponent = tokenizer.nextToken(); + pathComponent = pathComponent.replace('/', File.separatorChar); + pathComponent = pathComponent.replace('\\', File.separatorChar); + if (path.length() != 0) { + path.append(File.pathSeparatorChar); + } + path.append(pathComponent); + } + return path.toString(); + } + + /** + * "Normalize" the given absolute path. + * + *

+ * This includes: + *

    + *
  • Uppercase the drive letter if there is one.
  • + *
  • Remove redundant slashes after the drive spec.
  • + *
  • Resolve all ./, .\, ../ and ..\ sequences.
  • + *
  • DOS style paths that start with a drive letter will have \ as the separator.
  • + *
+ * Unlike {@link File#getCanonicalPath()} this method specifically does not resolve symbolic links. + * + * @param path the path to be normalized. + * @return the normalized version of the path. + * + * @throws java.lang.NullPointerException if path is null. + */ + public File normalize(final String path) { + Stack s = new Stack(); + String[] dissect = dissect(path); + s.push(dissect[0]); + + StringTokenizer tok = new StringTokenizer(dissect[1], File.separator); + while (tok.hasMoreTokens()) { + String thisToken = tok.nextToken(); + if (".".equals(thisToken)) { + continue; + } + if ("..".equals(thisToken)) { + if (s.size() < 2) { + // Cannot resolve it, so skip it. + return new File(path); + } + s.pop(); + } else { // plain component + s.push(thisToken); + } + } + StringBuffer sb = new StringBuffer(); + final int size = s.size(); + for (int i = 0; i < size; i++) { + if (i > 1) { + // not before the filesystem root and not after it, since root + // already contains one + sb.append(File.separatorChar); + } + sb.append(s.elementAt(i)); + } + return new File(sb.toString()); + } + + /** + * Dissect the specified absolute path. + * + * @param path the path to dissect. + * @return String[] {root, remaining path}. + * @throws java.lang.NullPointerException if path is null. + * @since Ant 1.7 + */ + public String[] dissect(String path) { + char sep = File.separatorChar; + path = path.replace('/', sep).replace('\\', sep); + + // make sure we are dealing with an absolute path + if (!isAbsolutePath(path)) { + throw new BuildException(path + " is not an absolute path"); + } + String root = null; + int colon = path.indexOf(':'); + if (colon > 0 && (ON_DOS || ON_NETWARE)) { + + int next = colon + 1; + root = path.substring(0, next); + char[] ca = path.toCharArray(); + root += sep; + //remove the initial separator; the root has it. + next = (ca[next] == sep) ? next + 1 : next; + + StringBuffer sbPath = new StringBuffer(); + // Eliminate consecutive slashes after the drive spec: + for (int i = next; i < ca.length; i++) { + if (ca[i] != sep || ca[i - 1] != sep) { + sbPath.append(ca[i]); + } + } + path = sbPath.toString(); + } else if (path.length() > 1 && path.charAt(1) == sep) { + // UNC drive + int nextsep = path.indexOf(sep, 2); + nextsep = path.indexOf(sep, nextsep + 1); + root = (nextsep > 2) ? path.substring(0, nextsep + 1) : path; + path = path.substring(root.length()); + } else { + root = File.separator; + path = path.substring(1); + } + return new String[]{root, path}; + } + + /** + * Returns a VMS String representation of a File object. This is useful since the JVM by default + * internally converts VMS paths to Unix style. The returned String is always an absolute path. + * + * @param f The File to get the VMS path for. + * @return The absolute VMS path to f. + */ + public String toVMSPath(File f) { + // format: "DEVICE:[DIR.SUBDIR]FILE" + String osPath; + String path = normalize(f.getAbsolutePath()).getPath(); + String name = f.getName(); + boolean isAbsolute = path.charAt(0) == File.separatorChar; + // treat directories specified using .DIR syntax as files + // CheckStyle:MagicNumber OFF + boolean isDirectory = f.isDirectory() + && !name.regionMatches(true, name.length() - 4, ".DIR", 0, 4); + // CheckStyle:MagicNumber ON + String device = null; + StringBuffer directory = null; + String file = null; + + int index = 0; + + if (isAbsolute) { + index = path.indexOf(File.separatorChar, 1); + if (index == -1) { + return path.substring(1) + ":[000000]"; + } + device = path.substring(1, index++); + } + if (isDirectory) { + directory = new StringBuffer(path.substring(index).replace(File.separatorChar, '.')); + } else { + int dirEnd = path.lastIndexOf(File.separatorChar, path.length()); + if (dirEnd == -1 || dirEnd < index) { + file = path.substring(index); + } else { + directory = new StringBuffer(path.substring(index, dirEnd). + replace(File.separatorChar, '.')); + index = dirEnd + 1; + if (path.length() > index) { + file = path.substring(index); + } + } + } + if (!isAbsolute && directory != null) { + directory.insert(0, '.'); + } + osPath = ((device != null) ? device + ":" : "") + + ((directory != null) ? "[" + directory + "]" : "") + + ((file != null) ? file : ""); + return osPath; + } + + /** + * Create a File object for a temporary file in a given directory. Without actually creating the file. + * + *

+ * The file denoted by the returned abstract pathname did not exist before this method was invoked, any subsequent + * invocation of this method will yield a different file name. + *

+ *

+ * The filename is prefixNNNNNsuffix where NNNN is a random number. + *

+ * + * @param prefix prefix before the random number. + * @param suffix file extension; include the '.'. + * @param parentDir Directory to create the temporary file in; java.io.tmpdir used if not specified. + * + * @deprecated since ant 1.7.1 use createTempFile(String, String, File, boolean, boolean) instead. + * @return a File reference to the new, nonexistent temporary file. + */ + public File createTempFile(String prefix, String suffix, File parentDir) { + return createTempFile(prefix, suffix, parentDir, false, false); + } + + private static final String NULL_PLACEHOLDER = "null"; + + /** + * Create a temporary file in a given directory. + * + *

+ * The file denoted by the returned abstract pathname did not exist before this method was invoked, any subsequent + * invocation of this method will yield a different file name.

+ * + * @param prefix prefix before the random number. + * @param suffix file extension; include the '.'. + * @param parentDir Directory to create the temporary file in; java.io.tmpdir used if not specified. + * @param deleteOnExit whether to set the tempfile for deletion on normal VM exit. + * @param createFile true if the file must actually be created. If false chances exist that a file with the same + * name is created in the time between invoking this method and the moment the file is actually created. If possible + * set to true. + * + * @return a File reference to the new temporary file. + * @since Ant 1.7.1 + */ + public File createTempFile(String prefix, String suffix, File parentDir, + boolean deleteOnExit, boolean createFile) { + File result = null; + String parent = (parentDir == null) + ? System.getProperty("java.io.tmpdir") + : parentDir.getPath(); + if (prefix == null) { + prefix = NULL_PLACEHOLDER; + } + if (suffix == null) { + suffix = NULL_PLACEHOLDER; + } + + if (createFile) { + try { + result = File.createTempFile(prefix, suffix, new File(parent)); + } catch (IOException e) { + throw new BuildException("Could not create tempfile in " + + parent, e); + } + } else { + DecimalFormat fmt = new DecimalFormat("#####"); + synchronized (rand) { + do { + result = new File(parent, prefix + + fmt.format(rand.nextInt(Integer.MAX_VALUE)) + suffix); + } while (result.exists()); + } + } + + if (deleteOnExit) { + result.deleteOnExit(); + } + return result; + } + + /** + * Create a File object for a temporary file in a given directory. Without actually creating the file. + * + *

+ * The file denoted by the returned abstract pathname did not exist before this method was invoked, any subsequent + * invocation of this method will yield a different file name. + *

+ *

+ * The filename is prefixNNNNNsuffix where NNNN is a random number. + *

+ * + * @param prefix prefix before the random number. + * @param suffix file extension; include the '.'. + * @param parentDir Directory to create the temporary file in; java.io.tmpdir used if not specified. + * @param deleteOnExit whether to set the tempfile for deletion on normal VM exit. + * + * @deprecated since ant 1.7.1 use createTempFile(String, String, File, boolean, boolean) instead. + * @return a File reference to the new, nonexistent temporary file. + */ + public File createTempFile(String prefix, String suffix, + File parentDir, boolean deleteOnExit) { + return createTempFile(prefix, suffix, parentDir, deleteOnExit, false); + } + +// /** +// * Compares the contents of two files. +// * +// * @param f1 the file whose content is to be compared. +// * @param f2 the other file whose content is to be compared. +// * +// * @return true if the content of the files is the same. +// * +// * @throws IOException if the files cannot be read. +// */ +// public boolean contentEquals(File f1, File f2) throws IOException { +// return contentEquals(f1, f2, false); +// } +// +// /** +// * Compares the contents of two files. +// * +// * @param f1 the file whose content is to be compared. +// * @param f2 the other file whose content is to be compared. +// * @param textfile true if the file is to be treated as a text file and +// * differences in kind of line break are to be ignored. +// * +// * @return true if the content of the files is the same. +// * +// * @throws IOException if the files cannot be read. +// * @since Ant 1.6.3 +// */ +// public boolean contentEquals(File f1, File f2, boolean textfile) throws IOException { +// return ResourceUtils.contentEquals(new FileResource(f1), new FileResource(f2), textfile); +// } + /** + * This was originally an emulation of {@link File#getParentFile} for JDK 1.1, but it is now implemented using that + * method (Ant 1.6.3 onwards). + * + * @param f the file whose parent is required. + * @return the given file's parent, or null if the file does not have a parent. + * @since 1.10 + * @deprecated since 1.7. Just use {@link File#getParentFile} directly. + */ + public File getParentFile(File f) { + return (f == null) ? null : f.getParentFile(); + } + + /** + * Read from reader till EOF. + * + * @param rdr the reader from which to read. + * @return the contents read out of the given reader. + * + * @throws IOException if the contents could not be read out from the reader. + */ + public static String readFully(Reader rdr) throws IOException { + return readFully(rdr, BUF_SIZE); + } + + /** + * Read from reader till EOF. + * + * @param rdr the reader from which to read. + * @param bufferSize the buffer size to use when reading. + * + * @return the contents read out of the given reader. + * + * @throws IOException if the contents could not be read out from the reader. + */ + public static String readFully(Reader rdr, int bufferSize) + throws IOException { + if (bufferSize <= 0) { + throw new IllegalArgumentException("Buffer size must be greater " + + "than 0"); + } + final char[] buffer = new char[bufferSize]; + int bufferLength = 0; + StringBuffer textBuffer = null; + while (bufferLength != -1) { + bufferLength = rdr.read(buffer); + if (bufferLength > 0) { + textBuffer = (textBuffer == null) ? new StringBuffer() : textBuffer; + textBuffer.append(new String(buffer, 0, bufferLength)); + } + } + return (textBuffer == null) ? null : textBuffer.toString(); + } + + /** + * Safe read fully - do not return a null for an empty reader. + * + * @param reader the input to read from. + * @return the string. + * @throws IOException if unable to read from reader. + * @since Ant 1.7.1 + */ + public static String safeReadFully(Reader reader) throws IOException { + String ret = readFully(reader); + return ret == null ? "" : ret; + } + + /** + * This was originally an emulation of File.createNewFile for JDK 1.1, but it is now implemented using that method + * (Ant 1.6.3 onwards). + * + *

+ * This method has historically not guaranteed that the operation was atomic. In its current + * implementation it is. + * + * @param f the file to be created. + * @return true if the file did not exist already. + * @throws IOException on error. + * @since Ant 1.5 + */ + public boolean createNewFile(File f) throws IOException { + return f.createNewFile(); + } + + /** + * Create a new file, optionally creating parent directories. + * + * @param f the file to be created. + * @param mkdirs boolean whether to create parent directories. + * @return true if the file did not exist already. + * @throws IOException on error. + * @since Ant 1.6.3 + */ + public boolean createNewFile(File f, boolean mkdirs) throws IOException { + File parent = f.getParentFile(); + if (mkdirs && !(parent.exists())) { + parent.mkdirs(); + } + return f.createNewFile(); + } + + /** + * Checks whether a given file is a symbolic link. + * + *

+ * It doesn't really test for symbolic links but whether the canonical and absolute paths of the file are + * identical--this may lead to false positives on some platforms.

+ * + * @param parent the parent directory of the file to test + * @param name the name of the file to test. + * + * @return true if the file is a symbolic link. + * @throws IOException on error. + * @since Ant 1.5 + * @deprecated use SymbolicLinkUtils instead + */ + public boolean isSymbolicLink(File parent, String name) + throws IOException { + SymbolicLinkUtils u = SymbolicLinkUtils.getSymbolicLinkUtils(); + if (parent == null) { + return u.isSymbolicLink(name); + } + return u.isSymbolicLink(parent, name); + } + + /** + * Removes a leading path from a second path. + * + * @param leading The leading path, must not be null, must be absolute. + * @param path The path to remove from, must not be null, must be absolute. + * + * @return path's normalized absolute if it doesn't start with leading; path's path with leading's path removed + * otherwise. + * + * @since Ant 1.5 + */ + public String removeLeadingPath(File leading, File path) { + String l = normalize(leading.getAbsolutePath()).getAbsolutePath(); + String p = normalize(path.getAbsolutePath()).getAbsolutePath(); + if (l.equals(p)) { + return ""; + } + // ensure that l ends with a / + // so we never think /foo was a parent directory of /foobar + if (!l.endsWith(File.separator)) { + l += File.separator; + } + return (p.startsWith(l)) ? p.substring(l.length()) : p; + } + + /** + * Learn whether one path "leads" another. + * + * @param leading The leading path, must not be null, must be absolute. + * @param path The path to remove from, must not be null, must be absolute. + * @return true if path starts with leading; false otherwise. + * @since Ant 1.7 + */ + public boolean isLeadingPath(File leading, File path) { + String l = normalize(leading.getAbsolutePath()).getAbsolutePath(); + String p = normalize(path.getAbsolutePath()).getAbsolutePath(); + if (l.equals(p)) { + return true; + } + // ensure that l ends with a / + // so we never think /foo was a parent directory of /foobar + if (!l.endsWith(File.separator)) { + l += File.separator; + } + return p.startsWith(l); + } + + /** + * Constructs a file: URI that represents the external form of the given pathname. + * + *

+ * Will be an absolute URI if the given path is absolute.

+ * + *

+ * This code encodes non ASCII characters too.

+ * + *

+ * The coding of the output is the same as what File.toURI().toASCIIString() produces

+ * + * See dt-sysid + * which makes some mention of how characters not supported by URI Reference syntax should be escaped. + * + * @param path the path in the local file system. + * @return the URI version of the local path. + * @since Ant 1.6 + */ + public String toURI(String path) { + return new File(path).toURI().toASCIIString(); + } + + /** + * Constructs a file path from a file: URI. + * + *

+ * Will be an absolute path if the given URI is absolute.

+ * + *

+ * Swallows '%' that are not followed by two characters, doesn't deal with non-ASCII characters.

+ * + * @param uri the URI designating a file in the local filesystem. + * @return the local file system path for the file. + * @since Ant 1.6 + */ + public String fromURI(String uri) { + synchronized (cacheFromUriLock) { + if (uri.equals(cacheFromUriRequest)) { + return cacheFromUriResponse; + } + String path = Locator.fromURI(uri); + String ret = isAbsolutePath(path) ? normalize(path).getAbsolutePath() : path; + cacheFromUriRequest = uri; + cacheFromUriResponse = ret; + return ret; + } + } + + /** + * Compares two filenames. + * + *

+ * Unlike java.io.File#equals this method will try to compare the absolute paths and "normalize" the + * filenames before comparing them.

+ * + * @param f1 the file whose name is to be compared. + * @param f2 the other file whose name is to be compared. + * + * @return true if the file are for the same file. + * + * @since Ant 1.5.3 + */ + public boolean fileNameEquals(File f1, File f2) { + return normalize(f1.getAbsolutePath()).getAbsolutePath().equals( + normalize(f2.getAbsolutePath()).getAbsolutePath()); + } + + /** + * Are the two File instances pointing to the same object on the file system? + * + * @since Ant 1.8.2 + */ + public boolean areSame(File f1, File f2) throws IOException { + if (f1 == null && f2 == null) { + return true; + } + if (f1 == null || f2 == null) { + return false; + } + File f1Normalized = normalize(f1.getAbsolutePath()); + File f2Normalized = normalize(f2.getAbsolutePath()); + return f1Normalized.equals(f2Normalized) + || f1Normalized.getCanonicalFile().equals(f2Normalized + .getCanonicalFile()); + } +// +// /** +// * Renames a file, even if that involves crossing file system boundaries. +// * +// *

+// * This will remove to (if it exists), ensure that to's parent directory exists and move +// * from, which involves deleting from as well.

+// * +// * @param from the file to move. +// * @param to the new file name. +// * +// * @throws IOException if anything bad happens during this process. Note that to may have been deleted +// * already when this happens. +// * +// * @since Ant 1.6 +// */ +// public void rename(File from, File to) throws IOException { +// // identical logic lives in Move.renameFile(): +// from = normalize(from.getAbsolutePath()).getCanonicalFile(); +// to = normalize(to.getAbsolutePath()); +// if (!from.exists()) { +// System.err.println("Cannot rename nonexistent file " + from); +// return; +// } +// if (from.getAbsolutePath().equals(to.getAbsolutePath())) { +// System.err.println("Rename of " + from + " to " + to + " is a no-op."); +// return; +// } +// if (to.exists() && !(areSame(from, to) || tryHardToDelete(to))) { +// throw new IOException("Failed to delete " + to + " while trying to rename " + from); +// } +// File parent = to.getParentFile(); +// if (parent != null && !parent.isDirectory() +// && !(parent.mkdirs() || parent.isDirectory())) { +// throw new IOException("Failed to create directory " + parent +// + " while trying to rename " + from); +// } +// if (!from.renameTo(to)) { +// copyFile(from, to); +// if (!tryHardToDelete(from)) { +// throw new IOException("Failed to delete " + from + " while trying to rename it."); +// } +// } +// } + + /** + * Get the granularity of file timestamps. The choice is made based on OS, which is incorrect--it should really be + * by filesystem. We do not have an easy way to probe for file systems, however, so this heuristic gives us a decent + * default. + * + * @return the difference, in milliseconds, which two file timestamps must have in order for the two files to be + * considered to have different timestamps. + */ + public long getFileTimestampGranularity() { + if (ON_WIN9X) { + return FAT_FILE_TIMESTAMP_GRANULARITY; + } + if (ON_WINDOWS) { + return NTFS_FILE_TIMESTAMP_GRANULARITY; + } + if (ON_DOS) { + return FAT_FILE_TIMESTAMP_GRANULARITY; + } + return UNIX_FILE_TIMESTAMP_GRANULARITY; + } + + /** + * test whether a file or directory exists, with an error in the upper/lower case spelling of the name. Using this + * method is only interesting on case insensitive file systems (Windows).
+ * It will return true only if 3 conditions are met : + *
+ *
    + *
  • operating system is case insensitive
  • + *
  • file exists
  • + *
  • actual name from directory reading is different from the supplied argument
  • + *
+ *
+ * the purpose is to identify files or directories on case-insensitive filesystems whose case is not what is + * expected.
+ * Possibly to rename them afterwards to the desired upper/lowercase combination. + *
+ * + * @param localFile file to test + * @return true if the file exists and the case of the actual file is not the case of the parameter + * @since Ant 1.7.1 + */ + public boolean hasErrorInCase(File localFile) { + localFile = normalize(localFile.getAbsolutePath()); + if (!localFile.exists()) { + return false; + } + final String localFileName = localFile.getName(); + FilenameFilter ff = new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.equalsIgnoreCase(localFileName) && (!name.equals(localFileName)); + } + }; + String[] names = localFile.getParentFile().list(ff); + return names != null && names.length == 1; + } + + /** + * Returns true if the source is older than the dest. If the dest file does not exist, then the test returns false; + * it is implicitly not up do date. + * + * @param source source file (should be the older). + * @param dest dest file (should be the newer). + * @param granularity an offset added to the source time. + * @return true if the source is older than the dest after accounting for granularity. + * @since Ant 1.6.3 + */ + public boolean isUpToDate(File source, File dest, long granularity) { + //do a check for the destination file existing + if (!dest.exists()) { + //if it does not, then the file is not up to date. + return false; + } + long sourceTime = source.lastModified(); + long destTime = dest.lastModified(); + return isUpToDate(sourceTime, destTime, granularity); + } + + /** + * Returns true if the source is older than the dest. + * + * @param source source file (should be the older). + * @param dest dest file (should be the newer). + * @return true if the source is older than the dest, taking the granularity into account. + * @since Ant 1.6.3 + */ + public boolean isUpToDate(File source, File dest) { + return isUpToDate(source, dest, getFileTimestampGranularity()); + } + + /** + * Compare two timestamps for being up to date using the specified granularity. + * + * @param sourceTime timestamp of source file. + * @param destTime timestamp of dest file. + * @param granularity os/filesys granularity. + * @return true if the dest file is considered up to date. + */ + public boolean isUpToDate(long sourceTime, long destTime, long granularity) { + return destTime != -1 && destTime >= sourceTime + granularity; + } + + /** + * Compare two timestamps for being up to date using the current granularity. + * + * @param sourceTime timestamp of source file. + * @param destTime timestamp of dest file. + * @return true if the dest file is considered up to date. + */ + public boolean isUpToDate(long sourceTime, long destTime) { + return isUpToDate(sourceTime, destTime, getFileTimestampGranularity()); + } + + /** + * Close a Writer without throwing any exception if something went wrong. Do not attempt to close it if the argument + * is null. + * + * @param device output writer, can be null. + */ + public static void close(Writer device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Close a Reader without throwing any exception if something went wrong. Do not attempt to close it if the argument + * is null. + * + * @param device Reader, can be null. + */ + public static void close(Reader device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Close a stream without throwing any exception if something went wrong. Do not attempt to close it if the argument + * is null. + * + * @param device stream, can be null. + */ + public static void close(OutputStream device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Close a stream without throwing any exception if something went wrong. Do not attempt to close it if the argument + * is null. + * + * @param device stream, can be null. + */ + public static void close(InputStream device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Close a Channel without throwing any exception if something went wrong. Do not attempt to close it if the + * argument is null. + * + * @param device channel, can be null. + * @since Ant 1.8.0 + */ + public static void close(Channel device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Closes an URLConnection if its concrete implementation provides a way to close it that Ant knows of. + * + * @param conn connection, can be null + * @since Ant 1.8.0 + */ + public static void close(URLConnection conn) { + if (conn != null) { + try { + if (conn instanceof JarURLConnection) { + JarURLConnection juc = (JarURLConnection) conn; + JarFile jf = juc.getJarFile(); + jf.close(); + jf = null; + } else if (conn instanceof HttpURLConnection) { + ((HttpURLConnection) conn).disconnect(); + } + } catch (IOException exc) { + //ignore + } + } + } + + /** + * Delete the file with {@link File#delete()} if the argument is not null. Do nothing on a null argument. + * + * @param file file to delete. + */ + public static void delete(File file) { + if (file != null) { + file.delete(); + } + } + + /** + * Accommodate Windows bug encountered in both Sun and IBM JDKs. Others possible. If the delete does not work, call + * System.gc(), wait a little and try again. + * + * @return whether deletion was successful + * @since Ant 1.8.0 + */ + public boolean tryHardToDelete(File f) { + return tryHardToDelete(f, ON_WINDOWS); + } + + /** + * If delete does not work, call System.gc() if asked to, wait a little and try again. + * + * @return whether deletion was successful + * @since Ant 1.8.3 + */ + public boolean tryHardToDelete(File f, boolean runGC) { + if (!f.delete()) { + if (runGC) { + System.gc(); + } + try { + Thread.sleep(DELETE_RETRY_SLEEP_MILLIS); + } catch (InterruptedException ex) { + // Ignore Exception + } + return f.delete(); + } + return true; + } + + /** + * Calculates the relative path between two files. + *

+ * Implementation note:
This function may throw an IOException if an I/O error occurs because its use of the + * canonical pathname may require filesystem queries. + *

+ * + * @param fromFile the File to calculate the path from + * @param toFile the File to calculate the path to + * @return the relative path between the files + * @throws Exception for undocumented reasons + * @see File#getCanonicalPath() + * + * @since Ant 1.7 + */ + public static String getRelativePath(File fromFile, File toFile) throws Exception { + String fromPath = fromFile.getCanonicalPath(); + String toPath = toFile.getCanonicalPath(); + + // build the path stack info to compare + String[] fromPathStack = getPathStack(fromPath); + String[] toPathStack = getPathStack(toPath); + + if (0 < toPathStack.length && 0 < fromPathStack.length) { + if (!fromPathStack[0].equals(toPathStack[0])) { + // not the same device (would be "" on Linux/Unix) + + return getPath(Arrays.asList(toPathStack)); + } + } else { + // no comparison possible + return getPath(Arrays.asList(toPathStack)); + } + + int minLength = Math.min(fromPathStack.length, toPathStack.length); + int same = 1; // Used outside the for loop + + // get index of parts which are equal + for (; + same < minLength && fromPathStack[same].equals(toPathStack[same]); + same++) { + // Do nothing + } + + List relativePathStack = new ArrayList(); + + // if "from" part is longer, fill it up with ".." + // to reach path which is equal to both paths + for (int i = same; i < fromPathStack.length; i++) { + relativePathStack.add(".."); + } + + // fill it up path with parts which were not equal + for (int i = same; i < toPathStack.length; i++) { + relativePathStack.add(toPathStack[i]); + } + + return getPath(relativePathStack); + } + + /** + * Gets all names of the path as an array of Strings. + * + * @param path to get names from + * @return Strings, never null + * + * @since Ant 1.7 + */ + public static String[] getPathStack(String path) { + String normalizedPath = path.replace(File.separatorChar, '/'); + + return normalizedPath.split("/"); + } + + /** + * Gets path from a List of Strings. + * + * @param pathStack List of Strings to be concatenated as a path. + * @return String, never null + * + * @since Ant 1.7 + */ + public static String getPath(List pathStack) { + // can safely use '/' because Windows understands '/' as separator + return getPath(pathStack, '/'); + } + + /** + * Gets path from a List of Strings. + * + * @param pathStack List of Strings to be concated as a path. + * @param separatorChar char to be used as separator between names in path + * @return String, never null + * + * @since Ant 1.7 + */ + public static String getPath(final List pathStack, final char separatorChar) { + final StringBuffer buffer = new StringBuffer(); + + final Iterator iter = pathStack.iterator(); + if (iter.hasNext()) { + buffer.append(iter.next()); + } + while (iter.hasNext()) { + buffer.append(separatorChar); + buffer.append(iter.next()); + } + return buffer.toString(); + } + + /** + * Get the default encoding. This is done by opening an InputStreamReader on a dummy InputStream and getting the + * encoding. Could use System.getProperty("file.encoding"), but cannot see where this is documented. + * + * @return the default file encoding. + */ + public String getDefaultEncoding() { + InputStreamReader is = new InputStreamReader( + new InputStream() { + public int read() { + return -1; + } + }); + try { + return is.getEncoding(); + } finally { + close(is); + } + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/SymbolicLinkUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/SymbolicLinkUtils.java new file mode 100644 index 000000000..81aae5541 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/SymbolicLinkUtils.java @@ -0,0 +1,291 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.util; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +//import org.apache.tools.ant.Task; +//import org.apache.tools.ant.taskdefs.Execute; + +/** + * Contains methods related to symbolic links - or what Ant thinks is a symbolic link based on the absent support for + * them in Java. + * + * @since Ant 1.8.0 + */ +public class SymbolicLinkUtils { + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * Shared instance. + */ + private static final SymbolicLinkUtils PRIMARY_INSTANCE + = new SymbolicLinkUtils(); + + /** + * Method to retrieve The SymbolicLinkUtils, which is shared by all users of this method. + * + * @return an instance of SymbolicLinkUtils. + */ + public static SymbolicLinkUtils getSymbolicLinkUtils() { + // keep the door open for Java X.Y specific subclass if symbolic + // links ever become supported in the classlib + return PRIMARY_INSTANCE; + } + + /** + * Empty constructor. + */ + protected SymbolicLinkUtils() { + } + + /** + * Checks whether a given file is a symbolic link. + * + *

+ * It doesn't really test for symbolic links but whether the canonical and absolute paths of the file are + * identical--this may lead to false positives on some platforms.

+ * + * @param file the file to test. Must not be null. + * + * @return true if the file is a symbolic link. + * @throws IOException on error. + */ + public boolean isSymbolicLink(File file) throws IOException { + return isSymbolicLink(file.getParentFile(), file.getName()); + } + + /** + * Checks whether a given file is a symbolic link. + * + *

+ * It doesn't really test for symbolic links but whether the canonical and absolute paths of the file are + * identical--this may lead to false positives on some platforms.

+ * + * @param name the name of the file to test. + * + * @return true if the file is a symbolic link. + * @throws IOException on error. + */ + public boolean isSymbolicLink(String name) throws IOException { + return isSymbolicLink(new File(name)); + } + + /** + * Checks whether a given file is a symbolic link. + * + *

+ * It doesn't really test for symbolic links but whether the canonical and absolute paths of the file are + * identical--this may lead to false positives on some platforms.

+ * + * @param parent the parent directory of the file to test + * @param name the name of the file to test. + * + * @return true if the file is a symbolic link. + * @throws IOException on error. + */ + public boolean isSymbolicLink(File parent, String name) + throws IOException { + File toTest = parent != null + ? new File(parent.getCanonicalPath(), name) + : new File(name); + return !toTest.getAbsolutePath().equals(toTest.getCanonicalPath()); + } + + /** + * Checks whether a given file is a broken symbolic link. + * + *

+ * It doesn't really test for symbolic links but whether Java reports that the File doesn't exist but its parent's + * child list contains it--this may lead to false positives on some platforms.

+ * + *

+ * Note that #isSymbolicLink returns false if this method returns true since Java won't produce a canonical name + * different from the abolute one if the link is broken.

+ * + * @param name the name of the file to test. + * + * @return true if the file is a broken symbolic link. + * @throws IOException on error. + */ + public boolean isDanglingSymbolicLink(String name) throws IOException { + return isDanglingSymbolicLink(new File(name)); + } + + /** + * Checks whether a given file is a broken symbolic link. + * + *

+ * It doesn't really test for symbolic links but whether Java reports that the File doesn't exist but its parent's + * child list contains it--this may lead to false positives on some platforms.

+ * + *

+ * Note that #isSymbolicLink returns false if this method returns true since Java won't produce a canonical name + * different from the abolute one if the link is broken.

+ * + * @param file the file to test. + * + * @return true if the file is a broken symbolic link. + * @throws IOException on error. + */ + public boolean isDanglingSymbolicLink(File file) throws IOException { + return isDanglingSymbolicLink(file.getParentFile(), file.getName()); + } + + /** + * Checks whether a given file is a broken symbolic link. + * + *

+ * It doesn't really test for symbolic links but whether Java reports that the File doesn't exist but its parent's + * child list contains it--this may lead to false positives on some platforms.

+ * + *

+ * Note that #isSymbolicLink returns false if this method returns true since Java won't produce a canonical name + * different from the abolute one if the link is broken.

+ * + * @param parent the parent directory of the file to test + * @param name the name of the file to test. + * + * @return true if the file is a broken symbolic link. + * @throws IOException on error. + */ + public boolean isDanglingSymbolicLink(File parent, String name) + throws IOException { + File f = new File(parent, name); + if (!f.exists()) { + final String localName = f.getName(); + String[] c = parent.list(new FilenameFilter() { + public boolean accept(File d, String n) { + return localName.equals(n); + } + }); + return c != null && c.length > 0; + } + return false; + } +// +// /** +// * Delete a symlink (without deleting the associated resource). +// * +// *

This is a utility method that removes a unix symlink without +// * removing the resource that the symlink points to. If it is +// * accidentally invoked on a real file, the real file will not be +// * harmed, but silently ignored.

+// * +// *

Normally this method works by +// * getting the canonical path of the link, using the canonical path to +// * rename the resource (breaking the link) and then deleting the link. +// * The resource is then returned to its original name inside a finally +// * block to ensure that the resource is unharmed even in the event of +// * an exception.

+// * +// *

There may be cases where the algorithm described above doesn't work, +// * in that case the method tries to use the native "rm" command on +// * the symlink instead.

+// * +// * @param link A File object of the symlink to delete. +// * @param task An Ant Task required if "rm" needs to be invoked. +// * +// * @throws IOException If calls to File.rename, +// * File.delete or File.getCanonicalPath +// * fail. +// * @throws BuildException if the execution of "rm" failed. +// */ +// public void deleteSymbolicLink(File link, Task task) +// throws IOException { +// if (isDanglingSymbolicLink(link)) { +// if (!link.delete()) { +// throw new IOException("failed to remove dangling symbolic link " +// + link); +// } +// return; +// } +// +// if (!isSymbolicLink(link)) { +// // plain file, not a link +// return; +// } +// +// if (!link.exists()) { +// throw new FileNotFoundException("No such symbolic link: " + link); +// } +// +// // find the resource of the existing link: +// File target = link.getCanonicalFile(); +// +// // no reason to try the renaming algorithm if we aren't allowed to +// // write to the target's parent directory. Let's hope that +// // File.canWrite works on all platforms. +// +// if (task == null || target.getParentFile().canWrite()) { +// +// // rename the resource, thus breaking the link: +// File temp = FILE_UTILS.createTempFile("symlink", ".tmp", +// target.getParentFile(), false, +// false); +// +// if (FILE_UTILS.isLeadingPath(target, link)) { +// // link points to a parent directory, renaming the parent +// // will rename the file +// link = new File(temp, +// FILE_UTILS.removeLeadingPath(target, link)); +// } +// +// boolean renamedTarget = false; +// try { +// try { +// FILE_UTILS.rename(target, temp); +// renamedTarget = true; +// } catch (IOException e) { +// throw new IOException("Couldn't rename resource when " +// + "attempting to delete '" + link +// + "'. Reason: " + e.getMessage()); +// } +// // delete the (now) broken link: +// if (!link.delete()) { +// throw new IOException("Couldn't delete symlink: " +// + link +// + " (was it a real file? is this " +// + "not a UNIX system?)"); +// } +// } finally { +// if (renamedTarget) { +// // return the resource to its original name: +// try { +// FILE_UTILS.rename(temp, target); +// } catch (IOException e) { +// throw new IOException("Couldn't return resource " +// + temp +// + " to its original name: " +// + target.getAbsolutePath() +// + ". Reason: " + e.getMessage() +// + "\n THE RESOURCE'S NAME ON DISK" +// + " HAS BEEN CHANGED BY THIS" +// + " ERROR!\n"); +// } +// } +// } +// } else { +// Execute.runCommand(task, +// new String[] {"rm", link.getAbsolutePath()}); +// } +// } + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/VectorSet.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/VectorSet.java new file mode 100644 index 000000000..36617fe00 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/VectorSet.java @@ -0,0 +1,242 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.util; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; +import java.util.Vector; + +/** + * Subclass of Vector that won't store duplicate entries and shows + * HashSet's constant time performance characteristics for the + * contains method. + * + *

This is not a general purpose class but has been written because + * the protected members of {@link + * org.apache.tools.ant.DirectoryScanner DirectoryScanner} prohibited + * later revisions from using a more efficient collection.

+ * + *

Methods are synchronized to keep Vector's contract.

+ * + * @since Ant 1.8.0 + */ +public final class VectorSet extends Vector { + private static final long serialVersionUID = 1L; + + private final HashSet set = new HashSet(); + + public VectorSet() { super(); } + + public VectorSet(int initialCapacity) { super(initialCapacity); } + + public VectorSet(int initialCapacity, int capacityIncrement) { + super(initialCapacity, capacityIncrement); + } + + public VectorSet(Collection c) { + if (c != null) { + for (E e : c) { + add(e); + } + } + } + + public synchronized boolean add(E o) { + if (!set.contains(o)) { + doAdd(size(), o); + return true; + } + return false; + } + + /** + * This implementation may not add the element at the given index + * if it is already contained in the collection. + */ + public void add(int index, E o) { + doAdd(index, o); + } + + private synchronized void doAdd(int index, E o) { + // Vector.add seems to delegate to insertElementAt, but this + // is not documented so we may better implement it ourselves + if (set.add(o)) { + int count = size(); + ensureCapacity(count + 1); + if (index != count) { + System.arraycopy(elementData, index, elementData, index + 1, + count - index); + } + elementData[index] = o; + elementCount++; + } + } + + public synchronized void addElement(E o) { + doAdd(size(), o); + } + + public synchronized boolean addAll(Collection c) { + boolean changed = false; + for (E e : c) { + changed |= add(e); + } + return changed; + } + + /** + * This implementation may not add all elements at the given index + * if any of them are already contained in the collection. + */ + public synchronized boolean addAll(int index, Collection c) { + LinkedList toAdd = new LinkedList(); + for (E e : c) { + if (set.add(e)) { + toAdd.add(e); + } + } + if (toAdd.isEmpty()) { + return false; + } + int count = size(); + ensureCapacity(count + toAdd.size()); + if (index != count) { + System.arraycopy(elementData, index, elementData, index + toAdd.size(), + count - index); + } + for (Object o : toAdd) { + elementData[index++] = o; + } + elementCount += toAdd.size(); + return true; + } + + public synchronized void clear() { + super.clear(); + set.clear(); + } + + public Object clone() { + @SuppressWarnings("unchecked") + final VectorSet vs = (VectorSet) super.clone(); + vs.set.addAll(set); + return vs; + } + + public synchronized boolean contains(Object o) { + return set.contains(o); + } + + public synchronized boolean containsAll(Collection c) { + return set.containsAll(c); + } + + public void insertElementAt(E o, int index) { + doAdd(index, o); + } + + public synchronized E remove(int index) { + E o = get(index); + remove(o); + return o; + } + + public boolean remove(Object o) { + return doRemove(o); + } + + private synchronized boolean doRemove(Object o) { + // again, remove seems to delegate to removeElement, but we + // shouldn't trust it + if (set.remove(o)) { + int index = indexOf(o); + if (index < elementData.length - 1) { + System.arraycopy(elementData, index + 1, elementData, index, + elementData.length - index - 1); + } + elementCount--; + return true; + } + return false; + } + + public synchronized boolean removeAll(Collection c) { + boolean changed = false; + for (Object o : c) { + changed |= remove(o); + } + return changed; + } + + public synchronized void removeAllElements() { + set.clear(); + super.removeAllElements(); + } + + public boolean removeElement(Object o) { + return doRemove(o); + } + + public synchronized void removeElementAt(int index) { + remove(get(index)); + } + + public synchronized void removeRange(final int fromIndex, int toIndex) { + while (toIndex > fromIndex) { + remove(--toIndex); + } + } + + public synchronized boolean retainAll(Collection c) { + if (!(c instanceof Set)) { + c = new HashSet(c); + } + LinkedList l = new LinkedList(); + for (E o : this) { + if (!c.contains(o)) { + l.addLast(o); + } + } + if (!l.isEmpty()) { + removeAll(l); + return true; + } + return false; + } + + public synchronized E set(int index, E o) { + E orig = get(index); + if (set.add(o)) { + elementData[index] = o; + set.remove(orig); + } else { + int oldIndexOfO = indexOf(o); + remove(o); + remove(orig); + add(oldIndexOfO > index ? index : index - 1, o); + } + return orig; + } + + public void setElementAt(E o, int index) { + set(index, o); + } + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/package-info.java new file mode 100644 index 000000000..963f6d332 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/package-info.java @@ -0,0 +1,14 @@ +/** + * + * + * org.owasp.dependencycheck.org.apache.tools.ant.util + * + * + * This is a copy of classes within Apache Ant. The DirectoryScanner + * is needed by dependency-check. However, we did not want to make + * Ant a dependency. As such, a few files were copied and slightly + * modified to remove any references to the Ant Project class. + * + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.util; diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java index 34727e9b1..2ebe930de 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java @@ -78,7 +78,7 @@ public final class FileUtils { if (!org.apache.commons.io.FileUtils.deleteQuietly(file)) { success = false; final String msg = String.format("Failed to delete file: %s; attempting to delete on exit.", file.getPath()); - LOGGER.log(Level.FINE, msg); + LOGGER.log(Level.INFO, msg); file.deleteOnExit(); } return success; diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java index 7ff76b16a..e8c413366 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java @@ -188,6 +188,14 @@ public final class Settings { * The properties key for using the proxy to reach Nexus. */ public static final String ANALYZER_NEXUS_PROXY = "analyzer.nexus.proxy"; + /** + * The properties key for whether the Central analyzer is enabled. + */ + public static final String ANALYZER_CENTRAL_ENABLED = "analyzer.central.enabled"; + /** + * The properties key for the Central search URL. + */ + public static final String ANALYZER_CENTRAL_URL = "analyzer.central.url"; /** * The path to mono, if available. */ diff --git a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScannerTest.java b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScannerTest.java new file mode 100644 index 000000000..99d533e31 --- /dev/null +++ b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScannerTest.java @@ -0,0 +1,732 @@ +/* + * Copyright 2014 OWASP. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.owasp.dependencycheck.org.apache.tools.ant; + +import org.owasp.dependencycheck.org.apache.tools.ant.DirectoryScanner; +import java.io.File; +import org.junit.After; +import org.junit.AfterClass; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + * @author Jeremy Long + */ +public class DirectoryScannerTest { + + public DirectoryScannerTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Testing the expected use of the directory scanner. + */ + @Test + public void testExpectedUse() { + DirectoryScanner scanner = new DirectoryScanner(); + scanner.setBasedir("./target"); + scanner.setIncludes("/test-classes/**"); + scanner.scan(); + + File base = new File("./target"); + for (String t : scanner.getIncludedFiles()) { + assertTrue(t.startsWith("test-classes")); + File test = new File(base, t); + assertTrue(test.exists()); + } + } + + /** + * Test of matchPatternStart method, of class DirectoryScanner. + */ + @Test + public void testMatchPatternStart_String_String() { + String pattern = "alpha/be?a/**"; + String str = "alpha/beta/gamma/"; + boolean expResult = true; + boolean result = DirectoryScanner.matchPatternStart(pattern, str); + assertEquals(expResult, result); + } + + /** + * Test of matchPatternStart method, of class DirectoryScanner. + */ + @Test + public void testMatchPatternStart_3args() { + String pattern = "Alpha/be?a/**"; + String str = "alpha/beta/gamma/"; + boolean isCaseSensitive = true; + boolean expResult = false; + boolean result = DirectoryScanner.matchPatternStart(pattern, str, isCaseSensitive); + assertEquals(expResult, result); + + isCaseSensitive = false; + expResult = true; + result = DirectoryScanner.matchPatternStart(pattern, str, isCaseSensitive); + assertEquals(expResult, result); + } + + /** + * Test of matchPath method, of class DirectoryScanner. + */ + @Test + public void testMatchPath_String_String() { + String pattern = "alpha/be?a/**"; + String str = "alpha/beta/gamma/"; + boolean expResult = true; + boolean result = DirectoryScanner.matchPath(pattern, str); + assertEquals(expResult, result); + } +// +// /** +// * Test of matchPath method, of class DirectoryScanner. +// */ +// @Test +// public void testMatchPath_3args() { +// System.out.println("matchPath"); +// String pattern = ""; +// String str = ""; +// boolean isCaseSensitive = false; +// boolean expResult = false; +// boolean result = DirectoryScanner.matchPath(pattern, str, isCaseSensitive); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of match method, of class DirectoryScanner. +// */ +// @Test +// public void testMatch_String_String() { +// System.out.println("match"); +// String pattern = ""; +// String str = ""; +// boolean expResult = false; +// boolean result = DirectoryScanner.match(pattern, str); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of match method, of class DirectoryScanner. +// */ +// @Test +// public void testMatch_3args() { +// System.out.println("match"); +// String pattern = ""; +// String str = ""; +// boolean isCaseSensitive = false; +// boolean expResult = false; +// boolean result = DirectoryScanner.match(pattern, str, isCaseSensitive); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getDefaultExcludes method, of class DirectoryScanner. +// */ +// @Test +// public void testGetDefaultExcludes() { +// System.out.println("getDefaultExcludes"); +// String[] expResult = null; +// String[] result = DirectoryScanner.getDefaultExcludes(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of addDefaultExclude method, of class DirectoryScanner. +// */ +// @Test +// public void testAddDefaultExclude() { +// System.out.println("addDefaultExclude"); +// String s = ""; +// boolean expResult = false; +// boolean result = DirectoryScanner.addDefaultExclude(s); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of removeDefaultExclude method, of class DirectoryScanner. +// */ +// @Test +// public void testRemoveDefaultExclude() { +// System.out.println("removeDefaultExclude"); +// String s = ""; +// boolean expResult = false; +// boolean result = DirectoryScanner.removeDefaultExclude(s); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of resetDefaultExcludes method, of class DirectoryScanner. +// */ +// @Test +// public void testResetDefaultExcludes() { +// System.out.println("resetDefaultExcludes"); +// DirectoryScanner.resetDefaultExcludes(); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setBasedir method, of class DirectoryScanner. +// */ +// @Test +// public void testSetBasedir_String() { +// System.out.println("setBasedir"); +// String basedir = ""; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setBasedir(basedir); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setBasedir method, of class DirectoryScanner. +// */ +// @Test +// public void testSetBasedir_File() { +// System.out.println("setBasedir"); +// File basedir = null; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setBasedir(basedir); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getBasedir method, of class DirectoryScanner. +// */ +// @Test +// public void testGetBasedir() { +// System.out.println("getBasedir"); +// DirectoryScanner instance = new DirectoryScanner(); +// File expResult = null; +// File result = instance.getBasedir(); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of isCaseSensitive method, of class DirectoryScanner. +// */ +// @Test +// public void testIsCaseSensitive() { +// System.out.println("isCaseSensitive"); +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.isCaseSensitive(); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setCaseSensitive method, of class DirectoryScanner. +// */ +// @Test +// public void testSetCaseSensitive() { +// System.out.println("setCaseSensitive"); +// boolean isCaseSensitive = false; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setCaseSensitive(isCaseSensitive); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setErrorOnMissingDir method, of class DirectoryScanner. +// */ +// @Test +// public void testSetErrorOnMissingDir() { +// System.out.println("setErrorOnMissingDir"); +// boolean errorOnMissingDir = false; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setErrorOnMissingDir(errorOnMissingDir); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of isFollowSymlinks method, of class DirectoryScanner. +// */ +// @Test +// public void testIsFollowSymlinks() { +// System.out.println("isFollowSymlinks"); +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.isFollowSymlinks(); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setFollowSymlinks method, of class DirectoryScanner. +// */ +// @Test +// public void testSetFollowSymlinks() { +// System.out.println("setFollowSymlinks"); +// boolean followSymlinks = false; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setFollowSymlinks(followSymlinks); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setMaxLevelsOfSymlinks method, of class DirectoryScanner. +// */ +// @Test +// public void testSetMaxLevelsOfSymlinks() { +// System.out.println("setMaxLevelsOfSymlinks"); +// int max = 0; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setMaxLevelsOfSymlinks(max); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setIncludes method, of class DirectoryScanner. +// */ +// @Test +// public void testSetIncludes() { +// System.out.println("setIncludes"); +// String[] includes = null; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setIncludes(includes); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setExcludes method, of class DirectoryScanner. +// */ +// @Test +// public void testSetExcludes() { +// System.out.println("setExcludes"); +// String[] excludes = null; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setExcludes(excludes); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of addExcludes method, of class DirectoryScanner. +// */ +// @Test +// public void testAddExcludes() { +// System.out.println("addExcludes"); +// String[] excludes = null; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.addExcludes(excludes); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setSelectors method, of class DirectoryScanner. +// */ +// @Test +// public void testSetSelectors() { +// System.out.println("setSelectors"); +// FileSelector[] selectors = null; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setSelectors(selectors); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of isEverythingIncluded method, of class DirectoryScanner. +// */ +// @Test +// public void testIsEverythingIncluded() { +// System.out.println("isEverythingIncluded"); +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.isEverythingIncluded(); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of scan method, of class DirectoryScanner. +// */ +// @Test +// public void testScan() { +// System.out.println("scan"); +// DirectoryScanner instance = new DirectoryScanner(); +// instance.scan(); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of clearResults method, of class DirectoryScanner. +// */ +// @Test +// public void testClearResults() { +// System.out.println("clearResults"); +// DirectoryScanner instance = new DirectoryScanner(); +// instance.clearResults(); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of slowScan method, of class DirectoryScanner. +// */ +// @Test +// public void testSlowScan() { +// System.out.println("slowScan"); +// DirectoryScanner instance = new DirectoryScanner(); +// instance.slowScan(); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of scandir method, of class DirectoryScanner. +// */ +// @Test +// public void testScandir() { +// System.out.println("scandir"); +// File dir = null; +// String vpath = ""; +// boolean fast = false; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.scandir(dir, vpath, fast); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of isIncluded method, of class DirectoryScanner. +// */ +// @Test +// public void testIsIncluded() { +// System.out.println("isIncluded"); +// String name = ""; +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.isIncluded(name); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of couldHoldIncluded method, of class DirectoryScanner. +// */ +// @Test +// public void testCouldHoldIncluded() { +// System.out.println("couldHoldIncluded"); +// String name = ""; +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.couldHoldIncluded(name); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of contentsExcluded method, of class DirectoryScanner. +// */ +// @Test +// public void testContentsExcluded() { +// System.out.println("contentsExcluded"); +// TokenizedPath path = null; +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.contentsExcluded(path); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of isExcluded method, of class DirectoryScanner. +// */ +// @Test +// public void testIsExcluded() { +// System.out.println("isExcluded"); +// String name = ""; +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.isExcluded(name); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of isSelected method, of class DirectoryScanner. +// */ +// @Test +// public void testIsSelected() { +// System.out.println("isSelected"); +// String name = ""; +// File file = null; +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.isSelected(name, file); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getIncludedFiles method, of class DirectoryScanner. +// */ +// @Test +// public void testGetIncludedFiles() { +// System.out.println("getIncludedFiles"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getIncludedFiles(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getIncludedFilesCount method, of class DirectoryScanner. +// */ +// @Test +// public void testGetIncludedFilesCount() { +// System.out.println("getIncludedFilesCount"); +// DirectoryScanner instance = new DirectoryScanner(); +// int expResult = 0; +// int result = instance.getIncludedFilesCount(); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getNotIncludedFiles method, of class DirectoryScanner. +// */ +// @Test +// public void testGetNotIncludedFiles() { +// System.out.println("getNotIncludedFiles"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getNotIncludedFiles(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getExcludedFiles method, of class DirectoryScanner. +// */ +// @Test +// public void testGetExcludedFiles() { +// System.out.println("getExcludedFiles"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getExcludedFiles(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getDeselectedFiles method, of class DirectoryScanner. +// */ +// @Test +// public void testGetDeselectedFiles() { +// System.out.println("getDeselectedFiles"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getDeselectedFiles(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getIncludedDirectories method, of class DirectoryScanner. +// */ +// @Test +// public void testGetIncludedDirectories() { +// System.out.println("getIncludedDirectories"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getIncludedDirectories(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getIncludedDirsCount method, of class DirectoryScanner. +// */ +// @Test +// public void testGetIncludedDirsCount() { +// System.out.println("getIncludedDirsCount"); +// DirectoryScanner instance = new DirectoryScanner(); +// int expResult = 0; +// int result = instance.getIncludedDirsCount(); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getNotIncludedDirectories method, of class DirectoryScanner. +// */ +// @Test +// public void testGetNotIncludedDirectories() { +// System.out.println("getNotIncludedDirectories"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getNotIncludedDirectories(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getExcludedDirectories method, of class DirectoryScanner. +// */ +// @Test +// public void testGetExcludedDirectories() { +// System.out.println("getExcludedDirectories"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getExcludedDirectories(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getDeselectedDirectories method, of class DirectoryScanner. +// */ +// @Test +// public void testGetDeselectedDirectories() { +// System.out.println("getDeselectedDirectories"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getDeselectedDirectories(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getNotFollowedSymlinks method, of class DirectoryScanner. +// */ +// @Test +// public void testGetNotFollowedSymlinks() { +// System.out.println("getNotFollowedSymlinks"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getNotFollowedSymlinks(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of addDefaultExcludes method, of class DirectoryScanner. +// */ +// @Test +// public void testAddDefaultExcludes() { +// System.out.println("addDefaultExcludes"); +// DirectoryScanner instance = new DirectoryScanner(); +// instance.addDefaultExcludes(); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getResource method, of class DirectoryScanner. +// */ +// @Test +// public void testGetResource() { +// System.out.println("getResource"); +// String name = ""; +// DirectoryScanner instance = new DirectoryScanner(); +// Resource expResult = null; +// Resource result = instance.getResource(name); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getScannedDirs method, of class DirectoryScanner. +// */ +// @Test +// public void testGetScannedDirs() { +// System.out.println("getScannedDirs"); +// DirectoryScanner instance = new DirectoryScanner(); +// Set expResult = null; +// Set result = instance.getScannedDirs(); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of ensureNonPatternSetsReady method, of class DirectoryScanner. +// */ +// @Test +// public void testEnsureNonPatternSetsReady() { +// System.out.println("ensureNonPatternSetsReady"); +// DirectoryScanner instance = new DirectoryScanner(); +// instance.ensureNonPatternSetsReady(); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } + +} diff --git a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/ChecksumTest.java b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/ChecksumTest.java index 142448b83..55b17ac6b 100644 --- a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/ChecksumTest.java +++ b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/ChecksumTest.java @@ -20,12 +20,11 @@ package org.owasp.dependencycheck.utils; import java.io.File; import java.io.IOException; import java.security.NoSuchAlgorithmException; -import org.junit.After; -import org.junit.AfterClass; + import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.owasp.dependencycheck.utils.Checksum; import org.owasp.dependencycheck.utils.Checksum; @@ -35,21 +34,8 @@ import org.owasp.dependencycheck.utils.Checksum; */ public class ChecksumTest { - @BeforeClass - public static void setUpClass() throws Exception { - } - - @AfterClass - public static void tearDownClass() throws Exception { - } - - @Before - public void setUp() throws Exception { - } - - @After - public void tearDown() throws Exception { - } + @Rule + public ExpectedException expectedException = ExpectedException.none(); /** * Test of getChecksum method, of class Checksum. @@ -83,13 +69,9 @@ public class ChecksumTest { public void testGetChecksum_FileNotFound() throws Exception { String algorithm = "MD5"; File file = new File("not a valid file"); - boolean exceptionThrown = false; - try { - byte[] result = Checksum.getChecksum(algorithm, file); - } catch (IOException ex) { - exceptionThrown = true; - } - Assert.assertTrue(exceptionThrown); + + expectedException.expect(IOException.class); + Checksum.getChecksum(algorithm, file); } /** @@ -102,13 +84,9 @@ public class ChecksumTest { public void testGetChecksum_NoSuchAlgorithm() throws Exception { String algorithm = "some unknown algorithm"; File file = new File(this.getClass().getClassLoader().getResource("checkSumTest.file").getPath()); - boolean exceptionThrown = false; - try { - byte[] result = Checksum.getChecksum(algorithm, file); - } catch (NoSuchAlgorithmException ex) { - exceptionThrown = true; - } - Assert.assertTrue(exceptionThrown); + + expectedException.expect(NoSuchAlgorithmException.class); + Checksum.getChecksum(algorithm, file); } /** diff --git a/pom.xml b/pom.xml index 078f3ecac..eb48cde22 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long org.owasp dependency-check-parent - 1.2.5 + 1.2.6-SNAPSHOT pom diff --git a/src/main/config/checkstyle-suppressions.xml b/src/main/config/checkstyle-suppressions.xml index 0c5f26ef9..30d198465 100644 --- a/src/main/config/checkstyle-suppressions.xml +++ b/src/main/config/checkstyle-suppressions.xml @@ -6,7 +6,8 @@ - - + + + \ No newline at end of file diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index 72a7b2793..50dc4c451 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -7,7 +7,7 @@ dependent libraries) to identify known vulnerable components. The problem with using known vulnerable components was covered in a paper by Jeff Williams and Arshan Dabirsiaghi titled, "[The Unfortunate Reality of Insecure -Libraries](https://www.aspectsecurity.com/uploads/downloads/2012/03/Aspect-Security-The-Unfortunate-Reality-of-Insecure-Libraries.pdf)". +Libraries](http://www1.contrastsecurity.com/the-unfortunate-reality-of-insecure-libraries?&__hssc=92971330.1.1412763139545&__hstc=92971330.5d71a97ce2c038f53e4109bfd029b71e.1412763139545.1412763139545.1412763139545.1&hsCtaTracking=7bbb964b-eac1-454d-9d5b-cc1089659590%7C816e01cf-4d75-449a-8691-bd0c6f9946a5)" (registration required). The gist of the paper is that we as a development community include third party libraries in our applications that contain well known published vulnerabilities \(such as those at the [National Vulnerability Database](http://web.nvd.nist.gov/view/vuln/search)\).