@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.
- package/README.md +273 -35
- package/bin/jos +76 -0
- package/package.json +21 -49
- package/src/commands/get.js +245 -0
- package/src/commands/repo.js +139 -0
- package/src/commands/run.js +225 -0
- package/src/commands/secrets.js +137 -0
- package/src/index.js +0 -0
- package/src/serve.js +780 -0
- package/LICENSE +0 -20
- package/NOTICE +0 -4
- package/THIRD_PARTY_NOTICES.md +0 -11
- package/bin/jos.js +0 -36
- package/examples/env-check.json +0 -39
- package/lib/resolve.js +0 -32
- package/lib/run.js +0 -43
- package/lib/serve.js +0 -6
- package/lib/validate.js +0 -11
- package/schemas/jos.v0.3.1.schema.json +0 -64
|
@@ -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
|