i18ntk 2.3.8 → 2.5.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 +10 -6
- package/main/i18ntk-backup-class.js +35 -423
- package/main/manage/commands/BackupCommand.js +62 -62
- package/main/manage/commands/FixerCommand.js +97 -97
- package/main/manage/managers/DebugMenu.js +10 -9
- package/main/manage/services/SetupService.js +444 -462
- package/package.json +61 -32
- package/runtime/index.js +14 -8
- package/utils/admin-auth.js +594 -576
- package/utils/config-manager.js +72 -72
- package/utils/env-manager.js +117 -26
- package/utils/i18n-helper.js +50 -49
- package/utils/json-output.js +13 -12
- package/utils/logger.js +7 -6
- package/utils/npm-version-warning.js +12 -141
- package/utils/prompt-helper.js +44 -41
- package/utils/secure-errors.js +156 -154
- package/utils/security.js +235 -233
- package/utils/setup-enforcer.js +110 -109
- package/utils/terminal-icons.js +164 -163
- package/settings/i18ntk-config.json +0 -283
- package/utils/admin-pin.js +0 -520
- package/utils/arg-parser.js +0 -40
- package/utils/cli-args.js +0 -210
- package/utils/mini-commander.js +0 -179
- package/utils/missing-key-validator.js +0 -858
- package/utils/path-utils.js +0 -33
- package/utils/performance-optimizer.js +0 -246
- package/utils/prompt-new.js +0 -55
- package/utils/promptPin.js +0 -76
- package/utils/safe-json.js +0 -40
- package/utils/secure-backup.js +0 -340
- package/utils/security-check-improved.js +0 -393
- package/utils/security-config.js +0 -239
- package/utils/setup-validator.js +0 -717
- package/utils/ultra-performance-optimizer.js +0 -352
|
@@ -1,393 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* i18ntk Security Check Utility - IMPROVED VERSION
|
|
5
|
-
* Performs comprehensive security validation before build/publish
|
|
6
|
-
* Enhanced to intelligently distinguish between safe and dangerous requires
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const fs = require('fs');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const crypto = require('crypto');
|
|
12
|
-
|
|
13
|
-
class SecurityChecker {
|
|
14
|
-
constructor() {
|
|
15
|
-
this.issues = [];
|
|
16
|
-
this.warnings = [];
|
|
17
|
-
this.projectRoot = path.resolve(__dirname, '..');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
log(message, type = 'info') {
|
|
21
|
-
const timestamp = new Date().toISOString();
|
|
22
|
-
const colors = {
|
|
23
|
-
error: '\x1b[31m',
|
|
24
|
-
warning: '\x1b[33m',
|
|
25
|
-
success: '\x1b[32m',
|
|
26
|
-
info: '\x1b[36m',
|
|
27
|
-
reset: '\x1b[0m'
|
|
28
|
-
};
|
|
29
|
-
console.log(`${colors[type]}[${timestamp}] ${message}${colors.reset}`);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
addIssue(message, file = null, line = null) {
|
|
33
|
-
this.issues.push({ message, file, line, type: 'error' });
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
addWarning(message, file = null, line = null) {
|
|
37
|
-
this.warnings.push({ message, file, line, type: 'warning' });
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async checkFileExists(filePath) {
|
|
41
|
-
try {
|
|
42
|
-
await fs.promises.access(filePath);
|
|
43
|
-
return true;
|
|
44
|
-
} catch {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async readFile(filePath) {
|
|
50
|
-
try {
|
|
51
|
-
return await fs.promises.readFile(filePath, 'utf8');
|
|
52
|
-
} catch (error) {
|
|
53
|
-
this.addIssue(`Cannot read file: ${filePath}`, filePath);
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async checkPackageJson() {
|
|
59
|
-
this.log('Checking package.json security...');
|
|
60
|
-
|
|
61
|
-
const packageJsonPath = path.join(this.projectRoot, 'package.json');
|
|
62
|
-
const content = await this.readFile(packageJsonPath);
|
|
63
|
-
|
|
64
|
-
if (!content) return;
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
const pkg = JSON.parse(content);
|
|
68
|
-
|
|
69
|
-
// Check for dangerous scripts
|
|
70
|
-
const dangerousScripts = ['preinstall', 'postinstall', 'preuninstall', 'postuninstall'];
|
|
71
|
-
const scripts = pkg.scripts || {};
|
|
72
|
-
|
|
73
|
-
for (const script of dangerousScripts) {
|
|
74
|
-
if (scripts[script]) {
|
|
75
|
-
this.addWarning(`Potentially dangerous script found: ${script}`, packageJsonPath);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Check dependencies for known vulnerabilities (basic check)
|
|
80
|
-
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
81
|
-
for (const [dep, version] of Object.entries(allDeps || {})) {
|
|
82
|
-
if (version.includes('*') || version.includes('latest')) {
|
|
83
|
-
this.addWarning(`Unpinned dependency version: ${dep}@${version}`, packageJsonPath);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Verify security scripts exist
|
|
88
|
-
const requiredScripts = ['security:check', 'security:test', 'security:audit'];
|
|
89
|
-
for (const script of requiredScripts) {
|
|
90
|
-
if (!scripts[script]) {
|
|
91
|
-
this.addIssue(`Missing required security script: ${script}`, packageJsonPath);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
} catch (error) {
|
|
96
|
-
this.addIssue(`Invalid JSON in package.json: ${error.message}`, packageJsonPath);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async checkSecurityUtils() {
|
|
101
|
-
this.log('Checking SecurityUtils implementation...');
|
|
102
|
-
|
|
103
|
-
const securityUtilsPath = path.join(this.projectRoot, 'utils/security.js');
|
|
104
|
-
if (!(await this.checkFileExists(securityUtilsPath))) {
|
|
105
|
-
this.addIssue('SecurityUtils file not found', securityUtilsPath);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const content = await this.readFile(securityUtilsPath);
|
|
110
|
-
if (!content) return;
|
|
111
|
-
|
|
112
|
-
// Check for required security methods
|
|
113
|
-
const requiredMethods = [
|
|
114
|
-
'safeReadFileSync',
|
|
115
|
-
'safeExistsSync',
|
|
116
|
-
'safeWriteFileSync',
|
|
117
|
-
'validatePath',
|
|
118
|
-
'sanitizeInput',
|
|
119
|
-
'safeParseJSON'
|
|
120
|
-
];
|
|
121
|
-
|
|
122
|
-
for (const method of requiredMethods) {
|
|
123
|
-
if (!content.includes(method)) {
|
|
124
|
-
this.addIssue(`Missing security method: ${method}`, securityUtilsPath);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Check for dangerous code execution patterns in SecurityUtils itself.
|
|
129
|
-
// Direct fs usage is expected here because this module provides vetted wrappers.
|
|
130
|
-
const dangerousPatterns = [
|
|
131
|
-
/eval\s*\(/g,
|
|
132
|
-
/Function\s*\(/g
|
|
133
|
-
];
|
|
134
|
-
|
|
135
|
-
for (const pattern of dangerousPatterns) {
|
|
136
|
-
const matches = content.match(pattern);
|
|
137
|
-
if (matches) {
|
|
138
|
-
this.addWarning(`Potentially unsafe pattern found: ${pattern}`, securityUtilsPath);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
async checkSourceFiles() {
|
|
144
|
-
this.log('Checking source files for security issues...');
|
|
145
|
-
|
|
146
|
-
const sourceDirs = ['main', 'utils', 'scripts', 'settings'];
|
|
147
|
-
const excludeFiles = ['security.js', 'security-fixed.js', 'security-check.js', 'security-check-improved.js'];
|
|
148
|
-
|
|
149
|
-
for (const dir of sourceDirs) {
|
|
150
|
-
const dirPath = path.join(this.projectRoot, dir);
|
|
151
|
-
if (!(await this.checkFileExists(dirPath))) continue;
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
const files = await fs.promises.readdir(dirPath);
|
|
155
|
-
for (const file of files) {
|
|
156
|
-
if (!file.endsWith('.js') || excludeFiles.includes(file)) continue;
|
|
157
|
-
|
|
158
|
-
const filePath = path.join(dirPath, file);
|
|
159
|
-
const content = await this.readFile(filePath);
|
|
160
|
-
if (!content) continue;
|
|
161
|
-
|
|
162
|
-
await this.analyzeFileSecurity(filePath, content);
|
|
163
|
-
}
|
|
164
|
-
} catch (error) {
|
|
165
|
-
this.addIssue(`Cannot read directory: ${dirPath}`, dirPath);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
async analyzeFileSecurity(filePath, content) {
|
|
171
|
-
const lines = content.split('\n');
|
|
172
|
-
|
|
173
|
-
lines.forEach((line, index) => {
|
|
174
|
-
// Check for direct fs operations
|
|
175
|
-
if (line.includes('fs.readFileSync(') && !line.includes('SecurityUtils')) {
|
|
176
|
-
this.addIssue('Direct fs.readFileSync usage (use SecurityUtils.safeReadFileSync)', filePath, index + 1);
|
|
177
|
-
}
|
|
178
|
-
if (line.includes('fs.writeFileSync(') && !line.includes('SecurityUtils')) {
|
|
179
|
-
this.addIssue('Direct fs.writeFileSync usage (use SecurityUtils.safeWriteFileSync)', filePath, index + 1);
|
|
180
|
-
}
|
|
181
|
-
if (line.includes('fs.existsSync(') && !line.includes('SecurityUtils')) {
|
|
182
|
-
this.addIssue('Direct fs.existsSync usage (use SecurityUtils.safeExistsSync)', filePath, index + 1);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Check for dangerous patterns
|
|
186
|
-
if (line.includes('eval(') || line.includes('Function(')) {
|
|
187
|
-
this.addIssue('Dangerous code execution pattern detected', filePath, index + 1);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Check for unsafe require patterns - be more intelligent
|
|
191
|
-
if (line.includes('require(')) {
|
|
192
|
-
this.analyzeRequireStatement(line, filePath, index + 1);
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
analyzeRequireStatement(line, filePath, lineNumber) {
|
|
198
|
-
// Extract the require path
|
|
199
|
-
const requireMatch = line.match(/require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/);
|
|
200
|
-
if (!requireMatch) return;
|
|
201
|
-
|
|
202
|
-
const requirePath = requireMatch[1];
|
|
203
|
-
|
|
204
|
-
// Skip safe built-in modules
|
|
205
|
-
// Note: child_process is intentionally excluded to keep runtime zero-shell
|
|
206
|
-
const safeBuiltins = ['fs', 'path', 'crypto', 'os', 'util', 'events', 'stream', 'buffer', 'http', 'https', 'url', 'querystring'];
|
|
207
|
-
if (safeBuiltins.includes(requirePath)) {
|
|
208
|
-
return; // Safe built-in module
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Skip safe relative requires within project structure
|
|
212
|
-
if (requirePath.startsWith('../') || requirePath.startsWith('./')) {
|
|
213
|
-
// Check if it's going too far up (more than 2 levels)
|
|
214
|
-
const upLevels = (requirePath.match(/\.\.\//g) || []).length;
|
|
215
|
-
if (upLevels > 2) {
|
|
216
|
-
this.addWarning('Deep relative require (more than 2 levels up)', filePath, lineNumber);
|
|
217
|
-
}
|
|
218
|
-
// Otherwise, relative requires within project are generally safe
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Check for dynamic requires (variables)
|
|
223
|
-
if (requirePath.includes('${') || requirePath.includes('+') || requirePath.includes('variable')) {
|
|
224
|
-
this.addIssue('Dynamic require statement detected (potential security risk)', filePath, lineNumber);
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Check for absolute paths outside node_modules
|
|
229
|
-
if (requirePath.startsWith('/') && !requirePath.includes('node_modules')) {
|
|
230
|
-
this.addWarning('Absolute path require outside node_modules', filePath, lineNumber);
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Check for suspicious patterns
|
|
235
|
-
const suspiciousPatterns = [
|
|
236
|
-
/\.\./, // path traversal
|
|
237
|
-
/^~/, // home directory shorthand
|
|
238
|
-
/\$(HOME|USER)\b/, // shell env expansions
|
|
239
|
-
/^[a-z][a-z0-9+.-]*:/i // URL/protocol-like require targets
|
|
240
|
-
];
|
|
241
|
-
for (const pattern of suspiciousPatterns) {
|
|
242
|
-
if (pattern.test(requirePath)) {
|
|
243
|
-
this.addIssue(`Suspicious require path pattern: ${pattern}`, filePath, lineNumber);
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// If we get here, it's likely a safe npm package require
|
|
249
|
-
// No action needed for legitimate package requires
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
async checkFilePermissions() {
|
|
253
|
-
this.log('Checking file permissions...');
|
|
254
|
-
|
|
255
|
-
// POSIX permission checks are noisy/non-actionable on Windows.
|
|
256
|
-
if (process.platform === 'win32') {
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const criticalFiles = [
|
|
261
|
-
'utils/security.js',
|
|
262
|
-
'tests/security.test.js',
|
|
263
|
-
'package.json'
|
|
264
|
-
];
|
|
265
|
-
|
|
266
|
-
for (const file of criticalFiles) {
|
|
267
|
-
const filePath = path.join(this.projectRoot, file);
|
|
268
|
-
if (!(await this.checkFileExists(filePath))) {
|
|
269
|
-
this.addIssue(`Critical file not found: ${file}`, filePath);
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
try {
|
|
274
|
-
const stats = await fs.promises.stat(filePath);
|
|
275
|
-
const permissions = (stats.mode & parseInt('777', 8)).toString(8);
|
|
276
|
-
|
|
277
|
-
// Check if file is writable by group or others
|
|
278
|
-
if (permissions[1] !== '0' || permissions[2] !== '0') {
|
|
279
|
-
this.addWarning(`File has overly permissive permissions: ${file} (${permissions})`, filePath);
|
|
280
|
-
}
|
|
281
|
-
} catch (error) {
|
|
282
|
-
this.addIssue(`Cannot check permissions for: ${file}`, filePath);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
async checkDependencies() {
|
|
288
|
-
this.log('Checking for dependency vulnerabilities...');
|
|
289
|
-
|
|
290
|
-
const packageJsonPath = path.join(this.projectRoot, 'package.json');
|
|
291
|
-
const content = await this.readFile(packageJsonPath);
|
|
292
|
-
|
|
293
|
-
if (!content) return;
|
|
294
|
-
|
|
295
|
-
try {
|
|
296
|
-
const pkg = JSON.parse(content);
|
|
297
|
-
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
298
|
-
|
|
299
|
-
// Check for zero dependencies claim
|
|
300
|
-
if (Object.keys(allDeps || {}).length > 0) {
|
|
301
|
-
this.addWarning('Package claims zero dependencies but has dependencies in package.json');
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Check for suspicious dependency names
|
|
305
|
-
const suspiciousDeps = ['malicious', 'hack', 'exploit', 'trojan'];
|
|
306
|
-
for (const dep of Object.keys(allDeps || {})) {
|
|
307
|
-
for (const suspicious of suspiciousDeps) {
|
|
308
|
-
if (dep.toLowerCase().includes(suspicious)) {
|
|
309
|
-
this.addIssue(`Suspicious dependency name: ${dep}`);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
} catch (error) {
|
|
314
|
-
this.addIssue(`Cannot parse package.json: ${error.message}`, packageJsonPath);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
async run() {
|
|
319
|
-
this.log('Starting i18ntk Security Check (IMPROVED VERSION)...', 'info');
|
|
320
|
-
|
|
321
|
-
try {
|
|
322
|
-
await this.checkPackageJson();
|
|
323
|
-
await this.checkSecurityUtils();
|
|
324
|
-
await this.checkSourceFiles();
|
|
325
|
-
await this.checkFilePermissions();
|
|
326
|
-
await this.checkDependencies();
|
|
327
|
-
|
|
328
|
-
// Generate report
|
|
329
|
-
this.generateReport();
|
|
330
|
-
|
|
331
|
-
// Final status with detailed counts
|
|
332
|
-
const totalIssues = this.issues.length + this.warnings.length;
|
|
333
|
-
if (this.issues.length > 0) {
|
|
334
|
-
this.log(`Security check FAILED: ${this.issues.length} critical issues, ${this.warnings.length} warnings found`, 'error');
|
|
335
|
-
this.log(`Total: ${totalIssues} issues detected`, 'error');
|
|
336
|
-
// Ensure output is flushed before exit
|
|
337
|
-
await new Promise(resolve => setImmediate(resolve));
|
|
338
|
-
process.exit(1);
|
|
339
|
-
} else if (this.warnings.length > 0) {
|
|
340
|
-
this.log('Security check PASSED: No critical issues found', 'success');
|
|
341
|
-
this.log(`${this.warnings.length} warnings found (non-blocking)`, 'warning');
|
|
342
|
-
this.log(`Total: ${totalIssues} issues detected`, 'warning');
|
|
343
|
-
} else {
|
|
344
|
-
this.log('Security check PASSED: No issues found', 'success');
|
|
345
|
-
}
|
|
346
|
-
} catch (error) {
|
|
347
|
-
this.log(`Security check failed with error: ${error.message}`, 'error');
|
|
348
|
-
console.error('Stack trace:', error.stack);
|
|
349
|
-
process.exit(1);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
generateReport() {
|
|
354
|
-
if (this.issues.length === 0 && this.warnings.length === 0) {
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
console.log('\n=== SECURITY CHECK REPORT (IMPROVED) ===\n');
|
|
359
|
-
|
|
360
|
-
if (this.issues.length > 0) {
|
|
361
|
-
console.log('🔴 CRITICAL ISSUES:');
|
|
362
|
-
this.issues.forEach(issue => {
|
|
363
|
-
console.log(` • ${issue.message}`);
|
|
364
|
-
if (issue.file) {
|
|
365
|
-
console.log(` File: ${issue.file}${issue.line ? `:${issue.line}` : ''}`);
|
|
366
|
-
}
|
|
367
|
-
});
|
|
368
|
-
console.log('');
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
if (this.warnings.length > 0) {
|
|
372
|
-
console.log('🟡 WARNINGS:');
|
|
373
|
-
this.warnings.forEach(warning => {
|
|
374
|
-
console.log(` • ${warning.message}`);
|
|
375
|
-
if (warning.file) {
|
|
376
|
-
console.log(` File: ${warning.file}${warning.line ? `:${warning.line}` : ''}`);
|
|
377
|
-
}
|
|
378
|
-
});
|
|
379
|
-
console.log('');
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Run security check if called directly
|
|
385
|
-
if (require.main === module) {
|
|
386
|
-
const checker = new SecurityChecker();
|
|
387
|
-
checker.run().catch(error => {
|
|
388
|
-
console.error('Security check failed:', error);
|
|
389
|
-
process.exit(1);
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
module.exports = SecurityChecker;
|
package/utils/security-config.js
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Security Configuration Utility
|
|
3
|
-
* Provides secure configuration management for the i18n Management Toolkit
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const crypto = require('crypto');
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
|
|
10
|
-
class SecurityConfig {
|
|
11
|
-
constructor() {
|
|
12
|
-
this.configDir = path.resolve(__dirname, '..');
|
|
13
|
-
this.configPath = path.join(this.configDir, 'security-config.json');
|
|
14
|
-
this.securityDefaults = {
|
|
15
|
-
pin: {
|
|
16
|
-
minLength: 4,
|
|
17
|
-
maxLength: 32,
|
|
18
|
-
requireStrongPin: true,
|
|
19
|
-
maxAttempts: 3,
|
|
20
|
-
lockDuration: 300000, // 5 minutes
|
|
21
|
-
algorithm: 'scrypt'
|
|
22
|
-
},
|
|
23
|
-
encryption: {
|
|
24
|
-
algorithm: 'aes-256-gcm',
|
|
25
|
-
keyLength: 32,
|
|
26
|
-
ivLength: 12,
|
|
27
|
-
authTagLength: 16
|
|
28
|
-
},
|
|
29
|
-
audit: {
|
|
30
|
-
enabled: true,
|
|
31
|
-
logLevel: 'info',
|
|
32
|
-
retentionDays: 30
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Generate secure configuration with settings-based configuration
|
|
39
|
-
*/
|
|
40
|
-
generateSecureConfig(settings = {}) {
|
|
41
|
-
const config = {
|
|
42
|
-
...this.securityDefaults,
|
|
43
|
-
secrets: {
|
|
44
|
-
adminPin: settings.adminPin || null,
|
|
45
|
-
encryptionKey: settings.encryptionKey || this.generateSecureKey(),
|
|
46
|
-
jwtSecret: settings.jwtSecret || this.generateSecureKey()
|
|
47
|
-
},
|
|
48
|
-
security: {
|
|
49
|
-
...this.securityDefaults,
|
|
50
|
-
environment: 'production',
|
|
51
|
-
disableWeakPinWarning: settings.disableWeakPinWarning === true
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
return config;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Generate cryptographically secure random key
|
|
60
|
-
*/
|
|
61
|
-
generateSecureKey(length = 32) {
|
|
62
|
-
return crypto.randomBytes(length).toString('hex');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Validate security configuration
|
|
67
|
-
*/
|
|
68
|
-
validateSecurityConfig(config) {
|
|
69
|
-
const errors = [];
|
|
70
|
-
|
|
71
|
-
// PIN validation
|
|
72
|
-
if (config.secrets?.adminPin) {
|
|
73
|
-
if (config.secrets.adminPin.length < config.security.pin.minLength) {
|
|
74
|
-
errors.push(`PIN must be at least ${config.security.pin.minLength} characters`);
|
|
75
|
-
}
|
|
76
|
-
if (this.isWeakPin(config.secrets.adminPin)) {
|
|
77
|
-
errors.push('PIN appears to be weak - consider using a stronger PIN');
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Encryption key validation
|
|
82
|
-
if (config.secrets?.encryptionKey) {
|
|
83
|
-
if (config.secrets.encryptionKey.length < 64) { // 32 bytes hex encoded
|
|
84
|
-
errors.push('Encryption key must be at least 32 bytes (64 hex chars)');
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
valid: errors.length === 0,
|
|
90
|
-
errors
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Check if PIN is weak
|
|
96
|
-
*/
|
|
97
|
-
isWeakPin(pin) {
|
|
98
|
-
const weakPatterns = [
|
|
99
|
-
/^\d{1,4}$/, // Only 1-4 digits
|
|
100
|
-
/^(.)\1+$/, // All same characters
|
|
101
|
-
'1234', '0000', '1111', '2222', '3333', '4444',
|
|
102
|
-
'5555', '6666', '7777', '8888', '9999', 'password',
|
|
103
|
-
'admin', 'root', '123456', '654321', 'qwerty'
|
|
104
|
-
];
|
|
105
|
-
|
|
106
|
-
return weakPatterns.some(pattern => {
|
|
107
|
-
if (typeof pattern === 'string') {
|
|
108
|
-
return pin.toLowerCase().includes(pattern);
|
|
109
|
-
}
|
|
110
|
-
return pattern.test(pin);
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Create secure configuration file
|
|
116
|
-
*/
|
|
117
|
-
createSecureConfig() {
|
|
118
|
-
const config = this.generateSecureConfig();
|
|
119
|
-
const validation = this.validateSecurityConfig(config);
|
|
120
|
-
|
|
121
|
-
if (!validation.valid) {
|
|
122
|
-
throw new Error(`Invalid security configuration: ${validation.errors.join(', ')}`);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Ensure config directory exists
|
|
126
|
-
const configDir = path.dirname(this.configPath);
|
|
127
|
-
if (!SecurityUtils.safeExistsSync(configDir)) {
|
|
128
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Remove actual secrets from config file (use env vars)
|
|
132
|
-
const safeConfig = {
|
|
133
|
-
...config,
|
|
134
|
-
secrets: {
|
|
135
|
-
adminPin: config.secrets.adminPin ? '***' : null,
|
|
136
|
-
encryptionKey: '***',
|
|
137
|
-
jwtSecret: '***'
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
SecurityUtils.safeWriteFileSync(this.configPath, JSON.stringify(safeConfig, null, 2));
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
configPath: this.configPath,
|
|
145
|
-
validation
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Load and validate existing configuration
|
|
151
|
-
*/
|
|
152
|
-
loadSecurityConfig() {
|
|
153
|
-
if (!SecurityUtils.safeExistsSync(this.configPath)) {
|
|
154
|
-
return this.createSecureConfig();
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
const config = JSON.parse(SecurityUtils.safeReadFileSync(this.configPath, path.dirname(this.configPath), 'utf8'));
|
|
159
|
-
const validation = this.validateSecurityConfig(config);
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
config,
|
|
163
|
-
validation,
|
|
164
|
-
configPath: this.configPath
|
|
165
|
-
};
|
|
166
|
-
} catch (error) {
|
|
167
|
-
throw new Error(`Failed to load security configuration: ${error.message}`);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Rotate encryption keys (advanced operation)
|
|
173
|
-
*/
|
|
174
|
-
rotateEncryptionKeys() {
|
|
175
|
-
console.warn('⚠️ Key rotation is an advanced operation. Ensure you have backups.');
|
|
176
|
-
|
|
177
|
-
const newKey = this.generateSecureKey();
|
|
178
|
-
const timestamp = new Date().toISOString();
|
|
179
|
-
|
|
180
|
-
// Create backup of old config
|
|
181
|
-
if (SecurityUtils.safeExistsSync(this.configPath)) {
|
|
182
|
-
fs.copyFileSync(this.configPath, `${this.configPath}.backup.${timestamp}`);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Update configuration with new keys
|
|
186
|
-
const config = this.generateSecureConfig();
|
|
187
|
-
config.secrets.encryptionKey = newKey;
|
|
188
|
-
config.secrets.jwtSecret = this.generateSecureKey();
|
|
189
|
-
config.lastKeyRotation = timestamp;
|
|
190
|
-
|
|
191
|
-
this.createSecureConfig();
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
oldKeyBackup: `${this.configPath}.backup.${timestamp}`,
|
|
195
|
-
newKeysGenerated: true,
|
|
196
|
-
timestamp
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Export for benchmark usage
|
|
202
|
-
async function validateConfiguration(config) {
|
|
203
|
-
const validator = new SecurityConfig();
|
|
204
|
-
|
|
205
|
-
// Simulate validation processing
|
|
206
|
-
const start = Date.now();
|
|
207
|
-
|
|
208
|
-
// Basic validation for benchmark purposes
|
|
209
|
-
const errors = [];
|
|
210
|
-
|
|
211
|
-
if (!config.languages || !Array.isArray(config.languages) || config.languages.length === 0) {
|
|
212
|
-
errors.push('languages must be a non-empty array');
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (!config.sourceDir || typeof config.sourceDir !== 'string') {
|
|
216
|
-
errors.push('sourceDir must be a string');
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
if (config.adminPin && typeof config.adminPin !== 'string') {
|
|
220
|
-
errors.push('adminPin must be a string');
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Simulate processing delay based on config complexity
|
|
224
|
-
const complexity = (config.languages?.length || 0) * 2;
|
|
225
|
-
await new Promise(resolve => setTimeout(resolve, complexity));
|
|
226
|
-
|
|
227
|
-
const end = Date.now();
|
|
228
|
-
|
|
229
|
-
return {
|
|
230
|
-
valid: errors.length === 0,
|
|
231
|
-
errors,
|
|
232
|
-
validationTime: end - start
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
module.exports = {
|
|
237
|
-
SecurityConfig,
|
|
238
|
-
validateConfiguration
|
|
239
|
-
};
|