Coverage Report - org.owasp.dependencycheck.analyzer.RubyGemspecAnalyzer
 
Classes in this File Line Coverage Branch Coverage Complexity
RubyGemspecAnalyzer
92%
39/42
60%
6/10
1.875
 
 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 Institute for Defense Analyses. All Rights Reserved.
 17  
  */
 18  
 package org.owasp.dependencycheck.analyzer;
 19  
 
 20  
 import org.apache.commons.io.FileUtils;
 21  
 import org.owasp.dependencycheck.Engine;
 22  
 import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
 23  
 import org.owasp.dependencycheck.dependency.Confidence;
 24  
 import org.owasp.dependencycheck.dependency.Dependency;
 25  
 import org.owasp.dependencycheck.dependency.EvidenceCollection;
 26  
 import org.owasp.dependencycheck.utils.FileFilterBuilder;
 27  
 import org.owasp.dependencycheck.utils.Settings;
 28  
 
 29  
 import java.io.FileFilter;
 30  
 import java.io.IOException;
 31  
 import java.util.regex.Matcher;
 32  
 import java.util.regex.Pattern;
 33  
 
 34  
 /**
 35  
  * Used to analyze Ruby Gem specifications and collect information that can be used to determine the associated CPE.
 36  
  * Regular expressions are used to parse the well-defined Ruby syntax that forms the specification.
 37  
  *
 38  
  * @author Dale Visser <dvisser@ida.org>
 39  
  */
 40  7
 public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
 41  
 
 42  
     /**
 43  
      * The name of the analyzer.
 44  
      */
 45  
     private static final String ANALYZER_NAME = "Ruby Gemspec Analyzer";
 46  
 
 47  
     /**
 48  
      * The phase that this analyzer is intended to run in.
 49  
      */
 50  1
     private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
 51  
 
 52  1
     private static final FileFilter FILTER =
 53  
             FileFilterBuilder.newInstance().addExtensions("gemspec").addFilenames("Rakefile").build();
 54  
 
 55  
     private static final String EMAIL = "email";
 56  
     private static final String GEMSPEC = "gemspec";
 57  
 
 58  
     /**
 59  
      * @return a filter that accepts files named Rakefile or matching the glob pattern, *.gemspec
 60  
      */
 61  
     @Override
 62  
     protected FileFilter getFileFilter() {
 63  855
         return FILTER;
 64  
     }
 65  
 
 66  
     @Override
 67  
     protected void initializeFileTypeAnalyzer() throws Exception {
 68  
         // NO-OP
 69  3
     }
 70  
 
 71  
     /**
 72  
      * Returns the name of the analyzer.
 73  
      *
 74  
      * @return the name of the analyzer.
 75  
      */
 76  
     @Override
 77  
     public String getName() {
 78  5
         return ANALYZER_NAME;
 79  
     }
 80  
 
 81  
     /**
 82  
      * Returns the phase that the analyzer is intended to run in.
 83  
      *
 84  
      * @return the phase that the analyzer is intended to run in.
 85  
      */
 86  
     @Override
 87  
     public AnalysisPhase getAnalysisPhase() {
 88  3
         return ANALYSIS_PHASE;
 89  
     }
 90  
 
 91  
     /**
 92  
      * Returns the key used in the properties file to reference the analyzer's enabled property.
 93  
      *
 94  
      * @return the analyzer's enabled property setting key
 95  
      */
 96  
     @Override
 97  
     protected String getAnalyzerEnabledSettingKey() {
 98  7
         return Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED;
 99  
     }
 100  
 
 101  
     /**
 102  
      * The capture group #1 is the block variable.
 103  
      */
 104  1
     private static final Pattern GEMSPEC_BLOCK_INIT =
 105  
             Pattern.compile("Gem::Specification\\.new\\s+?do\\s+?\\|(.+?)\\|");
 106  
 
 107  
     @Override
 108  
     protected void analyzeFileType(Dependency dependency, Engine engine)
 109  
             throws AnalysisException {
 110  
         String contents;
 111  
         try {
 112  1
             contents = FileUtils.readFileToString(dependency.getActualFile());
 113  0
         } catch (IOException e) {
 114  0
             throw new AnalysisException(
 115  
                     "Problem occurred while reading dependency file.", e);
 116  1
         }
 117  1
         final Matcher matcher = GEMSPEC_BLOCK_INIT.matcher(contents);
 118  1
         if (matcher.find()) {
 119  1
             contents = contents.substring(matcher.end());
 120  1
             final String blockVariable = matcher.group(1);
 121  1
             final EvidenceCollection vendor = dependency.getVendorEvidence();
 122  1
             addStringEvidence(vendor, contents, blockVariable, "author", Confidence.HIGHEST);
 123  1
             addListEvidence(vendor, contents, blockVariable, "authors", Confidence.HIGHEST);
 124  1
             final String email = addStringEvidence(vendor, contents, blockVariable, EMAIL, Confidence.MEDIUM);
 125  1
             if (email.isEmpty()) {
 126  0
                 addListEvidence(vendor, contents, blockVariable, EMAIL, Confidence.MEDIUM);
 127  
             }
 128  1
             addStringEvidence(vendor, contents, blockVariable, "homepage", Confidence.MEDIUM);
 129  1
             final EvidenceCollection product = dependency.getProductEvidence();
 130  1
             final String name = addStringEvidence(product, contents, blockVariable, "name", Confidence.HIGHEST);
 131  1
             if (!name.isEmpty()) {
 132  1
                 vendor.addEvidence(GEMSPEC, "name_project", name + "_project", Confidence.LOW);
 133  
             }
 134  1
             addStringEvidence(product, contents, blockVariable, "summary", Confidence.LOW);
 135  1
             addStringEvidence(dependency.getVersionEvidence(), contents, blockVariable, "version", Confidence.HIGHEST);
 136  
         }
 137  1
     }
 138  
 
 139  
     private void addListEvidence(EvidenceCollection evidences, String contents,
 140  
                                  String blockVariable, String field, Confidence confidence) {
 141  1
         final Matcher matcher = Pattern.compile(
 142  
                 String.format("\\s+?%s\\.%s\\s*?=\\s*?\\[(.*?)\\]", blockVariable, field)).matcher(contents);
 143  1
         if (matcher.find()) {
 144  1
             final String value = matcher.group(1).replaceAll("['\"]", " ").trim();
 145  1
             evidences.addEvidence(GEMSPEC, field, value, confidence);
 146  
         }
 147  1
     }
 148  
 
 149  
     private String addStringEvidence(EvidenceCollection evidences, String contents,
 150  
                                      String blockVariable, String field, Confidence confidence) {
 151  6
         final Matcher matcher = Pattern.compile(
 152  
                 String.format("\\s+?%s\\.%s\\s*?=\\s*?(['\"])(.*?)\\1", blockVariable, field)).matcher(contents);
 153  6
         String value = "";
 154  6
         if (matcher.find()) {
 155  5
             value = matcher.group(2);
 156  5
             evidences.addEvidence(GEMSPEC, field, value, confidence);
 157  
         }
 158  6
         return value;
 159  
     }
 160  
 }