Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions unzipfileandupload/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.testsigma.addons</groupId>
<artifactId>unzipfileandupload</artifactId>
<version>1.0.3</version>
<packaging>jar</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<testsigma.sdk.version>1.2.8_cloud</testsigma.sdk.version>
<junit.jupiter.version>5.8.0-M1</junit.jupiter.version>
<testsigma.addon.maven.plugin>1.0.0</testsigma.addon.maven.plugin>
<maven.source.plugin.version>3.2.1</maven.source.plugin.version>
<lombok.version>1.18.30</lombok.version>

</properties>

<dependencies>
<dependency>
<groupId>com.testsigma</groupId>
<artifactId>testsigma-java-sdk</artifactId>
<version>${testsigma.sdk.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.33.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.appium/java-client -->
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>9.4.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.17.0</version>
</dependency>

</dependencies>
<build>
<finalName>unzipfileandupload</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven.source.plugin.version}</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package com.testsigma.addons.web;

import com.testsigma.addons.web.util.DownloadUtilities;
import com.testsigma.addons.web.util.DownloadUtilitiesFactory;
import com.testsigma.sdk.ApplicationType;
import com.testsigma.sdk.Result;
import com.testsigma.sdk.WebAction;
import com.testsigma.sdk.annotation.Action;
import com.testsigma.sdk.annotation.RunTimeData;
import com.testsigma.sdk.annotation.TestData;
import lombok.Data;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.openqa.selenium.NoSuchElementException;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

@Data
@Action(
actionText = "ZIP: Extract complete file paths from latest zip file in the downloads and store it in runtime-variable variable-name",
description = "Extracts absolute file paths (excluding __MACOSX entries) from the latest downloaded zip file and stores them in a runtime variable",
applicationType = ApplicationType.WEB
)
public class ExtractLatestDownloadedZipFilePaths extends WebAction {

@TestData(reference = "variable-name", isRuntimeVariable = true)
private com.testsigma.sdk.TestData runtimeVariable;

@RunTimeData
private com.testsigma.sdk.RunTimeData runTimeData;

@Override
protected Result execute() throws NoSuchElementException {
Result result = Result.SUCCESS;

DownloadUtilities downloadUtilities =
DownloadUtilitiesFactory.create(driver, logger);

try {
logger.info("Initiated execution");

// Get latest downloaded ZIP file (copied to agent temp location)
File downloadedZipFile =
downloadUtilities.copyFileFromDownloads("zip", null);

logger.info("ZIP local path: " + downloadedZipFile.getAbsolutePath());

StringBuilder filePaths = new StringBuilder();

try (ZipFile zipFile = new ZipFile(downloadedZipFile)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
boolean first = true;

// Base directory where ZIP exists
File zipBaseDir = downloadedZipFile.getParentFile();

while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();

if (entry.isDirectory() || entry.getName().startsWith("__MACOSX")) {
continue;
}

File fullPath = new File(zipBaseDir, entry.getName());

File parent = fullPath.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
}
Comment on lines +69 to +74
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Zip Slip vulnerability: Validate entry paths before extraction.

Malicious ZIP files can contain entries with paths like ../../../etc/passwd. The code directly uses entry.getName() to construct the output path without validating that it stays within the intended extraction directory.

Proposed fix to prevent path traversal
+                    // Prevent Zip Slip vulnerability
+                    String canonicalDestPath = zipBaseDir.getCanonicalPath();
                     File fullPath = new File(zipBaseDir, entry.getName());
+                    if (!fullPath.getCanonicalPath().startsWith(canonicalDestPath + File.separator)) {
+                        throw new SecurityException("ZIP entry is outside of the target dir: " + entry.getName());
+                    }

                     File parent = fullPath.getParentFile();
                     if (!parent.exists()) {
                         parent.mkdirs();
                     }
🤖 Prompt for AI Agents
In
@unzipfileandupload/src/main/java/com/testsigma/addons/web/ExtractLatestDownloadedZipFilePaths.java
around lines 69 - 74, The code in ExtractLatestDownloadedZipFilePaths uses
entry.getName() directly to build File(fullPath, entry.getName()) and create
parent directories, which allows Zip Slip path traversal; fix by resolving
canonical paths: compute the canonical path of the extraction base directory and
the canonical path of the constructed target file (after joining zipBaseDir with
entry.getName()), reject any entry whose canonical target does not start with
the canonical base directory (or contains ".." / is absolute), and skip or throw
on such invalid entries before calling parent.mkdirs() or writing the file;
update the logic around File fullPath = new File(zipBaseDir, entry.getName()) /
parent.mkdirs() to perform this validation first.


try (InputStream is = zipFile.getInputStream(entry);
OutputStream os = new FileOutputStream(fullPath)) {
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
}

if (!first) {
filePaths.append(", ");
}

filePaths.append(fullPath.getAbsolutePath());
first = false;
}
}

if (filePaths.length() == 0) {
throw new Exception("Zip contains no valid files to extract");
}

runTimeData.setKey(runtimeVariable.getValue().toString());
runTimeData.setValue(filePaths.toString());

logger.info("Extracted file paths: " + filePaths);

setSuccessMessage(
"Successfully extracted file paths from the zip and stored in runtime variable '"
+ runTimeData.getKey() + "' with value: " + runTimeData.getValue()
);

} catch (RuntimeException e) {
logger.warn("Unable to locate latest ZIP file " + e);
setErrorMessage("Unable to find the latest zip file in downloads");
result = Result.FAILED;

} catch (Exception e) {
logger.warn("ZIP processing failed " + ExceptionUtils.getStackTrace(e));
setErrorMessage("Failed to extract file paths from zip: " + ExceptionUtils.getMessage(e));
result = Result.FAILED;
}

return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.testsigma.addons.web;

import com.testsigma.sdk.WebAction;
import com.testsigma.sdk.ApplicationType;
import com.testsigma.sdk.annotation.Action;
import com.testsigma.sdk.annotation.TestData;
import com.testsigma.sdk.annotation.RunTimeData;

import lombok.Data;

import org.apache.commons.lang3.exception.ExceptionUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

@Data
@Action(actionText = "Unzip the .CSV.GZ file absolutefilepath to destination destfilepath and store the filepath into a variable testdata",
description = "Unzip the .CSV.GZ file and store the extracted file path",
applicationType = ApplicationType.WEB,
useCustomScreenshot = false)
public class UnzipCSVGZFile extends WebAction {

@TestData(reference = "absolutefilepath")
private com.testsigma.sdk.TestData testData1;
@TestData(reference = "destfilepath")
private com.testsigma.sdk.TestData testData2;
@TestData(reference = "testdata" , isRuntimeVariable = true)
private com.testsigma.sdk.TestData testData3;

@RunTimeData
private com.testsigma.sdk.RunTimeData runTimeData;

@Override
public com.testsigma.sdk.Result execute() {
//Your Awesome code starts here
logger.info("Initiating execution");
com.testsigma.sdk.Result result = com.testsigma.sdk.Result.SUCCESS;
try {
String zipFilePath = testData1.getValue().toString();
String destDir = testData2.getValue().toString();
String fileNameUpload = "output" + System.currentTimeMillis() + ".csv";
String filePath = destDir + File.separator + fileNameUpload;
unzip(zipFilePath, filePath);
logger.info("Storing the filepath in runtime variable");
runTimeData.setValue(filePath);
runTimeData.setKey(testData3.getValue().toString());
logger.info("Stored successfully");
setSuccessMessage("File was unzipped and stored successfully in : " + testData3.getValue().toString() + " and the filepath is :" + filePath);
} catch (Exception e) {
String errorMessage = ExceptionUtils.getStackTrace(e);
result = com.testsigma.sdk.Result.FAILED;
setErrorMessage(errorMessage);
logger.warn(errorMessage);
}
return result;
}
private void unzip(String zipFilePath, String destFilepath) throws IOException {
logger.info("Extracting the file");
FileInputStream fis = new FileInputStream(zipFilePath);
GZIPInputStream gis = new GZIPInputStream(fis);
FileOutputStream fos = new FileOutputStream(destFilepath);
byte[] buffer = new byte[1024];
int len;
while ((len = gis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
gis.close();
fis.close();
logger.info("Successfully extracted to location: " + destFilepath);
}
Comment on lines +61 to +75
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Resource leak in unzip method.

The streams are not closed with try-with-resources. If an exception occurs during decompression, the streams may remain open, leading to resource leaks.

🛡️ Proposed fix
 private void unzip(String zipFilePath, String destFilepath) throws IOException {
 	logger.info("Extracting the file");
-	FileInputStream fis = new FileInputStream(zipFilePath);
-	GZIPInputStream gis = new GZIPInputStream(fis);
-	FileOutputStream fos = new FileOutputStream(destFilepath);
-	byte[] buffer = new byte[1024];
-	int len;
-	while ((len = gis.read(buffer)) > 0) {
-		fos.write(buffer, 0, len);
+	try (FileInputStream fis = new FileInputStream(zipFilePath);
+		 GZIPInputStream gis = new GZIPInputStream(fis);
+		 FileOutputStream fos = new FileOutputStream(destFilepath)) {
+		byte[] buffer = new byte[1024];
+		int len;
+		while ((len = gis.read(buffer)) > 0) {
+			fos.write(buffer, 0, len);
+		}
 	}
-	fos.close();
-	gis.close();
-	fis.close();
 	logger.info("Successfully extracted to location: " + destFilepath);
 }
🤖 Prompt for AI Agents
In
@unzipfileandupload/src/main/java/com/testsigma/addons/web/UnzipCSVGZFile.java
around lines 61 - 75, The unzip method (unzip) currently opens FileInputStream,
GZIPInputStream and FileOutputStream manually and closes them in finally-like
code, which leaks resources if an exception happens; refactor the method to use
try-with-resources to auto-close FileInputStream, GZIPInputStream and
FileOutputStream, change the read loop to check for -1 (while ((len =
gis.read(buffer)) != -1) ) and remove manual close() calls so streams are always
closed even on exceptions.

}
Loading
Loading