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  
39  /**
40   * Used to analyze Autoconf input files named configure.ac or configure.in. Files simply named "configure" are also analyzed,
41   * assuming they are generated 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 - Free Software Foundation (FSF)</a>
45   */
46  @Experimental
47  public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer {
48  
49      /**
50       * Autoconf output filename.
51       */
52      private static final String CONFIGURE = "configure";
53  
54      /**
55       * Autoconf input filename.
56       */
57      private static final String CONFIGURE_IN = "configure.in";
58  
59      /**
60       * Autoconf input filename.
61       */
62      private static final String CONFIGURE_AC = "configure.ac";
63  
64      /**
65       * The name of the analyzer.
66       */
67      private static final String ANALYZER_NAME = "Autoconf Analyzer";
68  
69      /**
70       * The phase that this analyzer is intended to run in.
71       */
72      private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
73  
74      /**
75       * The set of file extensions supported by this analyzer.
76       */
77      private static final String[] EXTENSIONS = {"ac", "in"};
78  
79      /**
80       * Matches AC_INIT variables in the output configure script.
81       */
82      private static final Pattern PACKAGE_VAR = Pattern.compile(
83              "PACKAGE_(.+?)='(.*?)'", Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
84  
85      /**
86       * Matches AC_INIT statement in configure.ac file.
87       */
88      private static final Pattern AC_INIT_PATTERN;
89  
90      static {
91          // each instance of param or sep_param has a capture group
92          final String param = "\\[{0,2}(.+?)\\]{0,2}";
93          final String sepParam = "\\s*,\\s*" + param;
94          // Group 1: Package
95          // Group 2: Version
96          // Group 3: optional
97          // Group 4: Bug report address (if it exists)
98          // Group 5: optional
99          // Group 6: Tarname (if it exists)
100         // Group 7: optional
101         // Group 8: URL (if it exists)
102         AC_INIT_PATTERN = Pattern.compile(String.format(
103                 "AC_INIT\\(%s%s(%s)?(%s)?(%s)?\\s*\\)", param, sepParam,
104                 sepParam, sepParam, sepParam), Pattern.DOTALL
105                 | Pattern.CASE_INSENSITIVE);
106     }
107 
108     /**
109      * The file filter used to determine which files this analyzer supports.
110      */
111     private static final FileFilter FILTER = FileFilterBuilder.newInstance().addFilenames(CONFIGURE).addExtensions(
112             EXTENSIONS).build();
113 
114     /**
115      * Returns the FileFilter
116      *
117      * @return the FileFilter
118      */
119     @Override
120     protected FileFilter getFileFilter() {
121         return FILTER;
122     }
123 
124     /**
125      * Returns the name of the analyzer.
126      *
127      * @return the name of the analyzer.
128      */
129     @Override
130     public String getName() {
131         return ANALYZER_NAME;
132     }
133 
134     /**
135      * Returns the phase that the analyzer is intended to run in.
136      *
137      * @return the phase that the analyzer is intended to run in.
138      */
139     @Override
140     public AnalysisPhase getAnalysisPhase() {
141         return ANALYSIS_PHASE;
142     }
143 
144     /**
145      * Returns the key used in the properties file to reference the analyzer's enabled property.
146      *
147      * @return the analyzer's enabled property setting key
148      */
149     @Override
150     protected String getAnalyzerEnabledSettingKey() {
151         return Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED;
152     }
153 
154     @Override
155     protected void analyzeFileType(Dependency dependency, Engine engine)
156             throws AnalysisException {
157         final File actualFile = dependency.getActualFile();
158         final String name = actualFile.getName();
159         if (name.startsWith(CONFIGURE)) {
160             final File parent = actualFile.getParentFile();
161             final String parentName = parent.getName();
162             dependency.setDisplayFileName(parentName + "/" + name);
163             final boolean isOutputScript = CONFIGURE.equals(name);
164             if (isOutputScript || CONFIGURE_AC.equals(name)
165                     || CONFIGURE_IN.equals(name)) {
166                 final String contents = getFileContents(actualFile);
167                 if (!contents.isEmpty()) {
168                     if (isOutputScript) {
169                         extractConfigureScriptEvidence(dependency, name,
170                                 contents);
171                     } else {
172                         gatherEvidence(dependency, name, contents);
173                     }
174                 }
175             }
176         } else {
177             // copy, alter and set in case some other thread is iterating over
178             final List<Dependency> dependencies = new ArrayList<Dependency>(
179                     engine.getDependencies());
180             dependencies.remove(dependency);
181             engine.setDependencies(dependencies);
182         }
183     }
184 
185     /**
186      * Extracts evidence from the configuration.
187      *
188      * @param dependency the dependency being analyzed
189      * @param name the name of the source of evidence
190      * @param contents the contents to analyze for evidence
191      */
192     private void extractConfigureScriptEvidence(Dependency dependency,
193             final String name, final String contents) {
194         final Matcher matcher = PACKAGE_VAR.matcher(contents);
195         while (matcher.find()) {
196             final String variable = matcher.group(1);
197             final String value = matcher.group(2);
198             if (!value.isEmpty()) {
199                 if (variable.endsWith("NAME")) {
200                     dependency.getProductEvidence().addEvidence(name, variable,
201                             value, Confidence.HIGHEST);
202                 } else if ("VERSION".equals(variable)) {
203                     dependency.getVersionEvidence().addEvidence(name, variable,
204                             value, Confidence.HIGHEST);
205                 } else if ("BUGREPORT".equals(variable)) {
206                     dependency.getVendorEvidence().addEvidence(name, variable,
207                             value, Confidence.HIGH);
208                 } else if ("URL".equals(variable)) {
209                     dependency.getVendorEvidence().addEvidence(name, variable,
210                             value, Confidence.HIGH);
211                 }
212             }
213         }
214     }
215 
216     /**
217      * Retrieves the contents of a given file.
218      *
219      * @param actualFile the file to read
220      * @return the contents of the file
221      * @throws AnalysisException thrown if there is an IO Exception
222      */
223     private String getFileContents(final File actualFile)
224             throws AnalysisException {
225         try {
226             return FileUtils.readFileToString(actualFile, Charset.defaultCharset()).trim();
227         } catch (IOException e) {
228             throw new AnalysisException(
229                     "Problem occurred while reading dependency file.", e);
230         }
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 }