Skip to content

Commit 8aee7ac

Browse files
committed
add a new skill
1 parent b7f375c commit 8aee7ac

2 files changed

Lines changed: 95 additions & 1 deletion

File tree

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
---
2+
name: secure-relative-url-validation
3+
description: Generate secure relative url validation code. Enforces secure generation of code validating an relative url. Invoke when writing any relative url validation related code.
4+
allowed-tools: Read Grep Glob
5+
metadata:
6+
category: security
7+
---
8+
9+
# Secure URL Validation Code Generation Rules
10+
11+
Apply **all** rules below when generating or reviewing any code related to validation of an relative URL.
12+
13+
## 1. URL validation (CRITICAL)
14+
15+
- 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.
16+
- ALWAYS ensure that the input data, once URL decoded, start with one of the following character: slash, letter, number, dash, underscore.
17+
- ALWAYS ensure that the input data is a valid URL according to the [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986).
18+
- ALWAYS ensure that URL is not a absolute URL.
19+
- ALWAYS ensure that URL never start with `//`.
20+
21+
```java
22+
import java.net.URI;
23+
import java.net.URISyntaxException;
24+
import java.net.URLDecoder;
25+
import java.nio.charset.StandardCharsets;
26+
27+
// BAD: No validation — user input used directly as redirect target
28+
// accepts "//evil.com", "%2F%2Fevil.com", "https://evil.com"
29+
String url = request.getParameter("url");
30+
response.sendRedirect(url);
31+
32+
// GOOD: All rules are applied
33+
public static String parseRelativeUrl(String input) {
34+
if (input == null || input.isEmpty()) {
35+
throw new IllegalArgumentException("URL must not be null or empty.");
36+
}
37+
38+
// Rule: recursively URL-decode to defeat encoding bypass attempts (%2F%2F, etc.)
39+
final int MAX_DECODE_ITERATIONS = 4;
40+
String decoded = input;
41+
String previous;
42+
int iterations = 0;
43+
do {
44+
if (iterations++ >= MAX_DECODE_ITERATIONS) {
45+
throw new IllegalArgumentException("URL decoding exceeded maximum iteration threshold (" + MAX_DECODE_ITERATIONS + ").");
46+
}
47+
previous = decoded;
48+
decoded = URLDecoder.decode(previous, StandardCharsets.UTF_8);
49+
} while (!decoded.equals(previous));
50+
51+
// Rule: decoded value must start with slash, letter, number, dash, or underscore
52+
if (!decoded.matches("^[/a-zA-Z0-9\\-_].*")) {
53+
throw new IllegalArgumentException("URL must start with a slash, letter, number, dash, or underscore.");
54+
}
55+
56+
// Rule: must not start with "//" (protocol relative reference)
57+
if (decoded.startsWith("//")) {
58+
throw new IllegalArgumentException("URL must not be a protocol relative reference (must not start with '//').");
59+
}
60+
61+
// Rule: must be a valid URI per RFC 3986
62+
URI uri;
63+
try {
64+
uri = new URI(decoded);
65+
} catch (URISyntaxException e) {
66+
throw new IllegalArgumentException("URL is not a valid RFC 3986 URI: " + e.getMessage(), e);
67+
}
68+
69+
// Rule: must not be an absolute URL (i.e. must have no scheme)
70+
if (uri.isAbsolute()) {
71+
throw new IllegalArgumentException("URL must not be absolute (must not have a scheme like \"https://\").");
72+
}
73+
74+
return decoded;
75+
}
76+
```
77+
78+
## 2. Output Checklist
79+
80+
Before finalizing generated code, verify:
81+
82+
- [ ] 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.
83+
- [ ] The decoded URL starts with a slash, letter, number, dash, or underscore.
84+
- [ ] The input data is valid according to the RFC 3986.
85+
- [ ] The URL is not an absolute one.
86+
- [ ] The URL do not start with `//`.
87+
88+
## References
89+
90+
- [Unvalidated Redirects and Forwards Cheat Sheet from OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html).
91+
- [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986).
92+
- [WHATWG URL Living Standard](https://url.spec.whatwg.org/).
93+
- [Absolute URLs vs. relative URLs](https://developer.mozilla.org/en-US/docs/Learn_web_development/Howto/Web_mechanics/What_is_a_URL#absolute_urls_vs._relative_urls).
94+
- [CWE Open Redirect](https://cwe.mitre.org/data/definitions/601.html).

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Every skill must have:
3333
- `ALWAYS …` rule statements (language-agnostic).
3434
- A Java BAD/GOOD code example illustrating every rule.
3535
3. A `## 2. Output Checklist` section with one checkbox per rule.
36-
4. A `## References` section linking to authoritative sources (OWASP, PortSwigger, NIST, ANSSI, SANS etc.).
36+
4. A `## References` section linking to authoritative sources (OWASP, PortSwigger, MITRE, NIST, ANSSI, SANS etc.).
3737

3838
### Rule quality checklist
3939

0 commit comments

Comments
 (0)