diff --git a/maven/src/it/3730-classifier-in-dependency-aggregate/childOne/pom.xml b/maven/src/it/3730-classifier-in-dependency-aggregate/childOne/pom.xml new file mode 100644 index 00000000000..c2554e6ba65 --- /dev/null +++ b/maven/src/it/3730-classifier-in-dependency-aggregate/childOne/pom.xml @@ -0,0 +1,40 @@ + + + + 4.0.0 + + org.owasp.test + 3730-classifier-in-dependency-aggregate + 1.0.0-SNAPSHOT + + 3730-classifier-in-dependency-aggregate-childOne + jar + + + com.google.inject + guice + no_aop + + + org.owasp.test + 3730-classifier-in-dependency-aggregate-childTwo + 1.0.0-SNAPSHOT + + + diff --git a/maven/src/it/3730-classifier-in-dependency-aggregate/childTwo/pom.xml b/maven/src/it/3730-classifier-in-dependency-aggregate/childTwo/pom.xml new file mode 100644 index 00000000000..1d11cad7c1d --- /dev/null +++ b/maven/src/it/3730-classifier-in-dependency-aggregate/childTwo/pom.xml @@ -0,0 +1,35 @@ + + + + 4.0.0 + + org.owasp.test + 3730-classifier-in-dependency-aggregate + 1.0.0-SNAPSHOT + + 3730-classifier-in-dependency-aggregate-childTwo + jar + + + com.google.inject + guice + classes + + + diff --git a/maven/src/it/3730-classifier-in-dependency-aggregate/invoker.properties b/maven/src/it/3730-classifier-in-dependency-aggregate/invoker.properties new file mode 100644 index 00000000000..5cbf9f5f4bd --- /dev/null +++ b/maven/src/it/3730-classifier-in-dependency-aggregate/invoker.properties @@ -0,0 +1,19 @@ +# +# This file is part of dependency-check-maven. +# +# 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. +# + +invoker.goals = --no-transfer-progress --batch-mode -Danalyzer.ossindex.enabled=false -Danalyzer.central.enabled=false ${project.groupId}:${project.artifactId}:${project.version}:aggregate -Dformat=XML -Dcve.startyear=2018 diff --git a/maven/src/it/3730-classifier-in-dependency-aggregate/pom.xml b/maven/src/it/3730-classifier-in-dependency-aggregate/pom.xml new file mode 100644 index 00000000000..04b0b75c0e7 --- /dev/null +++ b/maven/src/it/3730-classifier-in-dependency-aggregate/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + org.owasp.test + 3730-classifier-in-dependency-aggregate + 1.0.0-SNAPSHOT + pom + + childOne + childTwo + + + + + com.google.inject + guice + 4.2.2 + no_aop + provided + + + com.google.inject + guice + 4.2.2 + classes + provided + + + + diff --git a/maven/src/it/3730-classifier-in-dependency-aggregate/postbuild.groovy b/maven/src/it/3730-classifier-in-dependency-aggregate/postbuild.groovy new file mode 100644 index 00000000000..d264f127797 --- /dev/null +++ b/maven/src/it/3730-classifier-in-dependency-aggregate/postbuild.groovy @@ -0,0 +1,43 @@ +/* + * This file is part of dependency-check-maven. + * + * 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. + */ + +import org.apache.commons.io.FileUtils; +import org.w3c.dom.NodeList; + +import java.nio.charset.Charset; +import javax.xml.xpath.* +import javax.xml.parsers.DocumentBuilderFactory + +// Check to see if jackson-databind-2.5.3.jar was identified with a known CVE - using CVE-2018-7489. + +def countMatches(String xml, String xpathQuery) { + def xpath = XPathFactory.newInstance().newXPath() + def builder = DocumentBuilderFactory.newInstance().newDocumentBuilder() + def inputStream = new ByteArrayInputStream( xml.bytes ) + def records = builder.parse(inputStream).documentElement + NodeList nodes = xpath.evaluate( xpathQuery, records, XPathConstants.NODESET ) as NodeList + nodes.getLength(); +} + +String log = FileUtils.readFileToString(new File(basedir, "target/dependency-check-report.xml"), Charset.defaultCharset().name()); +int count = countMatches(log,"/analysis/dependencies/dependency[./fileName = 'guice-4.2.2-no_aop.jar']"); +if (count != 1){ + System.out.println(String.format("google guice no_aop was identified %s times, expected 1", count)); + return false; +} +return true; diff --git a/maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java b/maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java index 1a3582423ae..07e78fbb315 100644 --- a/maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java +++ b/maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java @@ -74,8 +74,9 @@ protected ExceptionCollection scanDependencies(final Engine engine) throws MojoE if (ex != null) { if (exCol == null) { exCol = ex; + } else { + exCol.getExceptions().addAll(ex.getExceptions()); } - exCol.getExceptions().addAll(ex.getExceptions()); if (ex.isFatal()) { exCol.setFatal(true); final String msg = String.format("Fatal exception(s) analyzing %s", childProject.getName()); diff --git a/maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java b/maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java index 2c6b515863d..fea0515bc1b 100644 --- a/maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java +++ b/maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java @@ -1323,7 +1323,7 @@ private ExceptionCollection collectMavenDependencies(Engine engine, MavenProject } } else { final Artifact dependencyArtifact = dependencyNode.getArtifact(); - final Artifact result; + Artifact result; if (dependencyArtifact.isResolved()) { //All transitive dependencies, excluding reactor and dependencyManagement artifacts should //have been resolved by Maven prior to invoking the plugin - resolving the dependencies @@ -1336,14 +1336,23 @@ private ExceptionCollection collectMavenDependencies(Engine engine, MavenProject final List dependencies = project.getDependencies(); final List managedDependencies = project.getDependencyManagement() == null ? null : project.getDependencyManagement().getDependencies(); - final ArtifactCoordinate theCoord = TransferUtils.toArtifactCoordinate(dependencyNode.getArtifact()); - if (theCoord.getClassifier() != null) { + if (coordinate.getClassifier() != null) { // This would trigger NPE when using the filter - MSHARED-998 - getLog().debug("Expensive lookup as workaround for MSHARED-998 for " + theCoord); - final Iterable allDeps - = dependencyResolver.resolveDependencies(buildingRequest, dependencies, managedDependencies, - null); - result = findClassifierArtifactInAllDeps(allDeps, theCoord); + getLog().debug("Expensive lookup as workaround for MSHARED-998 for " + coordinate); + try { + final Iterable allDeps + = dependencyResolver.resolveDependencies(buildingRequest, dependencies, managedDependencies, + null); + result = findClassifierArtifactInAllDeps(allDeps, coordinate, project); + } catch (DependencyResolverException dre) { + result = Mshared998Util.findArtifactInAetherDREResult(dre, coordinate); + if (result == null) { + throw new DependencyNotFoundException( + String.format("Failed to resolve dependency %s with dependencyResolver for " + + "project-artifact %s", coordinate, project.getArtifactId()), + dre); + } + } } else { final TransformableFilter filter = new PatternInclusionsFilter( Collections.singletonList( @@ -1357,7 +1366,7 @@ private ExceptionCollection collectMavenDependencies(Engine engine, MavenProject result = first.getArtifact(); } else { throw new DependencyNotFoundException(String.format("Failed to resolve dependency %s with " - + "dependencyResolver", coordinate)); + + "dependencyResolver for project-artifact %s", coordinate, project.getArtifactId())); } } } catch (DependencyNotFoundException | DependencyResolverException ex) { @@ -1467,11 +1476,13 @@ && addSnapshotReactorDependency(engine, dependencyNode.getArtifact())) { * dependencies * @param theCoord The ArtifactCoordinate of the artifact-with-classifier we * intended to resolve + * @param project The project in whose context resolution was attempted * @return the resolved artifact matching with {@code theCoord} * @throws DependencyNotFoundException Not expected to be thrown, but will * be thrown if {@code theCoord} could not be found within {@code allDeps} */ - private Artifact findClassifierArtifactInAllDeps(final Iterable allDeps, final ArtifactCoordinate theCoord) + private Artifact findClassifierArtifactInAllDeps(final Iterable allDeps, final ArtifactCoordinate theCoord, + final MavenProject project) throws DependencyNotFoundException { Artifact result = null; for (final ArtifactResult res : allDeps) { @@ -1482,7 +1493,7 @@ private Artifact findClassifierArtifactInAllDeps(final Iterable } if (result == null) { throw new DependencyNotFoundException(String.format("Expected dependency not found in resolved artifacts for " - + "dependency %s", theCoord)); + + "dependency %s of project-artifact %s", theCoord, project.getArtifactId())); } return result; } diff --git a/maven/src/main/java/org/owasp/dependencycheck/maven/Mshared998Util.java b/maven/src/main/java/org/owasp/dependencycheck/maven/Mshared998Util.java new file mode 100644 index 00000000000..f5afe585fb2 --- /dev/null +++ b/maven/src/main/java/org/owasp/dependencycheck/maven/Mshared998Util.java @@ -0,0 +1,81 @@ +/* + * This file is part of dependency-check-maven. + * + * 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) 2021 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.maven; + +import org.apache.maven.RepositoryUtils; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.shared.transfer.artifact.ArtifactCoordinate; +import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolverException; +import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.resolution.DependencyResolutionException; +import org.eclipse.aether.resolution.DependencyResult; + +import java.util.Objects; + +public final class Mshared998Util { + /** + * Empty constructor to prevent instantiation of utility-class. + */ + private Mshared998Util() { + } + + /** + * Find the artifact for the given coordinate among the available succesfull resolution attempts contained within the + * DependencyResolverException + * @param dre The DependencyResolverException that might have embedded successful resolution results + * @param coordinate The coordinates of the artifact we're interested in + * @return The resolved artifact matching {@code coordinate} or {@code null} if not found + */ + public static Artifact findArtifactInAetherDREResult(final DependencyResolverException dre, + final ArtifactCoordinate coordinate) { + Artifact result = null; + if (dre.getCause() instanceof DependencyResolutionException) { + final DependencyResolutionException adre = (DependencyResolutionException) dre.getCause(); + final DependencyResult dependencyResult = adre.getResult(); + if (dependencyResult != null) { + for (ArtifactResult artifactResult : dependencyResult.getArtifactResults()) { + if (matchesCoordinate(artifactResult, coordinate)) { + result = RepositoryUtils.toArtifact(artifactResult.getArtifact()); + break; + } + } + } + } + return result; + } + + /** + * Checks whether the given ArtifactResult contains an artifact that matches the coordinate + * @param artifactResult The ArtifactResult to inspect + * @param coordinate The coordinate to match with + * @return {@code true} when the artifactresult contains an artifact that matches the coordinate on GAV_C_E, + * false otherwise. + */ private static boolean matchesCoordinate(final ArtifactResult artifactResult, final ArtifactCoordinate coordinate) { + if (artifactResult.getArtifact() == null) { + return false; + } else { + final org.eclipse.aether.artifact.Artifact artifact = artifactResult.getArtifact(); + boolean result = Objects.equals(artifact.getGroupId(), coordinate.getGroupId()); + result &= Objects.equals(artifact.getArtifactId(), coordinate.getArtifactId()); + result &= Objects.equals(artifact.getVersion(), coordinate.getVersion()); + result &= Objects.equals(artifact.getClassifier(), coordinate.getClassifier()); + result &= Objects.equals(artifact.getExtension(), coordinate.getExtension()); + return result; + } + } +}