From f8f17571584a68e790dc92fc3ab7c43352d26109 Mon Sep 17 00:00:00 2001 From: Amodio Pesce Date: Fri, 24 Jan 2020 08:16:35 +0100 Subject: [PATCH 1/3] Use pecoff4j to read dll/exe version information metadata on windows Dll and exe on windows that are not .NET assembly are only analyzed by the filename. This is often not good enough because the filename can contain other numbers (x86, x64, ...) other than the version. To improve the situation I've reduced the confidence of the filename parsed version and created a new analyzer The FileVersionAnalyzer use the pecoff4j library to extract, if possible, the version from the file metadata --- .gitignore | 4 +- core/pom.xml | 5 + .../analyzer/FileNameAnalyzer.java | 2 +- .../analyzer/FileVersionAnalyzer.java | 131 ++++++++++++++++++ ...rg.owasp.dependencycheck.analyzer.Analyzer | 1 + .../owasp/dependencycheck/utils/Settings.java | 4 + 6 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/org/owasp/dependencycheck/analyzer/FileVersionAnalyzer.java diff --git a/.gitignore b/.gitignore index 6336b982d41..57122b3d187 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,6 @@ velocity.log /core/data/ # Apple macOS operating system Desktop Services Store files -.DS_Store \ No newline at end of file +.DS_Store +/.vscode +/maven/.factorypath diff --git a/core/pom.xml b/core/pom.xml index ec7fd016681..3698f54405a 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -168,6 +168,11 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. + + org.whitesource + pecoff4j + 0.0.2.1 + org.apache.commons commons-jcs-core diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java index 7444a9233d6..2d0c37bd244 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java @@ -130,7 +130,7 @@ protected void analyzeDependency(Dependency dependency, Engine engine) throws An if (version.getVersionParts() == null || version.getVersionParts().size() < 2) { dependency.addEvidence(EvidenceType.VERSION, "file", "version", version.toString(), Confidence.MEDIUM); } else { - dependency.addEvidence(EvidenceType.VERSION, "file", "version", version.toString(), Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "file", "version", version.toString(), Confidence.HIGH); } dependency.addEvidence(EvidenceType.VERSION, "file", "name", packageName, Confidence.MEDIUM); } diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/FileVersionAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/FileVersionAnalyzer.java new file mode 100644 index 00000000000..c4fa8e6cc9b --- /dev/null +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/FileVersionAnalyzer.java @@ -0,0 +1,131 @@ +/* + * 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) 2012 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import java.io.File; +import java.io.IOError; +import java.io.IOException; + +import org.apache.commons.io.FilenameUtils; +import org.boris.pecoff4j.PE; +import org.boris.pecoff4j.ResourceDirectory; +import org.boris.pecoff4j.ResourceEntry; +import org.boris.pecoff4j.constant.ResourceType; +import org.boris.pecoff4j.io.PEParser; +import org.boris.pecoff4j.io.ResourceParser; +import org.boris.pecoff4j.resources.StringFileInfo; +import org.boris.pecoff4j.resources.StringTable; +import org.boris.pecoff4j.resources.VersionInfo; +import org.boris.pecoff4j.util.ResourceHelper; + +import javax.annotation.concurrent.ThreadSafe; + +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.EvidenceType; +import org.owasp.dependencycheck.utils.Settings; + +/** + * Takes a dependency and analyze the version from the windows file version metadata, only for .exe and .dll + * + * @author Amodio Pesce + */ +@ThreadSafe +public class FileVersionAnalyzer extends AbstractAnalyzer { + + // + /** + * The name of the analyzer. + */ + private static final String ANALYZER_NAME = "File Version Analyzer"; + + /** + * The phase that this analyzer is intended to run in. + */ + private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION; + + /** + * 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. + */ + @Override + public AnalysisPhase getAnalysisPhase() { + return ANALYSIS_PHASE; + } + + /** + *

+ * Returns the setting key to determine if the analyzer is enabled.

+ * + * @return the key for the analyzer's enabled property + */ + @Override + protected String getAnalyzerEnabledSettingKey() { + return Settings.KEYS.ANALYZER_FILE_VERSION_ENABLED; + } + + /** + * Collects information about the file name. + * + * @param dependency the dependency to analyze. + * @param engine the engine that is scanning the dependencies + */ + @Override + protected void analyzeDependency(final Dependency dependency, final Engine engine) { + // strip any path information that may get added by ArchiveAnalyzer, etc. + try { + final File fileToCheck = dependency.getActualFile(); + final String ext = FilenameUtils.getExtension(fileToCheck.getName()); + if (ext.equals("dll") || ext.equals(".exe")) { + final PE pe = PEParser.parse(fileToCheck.getPath()); + final ResourceDirectory rd = pe.getImageData().getResourceTable(); + final ResourceEntry[] entries = ResourceHelper.findResources(rd, ResourceType.VERSION_INFO); + for (int i = 0; i < entries.length; i++) { + final byte[] data = entries[i].getData(); + final VersionInfo version = ResourceParser.readVersionInfo(data); + final StringFileInfo strings = version.getStringFileInfo(); + final StringTable table = strings.getTable(0); + for (int j = 0; j < table.getCount(); j++) { + final String key = table.getString(j).getKey(); + final String value = table.getString(j).getValue(); + if (key.equals("ProductVersion")) { + dependency.addEvidence(EvidenceType.VERSION, "winmetadata", "version", value, + Confidence.HIGHEST); + } + } + } + } + } catch (final IOException e) + { + + } + } +} \ No newline at end of file diff --git a/core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer b/core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer index aa49988569c..ba769bec041 100644 --- a/core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer +++ b/core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer @@ -1,5 +1,6 @@ org.owasp.dependencycheck.analyzer.ArchiveAnalyzer org.owasp.dependencycheck.analyzer.FileNameAnalyzer +org.owasp.dependencycheck.analyzer.FileVersionAnalyzer org.owasp.dependencycheck.analyzer.JarAnalyzer org.owasp.dependencycheck.analyzer.HintAnalyzer org.owasp.dependencycheck.analyzer.CPEAnalyzer diff --git a/utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java b/utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java index 2d84200e560..547183e1c3a 100644 --- a/utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java +++ b/utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java @@ -550,6 +550,10 @@ public static final class KEYS { * The key to determine if the File Name analyzer is enabled. */ public static final String ANALYZER_FILE_NAME_ENABLED = "analyzer.filename.enabled"; + /** + * The key to determine if the File Version analyzer is enabled. + */ + public static final String ANALYZER_FILE_VERSION_ENABLED = "analyzer.fileveraion.enabled"; /** * The key to determine if the Hint analyzer is enabled. */ From ac3fb7bc062edba61a473a27470977e95415d4cf Mon Sep 17 00:00:00 2001 From: Amodio Date: Fri, 24 Jan 2020 12:07:34 +0100 Subject: [PATCH 2/3] Fix typo in fileversion analyzer settings string --- .../src/main/java/org/owasp/dependencycheck/utils/Settings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java b/utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java index 547183e1c3a..181eb3ef87c 100644 --- a/utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java +++ b/utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java @@ -553,7 +553,7 @@ public static final class KEYS { /** * The key to determine if the File Version analyzer is enabled. */ - public static final String ANALYZER_FILE_VERSION_ENABLED = "analyzer.fileveraion.enabled"; + public static final String ANALYZER_FILE_VERSION_ENABLED = "analyzer.fileversion.enabled"; /** * The key to determine if the Hint analyzer is enabled. */ From 9a7545b0ba5e16f6170949b2c87da9231a98dde1 Mon Sep 17 00:00:00 2001 From: Jeremy Long Date: Fri, 24 Jan 2020 19:34:02 -0500 Subject: [PATCH 3/3] updated PR #2446 to move this to a more full fledged PE Analyzer retrieving more than just the version number from the PE Headers --- cli/src/test/resources/sample.properties | 1 + cli/src/test/resources/sample2.properties | 1 + core/pom.xml | 1 - .../org/owasp/dependencycheck/Engine.java | 1 + .../analyzer/AnalysisPhase.java | 4 + .../analyzer/AssemblyAnalyzer.java | 2 +- .../analyzer/FileVersionAnalyzer.java | 131 -------- .../dependencycheck/analyzer/PEAnalyzer.java | 282 ++++++++++++++++++ .../dependency/EvidenceCollection.java | 12 + ...rg.owasp.dependencycheck.analyzer.Analyzer | 2 +- .../main/resources/dependencycheck.properties | 1 + .../analyzer/AssemblyAnalyzerTest.java | 8 +- .../analyzer/PEAnalyzerTest.java | 99 ++++++ .../test/resources/dependencycheck.properties | 1 + pom.xml | 5 + .../owasp/dependencycheck/utils/Settings.java | 2 +- .../test/resources/dependencycheck.properties | 1 + 17 files changed, 412 insertions(+), 142 deletions(-) delete mode 100644 core/src/main/java/org/owasp/dependencycheck/analyzer/FileVersionAnalyzer.java create mode 100644 core/src/main/java/org/owasp/dependencycheck/analyzer/PEAnalyzer.java create mode 100644 core/src/test/java/org/owasp/dependencycheck/analyzer/PEAnalyzerTest.java diff --git a/cli/src/test/resources/sample.properties b/cli/src/test/resources/sample.properties index 3b214ca03e9..50afbad00d3 100644 --- a/cli/src/test/resources/sample.properties +++ b/cli/src/test/resources/sample.properties @@ -27,6 +27,7 @@ analyzer.dependencybundling.enabled=true analyzer.dependencymerging.enabled=true analyzer.falsepositive.enabled=true analyzer.filename.enabled=true +analyzer.pe.enabled=true analyzer.hint.enabled=true analyzer.nvdcve.enabled=true analyzer.vulnerabilitysuppression.enabled=true diff --git a/cli/src/test/resources/sample2.properties b/cli/src/test/resources/sample2.properties index 8f38c54a430..d5b553a956c 100644 --- a/cli/src/test/resources/sample2.properties +++ b/cli/src/test/resources/sample2.properties @@ -27,6 +27,7 @@ analyzer.dependencybundling.enabled=false analyzer.dependencymerging.enabled=false analyzer.falsepositive.enabled=false analyzer.filename.enabled=false +analyzer.pe.enabled=false analyzer.hint.enabled=false analyzer.nvdcve.enabled=false analyzer.vulnerabilitysuppression.enabled=false diff --git a/core/pom.xml b/core/pom.xml index 3698f54405a..b820bfffd42 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -171,7 +171,6 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. org.whitesource pecoff4j - 0.0.2.1 org.apache.commons diff --git a/core/src/main/java/org/owasp/dependencycheck/Engine.java b/core/src/main/java/org/owasp/dependencycheck/Engine.java index 8fe72d53aac..f178fe87632 100644 --- a/core/src/main/java/org/owasp/dependencycheck/Engine.java +++ b/core/src/main/java/org/owasp/dependencycheck/Engine.java @@ -98,6 +98,7 @@ public enum Mode { INITIAL, PRE_INFORMATION_COLLECTION, INFORMATION_COLLECTION, + INFORMATION_COLLECTION2, POST_INFORMATION_COLLECTION ), /** diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/AnalysisPhase.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/AnalysisPhase.java index d9e9e916478..818ba2186c8 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/AnalysisPhase.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/AnalysisPhase.java @@ -36,6 +36,10 @@ public enum AnalysisPhase { * Information collection phase. */ INFORMATION_COLLECTION, + /** + * Information collection phase 2. + */ + INFORMATION_COLLECTION2, /** * Post information collection phase. */ diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzer.java index 31b0657b94e..a7bef475bc9 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzer.java @@ -215,7 +215,7 @@ public void analyzeDependency(Dependency dependency, Engine engine) throws Analy dependency.addEvidence(EvidenceType.VERSION, "grokassembly", "ProductVersion", data.getProductVersion(), Confidence.HIGHEST); } if (!StringUtils.isEmpty(data.getFileVersion())) { - dependency.addEvidence(EvidenceType.VERSION, "grokassembly", "FileVersion", data.getFileVersion(), Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "grokassembly", "FileVersion", data.getFileVersion(), Confidence.HIGH); } if (data.getFileVersion() != null && data.getProductVersion() != null) { diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/FileVersionAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/FileVersionAnalyzer.java deleted file mode 100644 index c4fa8e6cc9b..00000000000 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/FileVersionAnalyzer.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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) 2012 Jeremy Long. All Rights Reserved. - */ -package org.owasp.dependencycheck.analyzer; - -import java.io.File; -import java.io.IOError; -import java.io.IOException; - -import org.apache.commons.io.FilenameUtils; -import org.boris.pecoff4j.PE; -import org.boris.pecoff4j.ResourceDirectory; -import org.boris.pecoff4j.ResourceEntry; -import org.boris.pecoff4j.constant.ResourceType; -import org.boris.pecoff4j.io.PEParser; -import org.boris.pecoff4j.io.ResourceParser; -import org.boris.pecoff4j.resources.StringFileInfo; -import org.boris.pecoff4j.resources.StringTable; -import org.boris.pecoff4j.resources.VersionInfo; -import org.boris.pecoff4j.util.ResourceHelper; - -import javax.annotation.concurrent.ThreadSafe; - -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.EvidenceType; -import org.owasp.dependencycheck.utils.Settings; - -/** - * Takes a dependency and analyze the version from the windows file version metadata, only for .exe and .dll - * - * @author Amodio Pesce - */ -@ThreadSafe -public class FileVersionAnalyzer extends AbstractAnalyzer { - - // - /** - * The name of the analyzer. - */ - private static final String ANALYZER_NAME = "File Version Analyzer"; - - /** - * The phase that this analyzer is intended to run in. - */ - private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION; - - /** - * 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. - */ - @Override - public AnalysisPhase getAnalysisPhase() { - return ANALYSIS_PHASE; - } - - /** - *

- * Returns the setting key to determine if the analyzer is enabled.

- * - * @return the key for the analyzer's enabled property - */ - @Override - protected String getAnalyzerEnabledSettingKey() { - return Settings.KEYS.ANALYZER_FILE_VERSION_ENABLED; - } - - /** - * Collects information about the file name. - * - * @param dependency the dependency to analyze. - * @param engine the engine that is scanning the dependencies - */ - @Override - protected void analyzeDependency(final Dependency dependency, final Engine engine) { - // strip any path information that may get added by ArchiveAnalyzer, etc. - try { - final File fileToCheck = dependency.getActualFile(); - final String ext = FilenameUtils.getExtension(fileToCheck.getName()); - if (ext.equals("dll") || ext.equals(".exe")) { - final PE pe = PEParser.parse(fileToCheck.getPath()); - final ResourceDirectory rd = pe.getImageData().getResourceTable(); - final ResourceEntry[] entries = ResourceHelper.findResources(rd, ResourceType.VERSION_INFO); - for (int i = 0; i < entries.length; i++) { - final byte[] data = entries[i].getData(); - final VersionInfo version = ResourceParser.readVersionInfo(data); - final StringFileInfo strings = version.getStringFileInfo(); - final StringTable table = strings.getTable(0); - for (int j = 0; j < table.getCount(); j++) { - final String key = table.getString(j).getKey(); - final String value = table.getString(j).getValue(); - if (key.equals("ProductVersion")) { - dependency.addEvidence(EvidenceType.VERSION, "winmetadata", "version", value, - Confidence.HIGHEST); - } - } - } - } - } catch (final IOException e) - { - - } - } -} \ No newline at end of file diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/PEAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/PEAnalyzer.java new file mode 100644 index 00000000000..23b102c2057 --- /dev/null +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/PEAnalyzer.java @@ -0,0 +1,282 @@ +/* + * 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) 2020 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import com.github.packageurl.MalformedPackageURLException; +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; + +import org.boris.pecoff4j.PE; +import org.boris.pecoff4j.ResourceDirectory; +import org.boris.pecoff4j.ResourceEntry; +import org.boris.pecoff4j.constant.ResourceType; +import org.boris.pecoff4j.io.PEParser; +import org.boris.pecoff4j.io.ResourceParser; +import org.boris.pecoff4j.resources.StringFileInfo; +import org.boris.pecoff4j.resources.StringTable; +import org.boris.pecoff4j.resources.VersionInfo; +import org.boris.pecoff4j.util.ResourceHelper; + +import javax.annotation.concurrent.ThreadSafe; +import org.apache.commons.lang3.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.Evidence; +import org.owasp.dependencycheck.dependency.EvidenceType; +import org.owasp.dependencycheck.dependency.naming.GenericIdentifier; +import org.owasp.dependencycheck.dependency.naming.PurlIdentifier; +import org.owasp.dependencycheck.exception.InitializationException; +import org.owasp.dependencycheck.utils.DependencyVersion; +import org.owasp.dependencycheck.utils.DependencyVersionUtil; +import org.owasp.dependencycheck.utils.FileFilterBuilder; +import org.owasp.dependencycheck.utils.FileUtils; +import org.owasp.dependencycheck.utils.Settings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Takes a dependency and analyze the PE header for meta data that can be used + * to identify the library. + * + * @author Amodio Pesce + */ +@ThreadSafe +@Experimental +public class PEAnalyzer extends AbstractFileTypeAnalyzer { + + // + /** + * Logger + */ + private static final Logger LOGGER = LoggerFactory.getLogger(AssemblyAnalyzer.class); + /** + * The name of the analyzer. + */ + private static final String ANALYZER_NAME = "PE Analyzer"; + + /** + * The phase that this analyzer is intended to run in. + */ + private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION2; + /** + * The set of file extensions supported by this analyzer. + */ + private static final String[] EXTENSIONS = {"exe", "dll"}; + + /** + * The file filter used to determine which files this analyzer supports. + */ + private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(EXTENSIONS).build(); + /** + * A descriptor for the type of dependencies processed or added by this + * analyzer. + */ + public static final String DEPENDENCY_ECOSYSTEM = "native"; + + /** + * 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. + */ + @Override + public AnalysisPhase getAnalysisPhase() { + return ANALYSIS_PHASE; + } + + /** + *

+ * Returns the setting key to determine if the analyzer is enabled.

+ * + * @return the key for the analyzer's enabled property + */ + @Override + protected String getAnalyzerEnabledSettingKey() { + return Settings.KEYS.ANALYZER_PE_ENABLED; + } + + /** + * Returns the FileFilter. + * + * @return the FileFilter + */ + @Override + protected FileFilter getFileFilter() { + return FILTER; + } + + @Override + protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { + //nothing to prepare + } + + /** + * Collects information about the file name. + * + * @param dependency the dependency to analyze. + * @param engine the engine that is scanning the dependencies + * @throws AnalysisException is thrown if there is an error analyzing the PE + * file. + */ + @Override + protected void analyzeDependency(final Dependency dependency, final Engine engine) throws AnalysisException { + for (Evidence e : dependency.getEvidence()) { + if ("grokassembly".equals(e.getSource())) { + LOGGER.debug("Skipping {} because it was already analyzed by the Assembly Analyzer", dependency.getFileName()); + return; + } + } + try { + final File fileToCheck = dependency.getActualFile(); + final PE pe = PEParser.parse(fileToCheck.getPath()); + final ResourceDirectory rd = pe.getImageData().getResourceTable(); + final ResourceEntry[] entries = ResourceHelper.findResources(rd, ResourceType.VERSION_INFO); + for (ResourceEntry entrie : entries) { + final byte[] data = entrie.getData(); + final VersionInfo version = ResourceParser.readVersionInfo(data); + final StringFileInfo strings = version.getStringFileInfo(); + final StringTable table = strings.getTable(0); + String pVersion = null; + String fVersion = null; + + for (int j = 0; j < table.getCount(); j++) { + final String key = table.getString(j).getKey(); + final String value = table.getString(j).getValue(); + switch (key) { + case "ProductVersion": + dependency.addEvidence(EvidenceType.VERSION, "PE Header", "ProductVersion", value, Confidence.HIGHEST); + pVersion = value; + break; + case "CompanyName": + dependency.addEvidence(EvidenceType.VENDOR, "PE Header", "CompanyName", value, Confidence.HIGHEST); + break; + case "FileVersion": + dependency.addEvidence(EvidenceType.VERSION, "PE Header", "FileVersion", value, Confidence.HIGH); + fVersion = value; + break; + case "InternalName": + dependency.addEvidence(EvidenceType.PRODUCT, "PE Header", "InternalName", value, Confidence.MEDIUM); + determineDependencyName(dependency, value); + break; + case "LegalCopyright": + dependency.addEvidence(EvidenceType.VENDOR, "PE Header", "LegalCopyright", value, Confidence.HIGHEST); + if (dependency.getLicense() != null && dependency.getLicense().length() > 0) { + dependency.setLicense(dependency.getLicense() + "/n/nLegal Copyright: " + value); + } else { + dependency.setLicense("Legal Copyright: " + value); + } + break; + case "OriginalFilename": + dependency.addEvidence(EvidenceType.VERSION, "PE Header", "OriginalFilename", value, Confidence.MEDIUM); + determineDependencyName(dependency, value); + break; + case "ProductName": + dependency.addEvidence(EvidenceType.PRODUCT, "PE Header", "ProductName", value, Confidence.HIGHEST); + determineDependencyName(dependency, value); + break; + } + if (fVersion != null && pVersion != null) { + final int max = fVersion.length() > pVersion.length() ? pVersion.length() : fVersion.length(); + int pos; + for (pos = 0; pos < max; pos++) { + if (fVersion.charAt(pos) != pVersion.charAt(pos)) { + break; + } + } + final DependencyVersion fileVersion = DependencyVersionUtil.parseVersion(fVersion, true); + final DependencyVersion productVersion = DependencyVersionUtil.parseVersion(pVersion, true); + if (pos > 0) { + final DependencyVersion matchingVersion = DependencyVersionUtil.parseVersion(fVersion.substring(0, pos), true); + if (fileVersion != null && fileVersion.toString().length() == fVersion.length()) { + if (matchingVersion != null && matchingVersion.getVersionParts().size() > 2) { + dependency.addEvidence(EvidenceType.VERSION, "PE Header", "FilteredVersion", + matchingVersion.toString(), Confidence.HIGHEST); + dependency.setVersion(matchingVersion.toString()); + } + } + } + if (dependency.getVersion() == null) { + if (fVersion.length() >= pVersion.length()) { + if (fileVersion != null && fileVersion.toString().length() == fVersion.length()) { + dependency.setVersion(fileVersion.toString()); + } else if (productVersion != null && productVersion.toString().length() == pVersion.length()) { + dependency.setVersion(productVersion.toString()); + } + } else { + if (productVersion != null && productVersion.toString().length() == pVersion.length()) { + dependency.setVersion(productVersion.toString()); + } else if (fileVersion != null && fileVersion.toString().length() == fVersion.length()) { + dependency.setVersion(fileVersion.toString()); + } + } + } + } else if (pVersion != null) { + final DependencyVersion productVersion = DependencyVersionUtil.parseVersion(pVersion, true); + if (productVersion != null && dependency.getActualFile().getName().contains(productVersion.toString())) { + dependency.setVersion(productVersion.toString()); + } + } else if (fVersion != null) { + final DependencyVersion fileVersion = DependencyVersionUtil.parseVersion(fVersion, true); + if (fileVersion != null && dependency.getActualFile().getName().contains(fileVersion.toString())) { + dependency.setVersion(fileVersion.toString()); + } + } + if (dependency.getName() != null && dependency.getVersion() != null) { + try { + dependency.addSoftwareIdentifier(new PurlIdentifier("generic", dependency.getName(), dependency.getVersion(), Confidence.MEDIUM)); + } catch (MalformedPackageURLException ex) { + LOGGER.debug("Unable to create Package URL Identifier for " + dependency.getName(), ex); + dependency.addSoftwareIdentifier(new GenericIdentifier( + String.format("%s@%s", dependency.getName(), dependency.getVersion()), + Confidence.MEDIUM)); + } + } + if (dependency.getEcosystem() == null) {//this could be an assembly + dependency.setEcosystem(DEPENDENCY_ECOSYSTEM); + } + } + } + } catch (IOException ex) { + throw new AnalysisException(ex); + } + } + + private void determineDependencyName(final Dependency dependency, final String value) { + if (dependency.getName() == null && StringUtils.containsIgnoreCase(dependency.getActualFile().getName(), value)) { + final String ext = FileUtils.getFileExtension(value); + if (ext != null) { + dependency.setName(value.substring(0, value.length() - ext.length() - 1)); + } else { + dependency.setName(value); + } + } + } +} diff --git a/core/src/main/java/org/owasp/dependencycheck/dependency/EvidenceCollection.java b/core/src/main/java/org/owasp/dependencycheck/dependency/EvidenceCollection.java index 8e5e7080009..3822de40d24 100644 --- a/core/src/main/java/org/owasp/dependencycheck/dependency/EvidenceCollection.java +++ b/core/src/main/java/org/owasp/dependencycheck/dependency/EvidenceCollection.java @@ -284,6 +284,18 @@ public synchronized Set getEvidence(EvidenceType type) { return null; } + /** + * Returns the unmodifiable set of evidence. + * + * @return the unmodifiable set of evidence + */ + public synchronized Set getEvidence() { + Set e = new HashSet<>(vendors); + e.addAll(products); + e.addAll(versions); + return Collections.unmodifiableSet(e); + } + /** * Tests if the evidence collection contains the given evidence. * diff --git a/core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer b/core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer index ba769bec041..d7dc3f89fa4 100644 --- a/core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer +++ b/core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer @@ -1,6 +1,6 @@ org.owasp.dependencycheck.analyzer.ArchiveAnalyzer org.owasp.dependencycheck.analyzer.FileNameAnalyzer -org.owasp.dependencycheck.analyzer.FileVersionAnalyzer +org.owasp.dependencycheck.analyzer.PEAnalyzer org.owasp.dependencycheck.analyzer.JarAnalyzer org.owasp.dependencycheck.analyzer.HintAnalyzer org.owasp.dependencycheck.analyzer.CPEAnalyzer diff --git a/core/src/main/resources/dependencycheck.properties b/core/src/main/resources/dependencycheck.properties index 315ab4a7fc3..f3240d6e64d 100644 --- a/core/src/main/resources/dependencycheck.properties +++ b/core/src/main/resources/dependencycheck.properties @@ -140,6 +140,7 @@ analyzer.dependencybundling.enabled=true analyzer.dependencymerging.enabled=true analyzer.falsepositive.enabled=true analyzer.filename.enabled=true +analyzer.pe.enabled=true analyzer.hint.enabled=true analyzer.nvdcve.enabled=true analyzer.vulnerabilitysuppression.enabled=true diff --git a/core/src/test/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzerTest.java b/core/src/test/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzerTest.java index d7c4ed0b549..583643933c2 100644 --- a/core/src/test/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzerTest.java +++ b/core/src/test/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzerTest.java @@ -18,14 +18,9 @@ package org.owasp.dependencycheck.analyzer; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; - -import org.apache.commons.io.IOUtils; import org.junit.After; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -42,7 +37,6 @@ import org.owasp.dependencycheck.dependency.Evidence; import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; -import org.owasp.dependencycheck.utils.FileUtils; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -118,7 +112,7 @@ public void testLog4Net() throws Exception { Dependency d = new Dependency(f); analyzer.analyze(d, null); - assertTrue(d.contains(EvidenceType.VERSION, new Evidence("grokassembly", "FileVersion", "1.2.13.0", Confidence.HIGHEST))); + assertTrue(d.contains(EvidenceType.VERSION, new Evidence("grokassembly", "FileVersion", "1.2.13.0", Confidence.HIGH))); assertEquals("1.2.13.0", d.getVersion()); assertTrue(d.contains(EvidenceType.VENDOR, new Evidence("grokassembly", "CompanyName", "The Apache Software Foundation", Confidence.HIGHEST))); assertTrue(d.contains(EvidenceType.PRODUCT, new Evidence("grokassembly", "ProductName", "log4net", Confidence.HIGHEST))); diff --git a/core/src/test/java/org/owasp/dependencycheck/analyzer/PEAnalyzerTest.java b/core/src/test/java/org/owasp/dependencycheck/analyzer/PEAnalyzerTest.java new file mode 100644 index 00000000000..441ff498647 --- /dev/null +++ b/core/src/test/java/org/owasp/dependencycheck/analyzer/PEAnalyzerTest.java @@ -0,0 +1,99 @@ +/* + * 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) 2020 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import java.io.File; +import org.junit.After; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import org.junit.Before; +import org.junit.Test; +import org.owasp.dependencycheck.BaseTest; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.analyzer.exception.UnexpectedAnalysisException; +import org.owasp.dependencycheck.dependency.Confidence; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.Evidence; +import org.owasp.dependencycheck.dependency.EvidenceType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Tests for the PEAnalyzer. + * + * @author Jeremy Long + * + */ +public class PEAnalyzerTest extends BaseTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(PEAnalyzerTest.class); + + private static final String LOG_KEY = "org.slf4j.simpleLogger.org.owasp.dependencycheck.analyzer.PEAnalyzer"; + + private PEAnalyzer analyzer; + + /** + * Sets up the analyzer. + * + * @throws Exception if anything goes sideways + */ + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + analyzer = new PEAnalyzer(); + analyzer.initialize(getSettings()); + analyzer.accept(new File("test.dll")); // trick into "thinking it is active" + analyzer.prepare(null); + } + + /** + * Tests to make sure the name is correct. + */ + @Test + public void testGetName() { + assertEquals("PE Analyzer", analyzer.getName()); + } + + @Test + public void testAnalysis() throws Exception { + File f = BaseTest.getResourceAsFile(this, "log4net.dll"); + + Dependency d = new Dependency(f); + analyzer.analyze(d, null); + assertTrue(d.contains(EvidenceType.VERSION, new Evidence("PE Header", "FileVersion", "1.2.13.0", Confidence.HIGH))); + assertEquals("1.2.13.0", d.getVersion()); + assertTrue(d.contains(EvidenceType.VENDOR, new Evidence("PE Header", "CompanyName", "The Apache Software Foundation", Confidence.HIGHEST))); + assertTrue(d.contains(EvidenceType.PRODUCT, new Evidence("PE Header", "ProductName", "log4net", Confidence.HIGHEST))); + assertEquals("log4net", d.getName()); + } + + @After + @Override + public void tearDown() throws Exception { + try { + analyzer.closeAnalyzer(); + } catch (Exception ex) { + throw new UnexpectedAnalysisException(ex); + } finally { + super.tearDown(); + } + } +} diff --git a/core/src/test/resources/dependencycheck.properties b/core/src/test/resources/dependencycheck.properties index d176bf7a424..f57a75eae1f 100644 --- a/core/src/test/resources/dependencycheck.properties +++ b/core/src/test/resources/dependencycheck.properties @@ -134,6 +134,7 @@ analyzer.dependencybundling.enabled=true analyzer.dependencymerging.enabled=true analyzer.falsepositive.enabled=true analyzer.filename.enabled=true +analyzer.pe.enabled=true analyzer.hint.enabled=true analyzer.nvdcve.enabled=true analyzer.vulnerabilitysuppression.enabled=true diff --git a/pom.xml b/pom.xml index 4ce39e0ef52..a53ff33d424 100644 --- a/pom.xml +++ b/pom.xml @@ -912,6 +912,11 @@ Copyright (c) 2012 - Jeremy Long compile true
+ + org.whitesource + pecoff4j + 0.0.2.1 + com.vdurmont semver4j diff --git a/utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java b/utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java index 181eb3ef87c..46c96670ade 100644 --- a/utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java +++ b/utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java @@ -553,7 +553,7 @@ public static final class KEYS { /** * The key to determine if the File Version analyzer is enabled. */ - public static final String ANALYZER_FILE_VERSION_ENABLED = "analyzer.fileversion.enabled"; + public static final String ANALYZER_PE_ENABLED = "analyzer.pe.enabled"; /** * The key to determine if the Hint analyzer is enabled. */ diff --git a/utils/src/test/resources/dependencycheck.properties b/utils/src/test/resources/dependencycheck.properties index 7d32d3101f0..0b192e8eb87 100644 --- a/utils/src/test/resources/dependencycheck.properties +++ b/utils/src/test/resources/dependencycheck.properties @@ -133,6 +133,7 @@ analyzer.dependencybundling.enabled=true analyzer.dependencymerging.enabled=true analyzer.falsepositive.enabled=true analyzer.filename.enabled=true +analyzer.pe.enabled=true analyzer.hint.enabled=true analyzer.nvdcve.enabled=true analyzer.vulnerabilitysuppression.enabled=true