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" +)