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