From c2b174258262472c8ad525acd9843bfde5473a8f Mon Sep 17 00:00:00 2001 From: bjiang Date: Tue, 3 May 2016 12:41:39 -0400 Subject: [PATCH 01/10] support cocoapods for swift --- .../analyzer/CocoaPodsAnalyzer.java | 257 ++++++++++++++++++ ...rg.owasp.dependencycheck.analyzer.Analyzer | 1 + .../analyzer/CocoaPodsAnalyzerTest.java | 89 ++++++ .../swift/cocoapods/EasyPeasy.podspec | 25 ++ 4 files changed, 372 insertions(+) create mode 100644 dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java create mode 100644 dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzerTest.java create mode 100644 dependency-check-core/src/test/resources/swift/cocoapods/EasyPeasy.podspec diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java new file mode 100644 index 000000000..9f5c4700a --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java @@ -0,0 +1,257 @@ +/* + * This file is part of dependency-check-core. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (c) 2015 Bianca Jiang. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.json.JsonObject; +import javax.json.JsonString; +import javax.json.JsonValue; + +import org.apache.commons.io.FileUtils; +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.dependency.Confidence; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.EvidenceCollection; +import org.owasp.dependencycheck.utils.FileFilterBuilder; +import org.owasp.dependencycheck.utils.Settings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Bianca Xue Jiang + * + */ +public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { + + /** + * The logger. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(CocoaPodsAnalyzer.class); + + /** + * The name of the analyzer. + */ + private static final String ANALYZER_NAME = "CocoaPods Package Analyzer"; + + /** + * The phase that this analyzer is intended to run in. + */ + private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION; + + /** + * The file name to scan. + */ + public static final String PODSPEC = "podspec"; + /** + * Filter that detects files named "package.json". + */ + private static final FileFilter PODSPEC_FILTER = FileFilterBuilder.newInstance().addExtensions(PODSPEC).build(); + + + /** + * The capture group #1 is the block variable. + * e.g. "Pod::Spec.new do |spec|" + */ + private static final Pattern PODSPEC_BLOCK_PATTERN + = Pattern.compile("Pod::Spec\\.new\\s+?do\\s+?\\|(.+?)\\|"); + + + /** + * Returns the FileFilter + * + * @return the FileFilter + */ + @Override + protected FileFilter getFileFilter() { + return PODSPEC_FILTER; + } + + @Override + protected void initializeFileTypeAnalyzer() throws Exception { + // NO-OP + } + + /** + * 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 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_NODE_PACKAGE_ENABLED; + } + + @Override + protected void analyzeFileType(Dependency dependency, Engine engine) + throws AnalysisException { + + String contents; + try { + contents = FileUtils.readFileToString(dependency.getActualFile()); + } catch (IOException e) { + throw new AnalysisException( + "Problem occurred while reading dependency file.", e); + } + final Matcher matcher = PODSPEC_BLOCK_PATTERN.matcher(contents); + if (matcher.find()) { + contents = contents.substring(matcher.end()); + final String blockVariable = matcher.group(1); + + final EvidenceCollection vendor = dependency.getVendorEvidence(); + final EvidenceCollection product = dependency.getProductEvidence(); + final EvidenceCollection version = dependency.getVersionEvidence(); + + final String name = addStringEvidence(product, contents, blockVariable, "name", "name", Confidence.HIGHEST); + if (!name.isEmpty()) { + vendor.addEvidence(PODSPEC, "name_project", name, Confidence.LOW); + } + addStringEvidence(product, contents, blockVariable, "summary", "summary", Confidence.LOW); + + addStringEvidence(vendor, contents, blockVariable, "author", "authors?", Confidence.HIGHEST); + addStringEvidence(vendor, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST); + addStringEvidence(vendor, contents, blockVariable, "license", "licen[cs]es?", Confidence.HIGHEST); + + addStringEvidence(version, contents, blockVariable, "version", "version", Confidence.HIGHEST); + } + + setPackagePath(dependency); + +// final File file = dependency.getActualFile(); +// JsonReader jsonReader; +// try { +// jsonReader = Json.createReader(FileUtils.openInputStream(file)); +// } catch (IOException e) { +// throw new AnalysisException( +// "Problem occurred while reading dependency file.", e); +// } +// try { +// final JsonObject json = jsonReader.readObject(); +// final EvidenceCollection productEvidence = dependency.getProductEvidence(); +// final EvidenceCollection vendorEvidence = dependency.getVendorEvidence(); +// if (json.containsKey("name")) { +// final Object value = json.get("name"); +// if (value instanceof JsonString) { +// final String valueString = ((JsonString) value).getString(); +// productEvidence.addEvidence(PODSPEC, "name", valueString, Confidence.HIGHEST); +// vendorEvidence.addEvidence(PODSPEC, "name_project", String.format("%s_project", valueString), Confidence.LOW); +// } else { +// LOGGER.warn("JSON value not string as expected: {}", value); +// } +// } +// addToEvidence(json, productEvidence, "description"); +// addToEvidence(json, vendorEvidence, "author"); +// addToEvidence(json, dependency.getVersionEvidence(), "version"); +// dependency.setDisplayFileName(String.format("%s/%s", file.getParentFile().getName(), file.getName())); +// } catch (JsonException e) { +// LOGGER.warn("Failed to parse package.json file.", e); +// } finally { +// jsonReader.close(); +// } + } + + private String addStringEvidence(EvidenceCollection evidences, String contents, + String blockVariable, String field, String fieldPattern, Confidence confidence) { + String value = ""; + + //capture array value between [ ] + final Matcher arrayMatcher = Pattern.compile( + String.format("\\s*?%s\\.%s\\s*?=\\s*?\\[(.*?)\\]", blockVariable, fieldPattern), Pattern.CASE_INSENSITIVE).matcher(contents); + if(arrayMatcher.find()) { + String arrayValue = arrayMatcher.group(1); + value = arrayValue.replaceAll("['\"]", "").trim(); //strip quotes + } + //capture single value between quotes + else { + final Matcher matcher = Pattern.compile( + String.format("\\s*?%s\\.%s\\s*?=\\s*?(['\"])(.*?)\\1", blockVariable, fieldPattern), Pattern.CASE_INSENSITIVE).matcher(contents); + if (matcher.find()) { + value = matcher.group(2); + } + } + if(value.length() > 0) + evidences.addEvidence(PODSPEC, field, value, confidence); + + return value; + } + + private void setPackagePath(Dependency dep) { + File file = new File(dep.getFilePath()); + String parent = file.getParent(); + if(parent != null) + dep.setPackagePath(parent); + } + + /** + * Adds information to an evidence collection from the node json configuration. + * + * @param json information from node.js + * @param collection a set of evidence about a dependency + * @param key the key to obtain the data from the json information + */ + private void addToEvidence(JsonObject json, EvidenceCollection collection, String key) { + if (json.containsKey(key)) { + final JsonValue value = json.get(key); + if (value instanceof JsonString) { + collection.addEvidence(PODSPEC, key, ((JsonString) value).getString(), Confidence.HIGHEST); + } else if (value instanceof JsonObject) { + final JsonObject jsonObject = (JsonObject) value; + for (final Map.Entry entry : jsonObject.entrySet()) { + final String property = entry.getKey(); + final JsonValue subValue = entry.getValue(); + if (subValue instanceof JsonString) { + collection.addEvidence(PODSPEC, + String.format("%s.%s", key, property), + ((JsonString) subValue).getString(), + Confidence.HIGHEST); + } else { + LOGGER.warn("JSON sub-value not string as expected: {}", subValue); + } + } + } else { + LOGGER.warn("JSON value not string or JSON object as expected: {}", value); + } + } + } +} diff --git a/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer b/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer index b0d1c95c8..3377ab6bc 100644 --- a/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer +++ b/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer @@ -22,3 +22,4 @@ org.owasp.dependencycheck.analyzer.RubyGemspecAnalyzer org.owasp.dependencycheck.analyzer.RubyBundleInstallDeploymentAnalyzer org.owasp.dependencycheck.analyzer.RubyBundleAuditAnalyzer org.owasp.dependencycheck.analyzer.ComposerLockAnalyzer +org.owasp.dependencycheck.analyzer.CocoaPodsAnalyzer diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzerTest.java new file mode 100644 index 000000000..e41e9155b --- /dev/null +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzerTest.java @@ -0,0 +1,89 @@ +package org.owasp.dependencycheck.analyzer; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.owasp.dependencycheck.BaseTest; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.dependency.Dependency; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.io.File; + +/** + * Unit tests for CocoaPodsAnalyzer. + * + * @author Bianca Jiang + */ +public class CocoaPodsAnalyzerTest extends BaseTest { + + /** + * The analyzer to test. + */ + CocoaPodsAnalyzer analyzer; + + /** + * Correctly setup the analyzer for testing. + * + * @throws Exception thrown if there is a problem + */ + @Before + public void setUp() throws Exception { + analyzer = new CocoaPodsAnalyzer(); + analyzer.setFilesMatched(true); + analyzer.initialize(); + } + + /** + * Cleanup the analyzer's temp files, etc. + * + * @throws Exception thrown if there is a problem + */ + @After + public void tearDown() throws Exception { + analyzer.close(); + analyzer = null; + } + + /** + * Test of getName method, of class PythonDistributionAnalyzer. + */ + @Test + public void testGetName() { + assertThat(analyzer.getName(), is("CocoaPods Package Analyzer")); + } + + /** + * Test of supportsExtension method, of class PythonDistributionAnalyzer. + */ + @Test + public void testSupportsFiles() { + assertThat(analyzer.accept(new File("test.podspec")), is(true)); + } + + /** + * Test of inspect method, of class PythonDistributionAnalyzer. + * + * @throws AnalysisException is thrown when an exception occurs. + */ + @Test + public void testAnalyzePackageJson() throws AnalysisException { + final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, + "swift/cocoapods/EasyPeasy.podspec")); + analyzer.analyze(result, null); + final String vendorString = result.getVendorEvidence().toString(); + + assertThat(vendorString, containsString("Carlos Vidal")); + assertThat(vendorString, containsString("https://github.com/nakiostudio/EasyPeasy")); + assertThat(vendorString, containsString("MIT")); + assertThat(result.getProductEvidence().toString(), containsString("EasyPeasy")); + assertThat(result.getVersionEvidence().toString(), containsString("0.2.3")); + + System.out.println("vendor: " + vendorString); + System.out.println("product: " + result.getProductEvidence().toString()); + System.out.println("version: " + result.getVersionEvidence().toString()); + } +} diff --git a/dependency-check-core/src/test/resources/swift/cocoapods/EasyPeasy.podspec b/dependency-check-core/src/test/resources/swift/cocoapods/EasyPeasy.podspec new file mode 100644 index 000000000..52d0ef7a8 --- /dev/null +++ b/dependency-check-core/src/test/resources/swift/cocoapods/EasyPeasy.podspec @@ -0,0 +1,25 @@ +Pod::Spec.new do |s| + s.name = "EasyPeasy" + s.version = "0.2.3" + s.summary = "EasyPeasy is a Swift framework that eases the creation of + Autolayout constraints programmatically" + s.description = <<-DESC + EasyPeasy is a Swift framework that lets you create Autolayout constraints + programmatically without headaches and never ending boilerplate code. Besides the + basics, **EasyPeasy** resolves most of the constraint conflicts for you and lets + you attach to a constraint conditional closures that are evaluated before applying + a constraint, this lets you apply (or not) a constraint depending on platform, size + classes, orientation... or the state of your controller, easy peasy! + DESC + s.homepage = "https://github.com/nakiostudio/EasyPeasy" + s.license = 'MIT' + s.author = { "Carlos Vidal" => "nakioparkour@gmail.com" } + s.source = { :git => "https://github.com/nakiostudio/EasyPeasy.git", :tag => s.version.to_s } + s.social_media_url = 'https://twitter.com/carlostify' + + s.platform = :ios, '8.0' + s.requires_arc = true + + s.source_files = 'EasyPeasy/**/*' + s.frameworks = 'UIKit' +end From 5fcf2a26239b0d5aadb681a83263995dec1e6231 Mon Sep 17 00:00:00 2001 From: bjiang Date: Tue, 3 May 2016 14:53:25 -0400 Subject: [PATCH 02/10] get authors field --- .../analyzer/CocoaPodsAnalyzer.java | 40 ++----------------- 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java index 9f5c4700a..3e79cc8c2 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java @@ -20,6 +20,7 @@ package org.owasp.dependencycheck.analyzer; import java.io.File; import java.io.FileFilter; import java.io.IOException; +import java.nio.charset.Charset; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -129,7 +130,7 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { String contents; try { - contents = FileUtils.readFileToString(dependency.getActualFile()); + contents = FileUtils.readFileToString(dependency.getActualFile(), Charset.defaultCharset()); } catch (IOException e) { throw new AnalysisException( "Problem occurred while reading dependency file.", e); @@ -157,38 +158,6 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { } setPackagePath(dependency); - -// final File file = dependency.getActualFile(); -// JsonReader jsonReader; -// try { -// jsonReader = Json.createReader(FileUtils.openInputStream(file)); -// } catch (IOException e) { -// throw new AnalysisException( -// "Problem occurred while reading dependency file.", e); -// } -// try { -// final JsonObject json = jsonReader.readObject(); -// final EvidenceCollection productEvidence = dependency.getProductEvidence(); -// final EvidenceCollection vendorEvidence = dependency.getVendorEvidence(); -// if (json.containsKey("name")) { -// final Object value = json.get("name"); -// if (value instanceof JsonString) { -// final String valueString = ((JsonString) value).getString(); -// productEvidence.addEvidence(PODSPEC, "name", valueString, Confidence.HIGHEST); -// vendorEvidence.addEvidence(PODSPEC, "name_project", String.format("%s_project", valueString), Confidence.LOW); -// } else { -// LOGGER.warn("JSON value not string as expected: {}", value); -// } -// } -// addToEvidence(json, productEvidence, "description"); -// addToEvidence(json, vendorEvidence, "author"); -// addToEvidence(json, dependency.getVersionEvidence(), "version"); -// dependency.setDisplayFileName(String.format("%s/%s", file.getParentFile().getName(), file.getName())); -// } catch (JsonException e) { -// LOGGER.warn("Failed to parse package.json file.", e); -// } finally { -// jsonReader.close(); -// } } private String addStringEvidence(EvidenceCollection evidences, String contents, @@ -197,10 +166,9 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { //capture array value between [ ] final Matcher arrayMatcher = Pattern.compile( - String.format("\\s*?%s\\.%s\\s*?=\\s*?\\[(.*?)\\]", blockVariable, fieldPattern), Pattern.CASE_INSENSITIVE).matcher(contents); + String.format("\\s*?%s\\.%s\\s*?=\\s*?\\{\\s*?(.*?)\\s*?\\}", blockVariable, fieldPattern), Pattern.CASE_INSENSITIVE).matcher(contents); if(arrayMatcher.find()) { - String arrayValue = arrayMatcher.group(1); - value = arrayValue.replaceAll("['\"]", "").trim(); //strip quotes + value = arrayMatcher.group(1); } //capture single value between quotes else { From 043f8e05232c09df8e479c6a0b24587a5ddca145 Mon Sep 17 00:00:00 2001 From: bjiang Date: Tue, 3 May 2016 15:45:08 -0400 Subject: [PATCH 03/10] cleanup --- .../owasp/dependencycheck/analyzer/CocoaPodsAnalyzerTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzerTest.java index e41e9155b..8fffebc7a 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzerTest.java @@ -81,9 +81,5 @@ public class CocoaPodsAnalyzerTest extends BaseTest { assertThat(vendorString, containsString("MIT")); assertThat(result.getProductEvidence().toString(), containsString("EasyPeasy")); assertThat(result.getVersionEvidence().toString(), containsString("0.2.3")); - - System.out.println("vendor: " + vendorString); - System.out.println("product: " + result.getProductEvidence().toString()); - System.out.println("version: " + result.getVersionEvidence().toString()); } } From d25f6e813c5f551d8c3cd2a6e8285c4b6870459c Mon Sep 17 00:00:00 2001 From: bjiang Date: Thu, 5 May 2016 19:21:21 -0400 Subject: [PATCH 04/10] new analyzer for Package.swift --- .../analyzer/CocoaPodsAnalyzer.java | 4 +- .../analyzer/SwiftPackageManagerAnalyzer.java | 224 ++++++++++++++++++ ...rg.owasp.dependencycheck.analyzer.Analyzer | 1 + .../analyzer/CocoaPodsAnalyzerTest.java | 85 ------- .../analyzer/SwiftAnalyzersTest.java | 128 ++++++++++ .../test/resources/swift/Gloss/Gloss.podspec | 17 ++ .../test/resources/swift/Gloss/Package.swift | 30 +++ 7 files changed, 402 insertions(+), 87 deletions(-) create mode 100644 dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java delete mode 100644 dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzerTest.java create mode 100644 dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/SwiftAnalyzersTest.java create mode 100644 dependency-check-core/src/test/resources/swift/Gloss/Gloss.podspec create mode 100644 dependency-check-core/src/test/resources/swift/Gloss/Package.swift diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java index 3e79cc8c2..947db3eff 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java @@ -146,9 +146,9 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { final String name = addStringEvidence(product, contents, blockVariable, "name", "name", Confidence.HIGHEST); if (!name.isEmpty()) { - vendor.addEvidence(PODSPEC, "name_project", name, Confidence.LOW); + vendor.addEvidence(PODSPEC, "name_project", name, Confidence.HIGHEST); } - addStringEvidence(product, contents, blockVariable, "summary", "summary", Confidence.LOW); + addStringEvidence(product, contents, blockVariable, "summary", "summary", Confidence.HIGHEST); addStringEvidence(vendor, contents, blockVariable, "author", "authors?", Confidence.HIGHEST); addStringEvidence(vendor, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java new file mode 100644 index 000000000..f2b123436 --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java @@ -0,0 +1,224 @@ +/* + * This file is part of dependency-check-core. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (c) 2015 Bianca Jiang. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.json.JsonObject; +import javax.json.JsonString; +import javax.json.JsonValue; + +import org.apache.commons.io.FileUtils; +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.dependency.Confidence; +import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.EvidenceCollection; +import org.owasp.dependencycheck.utils.FileFilterBuilder; +import org.owasp.dependencycheck.utils.Settings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Bianca Xue Jiang + * + */ +public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { + + /** + * The logger. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(SwiftPackageManagerAnalyzer.class); + + /** + * The name of the analyzer. + */ + private static final String ANALYZER_NAME = "SWIFT Package Manager Analyzer"; + + /** + * The phase that this analyzer is intended to run in. + */ + private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION; + + /** + * The file name to scan. + */ + public static final String SPM_FILE_NAME = "Package.swift"; + /** + * Filter that detects files named "package.json". + */ + private static final FileFilter SPM_FILE_FILTER = FileFilterBuilder.newInstance().addFilenames(SPM_FILE_NAME).build(); + + /** + * The capture group #1 is the block variable. + * e.g. + * "import PackageDescription + * let package = Package( + * name: "Gloss" + * )" + */ + private static final Pattern SPM_BLOCK_PATTERN + = Pattern.compile("let[^=]+=\\s*Package\\s*\\(\\s*([^)]*)\\s*\\)", Pattern.DOTALL); + + /** + * Returns the FileFilter + * + * @return the FileFilter + */ + @Override + protected FileFilter getFileFilter() { + return SPM_FILE_FILTER; + } + + @Override + protected void initializeFileTypeAnalyzer() throws Exception { + // NO-OP + } + + /** + * 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 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_NODE_PACKAGE_ENABLED; + } + + @Override + protected void analyzeFileType(Dependency dependency, Engine engine) + throws AnalysisException { + + String contents; + try { + contents = FileUtils.readFileToString(dependency.getActualFile(), Charset.defaultCharset()); + } catch (IOException e) { + throw new AnalysisException( + "Problem occurred while reading dependency file.", e); + } + final Matcher matcher = SPM_BLOCK_PATTERN.matcher(contents); + if (matcher.find()) { + contents = contents.substring(matcher.end()); + final String packageDescription = matcher.group(1); + if(packageDescription.isEmpty()) + return; + + final EvidenceCollection vendor = dependency.getVendorEvidence(); + final EvidenceCollection product = dependency.getProductEvidence(); +// final EvidenceCollection version = dependency.getVersionEvidence(); + + final String name = addStringEvidence(product, packageDescription, "name", "name", Confidence.HIGHEST); + if (!name.isEmpty()) { + vendor.addEvidence(SPM_FILE_NAME, "name_project", name, Confidence.HIGHEST); + } +// addStringEvidence(product, contents, blockVariable, "summary", "summary", Confidence.LOW); +// addStringEvidence(vendor, contents, blockVariable, "author", "authors?", Confidence.HIGHEST); +// addStringEvidence(vendor, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST); +// addStringEvidence(vendor, contents, blockVariable, "license", "licen[cs]es?", Confidence.HIGHEST); +// addStringEvidence(version, contents, blockVariable, "version", "version", Confidence.HIGHEST); + + setPackagePath(dependency); + } + } + + private String addStringEvidence(EvidenceCollection evidences, + String packageDescription, String field, String fieldPattern, Confidence confidence) { + String value = ""; + + //capture array value between [ ] + final Matcher matcher = Pattern.compile( + String.format("%s *:\\s*\"([^\"]*)", fieldPattern), Pattern.DOTALL).matcher(packageDescription); + if(matcher.find()) { + value = matcher.group(1); + } + + if(value != null) { + value = value.trim(); + if(value.length() > 0) + evidences.addEvidence (SPM_FILE_NAME, field, value, confidence); + } + + + return value; + } + + private void setPackagePath(Dependency dep) { + File file = new File(dep.getFilePath()); + String parent = file.getParent(); + if(parent != null) + dep.setPackagePath(parent); + } + + /** + * Adds information to an evidence collection from the node json configuration. + * + * @param json information from node.js + * @param collection a set of evidence about a dependency + * @param key the key to obtain the data from the json information + */ + private void addToEvidence(JsonObject json, EvidenceCollection collection, String key) { + if (json.containsKey(key)) { + final JsonValue value = json.get(key); + if (value instanceof JsonString) { + collection.addEvidence(SPM_FILE_NAME, key, ((JsonString) value).getString(), Confidence.HIGHEST); + } else if (value instanceof JsonObject) { + final JsonObject jsonObject = (JsonObject) value; + for (final Map.Entry entry : jsonObject.entrySet()) { + final String property = entry.getKey(); + final JsonValue subValue = entry.getValue(); + if (subValue instanceof JsonString) { + collection.addEvidence(SPM_FILE_NAME, + String.format("%s.%s", key, property), + ((JsonString) subValue).getString(), + Confidence.HIGHEST); + } else { + LOGGER.warn("JSON sub-value not string as expected: {}", subValue); + } + } + } else { + LOGGER.warn("JSON value not string or JSON object as expected: {}", value); + } + } + } +} diff --git a/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer b/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer index 3377ab6bc..8930b8dd6 100644 --- a/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer +++ b/dependency-check-core/src/main/resources/META-INF/services/org.owasp.dependencycheck.analyzer.Analyzer @@ -23,3 +23,4 @@ org.owasp.dependencycheck.analyzer.RubyBundleInstallDeploymentAnalyzer org.owasp.dependencycheck.analyzer.RubyBundleAuditAnalyzer org.owasp.dependencycheck.analyzer.ComposerLockAnalyzer org.owasp.dependencycheck.analyzer.CocoaPodsAnalyzer +org.owasp.dependencycheck.analyzer.SwiftPackageManagerAnalyzer diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzerTest.java deleted file mode 100644 index 8fffebc7a..000000000 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzerTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.owasp.dependencycheck.analyzer; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.owasp.dependencycheck.BaseTest; -import org.owasp.dependencycheck.analyzer.exception.AnalysisException; -import org.owasp.dependencycheck.dependency.Dependency; - -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; - -import java.io.File; - -/** - * Unit tests for CocoaPodsAnalyzer. - * - * @author Bianca Jiang - */ -public class CocoaPodsAnalyzerTest extends BaseTest { - - /** - * The analyzer to test. - */ - CocoaPodsAnalyzer analyzer; - - /** - * Correctly setup the analyzer for testing. - * - * @throws Exception thrown if there is a problem - */ - @Before - public void setUp() throws Exception { - analyzer = new CocoaPodsAnalyzer(); - analyzer.setFilesMatched(true); - analyzer.initialize(); - } - - /** - * Cleanup the analyzer's temp files, etc. - * - * @throws Exception thrown if there is a problem - */ - @After - public void tearDown() throws Exception { - analyzer.close(); - analyzer = null; - } - - /** - * Test of getName method, of class PythonDistributionAnalyzer. - */ - @Test - public void testGetName() { - assertThat(analyzer.getName(), is("CocoaPods Package Analyzer")); - } - - /** - * Test of supportsExtension method, of class PythonDistributionAnalyzer. - */ - @Test - public void testSupportsFiles() { - assertThat(analyzer.accept(new File("test.podspec")), is(true)); - } - - /** - * Test of inspect method, of class PythonDistributionAnalyzer. - * - * @throws AnalysisException is thrown when an exception occurs. - */ - @Test - public void testAnalyzePackageJson() throws AnalysisException { - final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, - "swift/cocoapods/EasyPeasy.podspec")); - analyzer.analyze(result, null); - final String vendorString = result.getVendorEvidence().toString(); - - assertThat(vendorString, containsString("Carlos Vidal")); - assertThat(vendorString, containsString("https://github.com/nakiostudio/EasyPeasy")); - assertThat(vendorString, containsString("MIT")); - assertThat(result.getProductEvidence().toString(), containsString("EasyPeasy")); - assertThat(result.getVersionEvidence().toString(), containsString("0.2.3")); - } -} diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/SwiftAnalyzersTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/SwiftAnalyzersTest.java new file mode 100644 index 000000000..e937af7a6 --- /dev/null +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/SwiftAnalyzersTest.java @@ -0,0 +1,128 @@ +package org.owasp.dependencycheck.analyzer; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.owasp.dependencycheck.BaseTest; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.dependency.Dependency; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.io.File; + +/** + * Unit tests for CocoaPodsAnalyzer. + * + * @author Bianca Jiang + */ +public class SwiftAnalyzersTest extends BaseTest { + + /** + * The analyzer to test. + */ + CocoaPodsAnalyzer podsAnalyzer; + SwiftPackageManagerAnalyzer spmAnalyzer; + + /** + * Correctly setup the analyzer for testing. + * + * @throws Exception thrown if there is a problem + */ + @Before + public void setUp() throws Exception { + podsAnalyzer = new CocoaPodsAnalyzer(); + podsAnalyzer.setFilesMatched(true); + podsAnalyzer.initialize(); + + spmAnalyzer = new SwiftPackageManagerAnalyzer(); + spmAnalyzer.setFilesMatched(true); + spmAnalyzer.initialize(); + } + + /** + * Cleanup the analyzer's temp files, etc. + * + * @throws Exception thrown if there is a problem + */ + @After + public void tearDown() throws Exception { + podsAnalyzer.close(); + podsAnalyzer = null; + + spmAnalyzer.close(); + spmAnalyzer = null; + } + + /** + * Test of getName method, of class CocoaPodsAnalyzer. + */ + @Test + public void testPodsGetName() { + assertThat(podsAnalyzer.getName(), is("CocoaPods Package Analyzer")); + } + + /** + * Test of getName method, of class SwiftPackageManagerAnalyzer. + */ + @Test + public void testSPMGetName() { + assertThat(spmAnalyzer.getName(), is("SWIFT Package Manager Analyzer")); + } + + /** + * Test of supportsFiles method, of class CocoaPodsAnalyzer. + */ + @Test + public void testPodsSupportsFiles() { + assertThat(podsAnalyzer.accept(new File("test.podspec")), is(true)); + } + + /** + * Test of supportsFiles method, of class SwiftPackageManagerAnalyzer. + */ + @Test + public void testSPMSupportsFiles() { + assertThat(spmAnalyzer.accept(new File("Package.swift")), is(true)); + } + + /** + * Test of analyze method, of class CocoaPodsAnalyzer. + * + * @throws AnalysisException is thrown when an exception occurs. + */ + @Test + public void testCocoaPodsAnalyzer() throws AnalysisException { + final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, + "swift/cocoapods/EasyPeasy.podspec")); + podsAnalyzer.analyze(result, null); + final String vendorString = result.getVendorEvidence().toString(); + + assertThat(vendorString, containsString("Carlos Vidal")); + assertThat(vendorString, containsString("https://github.com/nakiostudio/EasyPeasy")); + assertThat(vendorString, containsString("MIT")); + assertThat(result.getProductEvidence().toString(), containsString("EasyPeasy")); + assertThat(result.getVersionEvidence().toString(), containsString("0.2.3")); + } + + /** + * Test of analyze method, of class SwiftPackageManagerAnalyzer. + * + * @throws AnalysisException is thrown when an exception occurs. + */ + @Test + public void testSPMAnalyzer() throws AnalysisException { + final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, + "swift/Gloss/Package.swift")); + spmAnalyzer.analyze(result, null); + System.out.println(result.getProductEvidence().toString()); + +// assertThat(vendorString, containsString("Carlos Vidal")); +// assertThat(vendorString, containsString("https://github.com/nakiostudio/EasyPeasy")); +// assertThat(vendorString, containsString("MIT")); + assertThat(result.getProductEvidence().toString(), containsString("Gloss")); +// assertThat(result.getVersionEvidence().toString(), containsString("0.2.3")); + } +} diff --git a/dependency-check-core/src/test/resources/swift/Gloss/Gloss.podspec b/dependency-check-core/src/test/resources/swift/Gloss/Gloss.podspec new file mode 100644 index 000000000..3d05500ca --- /dev/null +++ b/dependency-check-core/src/test/resources/swift/Gloss/Gloss.podspec @@ -0,0 +1,17 @@ +Pod::Spec.new do |s| + s.name = "Gloss" + s.version = "0.7.2" + s.summary = "A shiny JSON parsing library in Swift" + s.description = "A shiny JSON parsing library in Swift. Features include mapping JSON to objects, mapping objects to JSON, handling of nested objects and custom transformations." + s.homepage = "https://github.com/hkellaway/Gloss" + s.license = { :type => "MIT", :file => "LICENSE" } + s.author = { "Harlan Kellaway" => "hello@harlankellaway.com" } + s.social_media_url = "http://twitter.com/HarlanKellaway" + s.source = { :git => "https://github.com/hkellaway/Gloss.git", :tag => s.version.to_s } + + s.platforms = { :ios => "8.0", :osx => "10.9", :tvos => "9.0", :watchos => "2.0" } + s.requires_arc = true + + s.source_files = 'Sources/*.{swift}' + +end diff --git a/dependency-check-core/src/test/resources/swift/Gloss/Package.swift b/dependency-check-core/src/test/resources/swift/Gloss/Package.swift new file mode 100644 index 000000000..ac1039468 --- /dev/null +++ b/dependency-check-core/src/test/resources/swift/Gloss/Package.swift @@ -0,0 +1,30 @@ +// +// Package.swift +// Gloss +// +// Copyright (c) 2015 Harlan Kellaway +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +import PackageDescription + +let package = Package( + name: "Gloss" +) From 99355d993a2ccd0477686ea0aae2699dee4f02e3 Mon Sep 17 00:00:00 2001 From: bjiang Date: Fri, 6 May 2016 10:24:28 -0400 Subject: [PATCH 05/10] code cleanup with more comments --- .../analyzer/SwiftPackageManagerAnalyzer.java | 55 ++----------------- 1 file changed, 6 insertions(+), 49 deletions(-) diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java index f2b123436..b378e68f5 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java @@ -46,11 +46,6 @@ import org.slf4j.LoggerFactory; */ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { - /** - * The logger. - */ - private static final Logger LOGGER = LoggerFactory.getLogger(SwiftPackageManagerAnalyzer.class); - /** * The name of the analyzer. */ @@ -65,6 +60,7 @@ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { * The file name to scan. */ public static final String SPM_FILE_NAME = "Package.swift"; + /** * Filter that detects files named "package.json". */ @@ -143,22 +139,17 @@ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { final String packageDescription = matcher.group(1); if(packageDescription.isEmpty()) return; - - final EvidenceCollection vendor = dependency.getVendorEvidence(); + final EvidenceCollection product = dependency.getProductEvidence(); -// final EvidenceCollection version = dependency.getVersionEvidence(); + final EvidenceCollection vendor = dependency.getVendorEvidence(); + //SPM is currently under development for SWIFT 3. Its current metadata includes package name and dependencies. + //Future interesting metadata: version, license, homepage, author, summary, etc. final String name = addStringEvidence(product, packageDescription, "name", "name", Confidence.HIGHEST); if (!name.isEmpty()) { vendor.addEvidence(SPM_FILE_NAME, "name_project", name, Confidence.HIGHEST); } -// addStringEvidence(product, contents, blockVariable, "summary", "summary", Confidence.LOW); -// addStringEvidence(vendor, contents, blockVariable, "author", "authors?", Confidence.HIGHEST); -// addStringEvidence(vendor, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST); -// addStringEvidence(vendor, contents, blockVariable, "license", "licen[cs]es?", Confidence.HIGHEST); -// addStringEvidence(version, contents, blockVariable, "version", "version", Confidence.HIGHEST); - - setPackagePath(dependency); + setPackagePath(dependency); } } @@ -166,7 +157,6 @@ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { String packageDescription, String field, String fieldPattern, Confidence confidence) { String value = ""; - //capture array value between [ ] final Matcher matcher = Pattern.compile( String.format("%s *:\\s*\"([^\"]*)", fieldPattern), Pattern.DOTALL).matcher(packageDescription); if(matcher.find()) { @@ -178,7 +168,6 @@ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { if(value.length() > 0) evidences.addEvidence (SPM_FILE_NAME, field, value, confidence); } - return value; } @@ -189,36 +178,4 @@ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { if(parent != null) dep.setPackagePath(parent); } - - /** - * Adds information to an evidence collection from the node json configuration. - * - * @param json information from node.js - * @param collection a set of evidence about a dependency - * @param key the key to obtain the data from the json information - */ - private void addToEvidence(JsonObject json, EvidenceCollection collection, String key) { - if (json.containsKey(key)) { - final JsonValue value = json.get(key); - if (value instanceof JsonString) { - collection.addEvidence(SPM_FILE_NAME, key, ((JsonString) value).getString(), Confidence.HIGHEST); - } else if (value instanceof JsonObject) { - final JsonObject jsonObject = (JsonObject) value; - for (final Map.Entry entry : jsonObject.entrySet()) { - final String property = entry.getKey(); - final JsonValue subValue = entry.getValue(); - if (subValue instanceof JsonString) { - collection.addEvidence(SPM_FILE_NAME, - String.format("%s.%s", key, property), - ((JsonString) subValue).getString(), - Confidence.HIGHEST); - } else { - LOGGER.warn("JSON sub-value not string as expected: {}", subValue); - } - } - } else { - LOGGER.warn("JSON value not string or JSON object as expected: {}", value); - } - } - } } From dc7245ff6e22075b0dd4217e41afa6779b607ade Mon Sep 17 00:00:00 2001 From: bjiang Date: Fri, 6 May 2016 12:55:59 -0400 Subject: [PATCH 06/10] code cleanup --- .../analyzer/CocoaPodsAnalyzer.java | 43 +------------------ 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java index 947db3eff..b46c8ac1e 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java @@ -21,14 +21,9 @@ import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.nio.charset.Charset; -import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.json.JsonObject; -import javax.json.JsonString; -import javax.json.JsonValue; - import org.apache.commons.io.FileUtils; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; @@ -37,8 +32,6 @@ import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.EvidenceCollection; import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.Settings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * @author Bianca Xue Jiang @@ -49,7 +42,7 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { /** * The logger. */ - private static final Logger LOGGER = LoggerFactory.getLogger(CocoaPodsAnalyzer.class); +// private static final Logger LOGGER = LoggerFactory.getLogger(CocoaPodsAnalyzer.class); /** * The name of the analyzer. @@ -66,7 +59,7 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { */ public static final String PODSPEC = "podspec"; /** - * Filter that detects files named "package.json". + * Filter that detects files named "*.podspec". */ private static final FileFilter PODSPEC_FILTER = FileFilterBuilder.newInstance().addExtensions(PODSPEC).build(); @@ -190,36 +183,4 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { if(parent != null) dep.setPackagePath(parent); } - - /** - * Adds information to an evidence collection from the node json configuration. - * - * @param json information from node.js - * @param collection a set of evidence about a dependency - * @param key the key to obtain the data from the json information - */ - private void addToEvidence(JsonObject json, EvidenceCollection collection, String key) { - if (json.containsKey(key)) { - final JsonValue value = json.get(key); - if (value instanceof JsonString) { - collection.addEvidence(PODSPEC, key, ((JsonString) value).getString(), Confidence.HIGHEST); - } else if (value instanceof JsonObject) { - final JsonObject jsonObject = (JsonObject) value; - for (final Map.Entry entry : jsonObject.entrySet()) { - final String property = entry.getKey(); - final JsonValue subValue = entry.getValue(); - if (subValue instanceof JsonString) { - collection.addEvidence(PODSPEC, - String.format("%s.%s", key, property), - ((JsonString) subValue).getString(), - Confidence.HIGHEST); - } else { - LOGGER.warn("JSON sub-value not string as expected: {}", subValue); - } - } - } else { - LOGGER.warn("JSON value not string or JSON object as expected: {}", value); - } - } - } } From 1e7bbfa7c1df6eccf139d6aa38e7c2d266609aed Mon Sep 17 00:00:00 2001 From: bjiang Date: Fri, 6 May 2016 13:43:05 -0400 Subject: [PATCH 07/10] bundle the same SWIFT package by different analyzers --- .../analyzer/CocoaPodsAnalyzer.java | 2 +- .../analyzer/DependencyBundlingAnalyzer.java | 38 ++++++++++++++++++- .../analyzer/SwiftPackageManagerAnalyzer.java | 11 +----- .../main/resources/dependencycheck.properties | 2 + .../analyzer/SwiftAnalyzersTest.java | 7 +--- .../owasp/dependencycheck/utils/Settings.java | 8 ++++ 6 files changed, 50 insertions(+), 18 deletions(-) diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java index b46c8ac1e..9b6d0d27a 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java @@ -114,7 +114,7 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { */ @Override protected String getAnalyzerEnabledSettingKey() { - return Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED; + return Settings.KEYS.ANALYZER_COCOAPODS_ENABLED; } @Override diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java index fd6911e6f..8a877c87a 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java @@ -112,6 +112,7 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal final ListIterator subIterator = engine.getDependencies().listIterator(mainIterator.nextIndex()); while (subIterator.hasNext()) { final Dependency nextDependency = subIterator.next(); + Dependency main = null; if (hashesMatch(dependency, nextDependency) && !containedInWar(dependency.getFilePath()) && !containedInWar(nextDependency.getFilePath())) { if (firstPathIsShortest(dependency.getFilePath(), nextDependency.getFilePath())) { @@ -138,8 +139,14 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal mergeDependencies(nextDependency, dependency, dependenciesToRemove); break; //since we merged into the next dependency - skip forward to the next in mainIterator } - } else if ( isSameRubyGem(dependency, nextDependency) ) { - Dependency main = getMainGemspecDependency(dependency, nextDependency); + } else if ( (main = getMainGemspecDependency(dependency, nextDependency)) != null ) { + if (main == dependency) { + mergeDependencies(dependency, nextDependency, dependenciesToRemove); + } else { + mergeDependencies(nextDependency, dependency, dependenciesToRemove); + break; //since we merged into the next dependency - skip forward to the next in mainIterator + } + } else if ( (main = getMainSwiftDependency(dependency, nextDependency)) != null) { if (main == dependency) { mergeDependencies(dependency, nextDependency, dependenciesToRemove); } else { @@ -348,6 +355,33 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal } return null; } + + /** + * Bundling same swift dependencies with the same packagePath but identified by different analyzers. + */ + private boolean isSameSwiftPackage(Dependency dependency1, Dependency dependency2) { + if (dependency1 == null || dependency2 == null || + (!dependency1.getFileName().endsWith(".podspec") && + !dependency1.getFileName().equals("Package.swift")) || + (!dependency2.getFileName().endsWith(".podspec") && + !dependency2.getFileName().equals("Package.swift")) || + dependency1.getPackagePath() == null || + dependency2.getPackagePath() == null) { + return false; + } + if (dependency1.getPackagePath().equalsIgnoreCase(dependency2.getPackagePath())) + return true; + + return false; + } + private Dependency getMainSwiftDependency(Dependency dependency1, Dependency dependency2) { + if (isSameSwiftPackage(dependency1, dependency2)) { + if(dependency1.getFileName().endsWith(".podspec")) + return dependency1; + return dependency2; + } + return null; + } /** * This is likely a very broken attempt at determining if the 'left' dependency is the 'core' library in comparison to the diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java index b378e68f5..e771c729b 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java @@ -21,14 +21,9 @@ import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.nio.charset.Charset; -import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.json.JsonObject; -import javax.json.JsonString; -import javax.json.JsonValue; - import org.apache.commons.io.FileUtils; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; @@ -37,8 +32,6 @@ import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.EvidenceCollection; import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.Settings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * @author Bianca Xue Jiang @@ -119,7 +112,7 @@ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { */ @Override protected String getAnalyzerEnabledSettingKey() { - return Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED; + return Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED; } @Override @@ -149,8 +142,8 @@ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { if (!name.isEmpty()) { vendor.addEvidence(SPM_FILE_NAME, "name_project", name, Confidence.HIGHEST); } - setPackagePath(dependency); } + setPackagePath(dependency); } private String addStringEvidence(EvidenceCollection evidences, diff --git a/dependency-check-core/src/main/resources/dependencycheck.properties b/dependency-check-core/src/main/resources/dependencycheck.properties index 118b5b50f..777ea470b 100644 --- a/dependency-check-core/src/main/resources/dependencycheck.properties +++ b/dependency-check-core/src/main/resources/dependencycheck.properties @@ -102,6 +102,8 @@ analyzer.nuspec.enabled=true analyzer.openssl.enabled=true analyzer.central.enabled=true analyzer.nexus.enabled=false +analyzer.cocoapods.enabled=true +analyzer.swift.package.manager.enabled=true #whether the nexus analyzer uses the proxy analyzer.nexus.proxy=true diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/SwiftAnalyzersTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/SwiftAnalyzersTest.java index e937af7a6..94e4b020d 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/SwiftAnalyzersTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/SwiftAnalyzersTest.java @@ -117,12 +117,7 @@ public class SwiftAnalyzersTest extends BaseTest { final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "swift/Gloss/Package.swift")); spmAnalyzer.analyze(result, null); - System.out.println(result.getProductEvidence().toString()); - -// assertThat(vendorString, containsString("Carlos Vidal")); -// assertThat(vendorString, containsString("https://github.com/nakiostudio/EasyPeasy")); -// assertThat(vendorString, containsString("MIT")); + assertThat(result.getProductEvidence().toString(), containsString("Gloss")); -// assertThat(result.getVersionEvidence().toString(), containsString("0.2.3")); } } diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java index 8f1f38147..bacae09fd 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java @@ -253,6 +253,14 @@ public final class Settings { * The properties key for whether the OpenSSL analyzer is enabled. */ public static final String ANALYZER_OPENSSL_ENABLED = "analyzer.openssl.enabled"; + /** + * The properties key for whether the cocoapods analyzer is enabled. + */ + public static final String ANALYZER_COCOAPODS_ENABLED = "analyzer.cocoapods.enabled"; + /** + * The properties key for whether the SWIFT package manager analyzer is enabled. + */ + public static final String ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED = "analyzer.swift.package.manager.enabled"; /** * The properties key for the Central search URL. */ From 74282c8ac5422beee0a689b86b501b419a7129a6 Mon Sep 17 00:00:00 2001 From: bjiang Date: Fri, 12 Aug 2016 13:15:29 -0400 Subject: [PATCH 08/10] filter out version from jar filename for name --- .../analyzer/FileNameAnalyzer.java | 9 +++--- .../utils/DependencyVersionUtil.java | 28 +++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java index fcaaeb102..5e6dee5b8 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java @@ -93,26 +93,27 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer { //add version evidence final DependencyVersion version = DependencyVersionUtil.parseVersion(fileName); + final String packageName = DependencyVersionUtil.parsePreVersion(fileName); if (version != null) { // If the version number is just a number like 2 or 23, reduce the confidence // a shade. This should hopefully correct for cases like log4j.jar or // struts2-core.jar if (version.getVersionParts() == null || version.getVersionParts().size() < 2) { - dependency.getVersionEvidence().addEvidence("file", "name", + dependency.getVersionEvidence().addEvidence("file", "version", version.toString(), Confidence.MEDIUM); } else { dependency.getVersionEvidence().addEvidence("file", "version", version.toString(), Confidence.HIGHEST); } dependency.getVersionEvidence().addEvidence("file", "name", - fileName, Confidence.MEDIUM); + packageName, Confidence.MEDIUM); } if (!IGNORED_FILES.accept(f)) { dependency.getProductEvidence().addEvidence("file", "name", - fileName, Confidence.HIGH); + packageName, Confidence.HIGH); dependency.getVendorEvidence().addEvidence("file", "name", - fileName, Confidence.HIGH); + packageName, Confidence.HIGH); } } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java index 483413dcb..b91510b1e 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java @@ -39,6 +39,11 @@ public final class DependencyVersionUtil { * are missing a version number using the previous regex. */ private static final Pattern RX_SINGLE_VERSION = Pattern.compile("\\d+(\\.?([_-](release|beta|alpha)|[a-zA-Z_-]{1,3}\\d{1,8}))?"); + + /** + * Regular expression to extract the part before the version numbers if there are any based on RX_VERSION. In most cases, this part represents a more accurate name. + */ + private static final Pattern RX_PRE_VERSION = Pattern.compile("^(.+)[_-](\\d+\\.\\d{1,6})+"); /** * Private constructor for utility class. @@ -95,4 +100,27 @@ public final class DependencyVersionUtil { } return new DependencyVersion(version); } + + /** + *

+ * A utility class to extract the part before version numbers from file names (or other strings containing version numbers. + * In most cases, this part represents a more accurate name than the full file name.

+ *
+     * Example:
+     * Give the file name: library-name-1.4.1r2-release.jar
+     * This function would return: library-name
+ * + * @param text the text being analyzed + * @return the part before the version numbers if any, otherwise return the text itself. + */ + public static String parsePreVersion(String text) { + if(parseVersion(text) == null) + return text; + + Matcher matcher = RX_PRE_VERSION.matcher(text); + if (matcher.find()) { + return matcher.group(1); + } + return text; + } } From 8cd377b99f857772277a452a11134227507a14c3 Mon Sep 17 00:00:00 2001 From: bjiang Date: Fri, 12 Aug 2016 13:32:25 -0400 Subject: [PATCH 09/10] use value of specification-version as version from Manifest --- .../java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java index fed1824a9..9edbcf6ab 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java @@ -685,7 +685,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { foundSomething = true; versionEvidence.addEvidence(source, key, value, Confidence.HIGH); } else if ("specification-version".equalsIgnoreCase(key)) { - specificationVersion = key; + specificationVersion = value; } else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_VENDOR.toString())) { foundSomething = true; vendorEvidence.addEvidence(source, key, value, Confidence.HIGH); From c093edf4594fec378220fea71202ec3864cc079a Mon Sep 17 00:00:00 2001 From: bjiang Date: Fri, 12 Aug 2016 17:12:12 -0400 Subject: [PATCH 10/10] update copyright and javadoc --- .../owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java | 7 +++++-- .../dependencycheck/analyzer/RubyBundlerAnalyzer.java | 4 ++-- .../analyzer/SwiftPackageManagerAnalyzer.java | 7 +++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java index c3b955bf2..4d05e6505 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. * - * Copyright (c) 2015 Bianca Jiang. All Rights Reserved. + * © Copyright IBM Corporation 2016. */ package org.owasp.dependencycheck.analyzer; @@ -34,9 +34,12 @@ import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.Settings; /** - * @author Bianca Xue Jiang + * This analyzer is used to analyze SWIFT and Objective-C packages by collecting + * information from .podspec files. CocoaPods dependency manager see https://cocoapods.org/. * + * @author Bianca Jiang (https://twitter.com/biancajiang) */ +@Experimental public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundlerAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundlerAnalyzer.java index ebe77e7b8..b89a11d92 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundlerAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundlerAnalyzer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. * - * Copyright (c) 2016 Bianca Jiang. All Rights Reserved. + * © Copyright IBM Corporation 2016. */ package org.owasp.dependencycheck.analyzer; @@ -43,7 +43,7 @@ import org.owasp.dependencycheck.dependency.Dependency; * {@link RubyGemspecAnalyzer}, so it will enabled/disabled with * {@link RubyGemspecAnalyzer}. * - * @author Bianca Jiang (biancajiang@gmail.com) + * @author Bianca Jiang (https://twitter.com/biancajiang) */ @Experimental public class RubyBundlerAnalyzer extends RubyGemspecAnalyzer { diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java index 21477296b..f28feecae 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. * - * Copyright (c) 2015 Bianca Jiang. All Rights Reserved. + * © Copyright IBM Corporation 2016. */ package org.owasp.dependencycheck.analyzer; @@ -34,9 +34,12 @@ import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.Settings; /** - * @author Bianca Xue Jiang + * This analyzer is used to analyze the SWIFT Package Manager (https://swift.org/package-manager/). + * It collects information about a package from Package.swift files. * + * @author Bianca Jiang (https://twitter.com/biancajiang) */ +@Experimental public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { /**