Generate secure relative url validation code for open redirect prevention. Enforces secure generation of code validating a relative url. Invoke when writing any relative url validation related code. See "security-considerations" metadata for strict validation behavior.
Installation
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Generate secure relative url validation code for open redirect prevention. Enforces secure generation of code validating a relative url. Invoke when writing any relative url validation related code. See "security-considerations" metadata for strict validation behavior.
allowed-tools
Read Grep Glob
metadata
{"category":"security","security-considerations":["Intentionally strict, it rejects valid but risky relative URL forms such as \"../page\", \"?query\", and \"#anchor\"."]}
Secure URL Validation Code Generation Rules
Apply all rules below when generating or reviewing any code related to validation of an relative URL.
1. URL validation (CRITICAL)
ALWAYS ensure that the input data is recursively URL decoded prior to be validated. A decoding iteration count threshold of 4 is used and an error must be raised if the threshold is reached.
ALWAYS ensure that the input data, once URL decoded, start with one of the following character: slash, letter, number, dash, underscore.
ALWAYS ensure that the input data is a valid URL according to the RFC 3986.
ALWAYS ensure the URL is not absolute (has no scheme); reject any URL where a scheme is present (e.g. javascript:, ssh://, data://, ftp://, file://, https://).
ALWAYS ensure that URL never start with //.
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
// BAD: No validation — user input used directly as redirect target// accepts "//evil.com", "%2F%2Fevil.com", "https://evil.com"Stringurl= request.getParameter("url");
response.sendRedirect(url);
// GOOD: All rules are appliedpublicstatic String parseRelativeUrl(String input) {
if (input == null || input.isEmpty()) {
thrownewIllegalArgumentException("URL must not be null or empty.");
}
// Rule: recursively URL-decode to defeat encoding bypass attempts (%2F%2F, etc.)finalintMAX_DECODE_ITERATIONS=4;
Stringdecoded= input;
String previous;
intiterations=0;
do {
if (iterations++ >= MAX_DECODE_ITERATIONS) {
thrownewIllegalArgumentException("URL decoding exceeded maximum iteration threshold (" + MAX_DECODE_ITERATIONS + ").");
}
previous = decoded;
decoded = URLDecoder.decode(previous, StandardCharsets.UTF_8);
} while (!decoded.equals(previous));
// Rule: decoded value must start with slash, letter, number, dash, or underscoreif (!decoded.matches("^[/a-zA-Z0-9\\-_].*")) {
thrownewIllegalArgumentException("URL must start with a slash, letter, number, dash, or underscore.");
}
// Rule: must not start with "//" (protocol relative reference)if (decoded.startsWith("//")) {
thrownewIllegalArgumentException("URL must not be a protocol relative reference (must not start with '//').");
}
// Rule: must be a valid URI per RFC 3986
URI uri;
try {
uri = newURI(decoded);
} catch (URISyntaxException e) {
thrownewIllegalArgumentException("URL is not a valid RFC 3986 URI: " + e.getMessage(), e);
}
// Rule: must not be absolute — rejects any scheme (javascript:, ssh://, data://, ftp://, file://, https://, etc.)if (uri.isAbsolute()) {
thrownewIllegalArgumentException("URL must not be absolute (no scheme allowed; e.g. javascript:, ssh://, https:// are all rejected).");
}
return decoded;
}
2. Output Checklist
Before finalizing generated code, verify:
The input data is recursively URL decoded prior to be validated, with a maximum of 4 decode iterations enforced and an error raised if the threshold is reached.
The decoded URL starts with a slash, letter, number, dash, or underscore.
The input data is valid according to the RFC 3986.
The URL is not absolute (no scheme present); any scheme (e.g. javascript:, ssh://, data://, ftp://, file://, https://) is rejected.