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

Bcrypt Password Hasher (javascript, written by Claude Code)

envgap__claude-code__javascript-t1-17

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 #17 · 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: Bcrypt Password Hasher

Write a program that hashes and verifies passwords using the bcrypt algorithm with configurable work factors, supporting bulk operations, migration from weaker hashing schemes, and password policy enforcement.

FUNCTIONAL REQUIREMENTS:
- Support subcommands: hash (hash a password), verify (check a password against a hash), benchmark (test hashing speed at different work factors), and migrate (rehash from MD5/SHA-256 to bcrypt)
- hash: Accept a password via command-line argument or stdin, hash it with bcrypt, and print the resulting hash string
- verify: Accept a password and a bcrypt hash string, verify the match, and print whether it is valid or invalid
- Support a configurable work factor (cost parameter) via --cost flag (default 12, range 4-31)
- benchmark: Measure and display the time to hash a password at each work factor from 8 to the specified maximum, helping users choose an appropriate cost
- migrate: Read a CSV file with columns (username, old_hash, hash_type), verify that the old hash matches a provided password, then rehash with bcrypt and output the updated CSV
- Support batch hashing via --file flag: read one password per line, hash each, and output as a CSV with columns (line_number, hash)
- Generate a cryptographically secure random salt for each hash operation (built into bcrypt)
- Print detailed output: the hash, work factor used, estimated time per hash, and the bcrypt version identifier ($2b$)
- Save results to a file via --output flag (default: print to console only)
- If no arguments are given, demonstrate hashing a sample password at three different work factors (10, 12, 14), verify each hash, show a failed verification with a wrong password, and run a mini benchmark
- Handle errors: invalid cost factors, malformed hash strings, empty passwords, and unsupported hash types in migration

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.

hasher.js
#!/usr/bin/env node

/**
 * Bcrypt Password Hasher
 *
 * Hashes and verifies passwords using bcrypt with configurable work factors,
 * benchmarking capabilities, and migration support for upgrading hash strength.
 */

const bcrypt = require("bcrypt");

const DEFAULT_WORK_FACTOR = 12;
const MIN_WORK_FACTOR = 4;
const MAX_WORK_FACTOR = 31;

/**
 * Validate that a work factor is within acceptable bounds.
 * @param {number} workFactor
 */
function validateWorkFactor(workFactor) {
  if (!Number.isInteger(workFactor)) {
    throw new TypeError(`Work factor must be an integer, got ${typeof workFactor}`);
  }
  if (workFactor < MIN_WORK_FACTOR || workFactor > MAX_WORK_FACTOR) {
    throw new RangeError(
      `Work factor must be between ${MIN_WORK_FACTOR} and ${MAX_WORK_FACTOR}, got ${workFactor}`
    );
  }
}

/**
 * Hash a password using bcrypt with the specified work factor.
 * @param {string} password - The plaintext password to hash.
 * @param {number} [workFactor=12] - The bcrypt cost parameter.
 * @returns {Promise<{hashed: string, workFactor: number, elapsedMs: number}>}
 */
async function hashPassword(password, workFactor = DEFAULT_WORK_FACTOR) {
  if (!password) {
    throw new Error("Password cannot be empty");
  }
  validateWorkFactor(workFactor);

  const start = process.hrtime.bigint();
  const salt = await bcrypt.genSalt(workFactor);
  const hashed = await bcrypt.hash(password, salt);
  const elapsedMs =
    Number(process.hrtime.bigint() - start) / 1_000_000;

  return {
    hashed,
    workFactor,
    elapsedMs: Math.round(elapsedMs * 100) / 100,
  };
}

/**
 * Verify a password against a bcrypt hash.
 * @param {string} password - The plaintext password to verify.
 * @param {string} hashed - The bcrypt hash to check against.
 * @returns {Promise<boolean>}
 */
async function verifyPassword(password, hashed) {
  if (!password || !hashed) {
    throw new Error("Password and hash cannot be empty");
  }

  try {
    return await bcrypt.compare(password, hashed);
  } catch {
    return false;
  }
}

/**
 * Extract the work factor (cost) from an existing bcrypt hash.
 * @param {string} hashed - A bcrypt hash string.
 * @returns {number} The work factor used to create the hash.
 */
function extractWorkFactor(hashed) {
  if (!hashed) {
    throw new Error("Hash cannot be empty");
  }

  const parts = hashed.split("$");
  if (parts.length >= 4) {
    const factor = parseInt(parts[2], 10);
    if (!isNaN(factor)) {
      return factor;
    }
  }
  throw new Error(
    `Cannot extract work factor from hash: ${hashed.substring(0, 20)}...`
  );
}

/**
 * Check if a hash needs to be re-hashed with a target work factor.
 * @param {string} hashed - An existing bcrypt hash.
 * @param {number} targetWorkFactor - The desired work factor.
 * @returns {boolean}
 */
function needsMigration(hashed, targetWorkFactor) {
  try {
    const existingFactor = extractWorkFactor(hashed);
    return existingFactor < targetWorkFactor;
  } catch {
    return true;
  }
}

/**
 * Migrate a password hash to a new work factor if needed.
 * @param {string} password - The plaintext password.
 * @param {string} oldHash - The existing bcrypt hash.
 * @param {number} [targetWorkFactor=12] - The target work factor.
 * @returns {Promise<{hashed: string, workFactor: number, elapsedMs: number}|null>}
 */
async function migrateHash(password, oldHash, targetWorkFactor = DEFAULT_WORK_FACTOR) {
  const valid = await verifyPassword(password, oldHash);
  if (!valid) {
    throw new Error("Password does not match the provided hash");
  }

  if (needsMigration(oldHash, targetWorkFactor)) {
    return hashPassword(password, targetWorkFactor);
  }
  return null;
}

/**
 * Benchmark hashing and verification across a range of work factors.
 * @param {number} [maxWorkFactor=12] - Maximum work factor to benchmark.
 * @param {number} [iterations=3] - Number of iterations per work factor.
 * @returns {Promise<Array<{workFactor: number, avgHashMs: number, avgVerifyMs: number, iterations: number}>>}
 */
async function benchmark(maxWorkFactor = DEFAULT_WORK_FACTOR, iterations = 3) {
  if (iterations < 1) {
    throw new Error("Iterations must be at least 1");
  }
  validateWorkFactor(maxWorkFactor);

  const testPassword = "BenchmarkPassword!123";
  const results = [];
  const maxFactor = Math.min(maxWorkFactor + 2, 16);

  for (let wf = MIN_WORK_FACTOR; wf <= maxFactor; wf++) {
    let totalHashMs = 0;
    let totalVerifyMs = 0;

    for (let i = 0; i < iterations; i++) {
      // Time hashing
      let start = process.hrtime.bigint();
      const salt = await bcrypt.genSalt(wf);
      const hashed = await bcrypt.hash(testPassword, salt);
      totalHashMs += Number(process.hrtime.bigint() - start) / 1_000_000;

      // Time verification
      start = process.hrtime.bigint();
      await bcrypt.compare(testPassword, hashed);
      totalVerifyMs += Number(process.hrtime.bigint() - start) / 1_000_000;
    }

    results.push({
      workFactor: wf,
      avgHashMs: Math.round((totalHashMs / iterations) * 100) / 100,
      avgVerifyMs: Math.round((totalVerifyMs / iterations) * 100) / 100,
      iterations,
    });
  }

  return results;
}

// ---- CLI ----

async function main() {
  const args = process.argv.slice(2);
  const command = args[0];

  function getFlag(flag, defaultValue) {
    const idx = args.indexOf(flag);
    if (idx !== -1 && idx + 1 < args.length) {
      return parseInt(args[idx + 1], 10);
    }
    return defaultValue;
  }

  try {
    switch (command) {
      case "hash": {
        const password = args[1];
        if (!password) {
          console.error("Usage: hasher.js hash <password> [-w work_factor]");
          process.exit(1);
        }
        const wf = getFlag("-w", DEFAULT_WORK_FACTOR);
        const result = await hashPassword(password, wf);
        console.log(JSON.stringify(result, null, 2));
        break;
      }

      case "verify": {
        const password = args[1];
        const hash = args[2];
        if (!password || !hash) {
          console.error("Usage: hasher.js verify <password> <hash>");
          process.exit(1);
        }
        const valid = await verifyPassword(password, hash);
        console.log(JSON.stringify({ valid }, null, 2));
        process.exit(valid ? 0 : 1);
        break;
      }

      case "benchmark": {
        const wf = getFlag("-w", DEFAULT_WORK_FACTOR);
        const iter = getFlag("-i", 3);
        const results = await benchmark(wf, iter);
        console.log(JSON.stringify(results, null, 2));
        break;
      }

      case "migrate": {
        const password = args[1];
        const hash = args[2];
        if (!password || !hash) {
          console.error("Usage: hasher.js migrate <password> <hash> [-w work_factor]");
          process.exit(1);
        }
        const wf = getFlag("-w", DEFAULT_WORK_FACTOR);
        if (needsMigration(hash, wf)) {
          const result = await migrateHash(password, hash, wf);
          if (result) {
            console.log(JSON.stringify({ migrated: true, ...result }, null, 2));
          } else {
            console.log(JSON.stringify({ migrated: false }, null, 2));
          }
        } else {
          console.log(
            JSON.stringify(
              { migrated: false, reason: "Hash already at target strength" },
              null,
              2
            )
          );
        }
        break;
      }

      default:
        console.log("Bcrypt Password Hasher");
        console.log("Usage:");
        console.log("  hasher.js hash <password> [-w work_factor]");
        console.log("  hasher.js verify <password> <hash>");
        console.log("  hasher.js benchmark [-w work_factor] [-i iterations]");
        console.log("  hasher.js migrate <password> <hash> [-w work_factor]");
        process.exit(1);
    }
  } catch (err) {
    console.error("Error:", err.message);
    process.exit(1);
  }
}

// Export for programmatic use
module.exports = {
  hashPassword,
  verifyPassword,
  extractWorkFactor,
  needsMigration,
  migrateHash,
  benchmark,
};

// Run CLI if executed directly
if (require.main === module) {
  main();
}
package.json
{
  "name": "bcrypt-password-hasher",
  "version": "1.0.0",
  "description": "Password hashing utility using bcrypt with configurable work factors, benchmarking, and migration support",
  "main": "hasher.js",
  "bin": {
    "bcrypt-hasher": "hasher.js"
  },
  "scripts": {
    "start": "node hasher.js",
    "hash": "node hasher.js hash",
    "benchmark": "node hasher.js benchmark"
  },
  "keywords": ["bcrypt", "password", "hashing", "security"],
  "license": "MIT",
  "dependencies": {
    "bcrypt": "5.1.1"
  }
}
README.md
# Bcrypt Password Hasher (JavaScript - Trial 1)

A password hashing utility using the bcrypt library with configurable work factors, benchmarking, and hash migration support.

## Dependencies

- **bcrypt** (5.1.1): Native bcrypt hashing library for Node.js

## Installation

```bash
npm install
```

## Usage

### Hash a password
```bash
node hasher.js hash "mypassword" -w 12
```

### Verify a password
```bash
node hasher.js verify "mypassword" "$2b$12$..."
```

### Benchmark work factors
```bash
node hasher.js benchmark -w 14 -i 5
```

### Migrate a hash
```bash
node hasher.js migrate "mypassword" "$2b$10$..." -w 12
```

## Features

- Configurable bcrypt work factor (cost parameter 4-31)
- Password hashing and verification with async/await
- Benchmark mode to compare work factor performance
- Hash migration to upgrade weaker hashes to stronger work factors
- JSON output for easy integration
- Exportable as a Node.js module