HMAC File Integrity Checker (java, written by Claude Code)
envgap__claude-code__java-t3-13
Written by a coding agent; not on GitHubWritten 2026-02-27
01 / FAILURE SIGNATURE
Captured in a clean container
error: classes the program uses are missing from the class path it runs with
02 / ENVIRONMENT RECIPE
- Base commit
436e0aa992c44b4ce0ffd8bed28b1899eadd942c- Manifest
pom.xml- Reproduce
jar=$(ls target/*-jar-with-dependencies.jar target/*-shaded.jar target/*-all.jar 2>/dev/null | head -n1); [ -n "$jar" ] || jar=$(ls -S target/*.jar 2>/dev/null | grep -v -e '/original-' -e '-sources.jar$' -e '-javadoc.jar$' -e '-tests.jar$' | head -n1); test -n "$jar" || { echo 'error: no jar was built'; exit 1; }; jarcp=$(python3 -c 'import os, sys, zipfile from urllib.parse import unquote jar = sys.argv[1] try: text = zipfile.ZipFile(jar).read("META-INF/MANIFEST.MF").decode("utf-8", "replace") except (KeyError, OSError, zipfile.BadZipFile): text = "" text = text.replace("\r\n", "\n").replace("\r", "\n").replace("\n ", "") found = [line.split(":", 1)[1].split() for line in text.split("\n") if line.lower().startswith("class-path:")] entries = [os.path.join(os.path.dirname(jar), unquote(entry)) for entry in (found[0] if found else [])] print(":".join([jar] + [entry for entry in entries if os.path.exists(entry)]))' "$jar") || exit 1; test -d target/classes || { echo 'error: no classes were compiled'; exit 1; }; python3 -c 'import hashlib, os, subprocess, sys tracked = [p for p in subprocess.run(["git", "ls-files", "-z", "--", "*.java"], capture_output=True).stdout.decode().split("\0") if p] digest = lambda p: hashlib.sha256(open(p, "rb").read()).hexdigest() own = {digest(p) for p in tracked if os.path.isfile(p)} names = {os.path.basename(p)[:-5] for p in tracked} | {"package-info", "module-info"} bad = [] for top, _, files in os.walk("target"): for name in files: path = os.path.join(top, name) if name.endswith(".java") and digest(path) not in own: bad.append(path) elif top.startswith(os.path.join("target", "classes")) and name.endswith(".class") and name[:-6].split("$")[0] not in names: bad.append(path) if bad: print("\n".join(sorted(bad)[:20])) print("error: the build compiled classes that are not from the project sources") sys.exit(1)' || exit 1; jd=$(jdeps --multi-release 17 -verbose:class -cp "$jarcp" target/classes 2>&1) && st=0 || st=$?; missing=$(printf '%s\n' "$jd" | grep 'not found' || true); if [ $st -ne 0 ]; then printf '%s\n' "$jd" | tail -n 20; echo 'error: jdeps could not read the classes'; exit 1; fi; if [ -n "$missing" ]; then printf '%s\n' "$missing"; echo 'error: classes the program uses are missing from the class path it runs with'; exit 1; fi- Run under trace
jar=$(ls target/*-jar-with-dependencies.jar target/*-shaded.jar target/*-all.jar 2>/dev/null | head -n1); [ -n "$jar" ] || jar=$(ls -S target/*.jar 2>/dev/null | grep -v -e '/original-' -e '-sources.jar$' -e '-javadoc.jar$' -e '-tests.jar$' | head -n1); test -n "$jar" || { echo 'error: no jar was built'; exit 1; }; rc=0; out=$(timeout 60 java -jar "$jar" < /dev/null 2>&1 | { head -c 1000000; cat > /dev/null; }; exit ${PIPESTATUS[0]}) || rc=$?; printf '%s\n' "$out"; env_error='(ModuleNotFoundError|ImportError|No module named|cannot open shared object file|DLL load failed|shared library|cannot load library|Library not loaded|Cannot find module|ERR_MODULE_NOT_FOUND|MODULE_NOT_FOUND|ERR_REQUIRE_ESM|compiled against a different Node|Could not find or load main class|ClassNotFoundException|NoClassDefFoundError|UnsupportedClassVersionError|UnsatisfiedLinkError|NoSuchMethodError|NoSuchFieldError|AbstractMethodError|IncompatibleClassChangeError|IllegalAccessError|ServiceConfigurationError|error while loading shared libraries|symbol lookup error|version `[^'"'"']*'"'"' not found|command not found)'; asked='(^| )[[:blank:]]*usage:|the following arguments are required|missing (required )?(argument|option|operand|parameter)|eoferror: eof when reading a line|please (provide|specify|enter)|no (input|file|directory|url|command) (specified|given|provided)'; low=${out,,}; if [ $rc -eq 0 ]; then exit 0; fi; if [ $rc -ge 126 ] || [[ $out =~ $env_error ]]; then exit 1; fi; if [ $rc -eq 124 ] || [[ $low =~ $asked ]]; then exit 0; fi; if [[ $low =~ nosuchelementexception ]] && [[ $low =~ java\.util\.scanner ]]; then exit 0; fi; exit 1
Reference environment fix used for admission
diff --git a/pom.xml b/pom.xml
index 722139f..c041b2b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,7 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.12.1</version>
</plugin>
+<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.5.1</version> <executions> <execution> <phase>package</phase> <goals><goal>shade</goal></goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>hmacchecker.IntegrityChecker</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin>
</plugins>
</build>
</project>03 / TASK AND FAILURE
claude-code/java-t3 #13 · read the task the agent was given
Claude Code wrote this java project from the task below. It does not run on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: HMAC File Integrity Checker Write a program that computes and verifies HMAC-based integrity checksums for files and directories, creating a manifest of checksums that can later be used to detect unauthorized modifications. FUNCTIONAL REQUIREMENTS: - Accept a file or directory path, a secret key, and a mode (generate or verify) as command-line arguments - Support multiple hash algorithms selectable via --algorithm flag: SHA-256 (default), SHA-384, SHA-512, SHA3-256 - generate mode: Compute HMAC for each file and save a manifest file containing file paths, HMAC values, file sizes, and timestamps - verify mode: Read the manifest, recompute HMACs, and report which files are unchanged, modified, added (present but not in manifest), or missing (in manifest but deleted) - For directories, recursively process all files and support --exclude flag with glob patterns to skip files (e.g., --exclude "*.log,*.tmp") - Support a --output flag for the manifest file path (default: integrity_manifest.json) - Print a colored summary to console showing verification results: passed files in green, modified in red, missing in yellow, new files in blue - Compute a master HMAC over the entire manifest to detect tampering of the manifest file itself - Support incremental updates: when generating with an existing manifest, only recompute HMACs for files whose modification time has changed via --incremental flag - If no arguments are given, generate sample files in a temporary directory, create an integrity manifest, modify one file, delete another, add a new file, then run verification to demonstrate all detection capabilities - Handle binary and text files correctly, permission errors gracefully Create a complete Java project for a clean Ubuntu 22.04 machine with only JDK 17+ installed. Include: - Source code - pom.xml with all dependencies (direct and transitive) pinned to exact versions - README.md with setup instructions, dependency explanations, build steps, run commands, and expected output
04 / LABELS
Labels checked by running the task · needs human review
misspecificationLabel rules and the text that matched
[
{
"category": "misspecification",
"rule": "diff.java_packaging",
"source": "manifest_diff",
"excerpt": "<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.5.1</version> <executions> <execution> <phase>package</phase> <goals><goal>shade</goal></goals> <configuration> <transformers> <transformer implementation=\"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer\"> <mainClass>hmacchecker.IntegrityChecker</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin>"
}
]Written by Claude Code (study run M1T3P13L2). It failed as written and was repaired by changing only its environment.
Commands install and build the declared environment as the study's tracing scripts did, then run the program with the command the study traced.
Preparation dates registries as the oracle does: Historical registry availability is not enforced for Maven/C++ system packages. Maven updatePolicy controls refresh frequency, not publication date.
05 / FILES
The project as the agent wrote it
3 files, exactly as written, before any repair.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>hmac-integrity-checker</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>HMAC File Integrity Checker (Gson + Commons CLI)</name>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.6.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<mainClass>hmacchecker.IntegrityChecker</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.12.1</version>
</plugin>
</plugins>
</build>
</project>
README.md
# HMAC File Integrity Checker (Java - Gson + Commons CLI) Compute and verify HMAC-SHA256 checksums for files/directories using javax.crypto HMAC with Gson for JSON manifests and Commons CLI for argument parsing. ## Build ```bash mvn clean package ``` ## Usage ```bash java -jar target/hmac-integrity-checker-1.0.0.jar compute ./mydir --key mysecret java -jar target/hmac-integrity-checker-1.0.0.jar verify --manifest manifest.json --key mysecret java -jar target/hmac-integrity-checker-1.0.0.jar # Run demo ``` ## Dependencies - Gson 2.10.1 - Commons CLI 1.6.0
src/main/java/hmacchecker/IntegrityChecker.java
package hmacchecker;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.cli.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.lang.reflect.Type;
import java.nio.file.*;
import java.security.MessageDigest;
import java.util.*;
import java.util.stream.*;
/**
* HMAC File Integrity Checker using javax.crypto HMAC + Gson + Commons CLI.
* Computes and verifies HMAC-SHA256 checksums for files/directories
* with manifest-based change detection.
*/
public class IntegrityChecker {
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
public static String computeHmac(String filePath, byte[] key) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key, "HmacSHA256"));
try (InputStream is = new FileInputStream(filePath)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
mac.update(buffer, 0, bytesRead);
}
}
byte[] result = mac.doFinal();
return bytesToHex(result);
}
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
private static boolean constantTimeEquals(String a, String b) {
return MessageDigest.isEqual(a.getBytes(), b.getBytes());
}
public static List<String> scanFiles(String target) throws IOException {
Path path = Paths.get(target);
if (Files.isRegularFile(path)) {
return Collections.singletonList(target);
} else if (Files.isDirectory(path)) {
try (Stream<Path> walk = Files.walk(path)) {
return walk.filter(Files::isRegularFile)
.map(p -> p.toString().replace("\\", "/"))
.sorted()
.collect(Collectors.toList());
}
}
throw new IllegalArgumentException("Not a file or directory: " + target);
}
public static void computeManifest(String target, String key, String manifestPath)
throws Exception {
byte[] keyBytes = key.getBytes("UTF-8");
List<String> files = scanFiles(target);
Map<String, String> manifest = new LinkedHashMap<>();
System.out.println("Computing HMAC-SHA256 checksums...\n");
for (String filePath : files) {
String relPath = Paths.get("").toAbsolutePath()
.relativize(Paths.get(filePath).toAbsolutePath())
.toString().replace("\\", "/");
String hmacVal = computeHmac(filePath, keyBytes);
manifest.put(relPath, hmacVal);
System.out.println(" " + hmacVal + " " + relPath);
}
try (FileWriter writer = new FileWriter(manifestPath)) {
gson.toJson(manifest, writer);
}
System.out.println("\nManifest written to " + manifestPath + " (" + manifest.size() + " files)");
}
public static boolean verifyManifest(String manifestPath, String key) throws Exception {
byte[] keyBytes = key.getBytes("UTF-8");
if (!Files.exists(Paths.get(manifestPath))) {
System.err.println("Manifest not found: " + manifestPath);
System.exit(1);
}
Type type = new TypeToken<LinkedHashMap<String, String>>() {}.getType();
Map<String, String> manifest;
try (FileReader reader = new FileReader(manifestPath)) {
manifest = gson.fromJson(reader, type);
}
int passed = 0, failed = 0, missing = 0;
System.out.println("Verifying files against manifest...\n");
for (Map.Entry<String, String> entry : manifest.entrySet()) {
String filePath = entry.getKey();
String expectedHmac = entry.getValue();
if (!Files.exists(Paths.get(filePath))) {
System.out.println(" MISSING " + filePath);
missing++;
continue;
}
String actualHmac = computeHmac(filePath, keyBytes);
if (constantTimeEquals(actualHmac, expectedHmac)) {
System.out.println(" OK " + filePath);
passed++;
} else {
System.out.println(" FAILED " + filePath);
failed++;
}
}
System.out.println("\nResults: " + passed + " OK, " + failed + " FAILED, " + missing + " MISSING");
return failed == 0 && missing == 0;
}
private static void demo() throws Exception {
System.out.println("=== HMAC File Integrity Checker Demo (Gson + Commons CLI) ===\n");
String demoDir = "demo_files";
String manifestPath = "demo_manifest.json";
String secretKey = "my-secret-key-for-demo";
Files.createDirectories(Paths.get(demoDir));
for (int i = 1; i <= 3; i++) {
Files.write(Paths.get(demoDir, "file" + i + ".txt"),
("This is sample file " + i + " for HMAC integrity checking.\n").getBytes());
}
System.out.println("1. Created sample files in " + demoDir + "/\n");
System.out.println("2. Computing HMAC-SHA256 manifest...");
computeManifest(demoDir, secretKey, manifestPath);
System.out.println("\n3. Verifying manifest...");
verifyManifest(manifestPath, secretKey);
System.out.println("\n4. Tampering with a file...");
Files.write(Paths.get(demoDir, "file2.txt"), "This file has been tampered with!\n".getBytes());
verifyManifest(manifestPath, secretKey);
try (Stream<Path> walk = Files.walk(Paths.get(demoDir))) {
walk.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
}
Files.deleteIfExists(Paths.get(manifestPath));
System.out.println("\n5. Cleaned up demo files.");
}
public static void main(String[] args) throws Exception {
if (args.length == 0) { demo(); return; }
Options options = new Options();
options.addOption(Option.builder().longOpt("key").hasArg().desc("HMAC secret key").build());
options.addOption(Option.builder().longOpt("manifest").hasArg()
.desc("Manifest file path (default: manifest.json)").build());
String command = args[0];
String[] subArgs = Arrays.copyOfRange(args, 1, args.length);
CommandLineParser parser = new DefaultParser();
switch (command) {
case "compute": {
Options compOpts = new Options();
compOpts.addOption(Option.builder().longOpt("key").hasArg().required().desc("HMAC secret key").build());
compOpts.addOption(Option.builder().longOpt("manifest").hasArg()
.desc("Manifest output path").build());
try {
CommandLine cmd = parser.parse(compOpts, subArgs, true);
String[] remaining = cmd.getArgs();
if (remaining.length < 1) {
System.err.println("Usage: IntegrityChecker compute <target> --key <key>");
System.exit(1);
}
String target = remaining[0];
String key = cmd.getOptionValue("key");
String manifest = cmd.getOptionValue("manifest", "manifest.json");
computeManifest(target, key, manifest);
} catch (ParseException e) {
System.err.println("Error: " + e.getMessage());
System.exit(1);
}
break;
}
case "verify": {
Options verOpts = new Options();
verOpts.addOption(Option.builder().longOpt("key").hasArg().required().desc("HMAC secret key").build());
verOpts.addOption(Option.builder().longOpt("manifest").hasArg()
.desc("Manifest file path").build());
try {
CommandLine cmd = parser.parse(verOpts, subArgs);
String key = cmd.getOptionValue("key");
String manifest = cmd.getOptionValue("manifest", "manifest.json");
boolean success = verifyManifest(manifest, key);
System.exit(success ? 0 : 1);
} catch (ParseException e) {
System.err.println("Error: " + e.getMessage());
System.exit(1);
}
break;
}
default:
System.err.println("Unknown command: " + command);
System.err.println("Usage: IntegrityChecker <compute|verify> [options]");
System.exit(1);
}
}
}