govind-gupta-tfs commented on PR #553:
URL: https://github.com/apache/commons-io/pull/553#issuecomment-3380782150
`package com.thermofisher.gsd.archive;
import org.apache.commons.lang3.Validate;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.Locale;
import java.util.Set;
public class PathValidator {
private static final Set<String> RESERVED_NAMES = Set.of(
"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4",
"COM5", "COM6",
"COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5",
"LPT6",
"LPT7", "LPT8", "LPT9"
);
public static void validateSafePathComponent(String component) {
// --- 1. Basic Checks (Using Apache Commons Lang) ---
Validate.notBlank(component, "Path component must not be blank");
Validate.isTrue(component.length() <= 255, "Path component too
long");
// Pre-decode raw dangerous percent encodings
String lower = component.toLowerCase(Locale.ROOT);
if (lower.contains("%2f") || lower.contains("%5c") ||
lower.contains("%00")) {
throw new IllegalArgumentException("Encoded separator/null not
allowed: " + component);
}
// --- 2. Decoding ---
String decoded;
try {
decoded = URLDecoder.decode(component, StandardCharsets.UTF_8);
} catch (IllegalArgumentException ex) {
throw new IllegalArgumentException("Invalid percent encoding: "
+ component);
}
// --- 3. Path Traversal/Normalization Check (Using Path API) ---
Path p;
try {
p = Path.of(decoded);
} catch (InvalidPathException e) {
throw new IllegalArgumentException("Invalid path syntax: " +
component);
}
// Critical Path Traversal check: must be a single name and not
contain '..' or '.'
if (p.isAbsolute() || p.getNameCount() != 1 ||
!p.normalize().equals(p)) {
throw new IllegalArgumentException("Traversal/dot segments or
absolute path not allowed: " + component);
}
String name = p.getFileName().toString();
// --- 4. Final Name/Custom Policy Checks ---
// Basic character / separator checks
if (name.contains("/") || name.contains("\\") || name.indexOf('\0')
>= 0 || name.contains("+")) {
throw new IllegalArgumentException("Unsafe characters
(separators/null/plus): " + component);
}
// Control chars
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if (c < 0x20 || c == 0x7F) {
throw new IllegalArgumentException("Control character
present: " + component);
}
}
// Dot-only or reserved dot forms & Trailing space/dot
if (name.equals(".") || name.equals("..") || name.matches("\\.{2,}")
||
name.endsWith(" ") || name.endsWith(".")) {
throw new IllegalArgumentException("Invalid dot sequence or
trailing space/dot: " + component);
}
// Allowlist
if (!name.matches("[A-Za-z0-9._-]+")) {
throw new IllegalArgumentException("Disallowed characters: " +
component);
}
// Windows reserved device names
if (RESERVED_NAMES.contains(name.toUpperCase(Locale.ROOT))) {
throw new IllegalArgumentException("Reserved device name: " +
component);
}
}
}`
@garydgregory can we use the above code to avoid path injection in user
provided paths in windows machine.
It can further be optimized for other OS
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]