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.apache.commons.io.FileUtils;
21  import org.owasp.dependencycheck.Engine;
22  import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
23  import org.owasp.dependencycheck.dependency.Confidence;
24  import org.owasp.dependencycheck.dependency.Dependency;
25  import org.owasp.dependencycheck.dependency.EvidenceCollection;
26  import org.owasp.dependencycheck.utils.FileFilterBuilder;
27  import org.owasp.dependencycheck.utils.Settings;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  
31  import java.io.File;
32  import java.io.FileFilter;
33  import java.io.IOException;
34  import java.util.Map;
35  import javax.json.Json;
36  import javax.json.JsonException;
37  import javax.json.JsonObject;
38  import javax.json.JsonReader;
39  import javax.json.JsonString;
40  import javax.json.JsonValue;
41  
42  /**
43   * Used to analyze Node Package Manager (npm) package.json files, and collect information that can be used to determine the
44   * associated CPE.
45   *
46   * @author Dale Visser
47   */
48  public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
49  
50      /**
51       * The logger.
52       */
53      private static final Logger LOGGER = LoggerFactory.getLogger(NodePackageAnalyzer.class);
54  
55      /**
56       * The name of the analyzer.
57       */
58      private static final String ANALYZER_NAME = "Node.js Package Analyzer";
59  
60      /**
61       * The phase that this analyzer is intended to run in.
62       */
63      private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
64  
65      /**
66       * The file name to scan.
67       */
68      public static final String PACKAGE_JSON = "package.json";
69      /**
70       * Filter that detects files named "package.json".
71       */
72      private static final FileFilter PACKAGE_JSON_FILTER = FileFilterBuilder.newInstance()
73              .addFilenames(PACKAGE_JSON).build();
74  
75      /**
76       * Returns the FileFilter
77       *
78       * @return the FileFilter
79       */
80      @Override
81      protected FileFilter getFileFilter() {
82          return PACKAGE_JSON_FILTER;
83      }
84  
85      @Override
86      protected void initializeFileTypeAnalyzer() throws Exception {
87          // NO-OP
88      }
89  
90      /**
91       * Returns the name of the analyzer.
92       *
93       * @return the name of the analyzer.
94       */
95      @Override
96      public String getName() {
97          return ANALYZER_NAME;
98      }
99  
100     /**
101      * Returns the phase that the analyzer is intended to run in.
102      *
103      * @return the phase that the analyzer is intended to run in.
104      */
105     @Override
106     public AnalysisPhase getAnalysisPhase() {
107         return ANALYSIS_PHASE;
108     }
109 
110     /**
111      * Returns the key used in the properties file to reference the analyzer's enabled property.
112      *
113      * @return the analyzer's enabled property setting key
114      */
115     @Override
116     protected String getAnalyzerEnabledSettingKey() {
117         return Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED;
118     }
119 
120     @Override
121     protected void analyzeFileType(Dependency dependency, Engine engine)
122             throws AnalysisException {
123         final File file = dependency.getActualFile();
124         JsonReader jsonReader;
125         try {
126             jsonReader = Json.createReader(FileUtils.openInputStream(file));
127         } catch (IOException e) {
128             throw new AnalysisException(
129                     "Problem occurred while reading dependency file.", e);
130         }
131         try {
132             final JsonObject json = jsonReader.readObject();
133             final EvidenceCollection productEvidence = dependency.getProductEvidence();
134             final EvidenceCollection vendorEvidence = dependency.getVendorEvidence();
135             if (json.containsKey("name")) {
136                 final Object value = json.get("name");
137                 if (value instanceof JsonString) {
138                     final String valueString = ((JsonString) value).getString();
139                     productEvidence.addEvidence(PACKAGE_JSON, "name", valueString, Confidence.HIGHEST);
140                     vendorEvidence.addEvidence(PACKAGE_JSON, "name_project", String.format("%s_project", valueString), Confidence.LOW);
141                 } else {
142                     LOGGER.warn("JSON value not string as expected: {}", value);
143                 }
144             }
145             addToEvidence(json, productEvidence, "description");
146             addToEvidence(json, vendorEvidence, "author");
147             addToEvidence(json, dependency.getVersionEvidence(), "version");
148             dependency.setDisplayFileName(String.format("%s/%s", file.getParentFile().getName(), file.getName()));
149         } catch (JsonException e) {
150             LOGGER.warn("Failed to parse package.json file.", e);
151         } finally {
152             jsonReader.close();
153         }
154     }
155 
156     /**
157      * Adds information to an evidence collection from the node json configuration.
158      *
159      * @param json information from node.js
160      * @param collection a set of evidence about a dependency
161      * @param key the key to obtain the data from the json information
162      */
163     private void addToEvidence(JsonObject json, EvidenceCollection collection, String key) {
164         if (json.containsKey(key)) {
165             final JsonValue value = json.get(key);
166             if (value instanceof JsonString) {
167                 collection.addEvidence(PACKAGE_JSON, key, ((JsonString) value).getString(), Confidence.HIGHEST);
168             } else if (value instanceof JsonObject) {
169                 final JsonObject jsonObject = (JsonObject) value;
170                 for (final Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
171                     final String property = entry.getKey();
172                     final JsonValue subValue = entry.getValue();
173                     if (subValue instanceof JsonString) {
174                         collection.addEvidence(PACKAGE_JSON,
175                                 String.format("%s.%s", key, property),
176                                 ((JsonString) subValue).getString(),
177                                 Confidence.HIGHEST);
178                     } else {
179                         LOGGER.warn("JSON sub-value not string as expected: {}", subValue);
180                     }
181                 }
182             } else {
183                 LOGGER.warn("JSON value not string or JSON object as expected: {}", value);
184             }
185         }
186     }
187 }