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) 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  import org.owasp.dependencycheck.utils.UrlStringUtils;
29  
30  import java.io.File;
31  import java.io.FileFilter;
32  import java.io.IOException;
33  import java.nio.charset.Charset;
34  import java.util.ArrayList;
35  import java.util.List;
36  import java.util.regex.Matcher;
37  import java.util.regex.Pattern;
38  import org.owasp.dependencycheck.exception.InitializationException;
39  
40  /**
41   * Used to analyze Autoconf input files named configure.ac or configure.in.
42   * Files simply named "configure" are also analyzed, assuming they are generated
43   * by Autoconf, and contain certain special package descriptor variables.
44   *
45   * @author Dale Visser
46   * @see <a href="https://www.gnu.org/software/autoconf/">Autoconf - GNU Project
47   * - Free Software Foundation (FSF)</a>
48   */
49  @Experimental
50  public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer {
51  
52      /**
53       * Autoconf output filename.
54       */
55      private static final String CONFIGURE = "configure";
56  
57      /**
58       * Autoconf input filename.
59       */
60      private static final String CONFIGURE_IN = "configure.in";
61  
62      /**
63       * Autoconf input filename.
64       */
65      private static final String CONFIGURE_AC = "configure.ac";
66  
67      /**
68       * The name of the analyzer.
69       */
70      private static final String ANALYZER_NAME = "Autoconf Analyzer";
71  
72      /**
73       * The phase that this analyzer is intended to run in.
74       */
75      private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
76  
77      /**
78       * The set of file extensions supported by this analyzer.
79       */
80      private static final String[] EXTENSIONS = {"ac", "in"};
81  
82      /**
83       * Matches AC_INIT variables in the output configure script.
84       */
85      private static final Pattern PACKAGE_VAR = Pattern.compile(
86              "PACKAGE_(.+?)='(.*?)'", Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
87  
88      /**
89       * Matches AC_INIT statement in configure.ac file.
90       */
91      private static final Pattern AC_INIT_PATTERN;
92  
93      static {
94          // each instance of param or sep_param has a capture group
95          final String param = "\\[{0,2}(.+?)\\]{0,2}";
96          final String sepParam = "\\s*,\\s*" + param;
97          // Group 1: Package
98          // Group 2: Version
99          // Group 3: optional
100         // Group 4: Bug report address (if it exists)
101         // Group 5: optional
102         // Group 6: Tarname (if it exists)
103         // Group 7: optional
104         // Group 8: URL (if it exists)
105         AC_INIT_PATTERN = Pattern.compile(String.format(
106                 "AC_INIT\\(%s%s(%s)?(%s)?(%s)?\\s*\\)", param, sepParam,
107                 sepParam, sepParam, sepParam), Pattern.DOTALL
108                 | Pattern.CASE_INSENSITIVE);
109     }
110 
111     /**
112      * The file filter used to determine which files this analyzer supports.
113      */
114     private static final FileFilter FILTER = FileFilterBuilder.newInstance().addFilenames(CONFIGURE).addExtensions(
115             EXTENSIONS).build();
116 
117     /**
118      * Returns the FileFilter
119      *
120      * @return the FileFilter
121      */
122     @Override
123     protected FileFilter getFileFilter() {
124         return FILTER;
125     }
126 
127     /**
128      * Returns the name of the analyzer.
129      *
130      * @return the name of the analyzer.
131      */
132     @Override
133     public String getName() {
134         return ANALYZER_NAME;
135     }
136 
137     /**
138      * Returns the phase that the analyzer is intended to run in.
139      *
140      * @return the phase that the analyzer is intended to run in.
141      */
142     @Override
143     public AnalysisPhase getAnalysisPhase() {
144         return ANALYSIS_PHASE;
145     }
146 
147     /**
148      * Returns the key used in the properties file to reference the analyzer's
149      * enabled property.
150      *
151      * @return the analyzer's enabled property setting key
152      */
153     @Override
154     protected String getAnalyzerEnabledSettingKey() {
155         return Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED;
156     }
157 
158     @Override
159     protected void analyzeFileType(Dependency dependency, Engine engine)
160             throws AnalysisException {
161         final File actualFile = dependency.getActualFile();
162         final String name = actualFile.getName();
163         if (name.startsWith(CONFIGURE)) {
164             final File parent = actualFile.getParentFile();
165             final String parentName = parent.getName();
166             dependency.setDisplayFileName(parentName + "/" + name);
167             final boolean isOutputScript = CONFIGURE.equals(name);
168             if (isOutputScript || CONFIGURE_AC.equals(name)
169                     || CONFIGURE_IN.equals(name)) {
170                 final String contents = getFileContents(actualFile);
171                 if (!contents.isEmpty()) {
172                     if (isOutputScript) {
173                         extractConfigureScriptEvidence(dependency, name,
174                                 contents);
175                     } else {
176                         gatherEvidence(dependency, name, contents);
177                     }
178                 }
179             }
180         } else {
181             // copy, alter and set in case some other thread is iterating over
182             final List<Dependency> dependencies = new ArrayList<Dependency>(
183                     engine.getDependencies());
184             dependencies.remove(dependency);
185             engine.setDependencies(dependencies);
186         }
187     }
188 
189     /**
190      * Extracts evidence from the configuration.
191      *
192      * @param dependency the dependency being analyzed
193      * @param name the name of the source of evidence
194      * @param contents the contents to analyze for evidence
195      */
196     private void extractConfigureScriptEvidence(Dependency dependency,
197             final String name, final String contents) {
198         final Matcher matcher = PACKAGE_VAR.matcher(contents);
199         while (matcher.find()) {
200             final String variable = matcher.group(1);
201             final String value = matcher.group(2);
202             if (!value.isEmpty()) {
203                 if (variable.endsWith("NAME")) {
204                     dependency.getProductEvidence().addEvidence(name, variable,
205                             value, Confidence.HIGHEST);
206                 } else if ("VERSION".equals(variable)) {
207                     dependency.getVersionEvidence().addEvidence(name, variable,
208                             value, Confidence.HIGHEST);
209                 } else if ("BUGREPORT".equals(variable)) {
210                     dependency.getVendorEvidence().addEvidence(name, variable,
211                             value, Confidence.HIGH);
212                 } else if ("URL".equals(variable)) {
213                     dependency.getVendorEvidence().addEvidence(name, variable,
214                             value, Confidence.HIGH);
215                 }
216             }
217         }
218     }
219 
220     /**
221      * Retrieves the contents of a given file.
222      *
223      * @param actualFile the file to read
224      * @return the contents of the file
225      * @throws AnalysisException thrown if there is an IO Exception
226      */
227     private String getFileContents(final File actualFile)
228             throws AnalysisException {
229         try {
230             return FileUtils.readFileToString(actualFile, Charset.defaultCharset()).trim();
231         } catch (IOException e) {
232             throw new AnalysisException(
233                     "Problem occurred while reading dependency file.", e);
234         }
235     }
236 
237     /**
238      * Gathers evidence from a given file
239      *
240      * @param dependency the dependency to add evidence to
241      * @param name the source of the evidence
242      * @param contents the evidence to analyze
243      */
244     private void gatherEvidence(Dependency dependency, final String name,
245             String contents) {
246         final Matcher matcher = AC_INIT_PATTERN.matcher(contents);
247         if (matcher.find()) {
248             final EvidenceCollection productEvidence = dependency
249                     .getProductEvidence();
250             productEvidence.addEvidence(name, "Package", matcher.group(1),
251                     Confidence.HIGHEST);
252             dependency.getVersionEvidence().addEvidence(name,
253                     "Package Version", matcher.group(2), Confidence.HIGHEST);
254             final EvidenceCollection vendorEvidence = dependency
255                     .getVendorEvidence();
256             if (null != matcher.group(3)) {
257                 vendorEvidence.addEvidence(name, "Bug report address",
258                         matcher.group(4), Confidence.HIGH);
259             }
260             if (null != matcher.group(5)) {
261                 productEvidence.addEvidence(name, "Tarname", matcher.group(6),
262                         Confidence.HIGH);
263             }
264             if (null != matcher.group(7)) {
265                 final String url = matcher.group(8);
266                 if (UrlStringUtils.isUrl(url)) {
267                     vendorEvidence.addEvidence(name, "URL", url,
268                             Confidence.HIGH);
269                 }
270             }
271         }
272     }
273 
274     /**
275      * Initializes the file type analyzer.
276      *
277      * @throws InitializationException thrown if there is an exception during
278      * initialization
279      */
280     @Override
281     protected void initializeFileTypeAnalyzer() throws InitializationException {
282         // No initialization needed.
283     }
284 }