claude-remote-cli 3.9.0 → 3.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,8 +26,7 @@ export async function verifyPin(pin, hash) {
26
26
  return false;
27
27
  }
28
28
  }
29
- // Legacy bcrypt hashes: require PIN reset
30
- console.warn('[auth] Legacy bcrypt PIN hash detected. Delete pinHash from config and restart to set a new PIN.');
29
+ // Legacy bcrypt hashes are migrated at startup; if one reaches here, reject it
31
30
  return false;
32
31
  }
33
32
  export function isRateLimited(ip) {
@@ -56,6 +55,9 @@ export function clearRateLimit(ip) {
56
55
  export function generateCookieToken() {
57
56
  return crypto.randomBytes(32).toString('hex');
58
57
  }
58
+ export function isLegacyHash(hash) {
59
+ return !!hash && !hash.startsWith('scrypt:');
60
+ }
59
61
  export function _resetForTesting() {
60
62
  attemptMap.clear();
61
63
  }
@@ -160,6 +160,11 @@ async function main() {
160
160
  catch (err) {
161
161
  console.warn('Analytics disabled: failed to initialize:', err instanceof Error ? err.message : err);
162
162
  }
163
+ if (config.pinHash && auth.isLegacyHash(config.pinHash)) {
164
+ console.log('Migrating legacy PIN hash to scrypt. You will need to set a new PIN.');
165
+ delete config.pinHash;
166
+ saveConfig(CONFIG_PATH, config);
167
+ }
163
168
  if (!config.pinHash) {
164
169
  const pin = await promptPin('Set up a PIN for claude-remote-cli:');
165
170
  config.pinHash = await auth.hashPin(pin);
@@ -1,6 +1,6 @@
1
1
  import { test } from 'node:test';
2
2
  import assert from 'node:assert';
3
- import { hashPin, verifyPin, isRateLimited, recordFailedAttempt, generateCookieToken, _resetForTesting, } from '../server/auth.js';
3
+ import { hashPin, verifyPin, isLegacyHash, isRateLimited, recordFailedAttempt, generateCookieToken, _resetForTesting, } from '../server/auth.js';
4
4
  test('hashPin returns scrypt hash with expected format', async () => {
5
5
  _resetForTesting();
6
6
  const hash = await hashPin('1234');
@@ -77,6 +77,17 @@ test('hashPin produces unique salts', async () => {
77
77
  assert.strictEqual(await verifyPin('1234', hash1), true);
78
78
  assert.strictEqual(await verifyPin('1234', hash2), true);
79
79
  });
80
+ test('isLegacyHash returns true for bcrypt hashes', () => {
81
+ assert.strictEqual(isLegacyHash('$2b$10$abcdefghijklmnopqrstuuABCDEFGHIJKLMNOPQRSTUVWXYZ012'), true);
82
+ assert.strictEqual(isLegacyHash('$2a$10$someotherbcrypthashvalue'), true);
83
+ });
84
+ test('isLegacyHash returns false for scrypt hashes', async () => {
85
+ const hash = await hashPin('1234');
86
+ assert.strictEqual(isLegacyHash(hash), false);
87
+ });
88
+ test('isLegacyHash returns false for empty string', () => {
89
+ assert.strictEqual(isLegacyHash(''), false);
90
+ });
80
91
  test('generateCookieToken returns non-empty string', () => {
81
92
  _resetForTesting();
82
93
  const token = generateCookieToken();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-remote-cli",
3
- "version": "3.9.0",
3
+ "version": "3.9.1",
4
4
  "description": "Remote web interface for Claude Code CLI sessions",
5
5
  "type": "module",
6
6
  "main": "dist/server/index.js",