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