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) 2016 IBM Corporation. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.analyzer;
19  
20  import java.io.File;
21  import java.io.FilenameFilter;
22  
23  import org.owasp.dependencycheck.Engine;
24  import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
25  import org.owasp.dependencycheck.dependency.Dependency;
26  
27  /**
28   * This analyzer accepts the fully resolved .gemspec created by the Ruby bundler
29   * (http://bundler.io) for better evidence results. It also tries to resolve the
30   * dependency packagePath to where the gem is actually installed. Then during {@link org.owasp.dependencycheck.analyzer.AnalysisPhase#PRE_FINDING_ANALYSIS}
31   * {@link DependencyBundlingAnalyzer} will merge two .gemspec dependencies
32   * together if <code>Dependency.getPackagePath()</code> are the same.
33   *
34   * Ruby bundler creates new .gemspec files under a folder called
35   * "specifications" at deploy time, in addition to the original .gemspec files
36   * from source. The bundler generated .gemspec files always contain fully
37   * resolved attributes thus provide more accurate evidences, whereas the
38   * original .gemspec from source often contain variables for attributes that
39   * can't be used for evidences.
40   *
41   * Note this analyzer share the same
42   * {@link org.owasp.dependencycheck.utils.Settings.KEYS#ANALYZER_RUBY_GEMSPEC_ENABLED} as
43   * {@link RubyGemspecAnalyzer}, so it will enabled/disabled with
44   * {@link RubyGemspecAnalyzer}.
45   *
46   * @author Bianca Jiang (https://twitter.com/biancajiang)
47   */
48  @Experimental
49  public class RubyBundlerAnalyzer extends RubyGemspecAnalyzer {
50  
51      /**
52       * The name of the analyzer.
53       */
54      private static final String ANALYZER_NAME = "Ruby Bundler Analyzer";
55  
56      /**
57       * Folder name that contains .gemspec files created by "bundle install"
58       */
59      private static final String SPECIFICATIONS = "specifications";
60  
61      /**
62       * Folder name that contains the gems by "bundle install"
63       */
64      private static final String GEMS = "gems";
65  
66      /**
67       * Returns the name of the analyzer.
68       *
69       * @return the name of the analyzer.
70       */
71      @Override
72      public String getName() {
73          return ANALYZER_NAME;
74      }
75  
76      /**
77       * Only accept *.gemspec files generated by "bundle install --deployment"
78       * under "specifications" folder.
79       *
80       * @param pathname the path name to test
81       * @return true if the analyzer can process the given file; otherwise false
82       */
83      @Override
84      public boolean accept(File pathname) {
85  
86          boolean accepted = super.accept(pathname);
87          if (accepted) {
88              final File parentDir = pathname.getParentFile();
89              accepted = parentDir != null && parentDir.getName().equals(SPECIFICATIONS);
90          }
91  
92          return accepted;
93      }
94  
95      @Override
96      protected void analyzeFileType(Dependency dependency, Engine engine)
97              throws AnalysisException {
98          super.analyzeFileType(dependency, engine);
99  
100         //find the corresponding gem folder for this .gemspec stub by "bundle install --deployment"
101         final File gemspecFile = dependency.getActualFile();
102         final String gemFileName = gemspecFile.getName();
103         final String gemName = gemFileName.substring(0, gemFileName.lastIndexOf(".gemspec"));
104         final File specificationsDir = gemspecFile.getParentFile();
105         if (specificationsDir != null && specificationsDir.getName().equals(SPECIFICATIONS) && specificationsDir.exists()) {
106             final File parentDir = specificationsDir.getParentFile();
107             if (parentDir != null && parentDir.exists()) {
108                 final File gemsDir = new File(parentDir, GEMS);
109                 if (gemsDir.exists()) {
110                     final File[] matchingFiles = gemsDir.listFiles(new FilenameFilter() {
111                         @Override
112                         public boolean accept(File dir, String name) {
113                             return name.equals(gemName);
114                         }
115                     });
116 
117                     if (matchingFiles != null && matchingFiles.length > 0) {
118                         final String gemPath = matchingFiles[0].getAbsolutePath();
119                         if (dependency.getActualFilePath().equals(dependency.getFilePath())) {
120                             if (gemPath != null) {
121                                 dependency.setPackagePath(gemPath);
122                             }
123                         } else {
124                             //.gemspec's actualFilePath and filePath are different when it's from a compressed file
125                             //in which case actualFilePath is the temp directory used by decompression.
126                             //packagePath should use the filePath of the identified gem file in "gems" folder
127                             final File gemspecStub = new File(dependency.getFilePath());
128                             final File specDir = gemspecStub.getParentFile();
129                             if (specDir != null && specDir.getName().equals(SPECIFICATIONS)) {
130                                 final File gemsDir2 = new File(specDir.getParentFile(), GEMS);
131                                 final File packageDir = new File(gemsDir2, gemName);
132                                 dependency.setPackagePath(packageDir.getAbsolutePath());
133                             }
134                         }
135                     }
136                 }
137             }
138         }
139     }
140 }