diff --git a/.gitignore b/.gitignore index b79573ae8..1c55ffdfe 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ .settings maven-eclipse.xml .externalToolBuilders +.pmd # Netbeans configuration nb-configuration.xml /target/ @@ -22,4 +23,4 @@ _site/** #unknown as to why these are showing up... but need to be ignored. .LCKpom.xml~ #coverity -/cov-int/ \ No newline at end of file +/cov-int/ 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 86656529c..74adb411c 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 @@ -252,6 +252,8 @@ public class App { final String suppressionFile = cli.getSuppressionFile(); final boolean jarDisabled = cli.isJarDisabled(); final boolean archiveDisabled = cli.isArchiveDisabled(); + final boolean pyDistDisabled = cli.isPythonDistributionDisabled(); + final boolean pyPkgDisabled = cli.isPythonPackageDisabled(); final boolean assemblyDisabled = cli.isAssemblyDisabled(); final boolean nuspecDisabled = cli.isNuspecDisabled(); final boolean centralDisabled = cli.isCentralDisabled(); @@ -317,6 +319,8 @@ public class App { //File Type Analyzer Settings Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !jarDisabled); Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !archiveDisabled); + Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, !pyDistDisabled); + Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, !pyPkgDisabled); Settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, !nuspecDisabled); Settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, !assemblyDisabled); diff --git a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java index 4d712596e..577a516ee 100644 --- a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java +++ b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java @@ -20,6 +20,7 @@ package org.owasp.dependencycheck; import java.io.File; import java.io.FileNotFoundException; import java.util.logging.Logger; + import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.HelpFormatter; @@ -327,6 +328,12 @@ public final class CliParser { .withDescription("Disable the .NET Assembly Analyzer.") .create(); + final Option disablePythonDistributionAnalyzer = OptionBuilder.withLongOpt(ARGUMENT.DISABLE_PY_DIST) + .withDescription("Disable the Python Distribution Analyzer.").create(); + + final Option disablePythonPackageAnalyzer = OptionBuilder.withLongOpt(ARGUMENT.DISABLE_PY_PKG) + .withDescription("Disable the Python Package Analyzer.").create(); + final Option disableCentralAnalyzer = OptionBuilder.withLongOpt(ARGUMENT.DISABLE_CENTRAL) .withDescription("Disable the Central Analyzer. If this analyzer is disabled it is likely you also want to disable " + "the Nexus Analyzer.") @@ -369,6 +376,8 @@ public final class CliParser { .addOption(disableJarAnalyzer) .addOption(disableArchiveAnalyzer) .addOption(disableAssemblyAnalyzer) + .addOption(disablePythonDistributionAnalyzer) + .addOption(disablePythonPackageAnalyzer) .addOption(disableNuspecAnalyzer) .addOption(disableCentralAnalyzer) .addOption(disableNexusAnalyzer) @@ -458,6 +467,24 @@ public final class CliParser { return (line != null) && line.hasOption(ARGUMENT.DISABLE_ASSEMBLY); } + /** + * Returns true if the disablePyDist command line argument was specified. + * + * @return true if the disablePyDist command line argument was specified; otherwise false + */ + public boolean isPythonDistributionDisabled() { + return (line != null) && line.hasOption(ARGUMENT.DISABLE_PY_DIST); + } + + /** + * Returns true if the disablePyPkg command line argument was specified. + * + * @return true if the disablePyPkg command line argument was specified; otherwise false + */ + public boolean isPythonPackageDisabled() { + return (line != null) && line.hasOption(ARGUMENT.DISABLE_PY_PKG); + } + /** * Returns true if the disableNexus command line argument was specified. * @@ -899,6 +926,14 @@ public final class CliParser { * Disables the Archive Analyzer. */ public static final String DISABLE_ARCHIVE = "disableArchive"; + /** + * Disables the Python Distribution Analyzer. + */ + public static final String DISABLE_PY_DIST = "disablePyDist"; + /** + * Disables the Python Package Analyzer. + */ + public static final String DISABLE_PY_PKG = "disablePyPkg"; /** * Disables the Assembly Analyzer. */ diff --git a/dependency-check-cli/src/site/markdown/arguments.md b/dependency-check-cli/src/site/markdown/arguments.md index 66bcaf68a..cda68ab11 100644 --- a/dependency-check-cli/src/site/markdown/arguments.md +++ b/dependency-check-cli/src/site/markdown/arguments.md @@ -23,6 +23,8 @@ Short | Argument Name        | Paramete -------|-----------------------|-----------------|----------------------------------------------------------------------------------|------------------- \-P | \-\-propertyfile | \ | Specifies a file that contains properties to use instead of applicaion defaults. |   | \-\-updateonly | | If set only the update phase of dependency-check will be executed; no scan will be executed and no report will be generated. |   + | \-\-disablePyDist | | Sets whether the Python Distribution Analyzer will be used. | false + | \-\-disablePyPkg | | Sets whether the Python Package Analyzer will be used. | false | \-\-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 the Jar Analyzer will be used. | false diff --git a/dependency-check-core/pom.xml b/dependency-check-core/pom.xml index 8e1de4e65..b04cbe3ab 100644 --- a/dependency-check-core/pom.xml +++ b/dependency-check-core/pom.xml @@ -541,6 +541,11 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. provided true + + com.sun.mail + mailapi + 1.5.2 + 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 ed383080d..d28f4ca2c 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 @@ -318,16 +318,17 @@ public class Engine { return null; } final String fileName = file.getName(); - final String extension = FileUtils.getFileExtension(fileName); + String extension = FileUtils.getFileExtension(fileName); + if (null == extension) { + extension = fileName; + } Dependency dependency = null; - if (extension != null) { - if (supportsExtension(extension)) { - dependency = new Dependency(file); - dependencies.add(dependency); + if (supportsExtension(extension)) { + dependency = new Dependency(file); + if (extension == fileName){ + dependency.setFileExtension(extension); } - } else { - final String msg = String.format("No file extension found on file '%s'. The file was not analyzed.", file.toString()); - LOGGER.log(Level.FINE, msg); + dependencies.add(dependency); } return dependency; } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzer.java new file mode 100644 index 000000000..635e1f7fd --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzer.java @@ -0,0 +1,344 @@ +/* + * This file is part of dependency-check-core. + * + * 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. + * + * Copyright (c) 2015 Institute for Defense Analyses. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FilenameFilter; +import java.net.MalformedURLException; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +import javax.mail.MessagingException; +import javax.mail.internet.InternetHeaders; + +import org.apache.commons.io.filefilter.NameFileFilter; +import org.apache.commons.io.filefilter.SuffixFileFilter; +import org.apache.commons.io.input.AutoCloseInputStream; +import org.apache.commons.lang.StringUtils; +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.dependency.Confidence; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.EvidenceCollection; +import org.owasp.dependencycheck.utils.ExtractionException; +import org.owasp.dependencycheck.utils.ExtractionUtil; +import org.owasp.dependencycheck.utils.FileUtils; +import org.owasp.dependencycheck.utils.Settings; +import org.owasp.dependencycheck.utils.UrlStringUtils; + +/** + * Used to analyze a Wheel or egg distriution files, or their contents in + * unzipped form, and collect information that can be used to determine the + * associated CPE. + * + * @author Dale Visser + */ +public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer { + + /** + * Name of egg metatdata files to analyze. + */ + private static final String PKG_INFO = "PKG-INFO"; + + /** + * Name of wheel metadata files to analyze. + */ + private static final String METADATA = "METADATA"; + + /** + * The logger. + */ + private static final Logger LOGGER = Logger + .getLogger(PythonDistributionAnalyzer.class.getName()); + + /** + * The count of directories created during analysis. This is used for + * creating temporary directories. + */ + private static int dirCount = 0; + + /** + * The name of the analyzer. + */ + private static final String ANALYZER_NAME = "Python Distribution Analyzer"; + /** + * The phase that this analyzer is intended to run in. + */ + private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION; + + /** + * The set of file extensions supported by this analyzer. + */ + private static final Set EXTENSIONS = newHashSet("whl", "egg", + "zip", METADATA, PKG_INFO); + + /** + * Used to match on egg archive candidate extenssions. + */ + private static final Pattern EGG_OR_ZIP = Pattern.compile("egg|zip"); + + /** + * The parent directory for the individual directories per archive. + */ + private File tempFileLocation; + + /** + * Filter that detects *.dist-info files (but doesn't verify they are + * directories. + */ + private static final FilenameFilter DIST_INFO_FILTER = new SuffixFileFilter( + ".dist-info"); + + /** + * Filter that detects files named "METADATA". + */ + private static final FilenameFilter EGG_INFO_FILTER = new NameFileFilter( + "EGG-INFO"); + + /** + * Filter that detects files named "METADATA". + */ + private static final FilenameFilter METADATA_FILTER = new NameFileFilter( + METADATA); + + /** + * Filter that detects files named "PKG-INFO". + */ + private static final FilenameFilter PKG_INFO_FILTER = new NameFileFilter( + PKG_INFO); + + /** + * Returns a list of file EXTENSIONS supported by this analyzer. + * + * @return a list of file EXTENSIONS supported by this analyzer. + */ + @Override + public Set getSupportedExtensions() { + return EXTENSIONS; + } + + /** + * Returns the name of the analyzer. + * + * @return the name of the analyzer. + */ + @Override + public String getName() { + return ANALYZER_NAME; + } + + /** + * Returns the phase that the analyzer is intended to run in. + * + * @return the phase that the analyzer is intended to run in. + */ + public AnalysisPhase getAnalysisPhase() { + return ANALYSIS_PHASE; + } + + /** + * Returns the key used in the properties file to reference the analyzer's + * enabled property. + * + * @return the analyzer's enabled property setting key + */ + @Override + protected String getAnalyzerEnabledSettingKey() { + return Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED; + } + + @Override + protected void analyzeFileType(Dependency dependency, Engine engine) + throws AnalysisException { + if ("whl".equals(dependency.getFileExtension())) { + collectMetadataFromArchiveFormat(dependency, DIST_INFO_FILTER, + METADATA_FILTER); + } else if (EGG_OR_ZIP.matcher( + StringUtils.stripToEmpty(dependency.getFileExtension())) + .matches()) { + collectMetadataFromArchiveFormat(dependency, EGG_INFO_FILTER, + PKG_INFO_FILTER); + } else { + final File actualFile = dependency.getActualFile(); + final String name = actualFile.getName(); + final boolean metadata = METADATA.equals(name); + if (metadata || PKG_INFO.equals(name)) { + final File parent = actualFile.getParentFile(); + final String parentName = parent.getName(); + dependency.setDisplayFileName(parentName + "/" + name); + if (parent.isDirectory() + && (metadata && parentName.endsWith(".dist-info") + || parentName.endsWith(".egg-info") || "EGG-INFO" + .equals(parentName))) { + collectWheelMetadata(dependency, actualFile); + } + } + } + } + + private void collectMetadataFromArchiveFormat(Dependency dependency, + FilenameFilter folderFilter, FilenameFilter metadataFilter) + throws AnalysisException { + final File temp = getNextTempDirectory(); + LOGGER.fine(String.format("%s exists? %b", temp, temp.exists())); + try { + ExtractionUtil.extractFilesUsingFilter( + new File(dependency.getActualFilePath()), temp, + metadataFilter); + } catch (ExtractionException ex) { + throw new AnalysisException(ex); + } + + collectWheelMetadata( + dependency, + getMatchingFile(getMatchingFile(temp, folderFilter), + metadataFilter)); + } + + /** + * Makes sure a usable temporary directory is available. + */ + @Override + protected void initializeFileTypeAnalyzer() throws Exception { + final File baseDir = Settings.getTempDirectory(); + tempFileLocation = File.createTempFile("check", "tmp", baseDir); + if (!tempFileLocation.delete()) { + final String msg = String.format( + "Unable to delete temporary file '%s'.", + tempFileLocation.getAbsolutePath()); + throw new AnalysisException(msg); + } + if (!tempFileLocation.mkdirs()) { + final String msg = String.format( + "Unable to create directory '%s'.", + tempFileLocation.getAbsolutePath()); + throw new AnalysisException(msg); + } + } + + /** + * Deletes any files extracted from the Wheel during analysis. + */ + @Override + public void close() { + if (tempFileLocation != null && tempFileLocation.exists()) { + LOGGER.log(Level.FINE, "Attempting to delete temporary files"); + final boolean success = FileUtils.delete(tempFileLocation); + if (!success) { + LOGGER.log(Level.WARNING, + "Failed to delete some temporary files, see the log for more details"); + } + } + } + + /** + * Gathers evidence from the METADATA file. + * + * @param dependency + * the dependency being analyzed + * @throws MalformedURLException + */ + private static void collectWheelMetadata(Dependency dependency, File file) + throws AnalysisException { + final InternetHeaders headers = getManifestProperties(file); + addPropertyToEvidence(headers, dependency.getVersionEvidence(), + "Version", Confidence.HIGHEST); + addPropertyToEvidence(headers, dependency.getProductEvidence(), "Name", + Confidence.HIGHEST); + final String url = headers.getHeader("Home-page", null); + final EvidenceCollection vendorEvidence = dependency + .getVendorEvidence(); + if (StringUtils.isNotBlank(url)) { + if (UrlStringUtils.isUrl(url)) { + vendorEvidence.addEvidence(METADATA, "vendor", url, + Confidence.MEDIUM); + } + } + addPropertyToEvidence(headers, vendorEvidence, "Author", Confidence.LOW); + final String summary = headers.getHeader("Summary", null); + if (StringUtils.isNotBlank(summary)) { + JarAnalyzer + .addDescription(dependency, summary, METADATA, "summary"); + } + } + + private static void addPropertyToEvidence(InternetHeaders headers, + EvidenceCollection evidence, String property, Confidence confidence) { + final String value = headers.getHeader(property, null); + LOGGER.fine(String.format("Property: %s, Value: %s\n", property, value)); + if (StringUtils.isNotBlank(value)) { + evidence.addEvidence(METADATA, property, value, confidence); + } + } + + private static final File getMatchingFile(File folder, FilenameFilter filter) { + File result = null; + final File[] matches = folder.listFiles(filter); + if (null != matches && 1 == matches.length) { + result = matches[0]; + } + return result; + } + + private static InternetHeaders getManifestProperties(File manifest) { + final InternetHeaders result = new InternetHeaders(); + if (null == manifest) { + LOGGER.fine("Manifest file not found."); + } else { + try { + result.load(new AutoCloseInputStream(new BufferedInputStream( + new FileInputStream(manifest)))); + } catch (MessagingException e) { + LOGGER.log(Level.WARNING, e.getMessage(), e); + } catch (FileNotFoundException e) { + LOGGER.log(Level.WARNING, e.getMessage(), e); + } + } + return result; + } + + /** + * Retrieves the next temporary destingation directory for extracting an + * archive. + * + * @return a directory + * @throws AnalysisException + * thrown if unable to create temporary directory + */ + private File getNextTempDirectory() throws AnalysisException { + File directory; + + // getting an exception for some directories not being able to be + // created; might be because the directory already exists? + do { + dirCount += 1; + directory = new File(tempFileLocation, String.valueOf(dirCount)); + } while (directory.exists()); + if (!directory.mkdirs()) { + throw new AnalysisException(String.format( + "Unable to create temp directory '%s'.", + directory.getAbsolutePath())); + } + return directory; + } +} \ No newline at end of file diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonPackageAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonPackageAnalyzer.java new file mode 100644 index 000000000..78d5e8c61 --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonPackageAnalyzer.java @@ -0,0 +1,284 @@ +/* + * This file is part of dependency-check-core. + * + * 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. + * + * Copyright (c) 2015 Institute for Defense Analyses. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.NameFileFilter; +import org.apache.commons.io.filefilter.SuffixFileFilter; +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.dependency.Confidence; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.EvidenceCollection; +import org.owasp.dependencycheck.utils.Settings; +import org.owasp.dependencycheck.utils.UrlStringUtils; + +/** + * Used to analyze a Python package, and collect information that can be used to + * determine the associated CPE. + * + * @author Dale Visser + */ +public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer { + + /** + * Used when compiling file scanning regex patterns. + */ + private static final int REGEX_OPTIONS = Pattern.DOTALL + | Pattern.CASE_INSENSITIVE; + + /** + * The logger. + */ + private static final Logger LOGGER = Logger + .getLogger(PythonDistributionAnalyzer.class.getName()); + + /** + * Filename extensions for files to be analyzed. + */ + private static final Set EXTENSIONS = Collections + .unmodifiableSet(Collections.singleton("py")); + + /** + * Pattern for matching the module docstring in a source file. + */ + private static final Pattern MODULE_DOCSTRING = Pattern.compile( + "^(['\\\"]{3})(.*?)\\1", REGEX_OPTIONS); + + /** + * Matches assignments to version variables in Python source code. + */ + private static final Pattern VERSION_PATTERN = Pattern.compile( + "\\b(__)?version(__)? *= *(['\"]+)(\\d+\\.\\d+.*?)\\3", + REGEX_OPTIONS); + + /** + * Matches assignments to title variables in Python source code. + */ + private static final Pattern TITLE_PATTERN = compileAssignPattern("title"); + + /** + * Matches assignments to summary variables in Python source code. + */ + private static final Pattern SUMMARY_PATTERN = compileAssignPattern("summary"); + + /** + * Matches assignments to URL/URL variables in Python source code. + */ + private static final Pattern URI_PATTERN = compileAssignPattern("ur[il]"); + + /** + * Matches assignments to home page variables in Python source code. + */ + private static final Pattern HOMEPAGE_PATTERN = compileAssignPattern("home_?page"); + + /** + * Matches assignments to author variables in Python source code. + */ + private static final Pattern AUTHOR_PATTERN = compileAssignPattern("author"); + + /** + * Filter that detects files named "__init__.py". + */ + private static final FileFilter INIT_PY_FILTER = new NameFileFilter( + "__init__.py"); + + private static final FileFilter PY_FILTER = new SuffixFileFilter(".py"); + + /** + * Returns the name of the Python Package Analyzer. + */ + @Override + public String getName() { + return "Python Package Analyzer"; + } + + /** + * Tell that we are used for information collection. + */ + @Override + public AnalysisPhase getAnalysisPhase() { + return AnalysisPhase.INFORMATION_COLLECTION; + } + + /** + * Return the set of supported file extensions. + */ + @Override + protected Set getSupportedExtensions() { + return EXTENSIONS; + } + + /** + * No-op initializer implementation. + */ + @Override + protected void initializeFileTypeAnalyzer() throws Exception { + // Nothing to do here. + } + + private static Pattern compileAssignPattern(String name) { + return Pattern.compile( + String.format("\\b(__)?%s(__)?\\b *= *(['\"]+)(.*?)\\3", name), + REGEX_OPTIONS); + } + + @Override + protected void analyzeFileType(Dependency dependency, Engine engine) + throws AnalysisException { + final File file = dependency.getActualFile(); + final File parent = file.getParentFile(); + final String parentName = parent.getName(); + boolean found = false; + if (INIT_PY_FILTER.accept(file)) { + for (final File sourcefile : parent.listFiles(PY_FILTER)) { + found |= analyzeFileContents(dependency, sourcefile); + } + } + if (found) { + dependency.setDisplayFileName(parentName + "/__init__.py"); + dependency.getProductEvidence().addEvidence(file.getName(), + "PackageName", parentName, Confidence.MEDIUM); + } else { + // copy, alter and set in case some other thread is iterating over + final List deps = new ArrayList( + engine.getDependencies()); + deps.remove(dependency); + engine.setDependencies(deps); + } + } + + /** + * This should gather information from leading docstrings, file comments, + * and assignments to __version__, __title__, __summary__, __uri__, __url__, + * __home*page__, __author__, and their all caps equivalents. + * + * @return whether evidence was found + */ + private boolean analyzeFileContents(Dependency dependency, File file) + throws AnalysisException { + String contents = ""; + try { + contents = FileUtils.readFileToString(file).trim(); + } catch (IOException e) { + throw new AnalysisException( + "Problem occured while reading dependency file.", e); + } + boolean found = false; + if (!contents.isEmpty()) { + final String source = file.getName(); + found = gatherEvidence(VERSION_PATTERN, contents, source, + dependency.getVersionEvidence(), "SourceVersion", + Confidence.MEDIUM); + found |= addSummaryInfo(dependency, SUMMARY_PATTERN, 4, contents, + source, "summary"); + if (INIT_PY_FILTER.accept(file)) { + found |= addSummaryInfo(dependency, MODULE_DOCSTRING, 2, + contents, source, "docstring"); + } + found |= gatherEvidence(TITLE_PATTERN, contents, source, + dependency.getProductEvidence(), "SourceTitle", + Confidence.LOW); + final EvidenceCollection vendorEvidence = dependency + .getVendorEvidence(); + found |= gatherEvidence(AUTHOR_PATTERN, contents, source, + vendorEvidence, "SourceAuthor", Confidence.MEDIUM); + try { + found |= gatherHomePageEvidence(URI_PATTERN, vendorEvidence, + source, "URL", contents); + found |= gatherHomePageEvidence(HOMEPAGE_PATTERN, + vendorEvidence, source, "HomePage", contents); + } catch (MalformedURLException e) { + LOGGER.warning(e.getMessage()); + } + } + return found; + } + + private boolean addSummaryInfo(Dependency dependency, Pattern pattern, + int group, String contents, String source, String key) { + final Matcher matcher = pattern.matcher(contents); + final boolean found = matcher.find(); + if (found) { + JarAnalyzer.addDescription(dependency, matcher.group(group), + source, key); + } + return found; + } + + private boolean gatherHomePageEvidence(Pattern pattern, + EvidenceCollection evidence, String source, String name, + String contents) throws MalformedURLException { + final Matcher matcher = pattern.matcher(contents); + boolean found = false; + if (matcher.find()) { + final String url = matcher.group(4); + if (UrlStringUtils.isUrl(url)) { + found = true; + evidence.addEvidence(source, name, url, Confidence.MEDIUM); + } + } + return found; + } + + /** + * Gather evidence from a Python source file usin the given string + * assignment regex pattern. + * + * @param pattern + * to scan contents with + * @param contents + * of Python source file + * @param source + * for storing evidence + * @param evidence + * to store evidence in + * @param name + * of evidence + * @param confidence + * in evidence + * @return whether evidence was found + */ + private boolean gatherEvidence(Pattern pattern, String contents, + String source, EvidenceCollection evidence, String name, + Confidence confidence) { + final Matcher matcher = pattern.matcher(contents); + final boolean found = matcher.find(); + if (found) { + evidence.addEvidence(source, name, matcher.group(4), confidence); + } + return found; + } + + @Override + protected String getAnalyzerEnabledSettingKey() { + return Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED; + } +} \ No newline at end of file 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 d3f88f257..3174680e3 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 @@ -222,7 +222,7 @@ public class Dependency implements Serializable, Comparable { } /** - * Sets the file name of the dependency. + * Sets the file extension of the dependency. * * @param fileExtension the file name of the dependency */ diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/ExtractionUtil.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/ExtractionUtil.java index 20346df96..e10651076 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/ExtractionUtil.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/ExtractionUtil.java @@ -17,19 +17,29 @@ */ package org.owasp.dependencycheck.utils; +import static org.owasp.dependencycheck.utils.FileUtils.getFileExtension; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; +import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FilenameFilter; import java.io.IOException; +import java.io.InputStream; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; + +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; import org.owasp.dependencycheck.Engine; -import static org.owasp.dependencycheck.utils.FileUtils.getFileExtension; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.analyzer.exception.ArchiveExtractionException; /** * @@ -106,12 +116,7 @@ public final class ExtractionUtil { try { fos = new FileOutputStream(file); bos = new BufferedOutputStream(fos, BUFFER_SIZE); - int count; - final byte[] data = new byte[BUFFER_SIZE]; - while ((count = zis.read(data, 0, BUFFER_SIZE)) != -1) { - bos.write(data, 0, count); - } - bos.flush(); + transferUsingBuffer(zis, bos); } catch (FileNotFoundException ex) { LOGGER.log(Level.FINE, null, ex); final String msg = String.format("Unable to find file '%s'.", file.getName()); @@ -121,13 +126,7 @@ public final class ExtractionUtil { final String msg = String.format("IO Exception while parsing file '%s'.", file.getName()); throw new ExtractionException(msg, ex); } finally { - if (bos != null) { - try { - bos.close(); - } catch (IOException ex) { - LOGGER.log(Level.FINEST, null, ex); - } - } + closeStream(bos); } } } @@ -137,11 +136,157 @@ public final class ExtractionUtil { LOGGER.log(Level.FINE, msg, ex); throw new ExtractionException(msg, ex); } finally { - try { - zis.close(); - } catch (IOException ex) { - LOGGER.log(Level.FINEST, null, ex); - } + closeStream(zis); } } + + /** + * Extracts the contents of an archive into the specified directory. + * + * @param archive + * an archive file such as a WAR or EAR + * @param destination + * a directory to extract the contents to + * @param filter + * determines which files get extracted + * @throws ExtractionException + * thrown if the archive is not found + */ + public static void extractFilesUsingFilter(File archive, File destination, + FilenameFilter filter) throws ExtractionException { + if (archive == null || destination == null) { + return; + } + + FileInputStream fis = null; + try { + fis = new FileInputStream(archive); + } catch (FileNotFoundException ex) { + LOGGER.log(Level.FINE, null, ex); + throw new ExtractionException("Archive file was not found.", ex); + } + try { + extractArchive(new ZipArchiveInputStream(new BufferedInputStream( + fis)), destination, filter); + } catch (ArchiveExtractionException ex) { + final String msg = String.format( + "Exception extracting archive '%s'.", archive.getName()); + LOGGER.log(Level.WARNING, msg); + LOGGER.log(Level.FINE, null, ex); + } finally { + try { + fis.close(); + } catch (IOException ex) { + LOGGER.log(Level.FINE, null, ex); + } + } + } + + /** + * Extracts files from an archive. + * + * @param input + * the archive to extract files from + * @param destination + * the location to write the files too + * @param filter + * determines which files get extracted + * @throws ArchiveExtractionException + * thrown if there is an exception extracting files from the + * archive + */ + private static void extractArchive(ArchiveInputStream input, + File destination, FilenameFilter filter) + throws ArchiveExtractionException { + ArchiveEntry entry; + try { + while ((entry = input.getNextEntry()) != null) { + if (entry.isDirectory()) { + final File dir = new File(destination, entry.getName()); + if (!dir.exists()) { + if (!dir.mkdirs()) { + final String msg = String.format( + "Unable to create directory '%s'.", + dir.getAbsolutePath()); + throw new AnalysisException(msg); + } + } + } else { + extractFile(input, destination, filter, entry); + } + } + } catch (IOException ex) { + throw new ArchiveExtractionException(ex); + } catch (Throwable ex) { + throw new ArchiveExtractionException(ex); + } finally { + closeStream(input); + } + } + + private static void extractFile(ArchiveInputStream input, File destination, + FilenameFilter filter, ArchiveEntry entry) throws ExtractionException { + final File file = new File(destination, entry.getName()); + if (filter.accept(file.getParentFile(), file.getName())) { + final String extracting = String.format("Extracting '%s'", + file.getPath()); + LOGGER.fine(extracting); + BufferedOutputStream bos = null; + FileOutputStream fos = null; + try { + createParentFile(file); + fos = new FileOutputStream(file); + bos = new BufferedOutputStream(fos, BUFFER_SIZE); + transferUsingBuffer(input, bos); + } catch (FileNotFoundException ex) { + LOGGER.log(Level.FINE, null, ex); + final String msg = String.format("Unable to find file '%s'.", + file.getName()); + throw new ExtractionException(msg, ex); + } catch (IOException ex) { + LOGGER.log(Level.FINE, null, ex); + final String msg = String + .format("IO Exception while parsing file '%s'.", + file.getName()); + throw new ExtractionException(msg, ex); + } finally { + closeStream(bos); + closeStream(fos); + } + } + } + + private static void transferUsingBuffer(InputStream input, + BufferedOutputStream bos) throws IOException { + int count; + final byte[] data = new byte[BUFFER_SIZE]; + while ((count = input.read(data, 0, BUFFER_SIZE)) != -1) { + bos.write(data, 0, count); + } + bos.flush(); + } + + private static void closeStream(Closeable stream) { + if (stream != null) { + try { + stream.close(); + } catch (IOException ex) { + LOGGER.log(Level.FINEST, null, ex); + } + } + } + + private static void createParentFile(final File file) + throws ExtractionException { + final File parent = file.getParentFile(); + if (!parent.isDirectory()) { + if (!parent.mkdirs()) { + final String msg = String.format( + "Unable to build directory '%s'.", + parent.getAbsolutePath()); + throw new ExtractionException(msg); + } + } + } + } 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 f2c4509c0..70e1f6d7f 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 @@ -11,4 +11,6 @@ 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 +org.owasp.dependencycheck.analyzer.AssemblyAnalyzer +org.owasp.dependencycheck.analyzer.PythonDistributionAnalyzer +org.owasp.dependencycheck.analyzer.PythonPackageAnalyzer \ 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 6b117e543..97d284f4f 100644 --- a/dependency-check-core/src/main/resources/dependencycheck-base-suppression.xml +++ b/dependency-check-core/src/main/resources/dependencycheck-base-suppression.xml @@ -83,5 +83,21 @@ org\.opensaml:xmltooling:.* cpe:/a:internet2:opensaml - + + + .*(\.(whl|egg)|\b(site|dist)-packages\b.*) + cpe:/a:python:python + cpe:/a:python_software_foundation:python + cpe:/a:class:class + cpe:/a:file:file + cpe:/a:gnupg:gnupg + cpe:/a:mongodb:mongodb + cpe:/a:mozilla:mozilla + cpe:/a:openssl:openssl + cpe:/a:sendfile:sendfile + cpe:/a:sendmail:sendmail + cpe:/a:yacc:yacc + \ No newline at end of file diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzerTest.java new file mode 100644 index 000000000..1d2afa668 --- /dev/null +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzerTest.java @@ -0,0 +1,165 @@ +/* + * This file is part of dependency-check-core. + * + * 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. + * + * Copyright (c) 2015 Institute for Defense Analyses. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; + +import org.apache.commons.lang.StringUtils; +import org.junit.Test; +import org.owasp.dependencycheck.BaseTest; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.Evidence; + +/** + * Unit tests for PythonDistributionAnalyzer. + * + * @author Dale Visser + */ +public class PythonDistributionAnalyzerTest extends BaseTest { + + /** + * Test of getName method, of class PythonDistributionAnalyzer. + */ + @Test + public void testGetName() { + assertEquals("Analyzer name wrong.", "Python Distribution Analyzer", + new PythonDistributionAnalyzer().getName()); + } + + /** + * Test of getSupportedExtensions method, of class + * PythonDistributionAnalyzer. + */ + @Test + public void testGetSupportedExtensions() { + final String[] expected = { "whl", "egg", "zip", "METADATA", "PKG-INFO" }; + assertEquals("Supported extensions should just have the following: " + + StringUtils.join(expected, ", "), + new HashSet(Arrays.asList(expected)), + new PythonDistributionAnalyzer().getSupportedExtensions()); + } + + /** + * Test of supportsExtension method, of class PythonDistributionAnalyzer. + */ + @Test + public void testSupportsExtension() { + final PythonDistributionAnalyzer analyzer = new PythonDistributionAnalyzer(); + assertTrue("Should support \"whl\" extension.", + analyzer.supportsExtension("whl")); + assertTrue("Should support \"egg\" extension.", + analyzer.supportsExtension("egg")); + assertTrue("Should support \"zip\" extension.", + analyzer.supportsExtension("zip")); + assertTrue("Should support \"METADATA\" extension.", + analyzer.supportsExtension("METADATA")); + assertTrue("Should support \"PKG-INFO\" extension.", + analyzer.supportsExtension("PKG-INFO")); + } + + /** + * Test of inspect method, of class PythonDistributionAnalyzer. + * + * @throws Exception + * is thrown when an exception occurs. + */ + @Test + public void testAnalyzeWheel() throws AnalysisException { + djangoAssertions(new Dependency(BaseTest.getResourceAsFile(this, + "python/Django-1.7.2-py2.py3-none-any.whl"))); + } + + /** + * Test of inspect method, of class PythonDistributionAnalyzer. + * + * @throws Exception + * is thrown when an exception occurs. + */ + @Test + public void testAnalyzeSitePackage() throws AnalysisException { + final Dependency result = new Dependency(BaseTest.getResourceAsFile( + this, "python/site-packages/Django-1.7.2.dist-info/METADATA")); + djangoAssertions(result); + assertEquals("Django-1.7.2.dist-info/METADATA", + result.getDisplayFileName()); + } + + private void djangoAssertions(final Dependency result) + throws AnalysisException { + new PythonDistributionAnalyzer().analyze(result, null); + assertTrue("Expected vendor evidence to contain \"djangoproject\".", + result.getVendorEvidence().toString().contains("djangoproject")); + boolean found = false; + for (final Evidence e : result.getVersionEvidence()) { + if ("Version".equals(e.getName()) && "1.7.2".equals(e.getValue())) { + found = true; + break; + } + } + assertTrue("Version 1.7.2 not found in Django dependency.", found); + } + + @Test + public void testAnalyzeEggInfoFolder() throws AnalysisException { + eggtestAssertions(this, + "python/site-packages/EggTest.egg-info/PKG-INFO", + new PythonDistributionAnalyzer()); + } + + @Test + public void testAnalyzeEggArchive() throws AnalysisException { + eggtestAssertions(this, "python/dist/EggTest-0.0.1-py2.7.egg", + new PythonDistributionAnalyzer()); + } + + @Test + public void testAnalyzeEggArchiveNamedZip() throws AnalysisException { + eggtestAssertions(this, "python/dist/EggTest-0.0.1-py2.7.zip", + new PythonDistributionAnalyzer()); + } + + @Test + public void testAnalyzeEggFolder() throws AnalysisException { + eggtestAssertions( + this, + "python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/PKG-INFO", + new PythonDistributionAnalyzer()); + } + + public static void eggtestAssertions(Object context, final String resource, + Analyzer analyzer) throws AnalysisException { + final Dependency result = new Dependency(BaseTest.getResourceAsFile( + context, resource)); + analyzer.analyze(result, null); + assertTrue("Expected vendor evidence to contain \"example\".", result + .getVendorEvidence().toString().contains("example")); + boolean found = false; + for (final Evidence e : result.getVersionEvidence()) { + if ("0.0.1".equals(e.getValue())) { + found = true; + break; + } + } + assertTrue("Version 0.0.1 not found in EggTest dependency.", found); + } +} \ No newline at end of file diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/PythonPackageAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/PythonPackageAnalyzerTest.java new file mode 100644 index 000000000..d6a7ad425 --- /dev/null +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/PythonPackageAnalyzerTest.java @@ -0,0 +1,73 @@ +/* + * This file is part of dependency-check-core. + * + * 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. + * + * Copyright (c) 2015 Institute for Defense Analyses. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; + +import org.apache.commons.lang.StringUtils; +import org.junit.Test; +import org.owasp.dependencycheck.BaseTest; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; + +/** + * Unit tests for PythonPackageAnalyzer. + * + * @author Dale Visser + */ +public class PythonPackageAnalyzerTest extends BaseTest { + + /** + * Test of getName method, of class PythonPackageAnalyzer. + */ + @Test + public void testGetName() { + assertEquals("Analyzer name wrong.", "Python Distribution Analyzer", + new PythonDistributionAnalyzer().getName()); + } + + /** + * Test of getSupportedExtensions method, of class PythonPackageAnalyzer. + */ + @Test + public void testGetSupportedExtensions() { + final String[] expected = { "py" }; + assertEquals("Supported extensions should just have the following: " + + StringUtils.join(expected, ", "), + new HashSet(Arrays.asList(expected)), + new PythonPackageAnalyzer().getSupportedExtensions()); + } + + /** + * Test of supportsExtension method, of class PythonPackageAnalyzer. + */ + @Test + public void testSupportsExtension() { + assertTrue("Should support \"py\" extension.", + new PythonPackageAnalyzer().supportsExtension("py")); + } + + @Test + public void testAnalyzeSourceMetadata() throws AnalysisException { + PythonDistributionAnalyzerTest.eggtestAssertions(this, + "python/eggtest/__init__.py", new PythonPackageAnalyzer()); + } +} \ No newline at end of file diff --git a/dependency-check-core/src/test/resources/python/Django-1.7.2-py2.py3-none-any.whl b/dependency-check-core/src/test/resources/python/Django-1.7.2-py2.py3-none-any.whl new file mode 100644 index 000000000..a06b3683a Binary files /dev/null and b/dependency-check-core/src/test/resources/python/Django-1.7.2-py2.py3-none-any.whl differ diff --git a/dependency-check-core/src/test/resources/python/dist/EggTest-0.0.1-py2.7.egg b/dependency-check-core/src/test/resources/python/dist/EggTest-0.0.1-py2.7.egg new file mode 100644 index 000000000..cddfe7954 Binary files /dev/null and b/dependency-check-core/src/test/resources/python/dist/EggTest-0.0.1-py2.7.egg differ diff --git a/dependency-check-core/src/test/resources/python/eggtest/__about__.py b/dependency-check-core/src/test/resources/python/eggtest/__about__.py new file mode 100644 index 000000000..b34ad23d0 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/eggtest/__about__.py @@ -0,0 +1,9 @@ +__all__ = ["__title__", "__summary__", "__uri__", "__version__", "__author__", + "__email__" ] + +__title__ = "EggTest" +__summary__ = "Simple project for producing an .egg." +__uri__ = "http://example.org/eggtest" +__version__ = "0.0.1" +__author__ = "Dale Visser" +__email__ = "dvisser@ida.org" \ No newline at end of file diff --git a/dependency-check-core/src/test/resources/python/eggtest/__init__.py b/dependency-check-core/src/test/resources/python/eggtest/__init__.py new file mode 100644 index 000000000..8bc561672 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/eggtest/__init__.py @@ -0,0 +1 @@ +from eggtest import main diff --git a/dependency-check-core/src/test/resources/python/eggtest/__init__.pyc b/dependency-check-core/src/test/resources/python/eggtest/__init__.pyc new file mode 100644 index 000000000..b469286ca Binary files /dev/null and b/dependency-check-core/src/test/resources/python/eggtest/__init__.pyc differ diff --git a/dependency-check-core/src/test/resources/python/eggtest/main.py b/dependency-check-core/src/test/resources/python/eggtest/main.py new file mode 100644 index 000000000..69d2d2985 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/eggtest/main.py @@ -0,0 +1 @@ +print 'Hello from eggtest!' diff --git a/dependency-check-core/src/test/resources/python/eggtest/main.pyc b/dependency-check-core/src/test/resources/python/eggtest/main.pyc new file mode 100644 index 000000000..b499669c6 Binary files /dev/null and b/dependency-check-core/src/test/resources/python/eggtest/main.pyc differ diff --git a/dependency-check-core/src/test/resources/python/setup.py b/dependency-check-core/src/test/resources/python/setup.py new file mode 100644 index 000000000..926587605 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/setup.py @@ -0,0 +1,11 @@ +from setuptools import setup + +about = {} +execfile('eggtest/__about__.py', about) +setup(name = about['__title__'], + packages = ['eggtest'], + version = about['__version__'], + description = about['__summary__'], + url = about['__uri__'], + author = about['__author__'], + author_email = about['__email__']) \ No newline at end of file diff --git a/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.data/scripts/django-admin.py b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.data/scripts/django-admin.py new file mode 100755 index 000000000..8648efa78 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.data/scripts/django-admin.py @@ -0,0 +1,5 @@ +#!python +from django.core import management + +if __name__ == "__main__": + management.execute_from_command_line() diff --git a/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/DESCRIPTION.rst b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/DESCRIPTION.rst new file mode 100644 index 000000000..e1187231a --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/DESCRIPTION.rst @@ -0,0 +1,3 @@ +UNKNOWN + + diff --git a/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/LICENSE.txt b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/LICENSE.txt new file mode 100644 index 000000000..5f4f225dd --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/LICENSE.txt @@ -0,0 +1,27 @@ +Copyright (c) Django Software Foundation and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of Django nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/METADATA b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/METADATA new file mode 100644 index 000000000..839abfe94 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/METADATA @@ -0,0 +1,31 @@ +Metadata-Version: 2.0 +Name: Django +Version: 1.7.2 +Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design. +Home-page: http://www.djangoproject.com/ +Author: Django Software Foundation +Author-email: foundation@djangoproject.com +License: BSD +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: Django +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Classifier: Topic :: Software Development :: Libraries :: Python Modules + +UNKNOWN + + diff --git a/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/WHEEL b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/WHEEL new file mode 100644 index 000000000..9dff69d86 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.24.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/entry_points.txt b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/entry_points.txt new file mode 100644 index 000000000..22df67eba --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +django-admin = django.core.management:execute_from_command_line + diff --git a/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/metadata.json b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/metadata.json new file mode 100644 index 000000000..7e0009efb --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/metadata.json @@ -0,0 +1 @@ +{"license": "BSD", "name": "Django", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "summary": "A high-level Python Web framework that encourages rapid development and clean, pragmatic design.", "version": "1.7.2", "extensions": {"python.details": {"project_urls": {"Home": "http://www.djangoproject.com/"}, "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "contacts": [{"role": "author", "email": "foundation@djangoproject.com", "name": "Django Software Foundation"}]}, "python.commands": {"wrap_console": {"django-admin": "django.core.management:execute_from_command_line"}}, "python.exports": {"console_scripts": {"django-admin": "django.core.management:execute_from_command_line"}}}, "classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Internet :: WWW/HTTP :: WSGI", "Topic :: Software Development :: Libraries :: Application Frameworks", "Topic :: Software Development :: Libraries :: Python Modules"]} \ No newline at end of file diff --git a/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/top_level.txt b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/top_level.txt new file mode 100644 index 000000000..d3e4ba564 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/Django-1.7.2.dist-info/top_level.txt @@ -0,0 +1 @@ +django diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/PKG-INFO b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/PKG-INFO new file mode 100644 index 000000000..d240517f5 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: EggTest +Version: 0.0.1 +Summary: Simple project for producing an .egg. +Home-page: http://example.org/eggtest +Author: Dale Visser +Author-email: dvisser@ida.org +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/SOURCES.txt b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/SOURCES.txt new file mode 100644 index 000000000..395cfb65c --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/SOURCES.txt @@ -0,0 +1,8 @@ +setup.py +EggTest.egg-info/PKG-INFO +EggTest.egg-info/SOURCES.txt +EggTest.egg-info/dependency_links.txt +EggTest.egg-info/top_level.txt +eggtest/__about__.py +eggtest/__init__.py +eggtest/main.py \ No newline at end of file diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/dependency_links.txt b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/dependency_links.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/top_level.txt b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/top_level.txt new file mode 100644 index 000000000..4f7a3578c --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +eggtest diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/zip-safe b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/zip-safe new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/EGG-INFO/zip-safe @@ -0,0 +1 @@ + diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/__about__.py b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/__about__.py new file mode 100644 index 000000000..b34ad23d0 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/__about__.py @@ -0,0 +1,9 @@ +__all__ = ["__title__", "__summary__", "__uri__", "__version__", "__author__", + "__email__" ] + +__title__ = "EggTest" +__summary__ = "Simple project for producing an .egg." +__uri__ = "http://example.org/eggtest" +__version__ = "0.0.1" +__author__ = "Dale Visser" +__email__ = "dvisser@ida.org" \ No newline at end of file diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/__about__.pyc b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/__about__.pyc new file mode 100644 index 000000000..9fbe255c2 Binary files /dev/null and b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/__about__.pyc differ diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/__init__.py b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/__init__.py new file mode 100644 index 000000000..8bc561672 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/__init__.py @@ -0,0 +1 @@ +from eggtest import main diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/__init__.pyc b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/__init__.pyc new file mode 100644 index 000000000..37cbd7639 Binary files /dev/null and b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/__init__.pyc differ diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/main.py b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/main.py new file mode 100644 index 000000000..69d2d2985 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/main.py @@ -0,0 +1 @@ +print 'Hello from eggtest!' diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/main.pyc b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/main.pyc new file mode 100644 index 000000000..9b67e22e7 Binary files /dev/null and b/dependency-check-core/src/test/resources/python/site-packages/EggTest-0.0.1-py2.7.egg/eggtest/main.pyc differ diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest.egg-info/PKG-INFO b/dependency-check-core/src/test/resources/python/site-packages/EggTest.egg-info/PKG-INFO new file mode 100644 index 000000000..d240517f5 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/EggTest.egg-info/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: EggTest +Version: 0.0.1 +Summary: Simple project for producing an .egg. +Home-page: http://example.org/eggtest +Author: Dale Visser +Author-email: dvisser@ida.org +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest.egg-info/SOURCES.txt b/dependency-check-core/src/test/resources/python/site-packages/EggTest.egg-info/SOURCES.txt new file mode 100644 index 000000000..08a77ccf5 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/EggTest.egg-info/SOURCES.txt @@ -0,0 +1,7 @@ +setup.py +EggTest.egg-info/PKG-INFO +EggTest.egg-info/SOURCES.txt +EggTest.egg-info/dependency_links.txt +EggTest.egg-info/top_level.txt +eggtest/__init__.py +eggtest/main.py \ No newline at end of file diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest.egg-info/dependency_links.txt b/dependency-check-core/src/test/resources/python/site-packages/EggTest.egg-info/dependency_links.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/EggTest.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/dependency-check-core/src/test/resources/python/site-packages/EggTest.egg-info/top_level.txt b/dependency-check-core/src/test/resources/python/site-packages/EggTest.egg-info/top_level.txt new file mode 100644 index 000000000..4f7a3578c --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/EggTest.egg-info/top_level.txt @@ -0,0 +1 @@ +eggtest diff --git a/dependency-check-core/src/test/resources/python/site-packages/django/__init__.py b/dependency-check-core/src/test/resources/python/site-packages/django/__init__.py new file mode 100644 index 000000000..5df6a56ca --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/django/__init__.py @@ -0,0 +1,21 @@ +VERSION = (1, 7, 2, 'final', 0) + + +def get_version(*args, **kwargs): + # Don't litter django/__init__.py with all the get_version stuff. + # Only import if it's actually called. + from django.utils.version import get_version + return get_version(*args, **kwargs) + + +def setup(): + """ + Configure the settings (this happens as a side effect of accessing the + first setting), configure logging and populate the app registry. + """ + from django.apps import apps + from django.conf import settings + from django.utils.log import configure_logging + + configure_logging(settings.LOGGING_CONFIG, settings.LOGGING) + apps.populate(settings.INSTALLED_APPS) diff --git a/dependency-check-core/src/test/resources/python/site-packages/django/shortcuts.py b/dependency-check-core/src/test/resources/python/site-packages/django/shortcuts.py new file mode 100644 index 000000000..62560472c --- /dev/null +++ b/dependency-check-core/src/test/resources/python/site-packages/django/shortcuts.py @@ -0,0 +1,168 @@ +""" +This module collects helper functions and classes that "span" multiple levels +of MVC. In other words, these functions/classes introduce controlled coupling +for convenience's sake. +""" +from django.template import loader, RequestContext +from django.http import HttpResponse, Http404 +from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect +from django.db.models.base import ModelBase +from django.db.models.manager import Manager +from django.db.models.query import QuerySet +from django.core import urlresolvers +from django.utils import six + + +def render_to_response(*args, **kwargs): + """ + Returns a HttpResponse whose content is filled with the result of calling + django.template.loader.render_to_string() with the passed arguments. + """ + httpresponse_kwargs = {'content_type': kwargs.pop('content_type', None)} + + return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs) + + +def render(request, *args, **kwargs): + """ + Returns a HttpResponse whose content is filled with the result of calling + django.template.loader.render_to_string() with the passed arguments. + Uses a RequestContext by default. + """ + httpresponse_kwargs = { + 'content_type': kwargs.pop('content_type', None), + 'status': kwargs.pop('status', None), + } + + if 'context_instance' in kwargs: + context_instance = kwargs.pop('context_instance') + if kwargs.get('current_app', None): + raise ValueError('If you provide a context_instance you must ' + 'set its current_app before calling render()') + else: + current_app = kwargs.pop('current_app', None) + context_instance = RequestContext(request, current_app=current_app) + + kwargs['context_instance'] = context_instance + + return HttpResponse(loader.render_to_string(*args, **kwargs), + **httpresponse_kwargs) + + +def redirect(to, *args, **kwargs): + """ + Returns an HttpResponseRedirect to the appropriate URL for the arguments + passed. + + The arguments could be: + + * A model: the model's `get_absolute_url()` function will be called. + + * A view name, possibly with arguments: `urlresolvers.reverse()` will + be used to reverse-resolve the name. + + * A URL, which will be used as-is for the redirect location. + + By default issues a temporary redirect; pass permanent=True to issue a + permanent redirect + """ + if kwargs.pop('permanent', False): + redirect_class = HttpResponsePermanentRedirect + else: + redirect_class = HttpResponseRedirect + + return redirect_class(resolve_url(to, *args, **kwargs)) + + +def _get_queryset(klass): + """ + Returns a QuerySet from a Model, Manager, or QuerySet. Created to make + get_object_or_404 and get_list_or_404 more DRY. + + Raises a ValueError if klass is not a Model, Manager, or QuerySet. + """ + if isinstance(klass, QuerySet): + return klass + elif isinstance(klass, Manager): + manager = klass + elif isinstance(klass, ModelBase): + manager = klass._default_manager + else: + if isinstance(klass, type): + klass__name = klass.__name__ + else: + klass__name = klass.__class__.__name__ + raise ValueError("Object is of type '%s', but must be a Django Model, " + "Manager, or QuerySet" % klass__name) + return manager.all() + + +def get_object_or_404(klass, *args, **kwargs): + """ + Uses get() to return an object, or raises a Http404 exception if the object + does not exist. + + klass may be a Model, Manager, or QuerySet object. All other passed + arguments and keyword arguments are used in the get() query. + + Note: Like with get(), an MultipleObjectsReturned will be raised if more than one + object is found. + """ + queryset = _get_queryset(klass) + try: + return queryset.get(*args, **kwargs) + except queryset.model.DoesNotExist: + raise Http404('No %s matches the given query.' % queryset.model._meta.object_name) + + +def get_list_or_404(klass, *args, **kwargs): + """ + Uses filter() to return a list of objects, or raise a Http404 exception if + the list is empty. + + klass may be a Model, Manager, or QuerySet object. All other passed + arguments and keyword arguments are used in the filter() query. + """ + queryset = _get_queryset(klass) + obj_list = list(queryset.filter(*args, **kwargs)) + if not obj_list: + raise Http404('No %s matches the given query.' % queryset.model._meta.object_name) + return obj_list + + +def resolve_url(to, *args, **kwargs): + """ + Return a URL appropriate for the arguments passed. + + The arguments could be: + + * A model: the model's `get_absolute_url()` function will be called. + + * A view name, possibly with arguments: `urlresolvers.reverse()` will + be used to reverse-resolve the name. + + * A URL, which will be returned as-is. + + """ + # If it's a model, use get_absolute_url() + if hasattr(to, 'get_absolute_url'): + return to.get_absolute_url() + + if isinstance(to, six.string_types): + # Handle relative URLs + if any(to.startswith(path) for path in ('./', '../')): + return to + + # Next try a reverse URL resolution. + try: + return urlresolvers.reverse(to, args=args, kwargs=kwargs) + except urlresolvers.NoReverseMatch: + # If this is a callable, re-raise. + if callable(to): + raise + # If this doesn't "feel" like a URL, re-raise. + if '/' not in to and '.' not in to: + raise + + # Finally, fall back and assume it's a URL + return to 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 32c006db6..bfa5acfd6 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 @@ -176,6 +176,14 @@ public final class Settings { * The properties key for whether the Archive analyzer is enabled. */ public static final String ANALYZER_ARCHIVE_ENABLED = "analyzer.archive.enabled"; + /** + * The properties key for whether the Python Distribution analyzer is enabled. + */ + public static final String ANALYZER_PYTHON_DISTRIBUTION_ENABLED = "analyzer.python.distribution.enabled"; + /** + * The properties key for whether the Python Package analyzer is enabled. + */ + public static final String ANALYZER_PYTHON_PACKAGE_ENABLED = "analyzer.python.package.enabled"; /** * The properties key for whether the .NET Assembly analyzer is enabled. */ @@ -264,7 +272,7 @@ public final class Settings { /** * Thread local settings. */ - private static ThreadLocal localSettings = new ThreadLocal(); + private static ThreadLocal localSettings = new ThreadLocal(); /** * The properties. */ @@ -369,7 +377,7 @@ public final class Settings { try { pw = new PrintWriter(sw); pw.format("%s:%n%n", header); - final Enumeration e = properties.propertyNames(); + final Enumeration e = properties.propertyNames(); while (e.hasMoreElements()) { final String key = (String) e.nextElement(); if (key.contains("password")) { diff --git a/src/main/config/checkstyle-header.txt b/src/main/config/checkstyle-header.txt index f188850eb..115153c36 100644 --- a/src/main/config/checkstyle-header.txt +++ b/src/main/config/checkstyle-header.txt @@ -13,6 +13,6 @@ ^ \* See the License for the specific language governing permissions and\s*$ ^ \* limitations under the License\.\s*$ ^ \*\s*$ -^ \* Copyright \(c\) 201[0-9] (Jeremy Long|Steve Springett)\. All Rights Reserved\.\s*$ +^ \* Copyright \(c\) 201[0-9] (Jeremy Long|Steve Springett|Institute for Defense Analyses)\. All Rights Reserved\.\s*$ ^ \*/\s*$ ^package