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 the SWIFT Package Manager
38   * (https://swift.org/package-manager/). It collects information about a package
39   * from Package.swift files.
40   *
41   * @author Bianca Jiang (https://twitter.com/biancajiang)
42   */
43  @Experimental
44  public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer {
45  
46      /**
47       * The name of the analyzer.
48       */
49      private static final String ANALYZER_NAME = "SWIFT Package Manager Analyzer";
50  
51      /**
52       * The phase that this analyzer is intended to run in.
53       */
54      private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
55  
56      /**
57       * The file name to scan.
58       */
59      public static final String SPM_FILE_NAME = "Package.swift";
60  
61      /**
62       * Filter that detects files named "package.json".
63       */
64      private static final FileFilter SPM_FILE_FILTER = FileFilterBuilder.newInstance().addFilenames(SPM_FILE_NAME).build();
65  
66      /**
67       * The capture group #1 is the block variable. e.g. "import
68       * PackageDescription let package = Package( name: "Gloss" )"
69       */
70      private static final Pattern SPM_BLOCK_PATTERN = Pattern.compile("let[^=]+=\\s*Package\\s*\\(\\s*([^)]*)\\s*\\)", Pattern.DOTALL);
71  
72      /**
73       * Returns the FileFilter
74       *
75       * @return the FileFilter
76       */
77      @Override
78      protected FileFilter getFileFilter() {
79          return SPM_FILE_FILTER;
80      }
81  
82      @Override
83      protected void initializeFileTypeAnalyzer() {
84          // NO-OP
85      }
86  
87      /**
88       * Returns the name of the analyzer.
89       *
90       * @return the name of the analyzer.
91       */
92      @Override
93      public String getName() {
94          return ANALYZER_NAME;
95      }
96  
97      /**
98       * Returns the phase that the analyzer is intended to run in.
99       *
100      * @return the phase that the analyzer is intended to run in.
101      */
102     @Override
103     public AnalysisPhase getAnalysisPhase() {
104         return ANALYSIS_PHASE;
105     }
106 
107     /**
108      * Returns the key used in the properties file to reference the analyzer's
109      * enabled property.
110      *
111      * @return the analyzer's enabled property setting key
112      */
113     @Override
114     protected String getAnalyzerEnabledSettingKey() {
115         return Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED;
116     }
117 
118     @Override
119     protected void analyzeDependency(Dependency dependency, Engine engine)
120             throws AnalysisException {
121 
122         String contents;
123         try {
124             contents = FileUtils.readFileToString(dependency.getActualFile(), Charset.defaultCharset());
125         } catch (IOException e) {
126             throw new AnalysisException(
127                     "Problem occurred while reading dependency file.", e);
128         }
129         final Matcher matcher = SPM_BLOCK_PATTERN.matcher(contents);
130         if (matcher.find()) {
131             final String packageDescription = matcher.group(1);
132             if (packageDescription.isEmpty()) {
133                 return;
134             }
135 
136             final EvidenceCollection product = dependency.getProductEvidence();
137             final EvidenceCollection vendor = dependency.getVendorEvidence();
138 
139             //SPM is currently under development for SWIFT 3. Its current metadata includes package name and dependencies.
140             //Future interesting metadata: version, license, homepage, author, summary, etc.
141             final String name = addStringEvidence(product, packageDescription, "name", "name", Confidence.HIGHEST);
142             if (name != null && !name.isEmpty()) {
143                 vendor.addEvidence(SPM_FILE_NAME, "name_project", name, Confidence.HIGHEST);
144             }
145         }
146         setPackagePath(dependency);
147     }
148 
149     /**
150      * Extracts evidence from the package description and adds it to the given
151      * evidence collection.
152      *
153      * @param evidences the evidence collection to update
154      * @param packageDescription the text to extract evidence from
155      * @param field the name of the field being searched for
156      * @param fieldPattern the field pattern within the contents to search for
157      * @param confidence the confidence level of the evidence if found
158      * @return the string that was added as evidence
159      */
160     private String addStringEvidence(EvidenceCollection evidences,
161             String packageDescription, String field, String fieldPattern, Confidence confidence) {
162         String value = "";
163 
164         final Matcher matcher = Pattern.compile(
165                 String.format("%s *:\\s*\"([^\"]*)", fieldPattern), Pattern.DOTALL).matcher(packageDescription);
166         if (matcher.find()) {
167             value = matcher.group(1);
168         }
169 
170         if (value != null) {
171             value = value.trim();
172             if (value.length() > 0) {
173                 evidences.addEvidence(SPM_FILE_NAME, field, value, confidence);
174             }
175         }
176 
177         return value;
178     }
179 
180     /**
181      * Sets the package path on the given dependency.
182      *
183      * @param dep the dependency to update
184      */
185     private void setPackagePath(Dependency dep) {
186         final File file = new File(dep.getFilePath());
187         final String parent = file.getParent();
188         if (parent != null) {
189             dep.setPackagePath(parent);
190         }
191     }
192 }