This is an automated email from the ASF dual-hosted git repository.
ottlinger pushed a commit to branch feature/TENTACLES-26
in repository https://gitbox.apache.org/repos/asf/creadur-tentacles.git
The following commit(s) were added to refs/heads/feature/TENTACLES-26 by this
push:
new 6a23354 TENTACLES-26: Check for possible zip slip entries in Archive
6a23354 is described below
commit 6a2335479ee32e3c7203c39c5b087ead71231527
Author: P. Ottlinger <[email protected]>
AuthorDate: Fri Jul 17 12:00:38 2026 +0200
TENTACLES-26: Check for possible zip slip entries in Archive
---
.../java/org/apache/creadur/tentacles/Archive.java | 25 +++++++++++++++-------
.../java/org/apache/creadur/tentacles/License.java | 5 +++--
.../java/org/apache/creadur/tentacles/Main.java | 23 ++++++++++----------
.../java/org/apache/creadur/tentacles/Notice.java | 5 +++--
4 files changed, 35 insertions(+), 23 deletions(-)
diff --git a/src/main/java/org/apache/creadur/tentacles/Archive.java
b/src/main/java/org/apache/creadur/tentacles/Archive.java
index ab28dc8..c146e88 100644
--- a/src/main/java/org/apache/creadur/tentacles/Archive.java
+++ b/src/main/java/org/apache/creadur/tentacles/Archive.java
@@ -1,4 +1,4 @@
-/**
+/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -7,7 +7,7 @@
* "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
+ * https://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
@@ -19,7 +19,9 @@
package org.apache.creadur.tentacles;
import java.io.File;
+import java.io.IOException;
import java.net.URI;
+import java.nio.file.Path;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@@ -46,7 +48,7 @@ public class Archive {
private Map<URI, URI> others;
public Archive(final File file, final FileSystem fileSystem,
- final Layout layout) {
+ final Layout layout) throws IOException {
this.fileSystem = fileSystem;
this.layout = layout;
this.uri =
@@ -92,14 +94,14 @@ public class Archive {
return this.map;
}
- public Map<URI, URI> getOtherLegal() {
+ public Map<URI, URI> getOtherLegal() throws IOException {
if (this.others == null) {
this.others = mapOther();
}
return this.others;
}
- private Map<URI, URI> mapOther() {
+ private Map<URI, URI> mapOther() throws IOException {
final File jarContents = contentsDirectory();
final List<File> legal =
this.fileSystem.legalDocumentsUndeclaredIn(jarContents);
@@ -121,7 +123,7 @@ public class Archive {
return map;
}
- private Map<URI, URI> map() {
+ private Map<URI, URI> map() throws IOException {
final File jarContents = contentsDirectory();
final List<File> legal =
this.fileSystem.legalDocumentsDeclaredIn(jarContents);
@@ -129,7 +131,7 @@ public class Archive {
return buildMapFrom(jarContents, legal);
}
- public File contentsDirectory() {
+ public File contentsDirectory() throws IOException {
final File archiveDocument = getFile();
String path =
archiveDocument.getAbsolutePath().substring(
@@ -146,11 +148,18 @@ public class Archive {
final File contents =
new File(this.layout.getContentRootDirectory(), path
+ ".contents");
+
+ Path targetDir =
this.layout.getContentRootDirectory().toPath().toAbsolutePath().normalize();
+ Path resolved = targetDir.resolve(path + ".contents").normalize();
+ if(!resolved.startsWith(targetDir)) {
+ throw new IOException("Invalid content directory: " + path +
".contents");
+ }
+
this.fileSystem.mkdirs(contents);
return contents;
}
- public URI contentsURI() {
+ public URI contentsURI() throws IOException {
return contentsDirectory().toURI();
}
}
\ No newline at end of file
diff --git a/src/main/java/org/apache/creadur/tentacles/License.java
b/src/main/java/org/apache/creadur/tentacles/License.java
index bb462d8..ba2f58a 100644
--- a/src/main/java/org/apache/creadur/tentacles/License.java
+++ b/src/main/java/org/apache/creadur/tentacles/License.java
@@ -1,4 +1,4 @@
-/**
+/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -19,6 +19,7 @@
package org.apache.creadur.tentacles;
import java.io.File;
+import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
@@ -52,7 +53,7 @@ public class License {
return this.locations;
}
- public Set<URI> locations(final Archive archive) {
+ public Set<URI> locations(final Archive archive) throws IOException {
final URI contents = archive.contentsURI();
final Set<URI> locations = new HashSet<>();
for (final File file : this.locations) {
diff --git a/src/main/java/org/apache/creadur/tentacles/Main.java
b/src/main/java/org/apache/creadur/tentacles/Main.java
index 3fa8922..5a3eceb 100644
--- a/src/main/java/org/apache/creadur/tentacles/Main.java
+++ b/src/main/java/org/apache/creadur/tentacles/Main.java
@@ -23,6 +23,7 @@ import static
org.apache.creadur.tentacles.RepositoryType.LOCAL_FILE_SYSTEM;
import java.io.File;
import java.io.IOException;
import java.net.URI;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -100,19 +101,17 @@ public class Main {
log.error("Error: Input parameter missing - you did not specify
any component to run Apache Tentacles on.");
log.error("Please launch Apache Tentacles with an URI to work
on such as
'https://repository.apache.org/content/repositories/orgapachecreadur-1000/'.");
} else {
- new Main(args).main();
+ new Main(args).run();
}
}
- private void main() throws Exception {
-
+ private void run() throws Exception {
unpackContents(mirrorRepositoryFrom(this.configuration));
-
reportOn(archivesIn(this.layout.getRepositoryDirectory()));
}
- private List<Archive> archivesIn(final File repository) {
+ private List<Archive> archivesIn(final File repository) throws IOException
{
final List<File> jars = this.fileSystem.documentsFrom(repository);
final List<Archive> archives = new ArrayList<>();
@@ -207,9 +206,7 @@ public class Main {
final List<File> files = this.fileSystem.licensesDeclaredIn(contents);
for (final File file : files) {
-
undeclared.remove(this.licenses.from(file));
-
}
archive.getOtherLicenses().addAll(undeclared);
@@ -220,7 +217,6 @@ public class Main {
archive.getDeclaredLicenses().addAll(declared);
for (final License license : undeclared) {
-
for (final License declare : declared) {
if (license.implies(declare)) {
archive.getOtherLicenses().remove(license);
@@ -233,7 +229,6 @@ public class Main {
throws IOException {
for (final Archive archive : archives) {
-
final Set<Notice> undeclared =
new HashSet<>(archive.getNotices());
@@ -356,12 +351,18 @@ public class Main {
final String path = entry.getName();
- final File fileEntry = new File(contents, path);
+ // check if entry has suspicious path traversal elements
+ Path target =
contents.toPath().toAbsolutePath().normalize();
+ Path resolved = target.resolve(path).normalize();
+
+ if(!resolved.startsWith(target)) {
+ throw new IOException("Invalid archive entry: " +
path);
+ }
+ final File fileEntry = new File(contents, path); // NOSONAR
this.fileSystem.mkparent(fileEntry);
// Open the output file
-
this.ioSystem.copy(zip, fileEntry);
if (fileEntry.getName().endsWith(".jar")) {
diff --git a/src/main/java/org/apache/creadur/tentacles/Notice.java
b/src/main/java/org/apache/creadur/tentacles/Notice.java
index ff6c419..01c206f 100644
--- a/src/main/java/org/apache/creadur/tentacles/Notice.java
+++ b/src/main/java/org/apache/creadur/tentacles/Notice.java
@@ -1,4 +1,4 @@
-/**
+/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -19,6 +19,7 @@
package org.apache.creadur.tentacles;
import java.io.File;
+import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
@@ -52,7 +53,7 @@ public class Notice {
return this.locations;
}
- public Set<URI> locations(final Archive archive) {
+ public Set<URI> locations(final Archive archive) throws IOException {
final URI contents = archive.contentsURI();
final Set<URI> locations = new HashSet<>();
for (final File file : this.locations) {