diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzer.java index 635e1f7fd..1f60c3f7a 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzer.java @@ -47,298 +47,291 @@ 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. + * Used to analyze a Wheel or egg distriution files, or their contents in unzipped form, and collect information that can be used + * to determine the associated CPE. * * @author Dale Visser */ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer { - /** - * Name of egg metatdata files to analyze. - */ - private static final String PKG_INFO = "PKG-INFO"; + /** + * Name of 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"; + /** + * 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 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 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 name of the analyzer. + */ + private static final String ANALYZER_NAME = "Python Distribution Analyzer"; + /** + * The phase that this analyzer is intended to run in. + */ + private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION; - /** - * The set of file extensions supported by this analyzer. - */ - private static final Set EXTENSIONS = newHashSet("whl", "egg", - "zip", METADATA, PKG_INFO); + /** + * The set of file extensions supported by this analyzer. + */ + private static final Set EXTENSIONS = newHashSet("whl", "egg", + "zip", METADATA, PKG_INFO); - /** - * Used to match on egg archive candidate extenssions. - */ - private static final Pattern EGG_OR_ZIP = Pattern.compile("egg|zip"); + /** + * 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; + /** + * 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 *.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 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 "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); + /** + * Filter that detects files named "PKG-INFO". + */ + private static final FilenameFilter PKG_INFO_FILTER = new NameFileFilter( + PKG_INFO); - /** - * Returns a list of file EXTENSIONS supported by this analyzer. - * - * @return a list of file EXTENSIONS supported by this analyzer. - */ - @Override - public Set getSupportedExtensions() { - return EXTENSIONS; - } + /** + * Returns a list of file EXTENSIONS supported by this analyzer. + * + * @return a list of file EXTENSIONS supported by this analyzer. + */ + @Override + public Set getSupportedExtensions() { + return EXTENSIONS; + } - /** - * Returns the name of the analyzer. - * - * @return the name of the analyzer. - */ - @Override - public String getName() { - return ANALYZER_NAME; - } + /** + * Returns the 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 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; - } + /** + * 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); - } - } - } - } + @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); - } + 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)); - } + 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); - } - } + /** + * 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"); - } - } - } + /** + * 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"); - } - } + /** + * 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 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 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; - } + 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; + /** + * Retrieves the next temporary destingation directory for extracting an archive. + * + * @return a directory + * @throws AnalysisException thrown if unable to create temporary directory + */ + private File getNextTempDirectory() throws AnalysisException { + File directory; // getting an exception for some directories not being able to be - // created; might be because the directory already exists? - do { - dirCount += 1; - directory = new File(tempFileLocation, String.valueOf(dirCount)); - } while (directory.exists()); - if (!directory.mkdirs()) { - throw new AnalysisException(String.format( - "Unable to create temp directory '%s'.", - directory.getAbsolutePath())); - } - return directory; - } -} \ No newline at end of file + // 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; + } +}