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 20 import java.io.FileNotFoundException; 21 import java.io.IOException; 22 import java.net.HttpURLConnection; 23 import java.net.MalformedURLException; 24 import java.net.URL; 25 import javax.annotation.concurrent.ThreadSafe; 26 import javax.xml.parsers.DocumentBuilder; 27 import javax.xml.parsers.ParserConfigurationException; 28 import javax.xml.xpath.XPath; 29 import javax.xml.xpath.XPathExpressionException; 30 import javax.xml.xpath.XPathFactory; 31 import org.owasp.dependencycheck.utils.Settings; 32 33 import org.owasp.dependencycheck.utils.URLConnectionFactory; 34 import org.owasp.dependencycheck.utils.XmlUtils; 35 import org.slf4j.Logger; 36 import org.slf4j.LoggerFactory; 37 import org.w3c.dom.Document; 38 import org.xml.sax.SAXException; 39 40 /** 41 * Class of methods to search Nexus repositories. 42 * 43 * @author colezlaw 44 */ 45 @ThreadSafe 46 public class NexusSearch { 47 48 /** 49 * The root URL for the Nexus repository service. 50 */ 51 private final URL rootURL; 52 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(NexusSearch.class); 65 66 /** 67 * Creates a NexusSearch for the given repository URL. 68 * 69 * @param settings the configured settings 70 * @param useProxy flag indicating if the proxy settings should be used 71 * @throws java.net.MalformedURLException thrown if the configured URL is 72 * invalid 73 */ NexusSearch(Settings settings, boolean useProxy)74 public NexusSearch(Settings settings, boolean useProxy) throws MalformedURLException { 75 this.settings = settings; 76 this.useProxy = useProxy; 77 78 final String searchUrl = settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL); 79 LOGGER.debug("Nexus Search URL: {}", searchUrl); 80 this.rootURL = new URL(searchUrl); 81 82 } 83 84 /** 85 * Searches the configured Nexus repository for the given sha1 hash. If the 86 * artifact is found, a <code>MavenArtifact</code> is populated with the 87 * coordinate information. 88 * 89 * @param sha1 The SHA-1 hash string for which to search 90 * @return the populated Maven coordinates 91 * @throws IOException if it's unable to connect to the specified repository 92 * or if the specified artifact is not found. 93 */ searchSha1(String sha1)94 public MavenArtifact searchSha1(String sha1) throws IOException { 95 if (null == sha1 || !sha1.matches("^[0-9A-Fa-f]{40}$")) { 96 throw new IllegalArgumentException("Invalid SHA1 format"); 97 } 98 99 final URL url = new URL(rootURL, String.format("identify/sha1/%s", 100 sha1.toLowerCase())); 101 102 LOGGER.debug("Searching Nexus url {}", url); 103 104 // Determine if we need to use a proxy. The rules: 105 // 1) If the proxy is set, AND the setting is set to true, use the proxy 106 // 2) Otherwise, don't use the proxy (either the proxy isn't configured, 107 // or proxy is specifically set to false 108 HttpURLConnection conn; 109 final URLConnectionFactory factory = new URLConnectionFactory(settings); 110 conn = factory.createHttpURLConnection(url, useProxy); 111 conn.setDoOutput(true); 112 113 // JSON would be more elegant, but there's not currently a dependency 114 // on JSON, so don't want to add one just for this 115 conn.addRequestProperty("Accept", "application/xml"); 116 conn.connect(); 117 118 switch (conn.getResponseCode()) { 119 case 200: 120 try { 121 final DocumentBuilder builder = XmlUtils.buildSecureDocumentBuilder(); 122 final Document doc = builder.parse(conn.getInputStream()); 123 final XPath xpath = XPathFactory.newInstance().newXPath(); 124 final String groupId = xpath 125 .evaluate( 126 "/org.sonatype.nexus.rest.model.NexusArtifact/groupId", 127 doc); 128 final String artifactId = xpath.evaluate( 129 "/org.sonatype.nexus.rest.model.NexusArtifact/artifactId", 130 doc); 131 final String version = xpath 132 .evaluate( 133 "/org.sonatype.nexus.rest.model.NexusArtifact/version", 134 doc); 135 final String link = xpath 136 .evaluate( 137 "/org.sonatype.nexus.rest.model.NexusArtifact/artifactLink", 138 doc); 139 final String pomLink = xpath 140 .evaluate( 141 "/org.sonatype.nexus.rest.model.NexusArtifact/pomLink", 142 doc); 143 final MavenArtifact ma = new MavenArtifact(groupId, artifactId, version); 144 if (link != null && !link.isEmpty()) { 145 ma.setArtifactUrl(link); 146 } 147 if (pomLink != null && !pomLink.isEmpty()) { 148 ma.setPomUrl(pomLink); 149 } 150 return ma; 151 } catch (ParserConfigurationException | IOException | SAXException | XPathExpressionException e) { 152 // Anything else is jacked-up XML stuff that we really can't recover 153 // from well 154 throw new IOException(e.getMessage(), e); 155 } 156 case 404: 157 throw new FileNotFoundException("Artifact not found in Nexus"); 158 default: 159 LOGGER.debug("Could not connect to Nexus received response code: {} {}", 160 conn.getResponseCode(), conn.getResponseMessage()); 161 throw new IOException("Could not connect to Nexus"); 162 } 163 } 164 165 /** 166 * Do a preflight request to see if the repository is actually working. 167 * 168 * @return whether the repository is listening and returns the /status URL 169 * correctly 170 */ preflightRequest()171 public boolean preflightRequest() { 172 HttpURLConnection conn; 173 try { 174 final URL url = new URL(rootURL, "status"); 175 final URLConnectionFactory factory = new URLConnectionFactory(settings); 176 conn = factory.createHttpURLConnection(url, useProxy); 177 conn.addRequestProperty("Accept", "application/xml"); 178 conn.connect(); 179 if (conn.getResponseCode() != 200) { 180 LOGGER.warn("Expected 200 result from Nexus, got {}", conn.getResponseCode()); 181 return false; 182 } 183 final DocumentBuilder builder = XmlUtils.buildSecureDocumentBuilder(); 184 185 final Document doc = builder.parse(conn.getInputStream()); 186 if (!"status".equals(doc.getDocumentElement().getNodeName())) { 187 LOGGER.warn("Expected root node name of status, got {}", doc.getDocumentElement().getNodeName()); 188 return false; 189 } 190 } catch (IOException | ParserConfigurationException | SAXException e) { 191 return false; 192 } 193 return true; 194 } 195 } 196