lsh-framework 1.7.2 → 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 +12 -5
- package/dist/commands/init.js +17 -7
- package/dist/lib/secrets-manager.js +75 -34
- package/dist/services/secrets/secrets.js +22 -5
- package/package.json +1 -1
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
|
-
-
|
|
526
|
-
-
|
|
527
|
-
-
|
|
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
|
|
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
|
package/dist/commands/init.js
CHANGED
|
@@ -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
|
-
|
|
261
|
-
|
|
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('
|
|
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.
|
|
335
|
-
console.log(
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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:
|
|
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
|
|
578
|
+
// Step 3: Ensure .gitignore includes .env
|
|
538
579
|
if (this.gitInfo?.isGitRepo) {
|
|
539
580
|
ensureEnvInGitignore(process.cwd());
|
|
540
581
|
}
|
|
541
|
-
// Step
|
|
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
|
|
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('
|
|
201
|
-
console.log(
|
|
202
|
-
console.log(
|
|
203
|
-
console.log('
|
|
204
|
-
console.log('
|
|
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.
|
|
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": {
|