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  
     private static final String GEMSPEC = "gemspec";
 53  
 
 54  1
     private static final FileFilter FILTER =
 55  
             FileFilterBuilder.newInstance().addExtensions(GEMSPEC).addFilenames("Rakefile").build();
 56  
 
 57  
     private static final String EMAIL = "email";
 58  
 
 59  
     /**
 60  
      * @return a filter that accepts files named Rakefile or matching the glob pattern, *.gemspec
 61  
      */
 62  
     @Override
 63  
     protected FileFilter getFileFilter() {
 64  855
         return FILTER;
 65  
     }
 66  
 
 67  
     @Override
 68  
     protected void initializeFileTypeAnalyzer() throws Exception {
 69  
         // NO-OP
 70  3
     }
 71  
 
 72  
     /**
 73  
      * Returns the name of the analyzer.
 74  
      *
 75  
      * @return the name of the analyzer.
 76  
      */
 77  
     @Override
 78  
     public String getName() {
 79  5
         return ANALYZER_NAME;
 80  
     }
 81  
 
 82  
     /**
 83  
      * Returns the phase that the analyzer is intended to run in.
 84  
      *
 85  
      * @return the phase that the analyzer is intended to run in.
 86  
      */
 87  
     @Override
 88  
     public AnalysisPhase getAnalysisPhase() {
 89  3
         return ANALYSIS_PHASE;
 90  
     }
 91  
 
 92  
     /**
 93  
      * Returns the key used in the properties file to reference the analyzer's enabled property.
 94  
      *
 95  
      * @return the analyzer's enabled property setting key
 96  
      */
 97  
     @Override
 98  
     protected String getAnalyzerEnabledSettingKey() {
 99  7
         return Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED;
 100  
     }
 101  
 
 102  
     /**
 103  
      * The capture group #1 is the block variable.
 104  
      */
 105  1
     private static final Pattern GEMSPEC_BLOCK_INIT =
 106  
             Pattern.compile("Gem::Specification\\.new\\s+?do\\s+?\\|(.+?)\\|");
 107  
 
 108  
     @Override
 109  
     protected void analyzeFileType(Dependency dependency, Engine engine)
 110  
             throws AnalysisException {
 111  
         String contents;
 112  
         try {
 113  1
             contents = FileUtils.readFileToString(dependency.getActualFile());
 114  0
         } catch (IOException e) {
 115  0
             throw new AnalysisException(
 116  
                     "Problem occurred while reading dependency file.", e);
 117  1
         }
 118  1
         final Matcher matcher = GEMSPEC_BLOCK_INIT.matcher(contents);
 119  1
         if (matcher.find()) {
 120  1
             contents = contents.substring(matcher.end());
 121  1
             final String blockVariable = matcher.group(1);
 122  1
             final EvidenceCollection vendor = dependency.getVendorEvidence();
 123  1
             addStringEvidence(vendor, contents, blockVariable, "author", Confidence.HIGHEST);
 124  1
             addListEvidence(vendor, contents, blockVariable, "authors", Confidence.HIGHEST);
 125  1
             final String email = addStringEvidence(vendor, contents, blockVariable, EMAIL, Confidence.MEDIUM);
 126  1
             if (email.isEmpty()) {
 127  0
                 addListEvidence(vendor, contents, blockVariable, EMAIL, Confidence.MEDIUM);
 128  
             }
 129  1
             addStringEvidence(vendor, contents, blockVariable, "homepage", Confidence.MEDIUM);
 130  1
             final EvidenceCollection product = dependency.getProductEvidence();
 131  1
             final String name = addStringEvidence(product, contents, blockVariable, "name", Confidence.HIGHEST);
 132  1
             if (!name.isEmpty()) {
 133  1
                 vendor.addEvidence(GEMSPEC, "name_project", name + "_project", Confidence.LOW);
 134  
             }
 135  1
             addStringEvidence(product, contents, blockVariable, "summary", Confidence.LOW);
 136  1
             addStringEvidence(dependency.getVersionEvidence(), contents, blockVariable, "version", Confidence.HIGHEST);
 137  
         }
 138  1
     }
 139  
 
 140  
     private void addListEvidence(EvidenceCollection evidences, String contents,
 141  
                                  String blockVariable, String field, Confidence confidence) {
 142  1
         final Matcher matcher = Pattern.compile(
 143  
                 String.format("\\s+?%s\\.%s\\s*?=\\s*?\\[(.*?)\\]", blockVariable, field)).matcher(contents);
 144  1
         if (matcher.find()) {
 145  1
             final String value = matcher.group(1).replaceAll("['\"]", " ").trim();
 146  1
             evidences.addEvidence(GEMSPEC, field, value, confidence);
 147  
         }
 148  1
     }
 149  
 
 150  
     private String addStringEvidence(EvidenceCollection evidences, String contents,
 151  
                                      String blockVariable, String field, Confidence confidence) {
 152  6
         final Matcher matcher = Pattern.compile(
 153  
                 String.format("\\s+?%s\\.%s\\s*?=\\s*?(['\"])(.*?)\\1", blockVariable, field)).matcher(contents);
 154  6
         String value = "";
 155  6
         if (matcher.find()) {
 156  5
             value = matcher.group(2);
 157  5
             evidences.addEvidence(GEMSPEC, field, value, confidence);
 158  
         }
 159  6
         return value;
 160  
     }
 161  
 }