ComposerLockParser.java

  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. import org.slf4j.Logger;
  20. import org.slf4j.LoggerFactory;

  21. import jakarta.json.Json;
  22. import jakarta.json.JsonArray;
  23. import jakarta.json.JsonException;
  24. import jakarta.json.JsonObject;
  25. import jakarta.json.JsonReader;
  26. import jakarta.json.stream.JsonParsingException;
  27. import java.io.InputStream;
  28. import java.util.ArrayList;
  29. import java.util.List;
  30. import javax.annotation.concurrent.NotThreadSafe;

  31. /**
  32.  * Parses a Composer.lock file from an input stream. In a separate class so it
  33.  * can hopefully be injected.
  34.  *
  35.  * @author colezlaw
  36.  */
  37. @NotThreadSafe
  38. public class ComposerLockParser {

  39.     /**
  40.      * The JsonReader for parsing JSON
  41.      */
  42.     private final JsonReader jsonReader;
  43.     /**
  44.      * The List of ComposerDependencies found
  45.      */
  46.     private final List<ComposerDependency> composerDependencies;
  47.     /**
  48.      * Whether to skip dev dependencies.
  49.      */
  50.     private final boolean skipDev;
  51.     /**
  52.      * The LOGGER
  53.      */
  54.     private static final Logger LOGGER = LoggerFactory.getLogger(ComposerLockParser.class);

  55.     /**
  56.      * Creates a ComposerLockParser from a JsonReader and an InputStream.
  57.      *
  58.      * @param inputStream the InputStream to parse
  59.      * @param skipDev whether to skip dev dependencies
  60.      */
  61.     public ComposerLockParser(InputStream inputStream, boolean skipDev) {
  62.         LOGGER.debug("Creating a ComposerLockParser");
  63.         this.jsonReader = Json.createReader(inputStream);
  64.         this.composerDependencies = new ArrayList<>();
  65.         this.skipDev = skipDev;
  66.     }

  67.     /**
  68.      * Process the input stream to create the list of dependencies.
  69.      */
  70.     public void process() {
  71.         LOGGER.debug("Beginning Composer lock processing");
  72.         try {
  73.             final JsonObject composer = jsonReader.readObject();
  74.             if (composer.containsKey("packages")) {
  75.                 LOGGER.debug("Found packages");
  76.                 final JsonArray packages = composer.getJsonArray("packages");
  77.                 for (JsonObject pkg : packages.getValuesAs(JsonObject.class)) {
  78.                     processPackageEntry(pkg);
  79.                 }
  80.             }
  81.             if (composer.containsKey("packages-dev") && !skipDev) {
  82.                 LOGGER.debug("Found packages-dev");
  83.                 final JsonArray devPackages = composer.getJsonArray("packages-dev");
  84.                 for (JsonObject pkg : devPackages.getValuesAs(JsonObject.class)) {
  85.                     processPackageEntry(pkg);
  86.                 }
  87.             }
  88.         } catch (JsonParsingException jsonpe) {
  89.             throw new ComposerException("Error parsing stream", jsonpe);
  90.         } catch (JsonException jsone) {
  91.             throw new ComposerException("Error reading stream", jsone);
  92.         } catch (IllegalStateException ise) {
  93.             throw new ComposerException("Illegal state in composer stream", ise);
  94.         } catch (ClassCastException cce) {
  95.             throw new ComposerException("Not exactly composer lock", cce);
  96.         }
  97.     }

  98.     protected void processPackageEntry(JsonObject pkg) {
  99.         if (pkg.containsKey("name")) {
  100.             final String groupName = pkg.getString("name");
  101.             if (groupName.indexOf('/') >= 0 && groupName.indexOf('/') <= groupName.length() - 1) {
  102.                 if (pkg.containsKey("version")) {
  103.                     final String group = groupName.substring(0, groupName.indexOf('/'));
  104.                     final String project = groupName.substring(groupName.indexOf('/') + 1);
  105.                     String version = pkg.getString("version");
  106.                     // Some version numbers begin with v - which doesn't end up matching CPE's
  107.                     if (version.startsWith("v")) {
  108.                         version = version.substring(1);
  109.                     }
  110.                     LOGGER.debug("Got package {}/{}/{}", group, project, version);
  111.                     composerDependencies.add(new ComposerDependency(group, project, version));
  112.                 } else {
  113.                     LOGGER.debug("Group/package {} does not have a version", groupName);
  114.                 }
  115.             } else {
  116.                 LOGGER.debug("Got a dependency with no name");
  117.             }
  118.         }
  119.     }

  120.     /**
  121.      * Gets the list of dependencies.
  122.      *
  123.      * @return the list of dependencies
  124.      */
  125.     public List<ComposerDependency> getDependencies() {
  126.         return composerDependencies;
  127.     }
  128. }