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) {

Reply via email to