NexusV2Search.java
/*
* This file is part of dependency-check-core.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright (c) 2014 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.data.nexus;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import javax.annotation.concurrent.ThreadSafe;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.hc.client5.http.HttpResponseException;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.message.BasicHeader;
import org.owasp.dependencycheck.utils.DownloadFailedException;
import org.owasp.dependencycheck.utils.Downloader;
import org.owasp.dependencycheck.utils.ResourceNotFoundException;
import org.owasp.dependencycheck.utils.Settings;
import org.owasp.dependencycheck.utils.ToXMLDocumentResponseHandler;
import org.owasp.dependencycheck.utils.TooManyRequestsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
/**
* Class of methods to search Nexus repositories.
*
* @author colezlaw
*/
@ThreadSafe
public class NexusV2Search implements NexusSearch {
/**
* The root URL for the Nexus repository service.
*/
private final URL rootURL;
/**
* Whether to use the Proxy when making requests.
*/
private final boolean useProxy;
/**
* The configured settings.
*/
private final Settings settings;
/**
* Used for logging.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(NexusV2Search.class);
/**
* Creates a NexusSearch for the given repository URL.
*
* @param settings the configured settings
* @param useProxy flag indicating if the proxy settings should be used
* @throws java.net.MalformedURLException thrown if the configured URL is
* invalid
*/
public NexusV2Search(Settings settings, boolean useProxy) throws MalformedURLException {
this.settings = settings;
this.useProxy = useProxy;
final String searchUrl = settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL);
LOGGER.debug("Nexus Search URL: {}", searchUrl);
this.rootURL = new URL(searchUrl);
}
@Override
public MavenArtifact searchSha1(String sha1) throws IOException {
if (null == sha1 || !sha1.matches("^[0-9A-Fa-f]{40}$")) {
throw new IllegalArgumentException("Invalid SHA1 format");
}
final URL url = new URL(rootURL, String.format("identify/sha1/%s",
sha1.toLowerCase()));
LOGGER.debug("Searching Nexus url {}", url);
try {
// JSON would be more elegant, but there's not currently a dependency
// on JSON, so don't want to add one just for this
final ToXMLDocumentResponseHandler handler = new ToXMLDocumentResponseHandler();
final Document doc = Downloader.getInstance().fetchAndHandle(url, handler, List.of(new BasicHeader(HttpHeaders.ACCEPT,
ContentType.APPLICATION_XML)));
final XPath xpath = XPathFactory.newInstance().newXPath();
final String groupId = xpath
.evaluate(
"/org.sonatype.nexus.rest.model.NexusArtifact/groupId",
doc);
final String artifactId = xpath.evaluate(
"/org.sonatype.nexus.rest.model.NexusArtifact/artifactId",
doc);
final String version = xpath
.evaluate(
"/org.sonatype.nexus.rest.model.NexusArtifact/version",
doc);
final String link = xpath
.evaluate(
"/org.sonatype.nexus.rest.model.NexusArtifact/artifactLink",
doc);
final String pomLink = xpath
.evaluate(
"/org.sonatype.nexus.rest.model.NexusArtifact/pomLink",
doc);
final MavenArtifact ma = new MavenArtifact(groupId, artifactId, version);
if (link != null && !link.isEmpty()) {
ma.setArtifactUrl(link);
}
if (pomLink != null && !pomLink.isEmpty()) {
ma.setPomUrl(pomLink);
}
return ma;
} catch (DownloadFailedException | TooManyRequestsException e) {
if (LOGGER.isDebugEnabled()) {
int responseCode = -1;
String responseMessage = "";
if (e.getCause() instanceof HttpResponseException) {
final HttpResponseException cause = (HttpResponseException) e.getCause();
responseCode = cause.getStatusCode();
responseMessage = cause.getReasonPhrase();
}
LOGGER.debug("Could not connect to Nexus received response code: {} {}",
responseCode, responseMessage);
}
throw new IOException("Could not connect to Nexus");
} catch (ResourceNotFoundException e) {
throw new FileNotFoundException("Artifact not found in Nexus");
} catch (XPathExpressionException | URISyntaxException e) {
throw new IOException(e.getMessage(), e);
}
}
@Override
public boolean preflightRequest() {
try {
final URL url = new URL(rootURL, "status");
final ToXMLDocumentResponseHandler handler = new ToXMLDocumentResponseHandler();
final Document doc = Downloader.getInstance().fetchAndHandle(url, handler, List.of(new BasicHeader(HttpHeaders.ACCEPT,
ContentType.APPLICATION_XML)));
if (!"status".equals(doc.getDocumentElement().getNodeName())) {
LOGGER.warn("Pre-flight request to Nexus failed; expected root node name of status, got {}", doc.getDocumentElement().getNodeName());
return false;
}
} catch (IOException | TooManyRequestsException | ResourceNotFoundException | URISyntaxException e) {
LOGGER.warn("Pre-flight request to Nexus failed: ", e);
return false;
}
return true;
}
}