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