mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-03-22 09:09:31 +01:00
Merge pull request #217 from dwvisser/python-github-pr
Add Analyzers for Python Distribution Files and Packages Former-commit-id: 972a63525744bbbd510b4ae2d92028dc3079787a
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -10,6 +10,7 @@
|
|||||||
.settings
|
.settings
|
||||||
maven-eclipse.xml
|
maven-eclipse.xml
|
||||||
.externalToolBuilders
|
.externalToolBuilders
|
||||||
|
.pmd
|
||||||
# Netbeans configuration
|
# Netbeans configuration
|
||||||
nb-configuration.xml
|
nb-configuration.xml
|
||||||
/target/
|
/target/
|
||||||
@@ -22,4 +23,4 @@ _site/**
|
|||||||
#unknown as to why these are showing up... but need to be ignored.
|
#unknown as to why these are showing up... but need to be ignored.
|
||||||
.LCKpom.xml~
|
.LCKpom.xml~
|
||||||
#coverity
|
#coverity
|
||||||
/cov-int/
|
/cov-int/
|
||||||
|
|||||||
@@ -252,6 +252,8 @@ public class App {
|
|||||||
final String suppressionFile = cli.getSuppressionFile();
|
final String suppressionFile = cli.getSuppressionFile();
|
||||||
final boolean jarDisabled = cli.isJarDisabled();
|
final boolean jarDisabled = cli.isJarDisabled();
|
||||||
final boolean archiveDisabled = cli.isArchiveDisabled();
|
final boolean archiveDisabled = cli.isArchiveDisabled();
|
||||||
|
final boolean pyDistDisabled = cli.isPythonDistributionDisabled();
|
||||||
|
final boolean pyPkgDisabled = cli.isPythonPackageDisabled();
|
||||||
final boolean assemblyDisabled = cli.isAssemblyDisabled();
|
final boolean assemblyDisabled = cli.isAssemblyDisabled();
|
||||||
final boolean nuspecDisabled = cli.isNuspecDisabled();
|
final boolean nuspecDisabled = cli.isNuspecDisabled();
|
||||||
final boolean centralDisabled = cli.isCentralDisabled();
|
final boolean centralDisabled = cli.isCentralDisabled();
|
||||||
@@ -317,6 +319,8 @@ public class App {
|
|||||||
//File Type Analyzer Settings
|
//File Type Analyzer Settings
|
||||||
Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !jarDisabled);
|
Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !jarDisabled);
|
||||||
Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !archiveDisabled);
|
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_NUSPEC_ENABLED, !nuspecDisabled);
|
||||||
Settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, !assemblyDisabled);
|
Settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, !assemblyDisabled);
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ package org.owasp.dependencycheck;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.apache.commons.cli.CommandLine;
|
import org.apache.commons.cli.CommandLine;
|
||||||
import org.apache.commons.cli.CommandLineParser;
|
import org.apache.commons.cli.CommandLineParser;
|
||||||
import org.apache.commons.cli.HelpFormatter;
|
import org.apache.commons.cli.HelpFormatter;
|
||||||
@@ -327,6 +328,12 @@ public final class CliParser {
|
|||||||
.withDescription("Disable the .NET Assembly Analyzer.")
|
.withDescription("Disable the .NET Assembly Analyzer.")
|
||||||
.create();
|
.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)
|
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 "
|
.withDescription("Disable the Central Analyzer. If this analyzer is disabled it is likely you also want to disable "
|
||||||
+ "the Nexus Analyzer.")
|
+ "the Nexus Analyzer.")
|
||||||
@@ -369,6 +376,8 @@ public final class CliParser {
|
|||||||
.addOption(disableJarAnalyzer)
|
.addOption(disableJarAnalyzer)
|
||||||
.addOption(disableArchiveAnalyzer)
|
.addOption(disableArchiveAnalyzer)
|
||||||
.addOption(disableAssemblyAnalyzer)
|
.addOption(disableAssemblyAnalyzer)
|
||||||
|
.addOption(disablePythonDistributionAnalyzer)
|
||||||
|
.addOption(disablePythonPackageAnalyzer)
|
||||||
.addOption(disableNuspecAnalyzer)
|
.addOption(disableNuspecAnalyzer)
|
||||||
.addOption(disableCentralAnalyzer)
|
.addOption(disableCentralAnalyzer)
|
||||||
.addOption(disableNexusAnalyzer)
|
.addOption(disableNexusAnalyzer)
|
||||||
@@ -458,6 +467,24 @@ public final class CliParser {
|
|||||||
return (line != null) && line.hasOption(ARGUMENT.DISABLE_ASSEMBLY);
|
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.
|
* Returns true if the disableNexus command line argument was specified.
|
||||||
*
|
*
|
||||||
@@ -899,6 +926,14 @@ public final class CliParser {
|
|||||||
* Disables the Archive Analyzer.
|
* Disables the Archive Analyzer.
|
||||||
*/
|
*/
|
||||||
public static final String DISABLE_ARCHIVE = "disableArchive";
|
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.
|
* Disables the Assembly Analyzer.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ Short | Argument Name | Paramete
|
|||||||
-------|-----------------------|-----------------|----------------------------------------------------------------------------------|-------------------
|
-------|-----------------------|-----------------|----------------------------------------------------------------------------------|-------------------
|
||||||
\-P | \-\-propertyfile | \<file\> | Specifies a file that contains properties to use instead of applicaion defaults. |
|
\-P | \-\-propertyfile | \<file\> | 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. |
|
| \-\-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
|
| \-\-disableArchive | | Sets whether the Archive Analyzer will be used. | false
|
||||||
| \-\-zipExtensions | \<strings\> | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. |
|
| \-\-zipExtensions | \<strings\> | 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
|
| \-\-disableJar | | Sets whether the Jar Analyzer will be used. | false
|
||||||
|
|||||||
@@ -541,6 +541,11 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
|||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.sun.mail</groupId>
|
||||||
|
<artifactId>mailapi</artifactId>
|
||||||
|
<version>1.5.2</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
|
|||||||
@@ -318,16 +318,17 @@ public class Engine {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final String fileName = file.getName();
|
final String fileName = file.getName();
|
||||||
final String extension = FileUtils.getFileExtension(fileName);
|
String extension = FileUtils.getFileExtension(fileName);
|
||||||
|
if (null == extension) {
|
||||||
|
extension = fileName;
|
||||||
|
}
|
||||||
Dependency dependency = null;
|
Dependency dependency = null;
|
||||||
if (extension != null) {
|
if (supportsExtension(extension)) {
|
||||||
if (supportsExtension(extension)) {
|
dependency = new Dependency(file);
|
||||||
dependency = new Dependency(file);
|
if (extension == fileName){
|
||||||
dependencies.add(dependency);
|
dependency.setFileExtension(extension);
|
||||||
}
|
}
|
||||||
} else {
|
dependencies.add(dependency);
|
||||||
final String msg = String.format("No file extension found on file '%s'. The file was not analyzed.", file.toString());
|
|
||||||
LOGGER.log(Level.FINE, msg);
|
|
||||||
}
|
}
|
||||||
return dependency;
|
return dependency;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 <dvisser@ida.org>
|
||||||
|
*/
|
||||||
|
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<String> 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<String> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <dvisser@ida.org>
|
||||||
|
*/
|
||||||
|
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<String> 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<String> 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<Dependency> deps = new ArrayList<Dependency>(
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -222,7 +222,7 @@ public class Dependency implements Serializable, Comparable<Dependency> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the file name of the dependency.
|
* Sets the file extension of the dependency.
|
||||||
*
|
*
|
||||||
* @param fileExtension the file name of the dependency
|
* @param fileExtension the file name of the dependency
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -17,19 +17,29 @@
|
|||||||
*/
|
*/
|
||||||
package org.owasp.dependencycheck.utils;
|
package org.owasp.dependencycheck.utils;
|
||||||
|
|
||||||
|
import static org.owasp.dependencycheck.utils.FileUtils.getFileExtension;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
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 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 {
|
try {
|
||||||
fos = new FileOutputStream(file);
|
fos = new FileOutputStream(file);
|
||||||
bos = new BufferedOutputStream(fos, BUFFER_SIZE);
|
bos = new BufferedOutputStream(fos, BUFFER_SIZE);
|
||||||
int count;
|
transferUsingBuffer(zis, bos);
|
||||||
final byte[] data = new byte[BUFFER_SIZE];
|
|
||||||
while ((count = zis.read(data, 0, BUFFER_SIZE)) != -1) {
|
|
||||||
bos.write(data, 0, count);
|
|
||||||
}
|
|
||||||
bos.flush();
|
|
||||||
} catch (FileNotFoundException ex) {
|
} catch (FileNotFoundException ex) {
|
||||||
LOGGER.log(Level.FINE, null, ex);
|
LOGGER.log(Level.FINE, null, ex);
|
||||||
final String msg = String.format("Unable to find file '%s'.", file.getName());
|
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());
|
final String msg = String.format("IO Exception while parsing file '%s'.", file.getName());
|
||||||
throw new ExtractionException(msg, ex);
|
throw new ExtractionException(msg, ex);
|
||||||
} finally {
|
} finally {
|
||||||
if (bos != null) {
|
closeStream(bos);
|
||||||
try {
|
|
||||||
bos.close();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
LOGGER.log(Level.FINEST, null, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,11 +136,157 @@ public final class ExtractionUtil {
|
|||||||
LOGGER.log(Level.FINE, msg, ex);
|
LOGGER.log(Level.FINE, msg, ex);
|
||||||
throw new ExtractionException(msg, ex);
|
throw new ExtractionException(msg, ex);
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
closeStream(zis);
|
||||||
zis.close();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
LOGGER.log(Level.FINEST, null, ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,4 +11,6 @@ org.owasp.dependencycheck.analyzer.VulnerabilitySuppressionAnalyzer
|
|||||||
org.owasp.dependencycheck.analyzer.CentralAnalyzer
|
org.owasp.dependencycheck.analyzer.CentralAnalyzer
|
||||||
org.owasp.dependencycheck.analyzer.NexusAnalyzer
|
org.owasp.dependencycheck.analyzer.NexusAnalyzer
|
||||||
org.owasp.dependencycheck.analyzer.NuspecAnalyzer
|
org.owasp.dependencycheck.analyzer.NuspecAnalyzer
|
||||||
org.owasp.dependencycheck.analyzer.AssemblyAnalyzer
|
org.owasp.dependencycheck.analyzer.AssemblyAnalyzer
|
||||||
|
org.owasp.dependencycheck.analyzer.PythonDistributionAnalyzer
|
||||||
|
org.owasp.dependencycheck.analyzer.PythonPackageAnalyzer
|
||||||
@@ -83,5 +83,21 @@
|
|||||||
<gav regex="true">org\.opensaml:xmltooling:.*</gav>
|
<gav regex="true">org\.opensaml:xmltooling:.*</gav>
|
||||||
<cpe>cpe:/a:internet2:opensaml</cpe>
|
<cpe>cpe:/a:internet2:opensaml</cpe>
|
||||||
</suppress>
|
</suppress>
|
||||||
|
<suppress base="true">
|
||||||
|
<notes><![CDATA[
|
||||||
|
Suppresses false positives for python:python.
|
||||||
|
]]></notes>
|
||||||
|
<filePath regex="true">.*(\.(whl|egg)|\b(site|dist)-packages\b.*)</filePath>
|
||||||
|
<cpe>cpe:/a:python:python</cpe>
|
||||||
|
<cpe>cpe:/a:python_software_foundation:python</cpe>
|
||||||
|
<cpe>cpe:/a:class:class</cpe>
|
||||||
|
<cpe>cpe:/a:file:file</cpe>
|
||||||
|
<cpe>cpe:/a:gnupg:gnupg</cpe>
|
||||||
|
<cpe>cpe:/a:mongodb:mongodb</cpe>
|
||||||
|
<cpe>cpe:/a:mozilla:mozilla</cpe>
|
||||||
|
<cpe>cpe:/a:openssl:openssl</cpe>
|
||||||
|
<cpe>cpe:/a:sendfile:sendfile</cpe>
|
||||||
|
<cpe>cpe:/a:sendmail:sendmail</cpe>
|
||||||
|
<cpe>cpe:/a:yacc:yacc</cpe>
|
||||||
|
</suppress>
|
||||||
</suppressions>
|
</suppressions>
|
||||||
@@ -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 <dvisser@ida.org>
|
||||||
|
*/
|
||||||
|
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<String>(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <dvisser@ida.org>
|
||||||
|
*/
|
||||||
|
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<String>(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());
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
BIN
dependency-check-core/src/test/resources/python/dist/EggTest-0.0.1-py2.7.egg
vendored
Normal file
BIN
dependency-check-core/src/test/resources/python/dist/EggTest-0.0.1-py2.7.egg
vendored
Normal file
Binary file not shown.
@@ -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"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
from eggtest import main
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
print 'Hello from eggtest!'
|
||||||
BIN
dependency-check-core/src/test/resources/python/eggtest/main.pyc
Normal file
BIN
dependency-check-core/src/test/resources/python/eggtest/main.pyc
Normal file
Binary file not shown.
11
dependency-check-core/src/test/resources/python/setup.py
Normal file
11
dependency-check-core/src/test/resources/python/setup.py
Normal file
@@ -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__'])
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#!python
|
||||||
|
from django.core import management
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
management.execute_from_command_line()
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
@@ -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.
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[console_scripts]
|
||||||
|
django-admin = django.core.management:execute_from_command_line
|
||||||
|
|
||||||
@@ -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"]}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
django
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
eggtest
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -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"
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
from eggtest import main
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
print 'Hello from eggtest!'
|
||||||
Binary file not shown.
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
eggtest
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -176,6 +176,14 @@ public final class Settings {
|
|||||||
* The properties key for whether the Archive analyzer is enabled.
|
* The properties key for whether the Archive analyzer is enabled.
|
||||||
*/
|
*/
|
||||||
public static final String ANALYZER_ARCHIVE_ENABLED = "analyzer.archive.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.
|
* The properties key for whether the .NET Assembly analyzer is enabled.
|
||||||
*/
|
*/
|
||||||
@@ -264,7 +272,7 @@ public final class Settings {
|
|||||||
/**
|
/**
|
||||||
* Thread local settings.
|
* Thread local settings.
|
||||||
*/
|
*/
|
||||||
private static ThreadLocal<Settings> localSettings = new ThreadLocal();
|
private static ThreadLocal<Settings> localSettings = new ThreadLocal<Settings>();
|
||||||
/**
|
/**
|
||||||
* The properties.
|
* The properties.
|
||||||
*/
|
*/
|
||||||
@@ -369,7 +377,7 @@ public final class Settings {
|
|||||||
try {
|
try {
|
||||||
pw = new PrintWriter(sw);
|
pw = new PrintWriter(sw);
|
||||||
pw.format("%s:%n%n", header);
|
pw.format("%s:%n%n", header);
|
||||||
final Enumeration e = properties.propertyNames();
|
final Enumeration<?> e = properties.propertyNames();
|
||||||
while (e.hasMoreElements()) {
|
while (e.hasMoreElements()) {
|
||||||
final String key = (String) e.nextElement();
|
final String key = (String) e.nextElement();
|
||||||
if (key.contains("password")) {
|
if (key.contains("password")) {
|
||||||
|
|||||||
@@ -13,6 +13,6 @@
|
|||||||
^ \* See the License for the specific language governing permissions and\s*$
|
^ \* See the License for the specific language governing permissions and\s*$
|
||||||
^ \* limitations under the License\.\s*$
|
^ \* limitations under the License\.\s*$
|
||||||
^ \*\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*$
|
^ \*/\s*$
|
||||||
^package
|
^package
|
||||||
|
|||||||
Reference in New Issue
Block a user