Skip to content

Commit c99cde4

Browse files
authored
Merge pull request #222 from sgayangi/master
Add file validation logic
2 parents 9cdb6ea + af4b6d5 commit c99cde4

2 files changed

Lines changed: 98 additions & 1 deletion

File tree

components/jaggery-core/org.jaggeryjs.jaggery.app.mgt/src/main/java/org/jaggeryjs/jaggery/app/mgt/JaggeryAppAdmin.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.apache.axis2.engine.AxisConfiguration;
2222
import org.apache.commons.logging.Log;
2323
import org.apache.commons.logging.LogFactory;
24+
import org.wso2.carbon.CarbonException;
2425
import org.jaggeryjs.jaggery.core.JaggeryCoreConstants;
2526
import org.wso2.carbon.utils.ArchiveManipulator;
2627
import org.wso2.carbon.webapp.mgt.SessionsWrapper;
@@ -92,6 +93,12 @@ public boolean uploadWebapp(WebappUploadData[] webappUploadDataList) throws Axis
9293

9394
for (WebappUploadData uploadData : webappUploadDataList) {
9495
String fName = uploadData.getFileName();
96+
try {
97+
// validate file name
98+
JaggeryDeploymentUtil.validateFileName(fName, jaggeryAppsPath);
99+
} catch (CarbonException e) {
100+
throw new AxisFault(e.getMessage(), e);
101+
}
95102
if (fName.contains(".")) {
96103
fName = fName.split("\\.")[0];
97104
}

components/jaggery-core/org.jaggeryjs.jaggery.app.mgt/src/main/java/org/jaggeryjs/jaggery/app/mgt/JaggeryDeploymentUtil.java

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import org.apache.commons.logging.Log;
55
import org.apache.commons.logging.LogFactory;
66
import org.jaggeryjs.jaggery.core.JaggeryCoreConstants;
7-
7+
import org.wso2.carbon.CarbonException;
88
import java.io.*;
99
import java.util.zip.ZipEntry;
1010
import java.util.zip.ZipInputStream;
@@ -82,4 +82,94 @@ static void unZip(InputStream is, String destDir) {
8282
log.error("Could not unzip the Jaggery App Archive", e);
8383
}
8484
}
85+
86+
87+
/**
88+
* Generic file validation method that combines all validation logic.
89+
* Validates file name, extension, and path security.
90+
*
91+
* @param fileName File name to validate
92+
* @param baseDirectory Base directory where files should be contained
93+
* @param allowedExtensions Array of allowed file extensions (without dot),
94+
* e.g., {"csv", "xml"}
95+
* @throws CarbonException if validation fails
96+
*/
97+
public static void validateFileName(String fileName, String baseDirectory, String[] allowedExtensions)
98+
throws CarbonException {
99+
// 1. Basic null/empty validation
100+
if (fileName == null || fileName.isEmpty()) {
101+
throw new RuntimeException("Invalid file name. File name is not available");
102+
}
103+
104+
fileName = fileName.trim();
105+
106+
// 2. File extension validation
107+
if (allowedExtensions != null && allowedExtensions.length > 0) {
108+
boolean validExtension = false;
109+
String lowerFileName = fileName.toLowerCase();
110+
111+
for (String extension : allowedExtensions) {
112+
if (lowerFileName.endsWith("." + extension.toLowerCase())) {
113+
validExtension = true;
114+
115+
// Ensure filename has content before the extension
116+
int minLength = extension.length() + 1; // extension + dot
117+
if (fileName.length() <= minLength) {
118+
throw new RuntimeException(
119+
"Invalid file name: filename cannot be just an extension. File: " + fileName);
120+
}
121+
break;
122+
}
123+
}
124+
125+
if (!validExtension) {
126+
String allowedExts = String.join(", ", allowedExtensions);
127+
throw new RuntimeException(
128+
"Invalid file type. Only " + allowedExts + " files are allowed. File: " + fileName);
129+
}
130+
}
131+
132+
// 3. Path validation
133+
if (baseDirectory != null) {
134+
try {
135+
// Canonical path validation
136+
File baseDir = new File(baseDirectory);
137+
File targetFile = new File(baseDir, fileName);
138+
139+
String baseDirCanonical = baseDir.getCanonicalPath();
140+
String targetFileCanonical = targetFile.getCanonicalPath();
141+
142+
// Ensure resolved path stays within base directory
143+
if (!targetFileCanonical.startsWith(baseDirCanonical + File.separator) &&
144+
!targetFileCanonical.equals(baseDirCanonical)) {
145+
throw new RuntimeException("File validation failed: Invalid file name: " + fileName);
146+
}
147+
148+
} catch (IOException e) {
149+
throw new CarbonException("File validation failed: Invalid file name: " + fileName);
150+
}
151+
}
152+
}
153+
154+
/**
155+
* Convenience method for file validation.
156+
*
157+
* @param fileName File name to validate
158+
* @param baseDirectory Base directory where files should be contained
159+
* @throws CarbonException if validation fails
160+
*/
161+
public static void validateFileName(String fileName, String baseDirectory) throws CarbonException {
162+
validateFileName(fileName, baseDirectory, new String[] { "jag", "zip" });
163+
}
164+
165+
/**
166+
* Validate the given fileName and path.
167+
*
168+
* @param fileName File name which needs to be validated.
169+
* @param baseDirectory Base directory where files should be contained
170+
* @throws CarbonException if validation fails
171+
*/
172+
public static void validatePath(String fileName, String baseDirectory) throws CarbonException {
173+
validateFileName(fileName, baseDirectory, null);
174+
}
85175
}

0 commit comments

Comments
 (0)