Coverage Report - org.owasp.dependencycheck.analyzer.NexusAnalyzer
 
Classes in this File Line Coverage Branch Coverage Complexity
NexusAnalyzer
19%
15/77
3%
1/30
3.778
 
 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) 2014 Jeremy Long. 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.data.nexus.MavenArtifact;
 24  
 import org.owasp.dependencycheck.data.nexus.NexusSearch;
 25  
 import org.owasp.dependencycheck.dependency.Confidence;
 26  
 import org.owasp.dependencycheck.dependency.Dependency;
 27  
 import org.owasp.dependencycheck.dependency.Evidence;
 28  
 import org.owasp.dependencycheck.xml.pom.PomUtils;
 29  
 import org.slf4j.Logger;
 30  
 import org.slf4j.LoggerFactory;
 31  
 
 32  
 import java.io.File;
 33  
 import java.io.FileFilter;
 34  
 import java.io.FileNotFoundException;
 35  
 import java.io.IOException;
 36  
 import java.net.MalformedURLException;
 37  
 import java.net.URL;
 38  
 import org.owasp.dependencycheck.exception.InitializationException;
 39  
 import org.owasp.dependencycheck.utils.DownloadFailedException;
 40  
 import org.owasp.dependencycheck.utils.Downloader;
 41  
 import org.owasp.dependencycheck.utils.FileFilterBuilder;
 42  
 import org.owasp.dependencycheck.utils.InvalidSettingException;
 43  
 import org.owasp.dependencycheck.utils.Settings;
 44  
 
 45  
 /**
 46  
  * Analyzer which will attempt to locate a dependency on a Nexus service by
 47  
  * SHA-1 digest of the dependency.
 48  
  *
 49  
  * There are two settings which govern this behavior:
 50  
  *
 51  
  * <ul>
 52  
  * <li>{@link org.owasp.dependencycheck.utils.Settings.KEYS#ANALYZER_NEXUS_ENABLED}
 53  
  * determines whether this analyzer is even enabled. This can be overridden by
 54  
  * setting the system property.</li>
 55  
  * <li>{@link org.owasp.dependencycheck.utils.Settings.KEYS#ANALYZER_NEXUS_URL}
 56  
  * the URL to a Nexus service to search by SHA-1. There is an expected
 57  
  * <code>%s</code> in this where the SHA-1 will get entered.</li>
 58  
  * </ul>
 59  
  *
 60  
  * @author colezlaw
 61  
  */
 62  8
 public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
 63  
 
 64  
     /**
 65  
      * The default URL - this will be used by the CentralAnalyzer to determine
 66  
      * whether to enable this.
 67  
      */
 68  
     public static final String DEFAULT_URL = "https://repository.sonatype.org/service/local/";
 69  
 
 70  
     /**
 71  
      * The logger.
 72  
      */
 73  1
     private static final Logger LOGGER = LoggerFactory.getLogger(NexusAnalyzer.class);
 74  
 
 75  
     /**
 76  
      * The name of the analyzer.
 77  
      */
 78  
     private static final String ANALYZER_NAME = "Nexus Analyzer";
 79  
 
 80  
     /**
 81  
      * The phase in which the analyzer runs.
 82  
      */
 83  1
     private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
 84  
 
 85  
     /**
 86  
      * The types of files on which this will work.
 87  
      */
 88  
     private static final String SUPPORTED_EXTENSIONS = "jar";
 89  
 
 90  
     /**
 91  
      * Whether or not the Nexus analyzer should use a proxy if configured.
 92  
      */
 93  
     private boolean useProxy;
 94  
     /**
 95  
      * The Nexus Search to be set up for this analyzer.
 96  
      */
 97  
     private NexusSearch searcher;
 98  
 
 99  
     /**
 100  
      * Field indicating if the analyzer is enabled.
 101  
      */
 102  8
     private final boolean enabled = checkEnabled();
 103  
 
 104  
     /**
 105  
      * Determines if this analyzer is enabled
 106  
      *
 107  
      * @return <code>true</code> if the analyzer is enabled; otherwise
 108  
      * <code>false</code>
 109  
      */
 110  
     private boolean checkEnabled() {
 111  
         /* Enable this analyzer ONLY if the Nexus URL has been set to something
 112  
          other than the default one (if it's the default one, we'll use the
 113  
          central one) and it's enabled by the user.
 114  
          */
 115  8
         boolean retval = false;
 116  
         try {
 117  8
             if (!DEFAULT_URL.equals(Settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL))
 118  0
                     && Settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED)) {
 119  0
                 LOGGER.info("Enabling Nexus analyzer");
 120  0
                 retval = true;
 121  
             } else {
 122  8
                 LOGGER.debug("Nexus analyzer disabled, using Central instead");
 123  
             }
 124  0
         } catch (InvalidSettingException ise) {
 125  0
             LOGGER.warn("Invalid setting. Disabling Nexus analyzer");
 126  8
         }
 127  
 
 128  8
         return retval;
 129  
     }
 130  
 
 131  
     /**
 132  
      * Determine whether to enable this analyzer or not.
 133  
      *
 134  
      * @return whether the analyzer should be enabled
 135  
      */
 136  
     @Override
 137  
     public boolean isEnabled() {
 138  864
         return enabled;
 139  
     }
 140  
 
 141  
     /**
 142  
      * Initializes the analyzer once before any analysis is performed.
 143  
      *
 144  
      * @throws InitializationException if there's an error during initialization
 145  
      */
 146  
     @Override
 147  
     public void initializeFileTypeAnalyzer() throws InitializationException {
 148  0
         LOGGER.debug("Initializing Nexus Analyzer");
 149  0
         LOGGER.debug("Nexus Analyzer enabled: {}", isEnabled());
 150  0
         if (isEnabled()) {
 151  0
             useProxy = useProxy();
 152  0
             final String searchUrl = Settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL);
 153  0
             LOGGER.debug("Nexus Analyzer URL: {}", searchUrl);
 154  
             try {
 155  0
                 searcher = new NexusSearch(new URL(searchUrl), useProxy);
 156  0
                 if (!searcher.preflightRequest()) {
 157  0
                     setEnabled(false);
 158  0
                     throw new InitializationException("There was an issue getting Nexus status. Disabling analyzer.");
 159  
                 }
 160  0
             } catch (MalformedURLException mue) {
 161  0
                 setEnabled(false);
 162  0
                 throw new InitializationException("Malformed URL to Nexus: " + searchUrl, mue);
 163  0
             }
 164  
         }
 165  0
     }
 166  
 
 167  
     /**
 168  
      * Returns the analyzer's name.
 169  
      *
 170  
      * @return the name of the analyzer
 171  
      */
 172  
     @Override
 173  
     public String getName() {
 174  20
         return ANALYZER_NAME;
 175  
     }
 176  
 
 177  
     /**
 178  
      * Returns the key used in the properties file to reference the analyzer's
 179  
      * enabled property.
 180  
      *
 181  
      * @return the analyzer's enabled property setting key
 182  
      */
 183  
     @Override
 184  
     protected String getAnalyzerEnabledSettingKey() {
 185  2
         return Settings.KEYS.ANALYZER_NEXUS_ENABLED;
 186  
     }
 187  
 
 188  
     /**
 189  
      * Returns the analysis phase under which the analyzer runs.
 190  
      *
 191  
      * @return the phase under which this analyzer runs
 192  
      */
 193  
     @Override
 194  
     public AnalysisPhase getAnalysisPhase() {
 195  6
         return ANALYSIS_PHASE;
 196  
     }
 197  
 
 198  
     /**
 199  
      * The file filter used to determine which files this analyzer supports.
 200  
      */
 201  1
     private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(SUPPORTED_EXTENSIONS).build();
 202  
 
 203  
     /**
 204  
      * Returns the FileFilter
 205  
      *
 206  
      * @return the FileFilter
 207  
      */
 208  
     @Override
 209  
     protected FileFilter getFileFilter() {
 210  858
         return FILTER;
 211  
     }
 212  
 
 213  
     /**
 214  
      * Performs the analysis.
 215  
      *
 216  
      * @param dependency the dependency to analyze
 217  
      * @param engine the engine
 218  
      * @throws AnalysisException when there's an exception during analysis
 219  
      */
 220  
     @Override
 221  
     public void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
 222  0
         if (!isEnabled()) {
 223  0
             return;
 224  
         }
 225  
         try {
 226  0
             final MavenArtifact ma = searcher.searchSha1(dependency.getSha1sum());
 227  0
             dependency.addAsEvidence("nexus", ma, Confidence.HIGH);
 228  0
             boolean pomAnalyzed = false;
 229  0
             LOGGER.debug("POM URL {}", ma.getPomUrl());
 230  0
             for (Evidence e : dependency.getVendorEvidence()) {
 231  0
                 if ("pom".equals(e.getSource())) {
 232  0
                     pomAnalyzed = true;
 233  0
                     break;
 234  
                 }
 235  0
             }
 236  0
             if (!pomAnalyzed && ma.getPomUrl() != null) {
 237  0
                 File pomFile = null;
 238  
                 try {
 239  0
                     final File baseDir = Settings.getTempDirectory();
 240  0
                     pomFile = File.createTempFile("pom", ".xml", baseDir);
 241  0
                     if (!pomFile.delete()) {
 242  0
                         LOGGER.warn("Unable to fetch pom.xml for {} from Nexus repository; "
 243  0
                                 + "this could result in undetected CPE/CVEs.", dependency.getFileName());
 244  0
                         LOGGER.debug("Unable to delete temp file");
 245  
                     }
 246  0
                     LOGGER.debug("Downloading {}", ma.getPomUrl());
 247  0
                     Downloader.fetchFile(new URL(ma.getPomUrl()), pomFile);
 248  0
                     PomUtils.analyzePOM(dependency, pomFile);
 249  0
                 } catch (DownloadFailedException ex) {
 250  0
                     LOGGER.warn("Unable to download pom.xml for {} from Nexus repository; "
 251  0
                             + "this could result in undetected CPE/CVEs.", dependency.getFileName());
 252  
                 } finally {
 253  0
                     if (pomFile != null && pomFile.exists() && !FileUtils.deleteQuietly(pomFile)) {
 254  0
                         LOGGER.debug("Failed to delete temporary pom file {}", pomFile.toString());
 255  0
                         pomFile.deleteOnExit();
 256  
                     }
 257  
                 }
 258  
             }
 259  0
         } catch (IllegalArgumentException iae) {
 260  
             //dependency.addAnalysisException(new AnalysisException("Invalid SHA-1"));
 261  0
             LOGGER.info("invalid sha-1 hash on {}", dependency.getFileName());
 262  0
         } catch (FileNotFoundException fnfe) {
 263  
             //dependency.addAnalysisException(new AnalysisException("Artifact not found on repository"));
 264  0
             LOGGER.debug("Artifact not found in repository '{}'", dependency.getFileName());
 265  0
             LOGGER.debug(fnfe.getMessage(), fnfe);
 266  0
         } catch (IOException ioe) {
 267  
             //dependency.addAnalysisException(new AnalysisException("Could not connect to repository", ioe));
 268  0
             LOGGER.debug("Could not connect to nexus repository", ioe);
 269  0
         }
 270  0
     }
 271  
 
 272  
     /**
 273  
      * Determine if a proxy should be used.
 274  
      *
 275  
      * @return {@code true} if a proxy should be used
 276  
      */
 277  
     public static boolean useProxy() {
 278  
         try {
 279  0
             return Settings.getString(Settings.KEYS.PROXY_SERVER) != null
 280  0
                     && Settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY);
 281  0
         } catch (InvalidSettingException ise) {
 282  0
             LOGGER.warn("Failed to parse proxy settings.", ise);
 283  0
             return false;
 284  
         }
 285  
     }
 286  
 }