← All tasks
javascriptclaude-code/javascript-t1 #11Not a task: already works

AES-256 File Encryption Tool (javascript, written by Claude Code)

envgap__claude-code__javascript-t1-11

Written by a coding agent; not on GitHubWritten 2026-02-27

01 / FAILURE SIGNATURE

As the study recorded it

No identifying execution failure has been captured.
Not a benchmark task.
  • The project already builds and runs before the fix, so there is nothing to repair.

02 / ENVIRONMENT RECIPE

Base commit
Not freshly verified
Manifest
package.json
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

claude-code/javascript-t1 #11 · read the task the agent was given
Claude Code wrote this javascript project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: AES-256 File Encryption Tool

Write a program that encrypts and decrypts files using AES-256 encryption in CBC mode with proper key derivation, initialization vectors, and authenticated encryption to prevent tampering.

FUNCTIONAL REQUIREMENTS:
- Accept a file path, a password, and a mode (encrypt or decrypt) as command-line arguments
- Derive the encryption key from the password using PBKDF2 with SHA-256, a random 16-byte salt, and at least 100,000 iterations
- Encrypt using AES-256-CBC with a random 16-byte initialization vector (IV) for each encryption
- Prepend the salt and IV to the encrypted output file so they are available for decryption
- Add HMAC-SHA256 authentication tag to the encrypted file to detect tampering during decryption
- During decryption, verify the HMAC before attempting to decrypt and report a clear error if the file has been modified
- Support encrypting entire directories recursively via --recursive flag, preserving directory structure in the output
- Support a --output flag to specify the output file or directory (default: append .enc for encryption, strip .enc for decryption)
- Display progress information for large files: file size, percentage complete, and throughput
- If no input file is given, generate a sample text file with random content, encrypt it, then decrypt it back and verify the output matches the original
- Handle errors gracefully: wrong password (HMAC mismatch), corrupted file, file not found, permission denied

Create a complete JavaScript project for a clean Ubuntu 22.04 machine with only Node.js 20+ (LTS) installed. Include:
- Source code
- package.json 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 from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]

05 / FILES

The project as the agent wrote it

3 files, exactly as written, before any repair.

encryptor.js
#!/usr/bin/env node
/**
 * AES-256 File Encryption Tool - Trial 1 (JavaScript)
 * Uses Node.js built-in 'crypto' module with AES-256-CBC and PBKDF2.
 */

"use strict";

const crypto = require("crypto");
const fs = require("fs");
const path = require("path");
const readline = require("readline");

// Constants
const MAGIC_HEADER = Buffer.from("ENC1", "ascii");
const SALT_SIZE = 32;
const IV_SIZE = 16;
const KEY_SIZE = 32; // AES-256
const PBKDF2_ITERATIONS = 600000;
const HMAC_SIZE = 32;
const ALGORITHM = "aes-256-cbc";

/**
 * Derive a 256-bit key from password using PBKDF2-HMAC-SHA256.
 */
function deriveKey(password, salt) {
  return crypto.pbkdf2Sync(password, salt, PBKDF2_ITERATIONS, KEY_SIZE, "sha256");
}

/**
 * Compute HMAC-SHA256 for integrity verification.
 */
function computeHmac(key, data) {
  return crypto.createHmac("sha256", key).update(data).digest();
}

/**
 * Encrypt a file using AES-256-CBC.
 */
function encryptFile(inputPath, outputPath, password) {
  const plaintext = fs.readFileSync(inputPath);

  // Generate random salt and IV
  const salt = crypto.randomBytes(SALT_SIZE);
  const iv = crypto.randomBytes(IV_SIZE);

  // Derive key
  const key = deriveKey(password, salt);

  // Encrypt
  const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
  const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);

  // Compute HMAC over salt + iv + ciphertext
  const hmacData = Buffer.concat([salt, iv, encrypted]);
  const hmac = computeHmac(key, hmacData);

  // Write output: MAGIC(4) + SALT(32) + IV(16) + CT_LEN(8) + CT(N) + HMAC(32)
  const ctLenBuf = Buffer.alloc(8);
  ctLenBuf.writeBigUInt64BE(BigInt(encrypted.length));

  const output = Buffer.concat([MAGIC_HEADER, salt, iv, ctLenBuf, encrypted, hmac]);
  fs.writeFileSync(outputPath, output);

  console.log(`Encrypted: ${inputPath} -> ${outputPath}`);
  console.log(`  Salt: ${salt.toString("hex").substring(0, 16)}...`);
  console.log(`  IV:   ${iv.toString("hex").substring(0, 16)}...`);
  console.log(`  Size: ${plaintext.length} bytes -> ${encrypted.length} bytes`);
}

/**
 * Decrypt a file encrypted with this tool.
 */
function decryptFile(inputPath, outputPath, password) {
  const data = fs.readFileSync(inputPath);

  // Verify magic header
  if (data.length < 4 || !data.subarray(0, 4).equals(MAGIC_HEADER)) {
    console.log("Error: Not a valid encrypted file (bad magic header).");
    process.exit(1);
  }

  let offset = 4;
  const salt = data.subarray(offset, offset + SALT_SIZE);
  offset += SALT_SIZE;
  const iv = data.subarray(offset, offset + IV_SIZE);
  offset += IV_SIZE;
  const ctLen = Number(data.readBigUInt64BE(offset));
  offset += 8;
  const ciphertext = data.subarray(offset, offset + ctLen);
  offset += ctLen;
  const storedHmac = data.subarray(offset, offset + HMAC_SIZE);

  // Derive key
  const key = deriveKey(password, salt);

  // Verify HMAC
  const hmacData = Buffer.concat([salt, iv, ciphertext]);
  const computedHmac = computeHmac(key, hmacData);

  if (!crypto.timingSafeEqual(storedHmac, computedHmac)) {
    console.log("Error: Integrity check failed. Wrong password or corrupted file.");
    process.exit(1);
  }

  // Decrypt
  try {
    const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
    const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);

    fs.writeFileSync(outputPath, plaintext);
    console.log(`Decrypted: ${inputPath} -> ${outputPath}`);
    console.log(`  Size: ${ciphertext.length} bytes -> ${plaintext.length} bytes`);
  } catch (err) {
    console.log("Error: Decryption failed. Wrong password or corrupted file.");
    process.exit(1);
  }
}

/**
 * Prompt for password input.
 */
function promptPassword(prompt) {
  return new Promise((resolve) => {
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout,
    });
    rl.question(prompt, (answer) => {
      rl.close();
      resolve(answer);
    });
  });
}

/**
 * Generate a sample file and demonstrate encrypt/decrypt.
 */
function runDemo() {
  const sampleFile = "sample_input.txt";
  const encryptedFile = "sample_encrypted.enc";
  const decryptedFile = "sample_decrypted.txt";
  const demoPassword = "demo_password_123";

  // Create sample file
  const sampleContent =
    "This is a sample file for AES-256-CBC encryption demo.\n" +
    "It contains multiple lines of text.\n" +
    "Line 3: The quick brown fox jumps over the lazy dog.\n" +
    "Line 4: 0123456789 ABCDEF !@#$%^&*()\n" +
    "Line 5: Unicode test - \u00e9\u00e8\u00ea\u00eb\n";

  fs.writeFileSync(sampleFile, sampleContent, "utf-8");
  console.log(`Created sample file: ${sampleFile} (${sampleContent.length} bytes)`);

  // Encrypt
  console.log("\n--- Encrypting ---");
  encryptFile(sampleFile, encryptedFile, demoPassword);

  // Decrypt
  console.log("\n--- Decrypting ---");
  decryptFile(encryptedFile, decryptedFile, demoPassword);

  // Verify
  const original = fs.readFileSync(sampleFile);
  const restored = fs.readFileSync(decryptedFile);
  if (original.equals(restored)) {
    console.log("\nVerification: SUCCESS - Decrypted file matches original.");
  } else {
    console.log("\nVerification: FAILED - Files do not match!");
  }

  // Test wrong password
  console.log("\n--- Testing wrong password ---");
  try {
    // Override process.exit for demo
    const origExit = process.exit;
    process.exit = () => {
      throw new Error("exit");
    };
    try {
      decryptFile(encryptedFile, "should_not_exist.txt", "wrong_password");
    } catch (e) {
      console.log("(Wrong password correctly rejected)");
    }
    process.exit = origExit;
  } catch (e) {
    // ignore
  }

  // Cleanup
  [sampleFile, encryptedFile, decryptedFile, "should_not_exist.txt"].forEach((f) => {
    try {
      fs.unlinkSync(f);
    } catch (e) {
      // ignore
    }
  });
  console.log("\nDemo complete. Temporary files cleaned up.");
}

/**
 * Main entry point - parse CLI arguments.
 */
async function main() {
  const args = process.argv.slice(2);

  if (args.length === 0) {
    console.log("No command specified. Running demo mode...");
    runDemo();
    return;
  }

  const command = args[0].toLowerCase();

  if (command === "demo") {
    runDemo();
    return;
  }

  // Parse optional flags
  let inputPath = args[1];
  let outputPath = null;
  let password = null;

  for (let i = 2; i < args.length; i++) {
    if (args[i] === "-o" && i + 1 < args.length) {
      outputPath = args[++i];
    } else if (args[i] === "-p" && i + 1 < args.length) {
      password = args[++i];
    }
  }

  if (!inputPath) {
    console.log("Error: Input file required.");
    console.log("Usage: node encryptor.js <encrypt|decrypt> <input> [-o output] [-p password]");
    process.exit(1);
  }

  if (!fs.existsSync(inputPath)) {
    console.log(`Error: Input file not found: ${inputPath}`);
    process.exit(1);
  }

  if (command === "encrypt") {
    if (!outputPath) outputPath = inputPath + ".enc";
    if (!password) {
      password = await promptPassword("Enter password: ");
      const confirm = await promptPassword("Confirm password: ");
      if (password !== confirm) {
        console.log("Error: Passwords do not match.");
        process.exit(1);
      }
    }
    encryptFile(inputPath, outputPath, password);
  } else if (command === "decrypt") {
    if (!outputPath) {
      outputPath = inputPath.endsWith(".enc")
        ? inputPath.slice(0, -4)
        : inputPath + ".dec";
    }
    if (!password) {
      password = await promptPassword("Enter password: ");
    }
    decryptFile(inputPath, outputPath, password);
  } else {
    console.log(`Unknown command: ${command}`);
    console.log("Usage: node encryptor.js <encrypt|decrypt|demo>");
    process.exit(1);
  }
}

main().catch((err) => {
  console.error("Fatal error:", err.message);
  process.exit(1);
});
package.json
{
  "name": "aes256-file-encryptor-t1",
  "version": "1.0.0",
  "description": "AES-256 File Encryption Tool using Node.js built-in crypto module",
  "main": "encryptor.js",
  "scripts": {
    "start": "node encryptor.js",
    "demo": "node encryptor.js demo",
    "encrypt": "node encryptor.js encrypt",
    "decrypt": "node encryptor.js decrypt"
  },
  "engines": {
    "node": ">=18.0.0"
  },
  "dependencies": {},
  "keywords": ["aes", "encryption", "aes-256", "crypto"],
  "license": "MIT"
}
README.md
# AES-256 File Encryption Tool - JavaScript Trial 1

## Dependencies
- **crypto** (built-in Node.js module): Provides AES-256-CBC, PBKDF2, and HMAC-SHA256. No external packages required.
- **Node.js 18+** required.

## Setup
```bash
npm install
```

## Usage

### Encrypt a file
```bash
node encryptor.js encrypt <input_file> -o <output_file> -p <password>
```

### Decrypt a file
```bash
node encryptor.js decrypt <input_file> -o <output_file> -p <password>
```

### Demo mode
```bash
node encryptor.js demo
# or simply:
node encryptor.js
```

## Design
- **Algorithm**: AES-256-CBC
- **Key Derivation**: PBKDF2-HMAC-SHA256 with 600,000 iterations
- **Integrity**: HMAC-SHA256 over salt + IV + ciphertext
- **File Format**: MAGIC(4) + SALT(32) + IV(16) + CT_LEN(8) + CT(N) + HMAC(32)