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