mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-02-25 11:55:21 +01:00
Added capability to scan extracted egg and wheel metadata in the local Python
environment. Former-commit-id: b0259d38134bf18b1eb72db9951dbe2d04ba8fb9
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -18,13 +18,10 @@
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
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.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -34,30 +31,33 @@ import java.util.regex.Pattern;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.InternetHeaders;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.ArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
|
||||
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.analyzer.exception.ArchiveExtractionException;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Used to load a Wheel distriution file and collect information that can be
|
||||
* used to determine the associated CPE.
|
||||
* Used to analyze a Wheel distriution file or *.dist-info folder, and collect
|
||||
* information that can be used to determine the associated CPE.
|
||||
*
|
||||
* @author Dale Visser <dvisser@ida.org>
|
||||
*/
|
||||
public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
private static final String PKG_INFO = "PKG-INFO";
|
||||
|
||||
/**
|
||||
* Name of wheel metadata files to analyze.
|
||||
*/
|
||||
private static final String METADATA = "METADATA";
|
||||
|
||||
/**
|
||||
@@ -66,26 +66,12 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
private static final Logger LOGGER = Logger
|
||||
.getLogger(PythonDistributionAnalyzer.class.getName());
|
||||
|
||||
/**
|
||||
* The buffer size to use when extracting files from the archive.
|
||||
*/
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
|
||||
/**
|
||||
* The count of directories created during analysis. This is used for
|
||||
* creating temporary directories.
|
||||
*/
|
||||
private static int dirCount = 0;
|
||||
|
||||
/**
|
||||
* Constructs a new PythonDistributionAnalyzer.
|
||||
*/
|
||||
public PythonDistributionAnalyzer() {
|
||||
super();
|
||||
}
|
||||
|
||||
// <editor-fold defaultstate="collapsed"
|
||||
// desc="All standard implmentation details of Analyzer">
|
||||
/**
|
||||
* The name of the analyzer.
|
||||
*/
|
||||
@@ -98,7 +84,39 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* The set of file extensions supported by this analyzer.
|
||||
*/
|
||||
private static final Set<String> EXTENSIONS = newHashSet("whl");
|
||||
private static final Set<String> EXTENSIONS = newHashSet("whl", METADATA,
|
||||
PKG_INFO);
|
||||
|
||||
/**
|
||||
* Pattern that captures the vendor from a home page URL.
|
||||
*/
|
||||
private static final Pattern HOMEPAGE_VENDOR = Pattern
|
||||
.compile("^[a-zA-Z]+://.*\\.(.+)\\.[a-zA-Z]+/?.*$");
|
||||
|
||||
/**
|
||||
* 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 METADATA_FILTER = new NameFileFilter(
|
||||
METADATA);
|
||||
|
||||
/**
|
||||
* Constructs a new PythonDistributionAnalyzer.
|
||||
*/
|
||||
public PythonDistributionAnalyzer() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of file EXTENSIONS supported by this analyzer.
|
||||
@@ -142,42 +160,48 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
return Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a specified JAR file and collects information from the manifest and
|
||||
* checksums to identify the correct CPE information.
|
||||
*
|
||||
* @param dependency
|
||||
* the dependency to analyze.
|
||||
* @param engine
|
||||
* the engine that is scanning the dependencies
|
||||
* @throws AnalysisException
|
||||
* is thrown if there is an error reading the JAR file.
|
||||
*/
|
||||
@Override
|
||||
public void analyzeFileType(Dependency dependency, Engine engine)
|
||||
protected void analyzeFileType(Dependency dependency, Engine engine)
|
||||
throws AnalysisException {
|
||||
final File tmpWheelFolder = getNextTempDirectory();
|
||||
LOGGER.fine(String.format("%s exists? %b", tmpWheelFolder,
|
||||
tmpWheelFolder.exists()));
|
||||
extractFiles(new File(dependency.getActualFilePath()), tmpWheelFolder,
|
||||
METADATA_FILTER);
|
||||
collectWheelMetadata(dependency, tmpWheelFolder);
|
||||
if ("whl".equals(dependency.getFileExtension())) {
|
||||
final File tmpWheelFolder = getNextTempDirectory();
|
||||
LOGGER.fine(String.format("%s exists? %b", tmpWheelFolder,
|
||||
tmpWheelFolder.exists()));
|
||||
try {
|
||||
ExtractionUtil.extractFilesUsingFilter(
|
||||
new File(dependency.getActualFilePath()),
|
||||
tmpWheelFolder, METADATA_FILTER);
|
||||
} catch (ExtractionException ex) {
|
||||
throw new AnalysisException(ex);
|
||||
}
|
||||
|
||||
collectWheelMetadata(
|
||||
dependency,
|
||||
getMatchingFile(
|
||||
getMatchingFile(tmpWheelFolder, DIST_INFO_FILTER),
|
||||
METADATA_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"))) {
|
||||
collectWheelMetadata(dependency, actualFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The parent directory for the individual directories per archive.
|
||||
*/
|
||||
private File tempFileLocation = null;
|
||||
|
||||
/**
|
||||
* Initializes the JarAnalyzer.
|
||||
*
|
||||
* @throws Exception
|
||||
* is thrown if there is an exception creating a temporary
|
||||
* directory
|
||||
* Makes sure a usable temporary directory is available.
|
||||
*/
|
||||
@Override
|
||||
public void initializeFileTypeAnalyzer() throws Exception {
|
||||
protected void initializeFileTypeAnalyzer() throws Exception {
|
||||
final File baseDir = Settings.getTempDirectory();
|
||||
tempFileLocation = File.createTempFile("check", "tmp", baseDir);
|
||||
if (!tempFileLocation.delete()) {
|
||||
@@ -209,33 +233,30 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern HOMEPAGE_VENDOR = Pattern
|
||||
.compile("^[a-zA-Z]+://.*\\.(.+)\\.[a-zA-Z]+/?.*$");
|
||||
|
||||
/**
|
||||
* Gathers evidence from the METADATA file.
|
||||
*
|
||||
* @param dependency
|
||||
* the dependency being analyzed
|
||||
*/
|
||||
private static void collectWheelMetadata(Dependency dependency,
|
||||
File wheelFolder) {
|
||||
InternetHeaders headers = getManifestProperties(wheelFolder);
|
||||
private static void collectWheelMetadata(Dependency dependency, File file) {
|
||||
final InternetHeaders headers = getManifestProperties(file);
|
||||
addPropertyToEvidence(headers, dependency.getVersionEvidence(),
|
||||
"Version", Confidence.HIGHEST);
|
||||
addPropertyToEvidence(headers, dependency.getProductEvidence(), "Name",
|
||||
Confidence.HIGHEST);
|
||||
String url = headers.getHeader("Home-page", null);
|
||||
EvidenceCollection vendorEvidence = dependency.getVendorEvidence();
|
||||
final String url = headers.getHeader("Home-page", null);
|
||||
final EvidenceCollection vendorEvidence = dependency
|
||||
.getVendorEvidence();
|
||||
if (StringUtils.isNotBlank(url)) {
|
||||
Matcher m = HOMEPAGE_VENDOR.matcher(url);
|
||||
if (m.matches()) {
|
||||
vendorEvidence.addEvidence(METADATA, "vendor", m.group(1),
|
||||
Confidence.MEDIUM);
|
||||
final Matcher matcher = HOMEPAGE_VENDOR.matcher(url);
|
||||
if (matcher.matches()) {
|
||||
vendorEvidence.addEvidence(METADATA, "vendor",
|
||||
matcher.group(1), Confidence.MEDIUM);
|
||||
}
|
||||
}
|
||||
addPropertyToEvidence(headers, vendorEvidence, "Author", Confidence.LOW);
|
||||
String summary = headers.getHeader("Summary", null);
|
||||
final String summary = headers.getHeader("Summary", null);
|
||||
if (StringUtils.isNotBlank(summary)) {
|
||||
JarAnalyzer
|
||||
.addDescription(dependency, summary, METADATA, "summary");
|
||||
@@ -244,52 +265,34 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
private static void addPropertyToEvidence(InternetHeaders headers,
|
||||
EvidenceCollection evidence, String property, Confidence confidence) {
|
||||
String value = headers.getHeader(property, null);
|
||||
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 FilenameFilter DIST_INFO_FILTER = new SuffixFileFilter(
|
||||
".dist-info");
|
||||
|
||||
private static final FilenameFilter METADATA_FILTER = new NameFileFilter(
|
||||
METADATA);
|
||||
|
||||
private static final File getMatchingFile(File folder, FilenameFilter filter) {
|
||||
File result = null;
|
||||
File[] matches = folder.listFiles(filter);
|
||||
final File[] matches = folder.listFiles(filter);
|
||||
if (null != matches && 1 == matches.length) {
|
||||
result = matches[0];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static InternetHeaders getManifestProperties(File wheelFolder) {
|
||||
InternetHeaders result = new InternetHeaders();
|
||||
LOGGER.fine(String.format("%s has %d entries.", wheelFolder,
|
||||
wheelFolder.list().length));
|
||||
File dist_info = getMatchingFile(wheelFolder, DIST_INFO_FILTER);
|
||||
if (null != dist_info && dist_info.isDirectory()) {
|
||||
LOGGER.fine(String.format("%s has %d entries.", dist_info,
|
||||
dist_info.list().length));
|
||||
File manifest = getMatchingFile(dist_info, METADATA_FILTER);
|
||||
LOGGER.fine(String.format("METADATA file found? %b",
|
||||
null != manifest));
|
||||
if (null != manifest) {
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
LOGGER.fine(String.format("%s contents: %s", dist_info,
|
||||
StringUtils.join(dist_info.list(), ";")));
|
||||
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;
|
||||
@@ -319,146 +322,4 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
return directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 AnalysisException
|
||||
* thrown if the archive is not found
|
||||
*/
|
||||
private static void extractFiles(File archive, File destination,
|
||||
FilenameFilter filter) throws AnalysisException {
|
||||
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 AnalysisException("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 d = new File(destination, entry.getName());
|
||||
if (!d.exists()) {
|
||||
if (!d.mkdirs()) {
|
||||
final String msg = String.format(
|
||||
"Unable to create directory '%s'.",
|
||||
d.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
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 AnalysisException(msg);
|
||||
}
|
||||
}
|
||||
fos = new FileOutputStream(file);
|
||||
bos = new BufferedOutputStream(fos, BUFFER_SIZE);
|
||||
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();
|
||||
} catch (FileNotFoundException ex) {
|
||||
LOGGER.log(Level.FINE, null, ex);
|
||||
final String msg = String
|
||||
.format("Unable to find file '%s'.",
|
||||
file.getName());
|
||||
throw new AnalysisException(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 AnalysisException(msg, ex);
|
||||
} finally {
|
||||
if (bos != null) {
|
||||
try {
|
||||
bos.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.log(Level.FINEST, null, ex);
|
||||
}
|
||||
}
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.log(Level.FINEST, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new ArchiveExtractionException(ex);
|
||||
} catch (Throwable ex) {
|
||||
throw new ArchiveExtractionException(ex);
|
||||
} finally {
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.log(Level.FINEST, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,11 +20,12 @@ package org.owasp.dependencycheck.analyzer;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
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;
|
||||
|
||||
@@ -41,9 +42,28 @@ public class PythonDistributionAnalyzerTest extends BaseTest {
|
||||
* is thrown when an exception occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testAnalyze() throws Exception {
|
||||
public void testAnalyzeWheel() throws AnalysisException {
|
||||
djangoAssertions(new Dependency(BaseTest.getResourceAsFile(this,
|
||||
"Django-1.7.2-py2.py3-none-any.whl")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of inspect method, of class JarAnalyzer.
|
||||
*
|
||||
* @throws Exception
|
||||
* is thrown when an exception occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testAnalyzeSitePackage() throws AnalysisException {
|
||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(
|
||||
this, "Django-1.7.2-py2.py3-none-any.whl"));
|
||||
this, "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"));
|
||||
@@ -54,9 +74,24 @@ public class PythonDistributionAnalyzerTest extends BaseTest {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertTrue(
|
||||
"implementation-version of 1.7.2 not found in Django wheel.",
|
||||
found);
|
||||
assertTrue("Version 1.7.2 not found in Django dependency.", found);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnalyzeEggInfo() throws AnalysisException {
|
||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(
|
||||
this, "site-packages/eggutils-0.0.2-py2.7.egg-info/PKG-INFO"));
|
||||
new PythonDistributionAnalyzer().analyze(result, null);
|
||||
assertTrue("Expected vendor evidence to contain \"python\".", result
|
||||
.getVendorEvidence().toString().contains("python"));
|
||||
boolean found = false;
|
||||
for (final Evidence e : result.getVersionEvidence()) {
|
||||
if ("Version".equals(e.getName()) && "0.0.2".equals(e.getValue())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertTrue("Version 0.0.2 not found in eggutils dependency.", found);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,8 +99,10 @@ public class PythonDistributionAnalyzerTest extends BaseTest {
|
||||
*/
|
||||
@Test
|
||||
public void testGetSupportedExtensions() {
|
||||
assertEquals("Supported extensions should just be \"whl\".",
|
||||
(Set<String>) Collections.singleton("whl"),
|
||||
assertEquals(
|
||||
"Supported extensions should just be \"whl\", \"METADATA\" and \"PKG-INFO\".",
|
||||
new HashSet<String>(Arrays
|
||||
.asList("whl", "METADATA", "PKG-INFO")),
|
||||
new PythonDistributionAnalyzer().getSupportedExtensions());
|
||||
}
|
||||
|
||||
@@ -83,7 +120,12 @@ public class PythonDistributionAnalyzerTest extends BaseTest {
|
||||
*/
|
||||
@Test
|
||||
public void testSupportsExtension() {
|
||||
final PythonDistributionAnalyzer analyzer = new PythonDistributionAnalyzer();
|
||||
assertTrue("Should support \"whl\" extension.",
|
||||
new PythonDistributionAnalyzer().supportsExtension("whl"));
|
||||
analyzer.supportsExtension("whl"));
|
||||
assertTrue("Should support \"METADATA\" extension.",
|
||||
analyzer.supportsExtension("METADATA"));
|
||||
assertTrue("Should support \"METADATA\" extension.",
|
||||
analyzer.supportsExtension("PKG-INFO"));
|
||||
}
|
||||
}
|
||||
@@ -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,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
|
||||
@@ -0,0 +1,28 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: eggutils
|
||||
Version: 0.0.2
|
||||
Summary: A set of utilities to create/manipulate eggs
|
||||
Home-page: http://pypi.python.org/pypi/eggutils
|
||||
Author: David Cournapeau
|
||||
Author-email: cournape@gmail.com
|
||||
License: BSD
|
||||
Description: Set of utilities to manipulate and create eggs without setuptools, and outside
|
||||
distutils context (i.e. without running setup).
|
||||
|
||||
Making an egg containing DLL
|
||||
============================
|
||||
|
||||
Given Windows model for DLLs, when several python packages depends on the same
|
||||
dll to be shared between extensions, it may be useful to have a "DLL egg" which
|
||||
put the dlls within the python installation such as the dll are automatically
|
||||
found by any extension to the corresponding python interpreter.
|
||||
|
||||
Usage:
|
||||
|
||||
::
|
||||
make-dll-egg -m PKG-INFO foo.dll bar.dll
|
||||
|
||||
This will create a DLL with metadata taken from the PKG-INFO file, containing
|
||||
both foo and bar dlls.
|
||||
|
||||
Platform: any
|
||||
@@ -0,0 +1,10 @@
|
||||
README
|
||||
setup.cfg
|
||||
setup.py
|
||||
eggutils/__init__.py
|
||||
eggutils/eggutils.py
|
||||
eggutils.egg-info/PKG-INFO
|
||||
eggutils.egg-info/SOURCES.txt
|
||||
eggutils.egg-info/dependency_links.txt
|
||||
eggutils.egg-info/entry_points.txt
|
||||
eggutils.egg-info/top_level.txt
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[console_scripts]
|
||||
make-dll-egg = eggutils.eggutils:wrap_main
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
../eggutils/__init__.py
|
||||
../eggutils/eggutils.py
|
||||
../eggutils/__init__.pyc
|
||||
../eggutils/eggutils.pyc
|
||||
./
|
||||
SOURCES.txt
|
||||
dependency_links.txt
|
||||
top_level.txt
|
||||
entry_points.txt
|
||||
PKG-INFO
|
||||
../../../../bin/make-dll-egg
|
||||
@@ -0,0 +1 @@
|
||||
eggutils
|
||||
Reference in New Issue
Block a user