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:
Jeremy Long
2015-04-26 08:14:03 -04:00
49 changed files with 1458 additions and 34 deletions

3
.gitignore vendored
View File

@@ -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/
/cov-int/

View File

@@ -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);

View File

@@ -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.
*/

View File

@@ -23,6 +23,8 @@ Short | Argument Name        | Paramete
-------|-----------------------|-----------------|----------------------------------------------------------------------------------|-------------------
\-P | \-\-propertyfile | \<file\> | Specifies a file that contains properties to use instead of applicaion defaults. | &nbsp;
| \-\-updateonly | | If set only the update phase of dependency-check will be executed; no scan will be executed and no report will be generated. | &nbsp;
| \-\-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 | \<strings\> | A comma-separated list of additional file extensions to be treated like a ZIP file, the contents will be extracted and analyzed. | &nbsp;
| \-\-disableJar | | Sets whether the Jar Analyzer will be used. | false

View File

@@ -541,6 +541,11 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>mailapi</artifactId>
<version>1.5.2</version>
</dependency>
</dependencies>
<profiles>
<profile>

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
*/

View File

@@ -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);
}
}
}
}

View File

@@ -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
org.owasp.dependencycheck.analyzer.AssemblyAnalyzer
org.owasp.dependencycheck.analyzer.PythonDistributionAnalyzer
org.owasp.dependencycheck.analyzer.PythonPackageAnalyzer

View File

@@ -83,5 +83,21 @@
<gav regex="true">org\.opensaml:xmltooling:.*</gav>
<cpe>cpe:/a:internet2:opensaml</cpe>
</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>

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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"

View File

@@ -0,0 +1 @@
from eggtest import main

View File

@@ -0,0 +1 @@
print 'Hello from eggtest!'

View 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__'])

View File

@@ -0,0 +1,5 @@
#!python
from django.core import management
if __name__ == "__main__":
management.execute_from_command_line()

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,3 @@
[console_scripts]
django-admin = django.core.management:execute_from_command_line

View File

@@ -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"]}

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -0,0 +1 @@
print 'Hello from eggtest!'

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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<Settings> localSettings = new ThreadLocal();
private static ThreadLocal<Settings> localSettings = new ThreadLocal<Settings>();
/**
* 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")) {

View File

@@ -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