lsh-framework 1.7.1 → 1.7.3

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.
package/README.md CHANGED
@@ -512,6 +512,11 @@ lsh -s script.sh
512
512
 
513
513
  ## Security
514
514
 
515
+ > **⚠️ CRITICAL: Read [SECURITY.md](SECURITY.md) for complete security guidelines**
516
+ >
517
+ > The security of your secrets depends entirely on how you store your `LSH_SECRETS_KEY`.
518
+ > **Never store it in your project's `.env` file** - use your shell profile instead.
519
+
515
520
  ### Encryption
516
521
 
517
522
  - **Algorithm**: AES-256-CBC
@@ -522,16 +527,18 @@ lsh -s script.sh
522
527
  ### Best Practices
523
528
 
524
529
  **✅ DO:**
525
- - Generate unique keys per project
526
- - Share keys via 1Password/LastPass
527
- - Use different keys for personal vs team projects
530
+ - Store `LSH_SECRETS_KEY` in your shell profile (`~/.zshrc`, `~/.bashrc`)
531
+ - Generate unique keys per project/team
532
+ - Share keys securely via 1Password/LastPass/Bitwarden
533
+ - Use different keys for dev/staging/production environments
528
534
  - Rotate keys periodically
529
- - Keep backups of your .env files
535
+ - Keep encrypted backups of your encryption key
530
536
 
531
537
  **❌ DON'T:**
538
+ - Store `LSH_SECRETS_KEY` in your project's `.env` file
532
539
  - Commit `LSH_SECRETS_KEY` to git
533
540
  - Share keys in plain text (Slack, email, etc.)
534
- - Reuse keys across projects
541
+ - Reuse keys across different teams/projects
535
542
  - Store production secrets in dev environment
536
543
 
537
544
  ### Command Validation
@@ -257,9 +257,8 @@ async function saveConfiguration(config) {
257
257
  // File doesn't exist, start fresh
258
258
  }
259
259
  // Update or add configuration
260
- const updates = {
261
- LSH_SECRETS_KEY: config.encryptionKey,
262
- };
260
+ // SECURITY: DO NOT save LSH_SECRETS_KEY to .env (circular encryption vulnerability)
261
+ const updates = {};
263
262
  if (config.storageType === 'supabase' && config.supabaseUrl && config.supabaseKey) {
264
263
  updates.SUPABASE_URL = config.supabaseUrl;
265
264
  updates.SUPABASE_ANON_KEY = config.supabaseKey;
@@ -327,12 +326,23 @@ function showSuccessMessage(config) {
327
326
  console.log(chalk.bold.green('✨ Setup complete!'));
328
327
  console.log(chalk.gray('━'.repeat(50)));
329
328
  console.log('');
330
- // Show encryption key
331
- console.log(chalk.yellow('📝 Your encryption key (save this securely):'));
329
+ // Show encryption key with shell profile instructions
330
+ console.log(chalk.yellow('🔑 Your encryption key (save this securely):'));
332
331
  console.log(chalk.cyan(` ${config.encryptionKey}`));
333
332
  console.log('');
334
- console.log(chalk.gray(' This key is saved in your .env file.'));
335
- console.log(chalk.gray(' Share it with your team to sync secrets.'));
333
+ console.log(chalk.bold('⚠️ IMPORTANT: Add this to your shell profile, NOT to .env'));
334
+ console.log('');
335
+ console.log(chalk.gray(' Add to your shell profile:'));
336
+ console.log(chalk.cyan(` echo 'export LSH_SECRETS_KEY="${config.encryptionKey}"' >> ~/.zshrc`));
337
+ console.log(chalk.cyan(' source ~/.zshrc'));
338
+ console.log('');
339
+ console.log(chalk.gray(' Then verify it\'s set:'));
340
+ console.log(chalk.cyan(' echo $LSH_SECRETS_KEY'));
341
+ console.log('');
342
+ console.log(chalk.gray(' Share this key securely with your team:'));
343
+ console.log(chalk.gray(' - Use 1Password, LastPass, or Bitwarden'));
344
+ console.log(chalk.gray(' - Send via encrypted email or Signal'));
345
+ console.log(chalk.gray(' - Never commit to git or post in public channels'));
336
346
  console.log('');
337
347
  // Storage info
338
348
  if (config.storageType === 'supabase') {
@@ -371,45 +371,84 @@ export class SecretsManager {
371
371
  return environment;
372
372
  }
373
373
  /**
374
- * Generate encryption key if not set
374
+ * Ensure encryption key is set in environment
375
+ * SECURITY: Key must be in shell profile, NEVER in project .env
375
376
  */
376
377
  async ensureEncryptionKey() {
377
- if (process.env.LSH_SECRETS_KEY) {
378
- return true; // Key already set
378
+ if (process.env.LSH_SECRETS_KEY || this.encryptionKey) {
379
+ return true; // Key already set (env or constructor)
380
+ }
381
+ // DO NOT AUTO-GENERATE - This is a security violation
382
+ console.log('');
383
+ logger.error('❌ LSH_SECRETS_KEY environment variable not set');
384
+ console.log('');
385
+ logger.error('🔒 For security, this key must be stored in your shell profile,');
386
+ logger.error(' NOT in your project\'s .env file.');
387
+ console.log('');
388
+ logger.error('📝 Quick setup:');
389
+ console.log('');
390
+ logger.error(' 1. Generate a key:');
391
+ logger.error(' lsh key');
392
+ console.log('');
393
+ logger.error(' 2. Add to your shell profile:');
394
+ logger.error(' echo "export LSH_SECRETS_KEY=\'your-key-here\'" >> ~/.zshrc');
395
+ logger.error(' source ~/.zshrc');
396
+ console.log('');
397
+ logger.error(' 3. Verify it\'s set:');
398
+ logger.error(' echo $LSH_SECRETS_KEY');
399
+ console.log('');
400
+ logger.error('📖 See SECURITY.md for complete details and team sharing.');
401
+ console.log('');
402
+ throw new Error('LSH_SECRETS_KEY not set in environment');
403
+ }
404
+ /**
405
+ * Check if LSH_SECRETS_KEY is in .env file (security violation)
406
+ * SECURITY: Keys in .env files create circular encryption and expose secrets
407
+ */
408
+ checkForKeyInEnvFile() {
409
+ // Skip check in test environment
410
+ if (process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID) {
411
+ return;
379
412
  }
380
- logger.warn('⚠️ No encryption key found. Generating a new key...');
381
- const key = crypto.randomBytes(32).toString('hex');
382
- // Try to add to .env file
383
413
  const envPath = path.join(process.cwd(), '.env');
414
+ if (!fs.existsSync(envPath)) {
415
+ return; // No .env file, nothing to check
416
+ }
384
417
  try {
385
- let content = '';
386
- if (fs.existsSync(envPath)) {
387
- content = fs.readFileSync(envPath, 'utf8');
388
- if (!content.endsWith('\n')) {
389
- content += '\n';
390
- }
391
- }
392
- // Check if LSH_SECRETS_KEY already exists (but empty)
418
+ const content = fs.readFileSync(envPath, 'utf8');
393
419
  if (content.includes('LSH_SECRETS_KEY=')) {
394
- content = content.replace(/LSH_SECRETS_KEY=.*$/m, `LSH_SECRETS_KEY=${key}`);
395
- }
396
- else {
397
- content += `\n# LSH Secrets Encryption Key (do not commit!)\nLSH_SECRETS_KEY=${key}\n`;
420
+ console.log('');
421
+ logger.error('🚨 SECURITY ISSUE: LSH_SECRETS_KEY found in .env file!');
422
+ console.log('');
423
+ logger.error('⚠️ This is a critical security vulnerability:');
424
+ logger.error(' - Creates circular encryption (key encrypts itself)');
425
+ logger.error(' - Exposes key if .env is accidentally committed');
426
+ logger.error(' - Makes IPFS storage completely insecure');
427
+ console.log('');
428
+ logger.error('🔧 Fix this immediately:');
429
+ console.log('');
430
+ logger.error(' 1. Copy the key value from .env');
431
+ logger.error(' 2. Add to your shell profile:');
432
+ logger.error(' echo "export LSH_SECRETS_KEY=\'your-key\'" >> ~/.zshrc');
433
+ logger.error(' source ~/.zshrc');
434
+ console.log('');
435
+ logger.error(' 3. Remove LSH_SECRETS_KEY line from .env:');
436
+ logger.error(' # Edit .env and DELETE the LSH_SECRETS_KEY= line');
437
+ console.log('');
438
+ logger.error(' 4. Verify it\'s set correctly:');
439
+ logger.error(' echo $LSH_SECRETS_KEY');
440
+ console.log('');
441
+ logger.error('📖 See SECURITY.md for complete details.');
442
+ console.log('');
443
+ throw new Error('LSH_SECRETS_KEY must not be in .env file');
398
444
  }
399
- fs.writeFileSync(envPath, content, 'utf8');
400
- // Set in current process
401
- process.env.LSH_SECRETS_KEY = key;
402
- this.encryptionKey = key;
403
- logger.info('✅ Generated and saved encryption key to .env');
404
- logger.info('💡 Load it now: export LSH_SECRETS_KEY=' + key.substring(0, 8) + '...');
405
- return true;
406
445
  }
407
446
  catch (error) {
408
- const _err = error;
409
- logger.error(`Failed to save encryption key: ${_err.message}`);
410
- logger.info('Please set it manually:');
411
- logger.info(`export LSH_SECRETS_KEY=${key}`);
412
- return false;
447
+ // If we already threw our error, re-throw it
448
+ if (error.message === 'LSH_SECRETS_KEY must not be in .env file') {
449
+ throw error;
450
+ }
451
+ // Otherwise, ignore read errors
413
452
  }
414
453
  }
415
454
  /**
@@ -528,17 +567,19 @@ LSH_SECRETS_KEY=${this.encryptionKey}
528
567
  }
529
568
  out();
530
569
  }
531
- // Step 1: Ensure encryption key exists
570
+ // Step 1: Check for security violations
571
+ this.checkForKeyInEnvFile(); // Must be BEFORE ensureEncryptionKey
572
+ // Step 2: Ensure encryption key exists
532
573
  if (!process.env.LSH_SECRETS_KEY) {
533
574
  logger.info('🔑 No encryption key found...');
534
575
  await this.ensureEncryptionKey();
535
576
  out();
536
577
  }
537
- // Step 2: Ensure .gitignore includes .env
578
+ // Step 3: Ensure .gitignore includes .env
538
579
  if (this.gitInfo?.isGitRepo) {
539
580
  ensureEnvInGitignore(process.cwd());
540
581
  }
541
- // Step 3: Check current status
582
+ // Step 4: Check current status
542
583
  const status = await this.status(envFilePath, effectiveEnv);
543
584
  out('📊 Current Status:');
544
585
  out(` Encryption key: ${status.keySet ? '✅' : '❌'}`);
@@ -548,7 +589,7 @@ LSH_SECRETS_KEY=${this.encryptionKey}
548
589
  out(` Key matches: ${status.keyMatches ? '✅' : '❌'}`);
549
590
  }
550
591
  out();
551
- // Step 4: Determine action and execute if auto mode
592
+ // Step 5: Determine action and execute if auto mode
552
593
  let _action = 'in-sync';
553
594
  if (status.cloudExists && status.keyMatches === false) {
554
595
  _action = 'key-mismatch';
@@ -7,6 +7,7 @@ import * as fs from 'fs';
7
7
  import * as path from 'path';
8
8
  import * as readline from 'readline';
9
9
  import { getGitRepoInfo } from '../../lib/git-utils.js';
10
+ import chalk from 'chalk';
10
11
  export async function init_secrets(program) {
11
12
  // Push secrets to cloud
12
13
  program
@@ -197,11 +198,27 @@ export async function init_secrets(program) {
197
198
  }
198
199
  else {
199
200
  // Interactive output with tips
200
- console.log('\n🔑 New encryption key (add to your .env):\n');
201
- console.log(`export LSH_SECRETS_KEY='${key}'\n`);
202
- console.log('💡 Tip: Share this key securely with your team to sync secrets.');
203
- console.log(' Never commit it to git!\n');
204
- console.log('💡 To load immediately: eval "$(lsh key --export)"\n');
201
+ console.log('');
202
+ console.log('🔑 New encryption key generated!\n');
203
+ console.log(chalk.cyan(` ${key}`));
204
+ console.log('');
205
+ console.log(chalk.bold('⚠️ IMPORTANT: Add to your shell profile, NOT to .env'));
206
+ console.log('');
207
+ console.log(chalk.gray(' 1. Add to your shell profile:'));
208
+ console.log(chalk.cyan(` echo 'export LSH_SECRETS_KEY="${key}"' >> ~/.zshrc`));
209
+ console.log(chalk.cyan(' source ~/.zshrc'));
210
+ console.log('');
211
+ console.log(chalk.gray(' 2. Verify it\'s set:'));
212
+ console.log(chalk.cyan(' echo $LSH_SECRETS_KEY'));
213
+ console.log('');
214
+ console.log(chalk.gray(' 3. Share securely with your team:'));
215
+ console.log(chalk.gray(' - Use 1Password, LastPass, or Bitwarden'));
216
+ console.log(chalk.gray(' - Send via encrypted email or Signal'));
217
+ console.log(chalk.gray(' - NEVER commit to git or .env file'));
218
+ console.log('');
219
+ console.log(chalk.gray('💡 Quick load: eval "$(lsh key --export)"'));
220
+ console.log(chalk.gray('📖 Details: See SECURITY.md'));
221
+ console.log('');
205
222
  }
206
223
  });
207
224
  // Create .env file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lsh-framework",
3
- "version": "1.7.1",
3
+ "version": "1.7.3",
4
4
  "description": "Simple, cross-platform encrypted secrets manager with automatic sync, IPFS audit logs, and multi-environment support. Just run lsh sync and start managing your secrets.",
5
5
  "main": "dist/app.js",
6
6
  "bin": {