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.FileFilter;
22  import java.io.IOException;
23  import java.nio.charset.Charset;
24  import java.util.regex.Matcher;
25  import java.util.regex.Pattern;
26  
27  import org.apache.commons.io.FileUtils;
28  import org.owasp.dependencycheck.Engine;
29  import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
30  import org.owasp.dependencycheck.dependency.Confidence;
31  import org.owasp.dependencycheck.dependency.Dependency;
32  import org.owasp.dependencycheck.dependency.EvidenceCollection;
33  import org.owasp.dependencycheck.utils.FileFilterBuilder;
34  import org.owasp.dependencycheck.utils.Settings;
35  
36  /**
37   * This analyzer is used to analyze SWIFT and Objective-C packages by collecting
38   * information from .podspec files. CocoaPods dependency manager see
39   * https://cocoapods.org/.
40   *
41   * @author Bianca Jiang (https://twitter.com/biancajiang)
42   */
43  @Experimental
44  public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer {
45  
46      /**
47       * The logger.
48       */
49  //    private static final Logger LOGGER = LoggerFactory.getLogger(CocoaPodsAnalyzer.class);
50      /**
51       * The name of the analyzer.
52       */
53      private static final String ANALYZER_NAME = "CocoaPods Package Analyzer";
54  
55      /**
56       * The phase that this analyzer is intended to run in.
57       */
58      private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
59  
60      /**
61       * The file name to scan.
62       */
63      public static final String PODSPEC = "podspec";
64      /**
65       * Filter that detects files named "*.podspec".
66       */
67      private static final FileFilter PODSPEC_FILTER = FileFilterBuilder.newInstance().addExtensions(PODSPEC).build();
68  
69      /**
70       * The capture group #1 is the block variable. e.g. "Pod::Spec.new do
71       * |spec|"
72       */
73      private static final Pattern PODSPEC_BLOCK_PATTERN = Pattern.compile("Pod::Spec\\.new\\s+?do\\s+?\\|(.+?)\\|");
74  
75      /**
76       * Returns the FileFilter
77       *
78       * @return the FileFilter
79       */
80      @Override
81      protected FileFilter getFileFilter() {
82          return PODSPEC_FILTER;
83      }
84  
85      @Override
86      protected void initializeFileTypeAnalyzer() {
87          // NO-OP
88      }
89  
90      /**
91       * Returns the name of the analyzer.
92       *
93       * @return the name of the analyzer.
94       */
95      @Override
96      public String getName() {
97          return ANALYZER_NAME;
98      }
99  
100     /**
101      * Returns the phase that the analyzer is intended to run in.
102      *
103      * @return the phase that the analyzer is intended to run in.
104      */
105     @Override
106     public AnalysisPhase getAnalysisPhase() {
107         return ANALYSIS_PHASE;
108     }
109 
110     /**
111      * Returns the key used in the properties file to reference the analyzer's
112      * enabled property.
113      *
114      * @return the analyzer's enabled property setting key
115      */
116     @Override
117     protected String getAnalyzerEnabledSettingKey() {
118         return Settings.KEYS.ANALYZER_COCOAPODS_ENABLED;
119     }
120 
121     @Override
122     protected void analyzeDependency(Dependency dependency, Engine engine)
123             throws AnalysisException {
124 
125         String contents;
126         try {
127             contents = FileUtils.readFileToString(dependency.getActualFile(), Charset.defaultCharset());
128         } catch (IOException e) {
129             throw new AnalysisException(
130                     "Problem occurred while reading dependency file.", e);
131         }
132         final Matcher matcher = PODSPEC_BLOCK_PATTERN.matcher(contents);
133         if (matcher.find()) {
134             contents = contents.substring(matcher.end());
135             final String blockVariable = matcher.group(1);
136 
137             final EvidenceCollection vendor = dependency.getVendorEvidence();
138             final EvidenceCollection product = dependency.getProductEvidence();
139             final EvidenceCollection version = dependency.getVersionEvidence();
140 
141             final String name = addStringEvidence(product, contents, blockVariable, "name", "name", Confidence.HIGHEST);
142             if (!name.isEmpty()) {
143                 vendor.addEvidence(PODSPEC, "name_project", name, Confidence.HIGHEST);
144             }
145             addStringEvidence(product, contents, blockVariable, "summary", "summary", Confidence.HIGHEST);
146 
147             addStringEvidence(vendor, contents, blockVariable, "author", "authors?", Confidence.HIGHEST);
148             addStringEvidence(vendor, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST);
149             addStringEvidence(vendor, contents, blockVariable, "license", "licen[cs]es?", Confidence.HIGHEST);
150 
151             addStringEvidence(version, contents, blockVariable, "version", "version", Confidence.HIGHEST);
152         }
153 
154         setPackagePath(dependency);
155     }
156 
157     /**
158      * Extracts evidence from the contents and adds it to the given evidence
159      * collection.
160      *
161      * @param evidences the evidence collection to update
162      * @param contents the text to extract evidence from
163      * @param blockVariable the block variable within the content to search for
164      * @param field the name of the field being searched for
165      * @param fieldPattern the field pattern within the contents to search for
166      * @param confidence the confidence level of the evidence if found
167      * @return the string that was added as evidence
168      */
169     private String addStringEvidence(EvidenceCollection evidences, String contents,
170             String blockVariable, String field, String fieldPattern, Confidence confidence) {
171         String value = "";
172 
173         //capture array value between [ ]
174         final Matcher arrayMatcher = Pattern.compile(
175                 String.format("\\s*?%s\\.%s\\s*?=\\s*?\\{\\s*?(.*?)\\s*?\\}", blockVariable, fieldPattern),
176                 Pattern.CASE_INSENSITIVE).matcher(contents);
177         if (arrayMatcher.find()) {
178             value = arrayMatcher.group(1);
179         } else { //capture single value between quotes
180             final Matcher matcher = Pattern.compile(
181                     String.format("\\s*?%s\\.%s\\s*?=\\s*?(['\"])(.*?)\\1", blockVariable, fieldPattern),
182                     Pattern.CASE_INSENSITIVE).matcher(contents);
183             if (matcher.find()) {
184                 value = matcher.group(2);
185             }
186         }
187         if (value.length() > 0) {
188             evidences.addEvidence(PODSPEC, field, value, confidence);
189         }
190         return value;
191     }
192 
193     /**
194      * Sets the package path on the given dependency.
195      *
196      * @param dep the dependency to update
197      */
198     private void setPackagePath(Dependency dep) {
199         final File file = new File(dep.getFilePath());
200         final String parent = file.getParent();
201         if (parent != null) {
202             dep.setPackagePath(parent);
203         }
204     }
205 }