diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NspAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NspAnalyzer.java index 54a4df555..8b153235f 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NspAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NspAnalyzer.java @@ -43,6 +43,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import javax.json.Json; +import javax.json.JsonArray; import javax.json.JsonException; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; @@ -232,11 +233,11 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer { processPackage(dependency, dependencies, "peerDependencies"); } if (packageJson.containsKey("bundleDependencies")) { - final JsonObject dependencies = packageJson.getJsonObject("bundleDependencies"); + final JsonArray dependencies = packageJson.getJsonArray("bundleDependencies"); processPackage(dependency, dependencies, "bundleDependencies"); } if (packageJson.containsKey("bundledDependencies")) { - final JsonObject dependencies = packageJson.getJsonObject("bundledDependencies"); + final JsonArray dependencies = packageJson.getJsonArray("bundledDependencies"); processPackage(dependency, dependencies, "bundledDependencies"); } @@ -244,7 +245,12 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer { * Adds the license if defined in package.json */ if (packageJson.containsKey("license")) { - dependency.setLicense(packageJson.getString("license")); + final Object value = packageJson.get("license"); + if (value instanceof JsonString) { + dependency.setLicense(packageJson.getString("license")); + } else { + dependency.setLicense(packageJson.getJsonObject("license").getString("type")); + } } /* @@ -267,7 +273,24 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer { } /** - * Processes a part of package.json (as defined by JsobObject) and update + * Processes a part of package.json (as defined by JsonArray) and update + * the specified dependency with relevant info. + * + * @param dependency the Dependency to update + * @param jsonArray the jsonArray to parse + * @param depType the dependency type + */ + private void processPackage(Dependency dependency, JsonArray jsonArray, String depType) { + JsonObjectBuilder builder = Json.createObjectBuilder(); + for (JsonString str : jsonArray.getValuesAs(JsonString.class)) { + builder.add(str.toString(), ""); + } + JsonObject jsonObject = builder.build(); + processPackage(dependency, jsonObject, depType); + } + + /** + * Processes a part of package.json (as defined by JsonObject) and update * the specified dependency with relevant info. * * @param dependency the Dependency to update diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NspAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NspAnalyzerTest.java new file mode 100644 index 000000000..3f675b771 --- /dev/null +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NspAnalyzerTest.java @@ -0,0 +1,82 @@ +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 java.io.File; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + +public class NspAnalyzerTest extends BaseTest { + private NspAnalyzer analyzer; + + @Before + public void setUp() throws Exception { + analyzer = new NspAnalyzer(); + analyzer.setFilesMatched(true); + analyzer.initialize(); + } + + @After + public void tearDown() throws Exception { + analyzer.close(); + analyzer = null; + } + + @Test + public void testGetName() { + assertThat(analyzer.getName(), is("Node Security Platform Analyzer")); + } + + @Test + public void testSupportsFiles() { + assertThat(analyzer.accept(new File("package.json")), is(true)); + } + + @Test + public void testAnalyzePackage() throws AnalysisException { + final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/package.json")); + analyzer.analyze(result, null); + + assertEquals(result.getVendorEvidence().toString(), "owasp-nodejs-goat_project "); + assertEquals(result.getProductEvidence().toString(), "A tool to learn OWASP Top 10 for node.js developers owasp-nodejs-goat "); + assertEquals(result.getVersionEvidence().toString(), "1.3.0 "); + } + + @Test + public void testAnalyzePackageJsonWithBundledDeps() throws AnalysisException { + final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/bundled.deps.package.json")); + analyzer.analyze(result, null); + + assertEquals(result.getVendorEvidence().toString(), "Philipp Dunkel fsevents_project "); + assertEquals(result.getProductEvidence().toString(), "Native Access to Mac OS-X FSEvents fsevents "); + assertEquals(result.getVersionEvidence().toString(), "1.1.1 "); + } + + @Test + public void testAnalyzePackageJsonWithLicenseObject() throws AnalysisException { + final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/license.obj.package.json")); + analyzer.analyze(result, null); + + assertEquals(result.getVendorEvidence().toString(), "Twitter, Inc. bootstrap_project "); + assertEquals(result.getProductEvidence().toString(), "The most popular front-end framework for developing responsive, mobile first projects on the web. bootstrap "); + assertEquals(result.getVersionEvidence().toString(), "3.2.0 "); + } + + @Test + public void testAnalyzePackageJsonInNodeModulesDirectory() throws AnalysisException { + final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nodejs/node_modules/dns-sync/package.json")); + analyzer.analyze(result, null); + final String vendorString = result.getVendorEvidence().toString(); + + // node modules are not scanned + assertTrue(vendorString.isEmpty()); + assertEquals(result.getProductEvidence().size(), 0); + assertEquals(result.getVersionEvidence().size(), 0); + } +} diff --git a/dependency-check-core/src/test/resources/nsp/bundled.deps.package.json b/dependency-check-core/src/test/resources/nsp/bundled.deps.package.json new file mode 100644 index 000000000..281347992 --- /dev/null +++ b/dependency-check-core/src/test/resources/nsp/bundled.deps.package.json @@ -0,0 +1,48 @@ +{ + "name": "fsevents", + "version": "1.1.1", + "description": "Native Access to Mac OS-X FSEvents", + "main": "fsevents.js", + "dependencies": { + "nan": "^2.3.0", + "node-pre-gyp": "^0.6.29" + }, + "os": [ + "darwin" + ], + "engines": { + "node": ">=0.8.0" + }, + "scripts": { + "install": "node install", + "prepublish": "if [ $(npm -v | head -c 1) -lt 3 ]; then exit 1; fi && npm dedupe", + "test": "tap ./test" + }, + "binary": { + "module_name": "fse", + "module_path": "./lib/binding/{configuration}/{node_abi}-{platform}-{arch}/", + "remote_path": "./v{version}/", + "package_name": "{module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz", + "host": "https://fsevents-binaries.s3-us-west-2.amazonaws.com" + }, + "repository": { + "type": "git", + "url": "https://github.com/strongloop/fsevents.git" + }, + "keywords": [ + "fsevents", + "mac" + ], + "author": "Philipp Dunkel ", + "license": "MIT", + "bugs": { + "url": "https://github.com/strongloop/fsevents/issues" + }, + "bundledDependencies": [ + "node-pre-gyp" + ], + "homepage": "https://github.com/strongloop/fsevents", + "devDependencies": { + "tap": "~0.4.8" + } +} diff --git a/dependency-check-core/src/test/resources/nsp/license.obj.package.json b/dependency-check-core/src/test/resources/nsp/license.obj.package.json new file mode 100644 index 000000000..3243fa8ba --- /dev/null +++ b/dependency-check-core/src/test/resources/nsp/license.obj.package.json @@ -0,0 +1,81 @@ +{ + "name": "bootstrap", + "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", + "version": "3.2.0", + "keywords": [ + "css", + "less", + "mobile-first", + "responsive", + "front-end", + "framework", + "web" + ], + "homepage": "http://getbootstrap.com", + "author": "Twitter, Inc.", + "scripts": { + "test": "grunt test" + }, + "style": "dist/css/bootstrap.css", + "less": "less/bootstrap.less", + "repository": { + "type": "git", + "url": "https://github.com/twbs/bootstrap.git" + }, + "bugs": { + "url": "https://github.com/twbs/bootstrap/issues" + }, + "license": { + "type": "MIT", + "url": "https://github.com/twbs/bootstrap/blob/master/LICENSE" + }, + "devDependencies": { + "btoa": "~1.1.2", + "glob": "~4.0.2", + "grunt": "~0.4.5", + "grunt-autoprefixer": "~0.7.6", + "grunt-banner": "~0.2.3", + "grunt-contrib-clean": "~0.5.0", + "grunt-contrib-concat": "~0.4.0", + "grunt-contrib-connect": "~0.8.0", + "grunt-contrib-copy": "~0.5.0", + "grunt-contrib-csslint": "~0.2.0", + "grunt-contrib-cssmin": "~0.10.0", + "grunt-contrib-jade": "~0.12.0", + "grunt-contrib-jshint": "~0.10.0", + "grunt-contrib-less": "~0.11.3", + "grunt-contrib-qunit": "~0.5.1", + "grunt-contrib-uglify": "~0.5.0", + "grunt-contrib-watch": "~0.6.1", + "grunt-csscomb": "~2.0.1", + "grunt-exec": "~0.4.5", + "grunt-html-validation": "~0.1.18", + "grunt-jekyll": "~0.4.2", + "grunt-jscs-checker": "~0.6.0", + "grunt-saucelabs": "~8.1.0", + "grunt-sed": "~0.1.1", + "load-grunt-tasks": "~0.6.0", + "markdown": "~0.5.0", + "npm-shrinkwrap": "~3.1.6", + "time-grunt": "~0.3.2" + }, + "engines": { + "node": "~0.10.1" + }, + "jspm": { + "main": "js/bootstrap", + "directories": { + "example": "examples", + "lib": "dist" + }, + "shim": { + "js/bootstrap": { + "imports": "jquery", + "exports": "$" + } + }, + "buildConfig": { + "uglify": true + } + } +}