@josfox/jos 3.1.0 → 4.0.0

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.
@@ -0,0 +1,137 @@
1
+ /**
2
+ * JOS SECRETS Command - Secure credential storage with AES-256-CBC
3
+ */
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const crypto = require('crypto');
8
+
9
+ // AURORA colors
10
+ const C = {
11
+ reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
12
+ purple: '\x1b[38;5;135m', magenta: '\x1b[38;5;198m', cyan: '\x1b[38;5;51m',
13
+ green: '\x1b[38;5;78m', red: '\x1b[38;5;196m', gray: '\x1b[38;5;245m',
14
+ white: '\x1b[38;5;255m', yellow: '\x1b[38;5;220m'
15
+ };
16
+
17
+ function ensureMasterKey(secretsDir) {
18
+ const masterPath = path.join(secretsDir, '.master');
19
+ if (!fs.existsSync(secretsDir)) {
20
+ fs.mkdirSync(secretsDir, { recursive: true, mode: 0o700 });
21
+ }
22
+ if (!fs.existsSync(masterPath)) {
23
+ // Generate secure master key
24
+ const key = crypto.randomBytes(32).toString('hex');
25
+ fs.writeFileSync(masterPath, key, { mode: 0o600 });
26
+ console.log(`${C.green}✓ Master key generated${C.reset}`);
27
+ }
28
+ return Buffer.from(fs.readFileSync(masterPath, 'utf8').trim(), 'hex');
29
+ }
30
+
31
+ exports.execute = async (args, home) => {
32
+ const action = args[0];
33
+ const key = args[1];
34
+ const val = args[2];
35
+ const showHelp = args.includes('--help') || args.includes('-h');
36
+
37
+ if (showHelp || !action) {
38
+ console.log(`
39
+ ${C.cyan}${C.bold}JOS SECRETS${C.reset} - Secure credential storage (AES-256-CBC)
40
+
41
+ ${C.white}Usage:${C.reset} jos secrets <action> [key] [value]
42
+
43
+ ${C.white}Actions:${C.reset}
44
+ set <key> <value> Encrypt and store a secret
45
+ get <key> Decrypt and retrieve a secret
46
+ list List all secret keys (not values)
47
+ delete <key> Remove a secret
48
+
49
+ ${C.white}Examples:${C.reset}
50
+ jos secrets set API_KEY sk-abc123
51
+ jos secrets get API_KEY
52
+ jos secrets list
53
+ jos secrets delete API_KEY
54
+
55
+ ${C.white}Security:${C.reset}
56
+ • Master key: ~/.jos/secrets/.master (0600)
57
+ • Vault: ~/.jos/secrets/vault.json
58
+ • Encryption: AES-256-CBC with random IV per secret
59
+ `);
60
+ return;
61
+ }
62
+
63
+ const secretsDir = path.join(home, 'secrets');
64
+ const vaultPath = path.join(secretsDir, 'vault.json');
65
+
66
+ try {
67
+ const masterKey = ensureMasterKey(secretsDir);
68
+ let vault = {};
69
+ if (fs.existsSync(vaultPath)) {
70
+ vault = JSON.parse(fs.readFileSync(vaultPath, 'utf8'));
71
+ }
72
+
73
+ switch (action) {
74
+ case 'set':
75
+ if (!key || !val) {
76
+ console.log(`${C.red}✖ Usage: jos secrets set <key> <value>${C.reset}`);
77
+ process.exit(1);
78
+ }
79
+ const iv = crypto.randomBytes(16);
80
+ const cipher = crypto.createCipheriv('aes-256-cbc', masterKey, iv);
81
+ let encrypted = cipher.update(val, 'utf8', 'hex');
82
+ encrypted += cipher.final('hex');
83
+ vault[key] = iv.toString('hex') + ':' + encrypted;
84
+ fs.writeFileSync(vaultPath, JSON.stringify(vault, null, 2), { mode: 0o600 });
85
+ console.log(`${C.green}🔒 Secret '${key}' saved securely.${C.reset}`);
86
+ break;
87
+
88
+ case 'get':
89
+ if (!key) {
90
+ console.log(`${C.red}✖ Usage: jos secrets get <key>${C.reset}`);
91
+ process.exit(1);
92
+ }
93
+ if (!vault[key]) {
94
+ console.log(`${C.yellow}⚠ Secret '${key}' not found${C.reset}`);
95
+ process.exit(1);
96
+ }
97
+ const [ivHex, encData] = vault[key].split(':');
98
+ const decipher = crypto.createDecipheriv('aes-256-cbc', masterKey, Buffer.from(ivHex, 'hex'));
99
+ let decrypted = decipher.update(encData, 'hex', 'utf8');
100
+ decrypted += decipher.final('utf8');
101
+ console.log(decrypted);
102
+ break;
103
+
104
+ case 'list':
105
+ const keys = Object.keys(vault);
106
+ if (keys.length === 0) {
107
+ console.log(`${C.dim}No secrets stored${C.reset}`);
108
+ } else {
109
+ console.log(`${C.cyan}🔐 Stored secrets (${keys.length}):${C.reset}`);
110
+ keys.forEach(k => console.log(` ${C.white}•${C.reset} ${k}`));
111
+ }
112
+ break;
113
+
114
+ case 'delete':
115
+ if (!key) {
116
+ console.log(`${C.red}✖ Usage: jos secrets delete <key>${C.reset}`);
117
+ process.exit(1);
118
+ }
119
+ if (vault[key]) {
120
+ delete vault[key];
121
+ fs.writeFileSync(vaultPath, JSON.stringify(vault, null, 2), { mode: 0o600 });
122
+ console.log(`${C.green}✓ Secret '${key}' deleted${C.reset}`);
123
+ } else {
124
+ console.log(`${C.yellow}⚠ Secret '${key}' not found${C.reset}`);
125
+ }
126
+ break;
127
+
128
+ default:
129
+ console.log(`${C.red}✖ Unknown action: ${action}${C.reset}`);
130
+ console.log(`${C.dim}Run 'jos secrets --help' for usage${C.reset}`);
131
+ process.exit(1);
132
+ }
133
+ } catch (e) {
134
+ console.log(`${C.red}✖ Error: ${e.message}${C.reset}`);
135
+ process.exit(1);
136
+ }
137
+ };
package/src/index.js ADDED
File without changes