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