View Javadoc
1   /*
2    * This file is part of dependency-check-core.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Copyright (c) 2015 Institute for Defense Analyses. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.analyzer;
19  
20  import org.junit.After;
21  import org.junit.Before;
22  import org.junit.Test;
23  import org.owasp.dependencycheck.BaseTest;
24  import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
25  import org.owasp.dependencycheck.dependency.Dependency;
26  
27  import java.io.File;
28  
29  import static org.hamcrest.CoreMatchers.containsString;
30  import static org.hamcrest.CoreMatchers.is;
31  import static org.hamcrest.MatcherAssert.assertThat;
32  import static org.junit.Assert.*;
33  
34  import org.junit.Assume;
35  import org.owasp.dependencycheck.Engine;
36  import org.owasp.dependencycheck.dependency.EvidenceType;
37  import org.owasp.dependencycheck.exception.InitializationException;
38  import org.owasp.dependencycheck.utils.InvalidSettingException;
39  import org.owasp.dependencycheck.utils.Settings;
40  
41  /**
42   * Unit tests for NodePackageAnalyzer.
43   *
44   * @author Dale Visser
45   */
46  public class NodePackageAnalyzerTest extends BaseTest {
47  
48      /**
49       * The analyzer to test.
50       */
51      private NodePackageAnalyzer analyzer;
52      /**
53       * A reference to the engine.
54       */
55      private Engine engine;
56  
57      /**
58       * Retrieves the node audit analyzer from the engine.
59       *
60       * @param engine the ODC engine
61       * @return returns the node audit analyzer from the engine
62       */
63      private NodeAuditAnalyzer getNodeAuditAnalyzer(Engine engine) {
64          for (Analyzer a : engine.getAnalyzers()) {
65              if (a instanceof NodeAuditAnalyzer) {
66                  return (NodeAuditAnalyzer) a;
67              }
68          }
69          return null;
70      }
71  
72      /**
73       * Retrieves the node package analyzer from the engine.
74       *
75       * @param engine the ODC engine
76       * @return returns the node package analyzer from the engine
77       */
78      private NodePackageAnalyzer getNodePackageAnalyzer(Engine engine) {
79          for (Analyzer a : engine.getAnalyzers()) {
80              if (a instanceof NodePackageAnalyzer) {
81                  return (NodePackageAnalyzer) a;
82              }
83          }
84          return null;
85      }
86  
87      /**
88       * Correctly setup the analyzer for testing.
89       *
90       * @throws Exception thrown if there is a problem
91       */
92      @Before
93      @Override
94      public void setUp() throws Exception {
95          super.setUp();
96          if (getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED)) {
97              engine = new Engine(this.getSettings());
98              NodeAuditAnalyzer auditAnalyzer = getNodeAuditAnalyzer(engine);
99              auditAnalyzer.setFilesMatched(true);
100             analyzer = getNodePackageAnalyzer(engine);
101             analyzer.setFilesMatched(true);
102             analyzer.initialize(getSettings());
103             try {
104                 analyzer.prepare(engine);
105             } catch (InitializationException ex) {
106                 if (!ex.getMessage().startsWith("Missing package.lock or npm-shrinkwrap.lock file")) {
107                     throw ex;
108                 }
109             }
110         }
111     }
112 
113     /**
114      * Cleanup temp files, close resources, etc.
115      *
116      * @throws Exception thrown if there is a problem
117      */
118     @After
119     @Override
120     public void tearDown() throws Exception {
121         if (getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED)) {
122             analyzer.close();
123             engine.close();
124         }
125         super.tearDown();
126 
127     }
128 
129     /**
130      * Test of getName method, of class PythonDistributionAnalyzer.
131      */
132     @Test
133     public void testGetName() throws InvalidSettingException {
134         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED), is(true));
135         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_ENABLED), is(true));
136         assertThat(analyzer.getName(), is("Node.js Package Analyzer"));
137     }
138 
139     /**
140      * Test of supportsExtension method, of class PythonDistributionAnalyzer.
141      */
142     @Test
143     public void testSupportsFiles() throws InvalidSettingException {
144         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED), is(true));
145         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_ENABLED), is(true));
146         assertThat(analyzer.accept(new File("package-lock.json")), is(true));
147         assertThat(analyzer.accept(new File("npm-shrinkwrap.json")), is(true));
148     }
149 
150     /**
151      * Test of inspect method, of class PythonDistributionAnalyzer.
152      *
153      * @throws AnalysisException is thrown when an exception occurs.
154      */
155     @Test
156     public void testAnalyzeShrinkwrapJson() throws AnalysisException, InvalidSettingException {
157         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED), is(true));
158         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_ENABLED), is(true));
159         final Dependency toScan = new Dependency(BaseTest.getResourceAsFile(this,
160                 "nodejs/npm-shrinkwrap.json"));
161         final Dependency toCombine = new Dependency(BaseTest.getResourceAsFile(this,
162                 "nodejs/node_modules/dns-sync/package.json"));
163         engine.addDependency(toScan);
164         engine.addDependency(toCombine);
165         analyzer.analyze(toScan, engine);
166         analyzer.analyze(toCombine, engine);
167 
168         testLock();
169     }
170 
171     private void testLock() {
172         final boolean isMac = System.getProperty("os.name").toLowerCase().contains("mac");
173 
174         // test some dependencies
175         boolean bracesFound = false;
176         boolean expandRangeFound = false;
177 
178         Dependency result = null;
179         for (Dependency dep : engine.getDependencies()) {
180             if (!isMac && "fsevents".equals(dep.getName())) {
181                 fail("fsevents need to be skipped on non mac");
182             }
183 
184             if ("react-dom".equals(dep.getName())) {
185                 fail("react-dom need to be skipped because it's an alias");
186             }
187 
188             if ("braces".equals(dep.getName())) {
189                 bracesFound = true;
190             }
191 
192             if ("expand-range".equals(dep.getName())) {
193                 expandRangeFound = true;
194             }
195 
196             if ("fake_submodule".equals(dep.getName())) {
197                 fail("start with file: need to be skipped because it's a local package");
198             }
199 
200             if ("react-dom".equals(dep.getName())) {
201                 fail("start with file: need to be skipped because it's a local package");
202             }
203 
204             if ("dns-sync".equals(dep.getName())) {
205                 result = dep;
206             }
207         }
208 
209         assertTrue("need to contain braces", bracesFound);
210         //check if dependencies of dependencies are imported
211         assertTrue("need to contain expand-range (dependency of braces)", expandRangeFound);
212 
213         final String vendorString = result.getEvidence(EvidenceType.VENDOR).toString();
214         assertThat(vendorString, containsString("Sanjeev Koranga"));
215         assertThat(vendorString, containsString("dns-sync"));
216         assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("dns-sync"));
217         assertThat(result.getEvidence(EvidenceType.VERSION).toString(), containsString("0.1.3"));
218         assertEquals(NodePackageAnalyzer.DEPENDENCY_ECOSYSTEM, result.getEcosystem());
219         assertEquals("dns-sync", result.getName());
220         assertEquals("0.1.3", result.getVersion());
221 
222         // with npm install run on a "non-macOs" system, 90 else
223         // dependencies length change often, maybe not a good idea to test the length, check some dependencies instead
224         //  assertEquals("Expected 40 dependencies", 40, engine.getDependencies().length);
225         // shrinkWrap is not removed because the NodeAudit analyzer is enabled
226         //assertFalse(shrinkwrap.equals(engine.getDependencies()[0]));
227     }
228 
229     /**
230      * Test of inspect method, of class PythonDistributionAnalyzer.
231      *
232      * @throws AnalysisException is thrown when an exception occurs.
233      */
234     @Test
235     public void testAnalyzePackageJsonWithShrinkwrap() throws AnalysisException, InvalidSettingException {
236         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED), is(true));
237         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_ENABLED), is(true));
238         final Dependency packageJson = new Dependency(BaseTest.getResourceAsFile(this,
239                 "nodejs/package.json"));
240         final Dependency shrinkwrap = new Dependency(BaseTest.getResourceAsFile(this,
241                 "nodejs/npm-shrinkwrap.json"));
242         engine.addDependency(packageJson);
243         engine.addDependency(shrinkwrap);
244         assertEquals(2, engine.getDependencies().length);
245         analyzer.analyze(packageJson, engine);
246         assertEquals(1, engine.getDependencies().length); //package-lock was removed without analysis
247         assertEquals(shrinkwrap, engine.getDependencies()[0]);
248         analyzer.analyze(shrinkwrap, engine);
249 
250         testLock();
251     }
252 
253     /**
254      * Test of inspect method, of class PythonDistributionAnalyzer.
255      *
256      * @throws AnalysisException is thrown when an exception occurs.
257      */
258     @Test
259     public void testWithoutLock() throws AnalysisException, InvalidSettingException {
260         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED), is(true));
261         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_ENABLED), is(true));
262         final Dependency packageJson = new Dependency(BaseTest.getResourceAsFile(this,
263                 "nodejs/no_lock/package.json"));
264         engine.addDependency(packageJson);
265         analyzer.analyze(packageJson, engine);
266 
267         //final boolean isMac = !System.getProperty("os.name").toLowerCase().contains("mac");
268         assertEquals("Expected 1 dependencies", 1, engine.getDependencies().length);
269     }
270 
271     /**
272      * Test of inspect method for package-lock v2
273      *
274      * @throws AnalysisException is thrown when an exception occurs.
275      */
276     @Test
277     public void testPackageLockV2() throws AnalysisException, InvalidSettingException {
278         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED), is(true));
279         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_ENABLED), is(true));
280         final Dependency packageJson = new Dependency(BaseTest.getResourceAsFile(this,
281                 "nodejs/test_lockv2/package.json"));
282         final Dependency packageLockJson = new Dependency(BaseTest.getResourceAsFile(this,
283                 "nodejs/test_lockv2/package-lock.json"));
284         engine.addDependency(packageJson);
285         engine.addDependency(packageLockJson);
286         analyzer.analyze(packageJson, engine);
287         assertEquals("Expected 1 dependencies", 1, engine.getDependencies().length);
288         analyzer.analyze(packageLockJson, engine);
289         assertEquals("Expected 1 dependencies", 6, engine.getDependencies().length);
290     }
291 
292     /**
293      * Test of inspect method for package-lock v3
294      *
295      * @throws AnalysisException is thrown when an exception occurs.
296      */
297     @Test
298     public void testPackageLockV3() throws AnalysisException, InvalidSettingException {
299         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED), is(true));
300         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_ENABLED), is(true));
301         final Dependency packageJson = new Dependency(BaseTest.getResourceAsFile(this,
302                 "nodejs/test_lockv3/package.json"));
303         final Dependency packageLockJson = new Dependency(BaseTest.getResourceAsFile(this,
304                 "nodejs/test_lockv3/package-lock.json"));
305         engine.addDependency(packageJson);
306         engine.addDependency(packageLockJson);
307         analyzer.analyze(packageJson, engine);
308         assertEquals("Expected 1 dependencies", 1, engine.getDependencies().length);
309         analyzer.analyze(packageLockJson, engine);
310         assertEquals("Expected 1 dependencies", 6, engine.getDependencies().length);
311     }
312 
313     /**
314      * Test of analysis of package with a local package as a dependency.
315      *
316      * This test crashes with an NPE if issue #1947 isn't resolved.
317      *
318      * @throws AnalysisException if there was a problem with the analysis
319      */
320     @Test
321     public void testLocalPackageDependency() throws AnalysisException, InvalidSettingException {
322         Assume.assumeThat(getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED), is(true));
323         final Dependency packageJson = new Dependency(BaseTest.getResourceAsFile(this,
324                 "nodejs/local_package/package.json"));
325         final Dependency packageLockJson = new Dependency(BaseTest.getResourceAsFile(this,
326                 "nodejs/local_package/package-lock.json"));
327         engine.addDependency(packageJson);
328         engine.addDependency(packageLockJson);
329         analyzer.analyze(packageJson, engine);
330         assertEquals("Expected 1 dependencies", 1, engine.getDependencies().length);
331         analyzer.analyze(packageLockJson, engine);
332         assertEquals("Expected 2 dependencies", 2, engine.getDependencies().length);
333     }
334 }