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 The OWASP Foundation. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.data.composer;
19  
20  import org.slf4j.Logger;
21  import org.slf4j.LoggerFactory;
22  
23  import javax.json.Json;
24  import javax.json.JsonArray;
25  import javax.json.JsonException;
26  import javax.json.JsonObject;
27  import javax.json.JsonReader;
28  import javax.json.stream.JsonParsingException;
29  import java.io.InputStream;
30  import java.util.ArrayList;
31  import java.util.List;
32  import javax.annotation.concurrent.NotThreadSafe;
33  
34  /**
35   * Parses a Composer.lock file from an input stream. In a separate class so it
36   * can hopefully be injected.
37   *
38   * @author colezlaw
39   */
40  @NotThreadSafe
41  public class ComposerLockParser {
42  
43      /**
44       * The JsonReader for parsing JSON
45       */
46      private final JsonReader jsonReader;
47      /**
48       * The List of ComposerDependencies found
49       */
50      private final List<ComposerDependency> composerDependencies;
51      /**
52       * Whether to skip dev dependencies.
53       */
54      private final boolean skipDev;
55      /**
56       * The LOGGER
57       */
58      private static final Logger LOGGER = LoggerFactory.getLogger(ComposerLockParser.class);
59  
60      /**
61       * Creates a ComposerLockParser from a JsonReader and an InputStream.
62       *
63       * @param inputStream the InputStream to parse
64       */
65      public ComposerLockParser(InputStream inputStream, boolean skipDev) {
66          LOGGER.debug("Creating a ComposerLockParser");
67          this.jsonReader = Json.createReader(inputStream);
68          this.composerDependencies = new ArrayList<>();
69          this.skipDev = skipDev;
70      }
71  
72      /**
73       * Process the input stream to create the list of dependencies.
74       */
75      public void process() {
76          LOGGER.debug("Beginning Composer lock processing");
77          try {
78              final JsonObject composer = jsonReader.readObject();
79              if (composer.containsKey("packages")) {
80                  LOGGER.debug("Found packages");
81                  final JsonArray packages = composer.getJsonArray("packages");
82                  for (JsonObject pkg : packages.getValuesAs(JsonObject.class)) {
83                      processPackageEntry(pkg);
84                  }
85              }
86              if (composer.containsKey("packages-dev") && !skipDev) {
87                  LOGGER.debug("Found packages-dev");
88                  final JsonArray devPackages = composer.getJsonArray("packages-dev");
89                  for (JsonObject pkg : devPackages.getValuesAs(JsonObject.class)) {
90                      processPackageEntry(pkg);
91                  }
92              }
93          } catch (JsonParsingException jsonpe) {
94              throw new ComposerException("Error parsing stream", jsonpe);
95          } catch (JsonException jsone) {
96              throw new ComposerException("Error reading stream", jsone);
97          } catch (IllegalStateException ise) {
98              throw new ComposerException("Illegal state in composer stream", ise);
99          } catch (ClassCastException cce) {
100             throw new ComposerException("Not exactly composer lock", cce);
101         }
102     }
103 
104     protected void processPackageEntry(JsonObject pkg) {
105         if (pkg.containsKey("name")) {
106             final String groupName = pkg.getString("name");
107             if (groupName.indexOf('/') >= 0 && groupName.indexOf('/') <= groupName.length() - 1) {
108                 if (pkg.containsKey("version")) {
109                     final String group = groupName.substring(0, groupName.indexOf('/'));
110                     final String project = groupName.substring(groupName.indexOf('/') + 1);
111                     String version = pkg.getString("version");
112                     // Some version numbers begin with v - which doesn't end up matching CPE's
113                     if (version.startsWith("v")) {
114                         version = version.substring(1);
115                     }
116                     LOGGER.debug("Got package {}/{}/{}", group, project, version);
117                     composerDependencies.add(new ComposerDependency(group, project, version));
118                 } else {
119                     LOGGER.debug("Group/package {} does not have a version", groupName);
120                 }
121             } else {
122                 LOGGER.debug("Got a dependency with no name");
123             }
124         }
125     }
126 
127     /**
128      * Gets the list of dependencies.
129      *
130      * @return the list of dependencies
131      */
132     public List<ComposerDependency> getDependencies() {
133         return composerDependencies;
134     }
135 }