Skip to content
Permalink
Browse files Browse the repository at this point in the history
fix: CVE-2022-41967
Dragonfly v0.3.0-SNAPSHOT fails to properly configure the
DocumentBuilderFactory to prevent XML enternal entity (XXE) attacks when
parsing maven-metadata.xml files provided by external Maven repositories
during "SNAPSHOT" version resolution.

This patches CVE-2022-41967 by disabling features which may lead to XXE.
If you are currently using v0.3.0-SNAPSHOT it is STRONGLY advised to
update Dragonfly to v0.3.1-SNAPSHOT just to be safe.
  • Loading branch information
joshuasing committed Dec 23, 2022
1 parent 887c489 commit 9661375
Show file tree
Hide file tree
Showing 18 changed files with 110 additions and 71 deletions.
8 changes: 0 additions & 8 deletions AUTHORS

This file was deleted.

2 changes: 1 addition & 1 deletion LICENSE
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021 Joshua Sing <joshua@hypera.dev>
Copyright (c) 2021-2022 Joshua Sing <joshua@hypera.dev>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
4 changes: 2 additions & 2 deletions SECURITY.md
Expand Up @@ -7,8 +7,8 @@

### Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 0.3.0 | :white_check_mark: |
|---------| ------------------ |
| 0.3.x | :white_check_mark: |
| < 0.3 | :x: |

### Reporting a Vulnerability
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Expand Up @@ -29,7 +29,7 @@

<groupId>dev.hypera</groupId>
<artifactId>Dragonfly</artifactId>
<version>0.3.0-SNAPSHOT</version>
<version>0.3.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Dragonfly</name>
Expand Down Expand Up @@ -121,7 +121,7 @@
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>22.0.0</version>
<version>23.1.0</version>
</dependency>
<dependency>
<groupId>me.lucko</groupId>
Expand Down
25 changes: 13 additions & 12 deletions src/main/java/dev/hypera/dragonfly/Dragonfly.java
Expand Up @@ -40,23 +40,24 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.jetbrains.annotations.ApiStatus.Internal;
import org.jetbrains.annotations.NotNull;

/**
* Main Dragonfly class.
* @author Joshua Sing <joshua@hypera.dev>
*/
public class Dragonfly {

private static final String VERSION = "0.3.0-SNAPSHOT";
private static final @NotNull String VERSION = "0.3.1-SNAPSHOT";

private final int timeout;
private final Path directory;
private final Set<String> repositories;
private final Consumer<Status> statusHandler;
private final @NotNull Path directory;
private final @NotNull Set<String> repositories;
private final @NotNull Consumer<Status> statusHandler;

private final DependencyDownloader dependencyDownloader = new DependencyDownloader(this);
private final DependencyRelocator dependencyRelocator;
private final DependencyLoader dependencyLoader;
private final @NotNull DependencyDownloader dependencyDownloader = new DependencyDownloader(this);
private final @NotNull DependencyRelocator dependencyRelocator;
private final @NotNull DependencyLoader dependencyLoader;


@Internal
Expand All @@ -74,7 +75,7 @@ protected Dragonfly(int timeout, IClassLoader classLoader, Path directory, Set<S
}
}

public static String getVersion() {
public static @NotNull String getVersion() {
return VERSION;
}

Expand All @@ -84,7 +85,7 @@ public static String getVersion() {
* @param dependencies Dependencies to be loaded.
* @return If the load was successful, in the form of a {@link CompletableFuture<Boolean>}.
*/
public CompletableFuture<Boolean> load(Dependency... dependencies) {
public @NotNull CompletableFuture<Boolean> load(@NotNull Dependency... dependencies) {
return CompletableFuture.supplyAsync(() -> {
try {
statusHandler.accept(Status.STARTING);
Expand Down Expand Up @@ -137,11 +138,11 @@ public int getTimeout() {
return timeout;
}

public Path getDirectory() {
public @NotNull Path getDirectory() {
return directory;
}

public Set<String> getRepositories() {
public @NotNull Set<String> getRepositories() {
return repositories;
}

Expand All @@ -151,7 +152,7 @@ public Set<String> getRepositories() {
* @return Stored instance of {@link DependencyDownloader}.
*/
@Internal
public DependencyDownloader getDependencyDownloader() {
public @NotNull DependencyDownloader getDependencyDownloader() {
return dependencyDownloader;
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/dev/hypera/dragonfly/DragonflyBuilder.java
Expand Up @@ -32,6 +32,7 @@
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

/**
Expand Down Expand Up @@ -115,6 +116,7 @@ private DragonflyBuilder(@NotNull IClassLoader classLoader, @NotNull Path direct
*
* @return New {@link Dragonfly} instance.
*/
@Contract(value = "-> new", pure = true)
public @NotNull Dragonfly build() {
try {
return new Dragonfly(timeout, classLoader, directory, repositories, deleteOnRelocate, statusHandler);
Expand Down
Expand Up @@ -41,8 +41,8 @@
@Downloader(MavenDependency.class)
public class MavenDownloader implements IDownloader<MavenDependency> {

private final MavenResolver resolver = new MavenResolver();
private final MavenSnapshotResolver snapshotResolver = new MavenSnapshotResolver();
private final @NotNull MavenResolver resolver = new MavenResolver();
private final @NotNull MavenSnapshotResolver snapshotResolver = new MavenSnapshotResolver();

@Override
public void download(@NotNull Dragonfly dragonfly, @NotNull MavenDependency dependency) throws DownloadFailureException {
Expand Down
Expand Up @@ -23,25 +23,29 @@

package dev.hypera.dragonfly.exceptions;

import org.jetbrains.annotations.NotNull;

public class DownloadFailureException extends DragonflyException {

private static final long serialVersionUID = 5648475409314204882L;

public DownloadFailureException() {
super();
}

public DownloadFailureException(String message) {
public DownloadFailureException(@NotNull String message) {
super(message);
}

public DownloadFailureException(String message, Throwable cause) {
public DownloadFailureException(@NotNull String message, @NotNull Throwable cause) {
super(message, cause);
}

public DownloadFailureException(Throwable cause) {
public DownloadFailureException(@NotNull Throwable cause) {
super(cause);
}

protected DownloadFailureException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
protected DownloadFailureException(@NotNull String message, @NotNull Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}

Expand Down
Expand Up @@ -23,29 +23,33 @@

package dev.hypera.dragonfly.exceptions;

import org.jetbrains.annotations.NotNull;

/**
* Dragonfly Exception.
* @author Joshua Sing <joshua@hypera.dev>
*/
public class DragonflyException extends Exception {

private static final long serialVersionUID = 3565376065959848642L;

public DragonflyException() {
super();
}

public DragonflyException(String message) {
public DragonflyException(@NotNull String message) {
super(message);
}

public DragonflyException(String message, Throwable cause) {
public DragonflyException(@NotNull String message, @NotNull Throwable cause) {
super(message, cause);
}

public DragonflyException(Throwable cause) {
public DragonflyException(@NotNull Throwable cause) {
super(cause);
}

protected DragonflyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
protected DragonflyException(@NotNull String message, @NotNull Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}

Expand Down
Expand Up @@ -23,25 +23,29 @@

package dev.hypera.dragonfly.exceptions;

import org.jetbrains.annotations.NotNull;

public class LoadFailureException extends DragonflyException {

private static final long serialVersionUID = -8555618454694039541L;

public LoadFailureException() {
super();
}

public LoadFailureException(String message) {
public LoadFailureException(@NotNull String message) {
super(message);
}

public LoadFailureException(String message, Throwable cause) {
public LoadFailureException(@NotNull String message, @NotNull Throwable cause) {
super(message, cause);
}

public LoadFailureException(Throwable cause) {
public LoadFailureException(@NotNull Throwable cause) {
super(cause);
}

protected LoadFailureException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
protected LoadFailureException(@NotNull String message, @NotNull Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}

Expand Down
Expand Up @@ -23,25 +23,29 @@

package dev.hypera.dragonfly.exceptions;

import org.jetbrains.annotations.NotNull;

public class RelocationFailureException extends DragonflyException {

private static final long serialVersionUID = -2817144091943660838L;

public RelocationFailureException() {
super();
}

public RelocationFailureException(String message) {
public RelocationFailureException(@NotNull String message) {
super(message);
}

public RelocationFailureException(String message, Throwable cause) {
public RelocationFailureException(@NotNull String message, @NotNull Throwable cause) {
super(message, cause);
}

public RelocationFailureException(Throwable cause) {
public RelocationFailureException(@NotNull Throwable cause) {
super(cause);
}

protected RelocationFailureException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
protected RelocationFailureException(@NotNull String message, @NotNull Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}

Expand Down
Expand Up @@ -23,25 +23,29 @@

package dev.hypera.dragonfly.exceptions;

import org.jetbrains.annotations.NotNull;

public class ResolveFailureException extends DownloadFailureException {

private static final long serialVersionUID = -8393996338726021911L;

public ResolveFailureException() {
super();
}

public ResolveFailureException(String message) {
public ResolveFailureException(@NotNull String message) {
super(message);
}

public ResolveFailureException(String message, Throwable cause) {
public ResolveFailureException(@NotNull String message, @NotNull Throwable cause) {
super(message, cause);
}

public ResolveFailureException(Throwable cause) {
public ResolveFailureException(@NotNull Throwable cause) {
super(cause);
}

protected ResolveFailureException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
protected ResolveFailureException(@NotNull String message, @NotNull Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}

Expand Down
11 changes: 6 additions & 5 deletions src/main/java/dev/hypera/dragonfly/loading/DependencyLoader.java
Expand Up @@ -29,6 +29,7 @@
import java.net.MalformedURLException;
import java.util.List;
import org.jetbrains.annotations.ApiStatus.Internal;
import org.jetbrains.annotations.NotNull;

/**
* Dependency loader.
Expand All @@ -37,11 +38,11 @@
*/
public class DependencyLoader {

private final Dragonfly dragonfly;
private final IClassLoader classLoader;
private final @NotNull Dragonfly dragonfly;
private final @NotNull IClassLoader classLoader;

@Internal
public DependencyLoader(Dragonfly dragonfly, IClassLoader classLoader) {
public DependencyLoader(@NotNull Dragonfly dragonfly, @NotNull IClassLoader classLoader) {
this.dragonfly = dragonfly;
this.classLoader = classLoader;
}
Expand All @@ -52,7 +53,7 @@ public DependencyLoader(Dragonfly dragonfly, IClassLoader classLoader) {
* @param dependencies Dependencies to be loaded.
* @throws LoadFailureException if something went wrong while loading the dependencies.
*/
public void load(List<Dependency> dependencies) throws LoadFailureException {
public void load(@NotNull List<Dependency> dependencies) throws LoadFailureException {
for (Dependency dependency : dependencies) {
load(dependency);
}
Expand All @@ -64,7 +65,7 @@ public void load(List<Dependency> dependencies) throws LoadFailureException {
* @param dependency Dependency to be loaded.
* @throws LoadFailureException if something went wrong while loading the dependency.
*/
private void load(Dependency dependency) throws LoadFailureException {
private void load(@NotNull Dependency dependency) throws LoadFailureException {
try {
classLoader.addURL(dragonfly.getDirectory().resolve(dependency.getFileName()).toUri().toURL());
} catch (MalformedURLException ex) {
Expand Down
Expand Up @@ -26,6 +26,7 @@
import java.net.URL;
import java.net.URLClassLoader;
import org.jetbrains.annotations.ApiStatus.Internal;
import org.jetbrains.annotations.NotNull;

/**
* Dragonfly class loader, a child-first {@link URLClassLoader} used for loading Dragonfly's internal dependencies.
Expand All @@ -35,17 +36,17 @@
@Internal
public class DragonflyClassLoader extends URLClassLoader {

public DragonflyClassLoader(ClassLoader classLoader) {
public DragonflyClassLoader(@NotNull ClassLoader classLoader) {
super(new URL[0], classLoader);
}

@Override
public void addURL(URL url) {
public void addURL(@NotNull URL url) {
super.addURL(url);
}

@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
protected @NotNull Class<?> loadClass(@NotNull String name, boolean resolve) throws ClassNotFoundException {
Class<?> loadedClass = findLoadedClass(name);
if (null == loadedClass) {
try {
Expand Down

0 comments on commit 9661375

Please sign in to comment.