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.analyzer;
19  
20  import org.owasp.dependencycheck.Engine;
21  import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
22  import org.owasp.dependencycheck.data.composer.ComposerDependency;
23  import org.owasp.dependencycheck.data.composer.ComposerException;
24  import org.owasp.dependencycheck.data.composer.ComposerLockParser;
25  import org.owasp.dependencycheck.dependency.Confidence;
26  import org.owasp.dependencycheck.dependency.Dependency;
27  import org.owasp.dependencycheck.exception.InitializationException;
28  import org.owasp.dependencycheck.utils.Checksum;
29  import org.owasp.dependencycheck.utils.FileFilterBuilder;
30  import org.owasp.dependencycheck.utils.Settings;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  
34  import java.io.FileFilter;
35  import java.io.FileInputStream;
36  import java.io.FileNotFoundException;
37  import java.nio.charset.Charset;
38  import java.security.MessageDigest;
39  import java.security.NoSuchAlgorithmException;
40  
41  /**
42   * Used to analyze a composer.lock file for a composer PHP app.
43   *
44   * @author colezlaw
45   */
46  @Experimental
47  public class ComposerLockAnalyzer extends AbstractFileTypeAnalyzer {
48  
49      /**
50       * The logger.
51       */
52      private static final Logger LOGGER = LoggerFactory.getLogger(ComposerLockAnalyzer.class);
53  
54      /**
55       * The analyzer name.
56       */
57      private static final String ANALYZER_NAME = "Composer.lock analyzer";
58  
59      /**
60       * composer.json.
61       */
62      private static final String COMPOSER_LOCK = "composer.lock";
63  
64      /**
65       * The FileFilter.
66       */
67      private static final FileFilter FILE_FILTER = FileFilterBuilder.newInstance().addFilenames(COMPOSER_LOCK).build();
68  
69      /**
70       * Returns the FileFilter.
71       *
72       * @return the FileFilter
73       */
74      @Override
75      protected FileFilter getFileFilter() {
76          return FILE_FILTER;
77      }
78  
79      /**
80       * Initializes the analyzer.
81       *
82       * @throws InitializationException thrown if an exception occurs getting an
83       * instance of SHA1
84       */
85      @Override
86      protected void initializeFileTypeAnalyzer() throws InitializationException {
87          try {
88              getSha1MessageDigest();
89          } catch (IllegalStateException ex) {
90              setEnabled(false);
91              throw new InitializationException("Unable to create SHA1 MessageDigest", ex);
92          }
93      }
94  
95      /**
96       * Entry point for the analyzer.
97       *
98       * @param dependency the dependency to analyze
99       * @param engine the engine scanning
100      * @throws AnalysisException if there's a failure during analysis
101      */
102     @Override
103     protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
104         FileInputStream fis = null;
105         try {
106             fis = new FileInputStream(dependency.getActualFile());
107             final ComposerLockParser clp = new ComposerLockParser(fis);
108             LOGGER.info("Checking composer.lock file {}", dependency.getActualFilePath());
109             clp.process();
110             for (ComposerDependency dep : clp.getDependencies()) {
111                 final Dependency d = new Dependency(dependency.getActualFile());
112                 d.setDisplayFileName(String.format("%s:%s/%s", dependency.getDisplayFileName(), dep.getGroup(), dep.getProject()));
113                 final String filePath = String.format("%s:%s/%s", dependency.getFilePath(), dep.getGroup(), dep.getProject());
114                 final MessageDigest sha1 = getSha1MessageDigest();
115                 d.setFilePath(filePath);
116                 d.setSha1sum(Checksum.getHex(sha1.digest(filePath.getBytes(Charset.defaultCharset()))));
117                 d.getVendorEvidence().addEvidence(COMPOSER_LOCK, "vendor", dep.getGroup(), Confidence.HIGHEST);
118                 d.getProductEvidence().addEvidence(COMPOSER_LOCK, "product", dep.getProject(), Confidence.HIGHEST);
119                 d.getVersionEvidence().addEvidence(COMPOSER_LOCK, "version", dep.getVersion(), Confidence.HIGHEST);
120                 LOGGER.info("Adding dependency {}", d);
121                 engine.getDependencies().add(d);
122             }
123         } catch (FileNotFoundException fnfe) {
124             LOGGER.warn("Error opening dependency {}", dependency.getActualFilePath());
125         } catch (ComposerException ce) {
126             LOGGER.warn("Error parsing composer.json {}", dependency.getActualFilePath(), ce);
127         } finally {
128             if (fis != null) {
129                 try {
130                     fis.close();
131                 } catch (Exception e) {
132                     LOGGER.debug("Unable to close file", e);
133                 }
134             }
135         }
136     }
137 
138     /**
139      * Gets the key to determine whether the analyzer is enabled.
140      *
141      * @return the key specifying whether the analyzer is enabled
142      */
143     @Override
144     protected String getAnalyzerEnabledSettingKey() {
145         return Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED;
146     }
147 
148     /**
149      * Returns the analyzer's name.
150      *
151      * @return the analyzer's name
152      */
153     @Override
154     public String getName() {
155         return ANALYZER_NAME;
156     }
157 
158     /**
159      * Returns the phase this analyzer should run under.
160      *
161      * @return the analysis phase
162      */
163     @Override
164     public AnalysisPhase getAnalysisPhase() {
165         return AnalysisPhase.INFORMATION_COLLECTION;
166     }
167 
168     /**
169      * Returns the sha1 message digest.
170      *
171      * @return the sha1 message digest
172      */
173     private MessageDigest getSha1MessageDigest() {
174         try {
175             return MessageDigest.getInstance("SHA1");
176         } catch (NoSuchAlgorithmException e) {
177             LOGGER.error(e.getMessage());
178             throw new IllegalStateException("Failed to obtain the SHA1 message digest.", e);
179         }
180     }
181 }