NexusV2Search.java

  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.data.nexus;

  19. import java.io.FileNotFoundException;
  20. import java.io.IOException;
  21. import java.net.MalformedURLException;
  22. import java.net.URISyntaxException;
  23. import java.net.URL;
  24. import java.util.List;
  25. import javax.annotation.concurrent.ThreadSafe;
  26. import javax.xml.xpath.XPath;
  27. import javax.xml.xpath.XPathExpressionException;
  28. import javax.xml.xpath.XPathFactory;

  29. import org.apache.hc.client5.http.HttpResponseException;
  30. import org.apache.hc.core5.http.ContentType;
  31. import org.apache.hc.core5.http.HttpHeaders;
  32. import org.apache.hc.core5.http.message.BasicHeader;
  33. import org.owasp.dependencycheck.utils.DownloadFailedException;
  34. import org.owasp.dependencycheck.utils.Downloader;
  35. import org.owasp.dependencycheck.utils.ResourceNotFoundException;
  36. import org.owasp.dependencycheck.utils.Settings;

  37. import org.owasp.dependencycheck.utils.ToXMLDocumentResponseHandler;
  38. import org.owasp.dependencycheck.utils.TooManyRequestsException;
  39. import org.slf4j.Logger;
  40. import org.slf4j.LoggerFactory;
  41. import org.w3c.dom.Document;

  42. /**
  43.  * Class of methods to search Nexus repositories.
  44.  *
  45.  * @author colezlaw
  46.  */
  47. @ThreadSafe
  48. public class NexusV2Search implements NexusSearch {

  49.     /**
  50.      * The root URL for the Nexus repository service.
  51.      */
  52.     private final URL rootURL;

  53.     /**
  54.      * Whether to use the Proxy when making requests.
  55.      */
  56.     private final boolean useProxy;
  57.     /**
  58.      * The configured settings.
  59.      */
  60.     private final Settings settings;
  61.     /**
  62.      * Used for logging.
  63.      */
  64.     private static final Logger LOGGER = LoggerFactory.getLogger(NexusV2Search.class);

  65.     /**
  66.      * Creates a NexusSearch for the given repository URL.
  67.      *
  68.      * @param settings the configured settings
  69.      * @param useProxy flag indicating if the proxy settings should be used
  70.      * @throws java.net.MalformedURLException thrown if the configured URL is
  71.      * invalid
  72.      */
  73.     public NexusV2Search(Settings settings, boolean useProxy) throws MalformedURLException {
  74.         this.settings = settings;
  75.         this.useProxy = useProxy;

  76.         final String searchUrl = settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL);
  77.         LOGGER.debug("Nexus Search URL: {}", searchUrl);
  78.         this.rootURL = new URL(searchUrl);

  79.     }

  80.     @Override
  81.     public MavenArtifact searchSha1(String sha1) throws IOException {
  82.         if (null == sha1 || !sha1.matches("^[0-9A-Fa-f]{40}$")) {
  83.             throw new IllegalArgumentException("Invalid SHA1 format");
  84.         }

  85.         final URL url = new URL(rootURL, String.format("identify/sha1/%s",
  86.                 sha1.toLowerCase()));

  87.         LOGGER.debug("Searching Nexus url {}", url);

  88.         try {
  89.             // JSON would be more elegant, but there's not currently a dependency
  90.             // on JSON, so don't want to add one just for this
  91.             final ToXMLDocumentResponseHandler handler = new ToXMLDocumentResponseHandler();
  92.             final Document doc = Downloader.getInstance().fetchAndHandle(url, handler, List.of(new BasicHeader(HttpHeaders.ACCEPT,
  93.                     ContentType.APPLICATION_XML)));
  94.             final XPath xpath = XPathFactory.newInstance().newXPath();
  95.             final String groupId = xpath
  96.                     .evaluate(
  97.                             "/org.sonatype.nexus.rest.model.NexusArtifact/groupId",
  98.                             doc);
  99.             final String artifactId = xpath.evaluate(
  100.                     "/org.sonatype.nexus.rest.model.NexusArtifact/artifactId",
  101.                     doc);
  102.             final String version = xpath
  103.                     .evaluate(
  104.                             "/org.sonatype.nexus.rest.model.NexusArtifact/version",
  105.                             doc);
  106.             final String link = xpath
  107.                     .evaluate(
  108.                             "/org.sonatype.nexus.rest.model.NexusArtifact/artifactLink",
  109.                             doc);
  110.             final String pomLink = xpath
  111.                     .evaluate(
  112.                             "/org.sonatype.nexus.rest.model.NexusArtifact/pomLink",
  113.                             doc);
  114.             final MavenArtifact ma = new MavenArtifact(groupId, artifactId, version);
  115.             if (link != null && !link.isEmpty()) {
  116.                 ma.setArtifactUrl(link);
  117.             }
  118.             if (pomLink != null && !pomLink.isEmpty()) {
  119.                 ma.setPomUrl(pomLink);
  120.             }
  121.             return ma;
  122.         } catch (DownloadFailedException | TooManyRequestsException e) {
  123.                 if (LOGGER.isDebugEnabled()) {
  124.                     int responseCode = -1;
  125.                     String responseMessage = "";
  126.                     if (e.getCause() instanceof HttpResponseException) {
  127.                         final HttpResponseException cause = (HttpResponseException) e.getCause();
  128.                         responseCode = cause.getStatusCode();
  129.                         responseMessage = cause.getReasonPhrase();
  130.                     }
  131.                     LOGGER.debug("Could not connect to Nexus received response code: {} {}",
  132.                             responseCode, responseMessage);
  133.                 }
  134.                 throw new IOException("Could not connect to Nexus");
  135.         } catch (ResourceNotFoundException e) {
  136.             throw new FileNotFoundException("Artifact not found in Nexus");
  137.         } catch (XPathExpressionException | URISyntaxException e) {
  138.             throw new IOException(e.getMessage(), e);
  139.         }
  140.     }

  141.     @Override
  142.     public boolean preflightRequest() {
  143.         try {
  144.             final URL url = new URL(rootURL, "status");
  145.             final ToXMLDocumentResponseHandler handler = new ToXMLDocumentResponseHandler();
  146.             final Document doc = Downloader.getInstance().fetchAndHandle(url, handler, List.of(new BasicHeader(HttpHeaders.ACCEPT,
  147.                     ContentType.APPLICATION_XML)));
  148.             if (!"status".equals(doc.getDocumentElement().getNodeName())) {
  149.                 LOGGER.warn("Pre-flight request to Nexus failed; expected root node name of status, got {}", doc.getDocumentElement().getNodeName());
  150.                 return false;
  151.             }
  152.         } catch (IOException | TooManyRequestsException | ResourceNotFoundException | URISyntaxException e) {
  153.             LOGGER.warn("Pre-flight request to Nexus failed: ", e);
  154.             return false;
  155.         }
  156.         return true;
  157.     }

  158. }