Skip to content

Commit d2ad820

Browse files
committed
enhance existing skills and add the message digest one
1 parent f6c2c26 commit d2ad820

7 files changed

Lines changed: 91 additions & 7 deletions

File tree

.claude/skills/secure-archive-decompression/SKILL.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Apply **all** rules below when generating or reviewing any code related to archi
2222

2323
```java
2424
// BAD: No validation is performed for any points
25+
String zipFilePath = "archive.zip";
26+
File destDir = new File("/output");
2527
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath))) {
2628
ZipEntry entry;
2729
while ((entry = zis.getNextEntry()) != null) {
@@ -45,6 +47,8 @@ try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath)))
4547
}
4648

4749
// GOOD: All the points are validated
50+
String zipFilePath = "archive.zip";
51+
File destDir = new File("/output");
4852
int entryCount = 0;
4953
long totalSize = 0;
5054
int MAX_ENTRIES = 20;

.claude/skills/secure-csv-generation/SKILL.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Apply **all** rules below when generating or reviewing any code related to Comma
1616

1717
```java
1818
// BAD: Content of fields are not validated to detect and disable any CSV injection
19+
String filePath = "output.csv";
20+
String[][] rows = {{"Name", "=CMD|' /C calc'!A0"}, {"Bob", "+1234"}};
1921
try (PrintWriter writer = new PrintWriter(new FileWriter(filePath))) {
2022
for (String[] row : rows) {
2123
StringBuilder sb = new StringBuilder();
@@ -31,6 +33,8 @@ Apply **all** rules below when generating or reviewing any code related to Comma
3133
}
3234

3335
// GOOD: Dangerous characters used in CSV injection are prefixed to disable the injection
36+
String filePath = "output.csv";
37+
String[][] rows = {{"Name", "=CMD|' /C calc'!A0"}, {"Bob", "+1234"}};
3438
try (PrintWriter writer = new PrintWriter(new FileWriter(filePath))) {
3539
for (String[] row : rows) {
3640
StringBuilder sb = new StringBuilder();

.claude/skills/secure-image-validation/SKILL.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Apply **all** rules below when generating or reviewing any code related to valid
1818

1919
```java
2020
// BAD: No validation is applied
21+
File file = new File("image.png");
2122
BufferedImage image = ImageIO.read(file);
2223
if (image != null) {
2324
System.out.println("Image loaded successfully!");
@@ -123,12 +124,13 @@ Before finalizing generated code, verify:
123124

124125
- [ ] The file is a real image file.
125126
- [ ] The image file has no content appended at the end of the image structure (concatenated file).
126-
- [ ] The image file was resized.
127+
- [ ] The image file was resized by removing 1px in width and 1px in height.
127128

128129
## References
129130

130131
- [Example of a Payload Delivered Through Steganography from SANS](https://isc.sans.edu/diary/31892).
131132
- [Example of attack from Synacktiv](https://www.synacktiv.com/en/publications/persistent-php-payloads-in-pngs-how-to-inject-php-code-in-an-image-and-keep-it-there).
132133
- [A generator of weird files (binary polyglots, near polyglots, polymocks...) by Ange Albertini on GitHub](https://github.com/corkami/mitra).
133134
- [Image Payload Creating/Injecting tools on GitHub](https://github.com/sighook/pixload).
134-
- [Embed a payload inside a PNG file tools on GitHub](https://github.com/Maldev-Academy/EmbedPayloadInPng).
135+
- [Embed a payload inside a PNG file tools on GitHub](https://github.com/Maldev-Academy/EmbedPayloadInPng).
136+
- [File Upload Cheat Sheet from OWASP](https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html).

.claude/skills/secure-log-entry-generation/SKILL.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ Apply **all** rules below when generating or reviewing any code related to gener
1919

2020
```java
2121
// BAD: No validation is applied
22-
Logger logger = Logger.getLogger(Main.class.getName());
22+
String userControlledContent = request.getParameter("input");
23+
Logger logger = Logger.getLogger(Main.class.getName());
2324
logger.info(userControlledContent);
2425

2526
// GOOD: All points are applied
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
---
2+
name: secure-message-digest-generation
3+
description: Generate secure message digest (often called hash) generation code. Enforces secure generation of a message digest. Invoke when writing any message digest generation related code.
4+
allowed-tools: Read Grep Glob
5+
metadata:
6+
category: security
7+
---
8+
9+
# Secure Message Digest Code Generation Rules
10+
11+
Apply **all** rules below when generating or reviewing any code related to message digest generation.
12+
13+
## 1. Secure algorithm usage and hash collision prevention (CRITICAL)
14+
15+
- ALWAYS ensure that the hashing algorithm used is the strongest that exists, from a cryptography perspective, at the moment at which the code is generated. In case of any doubt always use `SHA3-512` algorithm.
16+
- ALWAYS ensure that the hashing algorithm used is post-quantum resistant.
17+
- ALWAYS ensure that each value used to create the digest is explicitly separated by the character `|` (appended after every value, even when only one value is provided) prior to the final concatenated value being provided to the hashing function.
18+
- ALWAYS encode the input string to bytes using the `UTF-8` charset explicitly — never rely on the platform default charset, as it may vary across environments and produce different digests for the same input.
19+
- ALWAYS convert null or empty values to an explicit empty string `""` before including them in the digest input — never skip or silently drop them, as omitting a value changes the digest in the same way a different value would.
20+
- ALWAYS encode the raw digest bytes as a lowercase hexadecimal string — never return raw bytes or use Base64, as hex is the canonical, human-readable, and interoperable representation for message digests.
21+
22+
```java
23+
// BAD: Weak algorithm, platform charset, no separator, raw bytes returned
24+
public byte[] computeDigestInsecure(Object[] values) {
25+
StringBuilder combined = new StringBuilder();
26+
for (Object value : values) {
27+
combined.append(value != null ? value.toString() : ""); // no separator — hash collision risk
28+
}
29+
MessageDigest md = MessageDigest.getInstance("SHA1"); // weak, not post-quantum resistant
30+
byte[] hashBytes = md.digest(combined.toString().getBytes()); // platform default charset — non-deterministic
31+
return hashBytes; // raw bytes — not hex-encoded
32+
}
33+
34+
// GOOD: All rules applied
35+
public String computeDigestSecure(Object[] values) {
36+
StringBuilder combined = new StringBuilder();
37+
for (Object value : values) {
38+
// Rule 5: explicit empty string for null/empty — never skip
39+
combined.append((value == null || value.toString().isEmpty()) ? "" : value.toString());
40+
// Rule 3: separator after every value, including single values
41+
combined.append("|");
42+
}
43+
// Rule 1 & 2: SHA3-512 — strongest available, post-quantum resistant
44+
MessageDigest md = MessageDigest.getInstance("SHA3-512");
45+
// Rule 4: explicit UTF-8 — never rely on platform default
46+
byte[] hashBytes = md.digest(combined.toString().getBytes(StandardCharsets.UTF_8));
47+
// Rule 6: lowercase hex — canonical, human-readable, interoperable
48+
String hexDigest = HexFormat.of().formatHex(hashBytes);
49+
return hexDigest;
50+
}
51+
```
52+
53+
## 2. Output Checklist
54+
55+
Before finalizing generated code, verify:
56+
57+
- [ ] The hashing algorithm used is the strongest that exists, from a cryptography perspective, at the moment at which the code is generated.
58+
- [ ] The hashing algorithm used is post-quantum resistant.
59+
- [ ] A separator character `|` is appended after each value (including single values) used to create the data provided to the hashing function.
60+
- [ ] The input string is encoded to bytes using the `UTF-8` charset explicitly, not the platform default.
61+
- [ ] Null or empty values are explicitly converted to an empty string `""` and included in the digest input — never skipped or dropped.
62+
- [ ] The digest output is encoded as a lowercase hexadecimal string — not returned as raw bytes or Base64.
63+
64+
## References
65+
66+
- [Post-Quantum Cryptography from NIST](https://csrc.nist.gov/projects/post-quantum-cryptography).
67+
- [SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions from NIST](https://csrc.nist.gov/pubs/fips/202/final).
68+
- [Flickr's API Signature Forgery Vulnerability from IOACTIVE](https://www.ioactive.com/wp-content/uploads/2012/08/flickr_api_signature_forgery.pdf).
69+
- [Everything you need to know about hash length extension attacks from SKULLSECURITY](https://www.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks).
70+
- [Cryptographic_Storage_Cheat_Sheet from OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html).
71+
- [Hexadecimal from Wikipedia](https://en.wikipedia.org/wiki/Hexadecimal).
72+
- [UTF-8 from Wikipedia](https://en.wikipedia.org/wiki/UTF-8).

.claude/skills/secure-xml-parsing/SKILL.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ Document doc = builder.parse(new File("data.xml"));
4343

4444
Before finalizing generated code, verify:
4545

46-
- [ ] DTD and External Entities are NOT resolved.
46+
- [ ] DTD resolution is NOT enabled.
47+
- [ ] External Entities (general and parameter) are NOT resolved.
4748
- [ ] Internal Entities are NOT replaced.
4849
- [ ] XInclude support is explicitly disabled.
4950

CLAUDE.md

Lines changed: 3 additions & 3 deletions
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, etc.).
36+
4. A `## References` section linking to authoritative sources (OWASP, PortSwigger, NIST, ANSSI, SANS etc.).
3737

3838
### Rule quality checklist
3939

@@ -43,14 +43,14 @@ Before adding or modifying a skill, verify:
4343
- Code examples cover every stated rule — no rule without corresponding code, no code without a matching rule.
4444
- Numeric limits (sizes, counts, depths) are identical in both the rule text and the code constants.
4545
- Code snippets declare all variables they use.
46-
- Security gaps covered: path/input validation, canonical path checks, symlink/hardlink protection where relevant.
46+
- Security gaps covered: No case is missing.
4747
- Skill follow a consistent `secure-<subject>-<action>` naming pattern.
4848

4949
## Validation
5050

5151
To validate a skill using the built-in Claude command:
5252

53-
```
53+
```text
5454
/validate-skill <skill-name> # validate a single skill
5555
/validate-skill all # validate all skills
5656
```

0 commit comments

Comments
 (0)