i18ntk 1.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.
Files changed (86) hide show
  1. package/CHANGELOG.md +401 -0
  2. package/LICENSE +21 -0
  3. package/README.md +507 -0
  4. package/dev/README.md +37 -0
  5. package/dev/debug/README.md +30 -0
  6. package/dev/debug/complete-console-translations.js +295 -0
  7. package/dev/debug/console-key-checker.js +408 -0
  8. package/dev/debug/console-translations.js +335 -0
  9. package/dev/debug/debugger.js +408 -0
  10. package/dev/debug/export-missing-keys.js +432 -0
  11. package/dev/debug/final-normalize.js +236 -0
  12. package/dev/debug/find-extra-keys.js +68 -0
  13. package/dev/debug/normalize-locales.js +153 -0
  14. package/dev/debug/refactor-locales.js +240 -0
  15. package/dev/debug/reorder-locales.js +85 -0
  16. package/dev/debug/replace-hardcoded-console.js +378 -0
  17. package/docs/INSTALLATION.md +449 -0
  18. package/docs/README.md +222 -0
  19. package/docs/TODO_ROADMAP.md +279 -0
  20. package/docs/api/API_REFERENCE.md +377 -0
  21. package/docs/api/COMPONENTS.md +492 -0
  22. package/docs/api/CONFIGURATION.md +651 -0
  23. package/docs/api/NPM_PUBLISHING_GUIDE.md +434 -0
  24. package/docs/debug/DEBUG_README.md +30 -0
  25. package/docs/debug/DEBUG_TOOLS.md +494 -0
  26. package/docs/development/AGENTS.md +351 -0
  27. package/docs/development/DEVELOPMENT_RULES.md +165 -0
  28. package/docs/development/DEV_README.md +37 -0
  29. package/docs/release-notes/RELEASE_NOTES_v1.0.0.md +173 -0
  30. package/docs/release-notes/RELEASE_NOTES_v1.6.0.md +141 -0
  31. package/docs/release-notes/RELEASE_NOTES_v1.6.1.md +185 -0
  32. package/docs/release-notes/RELEASE_NOTES_v1.6.3.md +199 -0
  33. package/docs/reports/ANALYSIS_README.md +17 -0
  34. package/docs/reports/CONSOLE_MISMATCH_BUG_REPORT_v1.5.0.md +181 -0
  35. package/docs/reports/SIZING_README.md +18 -0
  36. package/docs/reports/SUMMARY_README.md +18 -0
  37. package/docs/reports/TRANSLATION_BUG_REPORT_v1.5.0.md +129 -0
  38. package/docs/reports/USAGE_README.md +18 -0
  39. package/docs/reports/VALIDATION_README.md +18 -0
  40. package/locales/de/auth.json +3 -0
  41. package/locales/de/common.json +16 -0
  42. package/locales/de/pagination.json +6 -0
  43. package/locales/en/auth.json +3 -0
  44. package/locales/en/common.json +16 -0
  45. package/locales/en/pagination.json +6 -0
  46. package/locales/es/auth.json +3 -0
  47. package/locales/es/common.json +16 -0
  48. package/locales/es/pagination.json +6 -0
  49. package/locales/fr/auth.json +3 -0
  50. package/locales/fr/common.json +16 -0
  51. package/locales/fr/pagination.json +6 -0
  52. package/locales/ru/auth.json +3 -0
  53. package/locales/ru/common.json +16 -0
  54. package/locales/ru/pagination.json +6 -0
  55. package/main/i18ntk-analyze.js +625 -0
  56. package/main/i18ntk-autorun.js +461 -0
  57. package/main/i18ntk-complete.js +494 -0
  58. package/main/i18ntk-init.js +686 -0
  59. package/main/i18ntk-manage.js +848 -0
  60. package/main/i18ntk-sizing.js +557 -0
  61. package/main/i18ntk-summary.js +671 -0
  62. package/main/i18ntk-usage.js +1282 -0
  63. package/main/i18ntk-validate.js +762 -0
  64. package/main/ui-i18n.js +332 -0
  65. package/package.json +152 -0
  66. package/scripts/fix-missing-translation-keys.js +214 -0
  67. package/scripts/verify-package.js +168 -0
  68. package/ui-locales/de.json +637 -0
  69. package/ui-locales/en.json +688 -0
  70. package/ui-locales/es.json +637 -0
  71. package/ui-locales/fr.json +637 -0
  72. package/ui-locales/ja.json +637 -0
  73. package/ui-locales/ru.json +637 -0
  74. package/ui-locales/zh.json +637 -0
  75. package/utils/admin-auth.js +317 -0
  76. package/utils/admin-cli.js +353 -0
  77. package/utils/admin-pin.js +409 -0
  78. package/utils/detect-language-mismatches.js +454 -0
  79. package/utils/i18n-helper.js +128 -0
  80. package/utils/maintain-language-purity.js +433 -0
  81. package/utils/native-translations.js +478 -0
  82. package/utils/security.js +384 -0
  83. package/utils/test-complete-system.js +356 -0
  84. package/utils/test-console-i18n.js +402 -0
  85. package/utils/translate-mismatches.js +571 -0
  86. package/utils/validate-language-purity.js +531 -0
@@ -0,0 +1,384 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const crypto = require('crypto');
4
+ const SettingsManager = require('../settings/settings-manager');
5
+ const { t } = require('./i18n-helper');
6
+
7
+ /**
8
+ * Security utility module for i18nTK
9
+ * Provides secure file operations, path validation, and input sanitization
10
+ * to prevent path traversal, code injection, and other security vulnerabilities
11
+ */
12
+ class SecurityUtils {
13
+ /**
14
+ * Validates and sanitizes file paths to prevent path traversal attacks
15
+ * @param {string} inputPath - The input path to validate
16
+ * @param {string} basePath - The base path that the input should be within (optional)
17
+ * @returns {string|null} - Sanitized path or null if invalid
18
+ */
19
+ static validatePath(filePath, basePath = process.cwd()) {
20
+ try {
21
+ if (!filePath || typeof filePath !== 'string') {
22
+ SecurityUtils.logSecurityEvent('path_validation_failed', {
23
+ inputPath: filePath,
24
+ reason: 'Invalid input type'
25
+ });
26
+ return null;
27
+ }
28
+
29
+ // Sanitize the input path
30
+ const sanitizedPath = path.normalize(filePath).replace(/\.\./g, '');
31
+
32
+ // Resolve the full path
33
+ const resolvedPath = path.resolve(basePath, sanitizedPath);
34
+
35
+ // Check if the resolved path is within the base path
36
+ const relativePath = path.relative(basePath, resolvedPath);
37
+ if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
38
+ SecurityUtils.logSecurityEvent('path_traversal_attempt', {
39
+ inputPath: filePath,
40
+ resolvedPath,
41
+ basePath
42
+ });
43
+ return null;
44
+ }
45
+
46
+ SecurityUtils.logSecurityEvent('path_validated', {
47
+ inputPath: filePath,
48
+ resolvedPath
49
+ });
50
+
51
+ return resolvedPath;
52
+ } catch (error) {
53
+ SecurityUtils.logSecurityEvent('path_validation_error', {
54
+ inputPath: filePath,
55
+ error: error.message
56
+ });
57
+ return null;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Safely reads a file with path validation and error handling
63
+ * @param {string} filePath - Path to the file
64
+ * @param {string} basePath - Base path for validation
65
+ * @param {string} encoding - File encoding (default: 'utf8')
66
+ * @returns {Promise<string|null>} - File content or null if error
67
+ */
68
+ static async safeReadFile(filePath, basePath, encoding = 'utf8') {
69
+ const validatedPath = this.validatePath(filePath, basePath);
70
+ if (!validatedPath) {
71
+ return null;
72
+ }
73
+
74
+ try {
75
+ // Check if file exists and is readable
76
+ await fs.promises.access(validatedPath, fs.constants.R_OK);
77
+
78
+ // Read file with size limit (10MB max)
79
+ const stats = await fs.promises.stat(validatedPath);
80
+ if (stats.size > 10 * 1024 * 1024) {
81
+ console.warn(`Security: File too large: ${validatedPath}`);
82
+ return null;
83
+ }
84
+
85
+ return await fs.promises.readFile(validatedPath, encoding);
86
+ } catch (error) {
87
+ console.warn(`Security: File read error: ${error.message}`);
88
+ return null;
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Safely reads a file synchronously with path validation and error handling
94
+ * @param {string} filePath - Path to the file
95
+ * @param {string} basePath - Base path for validation
96
+ * @param {string} encoding - File encoding (default: 'utf8')
97
+ * @returns {string|null} - File content or null if error
98
+ */
99
+ static safeReadFileSync(filePath, basePath, encoding = 'utf8') {
100
+ const validatedPath = this.validatePath(filePath, basePath);
101
+ if (!validatedPath) {
102
+ return null;
103
+ }
104
+
105
+ try {
106
+ // Check if file exists and is readable
107
+ fs.accessSync(validatedPath, fs.constants.R_OK);
108
+
109
+ // Read file with size limit (10MB max)
110
+ const stats = fs.statSync(validatedPath);
111
+ if (stats.size > 10 * 1024 * 1024) {
112
+ console.warn(`Security: File too large: ${validatedPath}`);
113
+ return null;
114
+ }
115
+
116
+ return fs.readFileSync(validatedPath, encoding);
117
+ } catch (error) {
118
+ console.warn(`Security: File read error: ${error.message}`);
119
+ return null;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Safely writes a file with path validation and error handling
125
+ * @param {string} filePath - Path to the file
126
+ * @param {string} content - Content to write
127
+ * @param {string} basePath - Base path for validation
128
+ * @param {string} encoding - File encoding (default: 'utf8')
129
+ * @returns {Promise<boolean>} - Success status
130
+ */
131
+ static async safeWriteFile(filePath, content, basePath, encoding = 'utf8') {
132
+ const validatedPath = this.validatePath(filePath, basePath);
133
+ if (!validatedPath) {
134
+ return false;
135
+ }
136
+
137
+ try {
138
+ // Validate content size (10MB max)
139
+ if (typeof content === 'string' && content.length > 10 * 1024 * 1024) {
140
+ console.warn(`Security: Content too large for file: ${validatedPath}`);
141
+ return false;
142
+ }
143
+
144
+ // Ensure directory exists
145
+ const dir = path.dirname(validatedPath);
146
+ await fs.promises.mkdir(dir, { recursive: true });
147
+
148
+ // Write file with proper permissions
149
+ await fs.promises.writeFile(validatedPath, content, { encoding, mode: 0o644 });
150
+ return true;
151
+ } catch (error) {
152
+ console.warn(`Security: File write error: ${error.message}`);
153
+ return false;
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Safely parses JSON with error handling and validation
159
+ * @param {string} jsonString - JSON string to parse
160
+ * @param {number} maxSize - Maximum allowed size (default: 1MB)
161
+ * @returns {object|null} - Parsed object or null if error
162
+ */
163
+ static safeParseJSON(jsonString, maxSize = 1024 * 1024) {
164
+ if (!jsonString || typeof jsonString !== 'string') {
165
+ return null;
166
+ }
167
+
168
+ if (jsonString.length > maxSize) {
169
+ console.warn('Security: JSON string too large');
170
+ return null;
171
+ }
172
+
173
+ try {
174
+ return JSON.parse(jsonString);
175
+ } catch (error) {
176
+ console.warn(`Security: JSON parse error: ${error.message}`);
177
+ return null;
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Sanitizes user input to prevent injection attacks
183
+ * @param {string} input - User input to sanitize
184
+ * @param {object} options - Sanitization options
185
+ * @returns {string} - Sanitized input
186
+ */
187
+ static sanitizeInput(input, options = {}) {
188
+ if (!input || typeof input !== 'string') {
189
+ return '';
190
+ }
191
+
192
+ const {
193
+ allowedChars = /^[a-zA-Z0-9\s\-_\.\,\!\?\(\)\[\]\{\}\:;"']+$/,
194
+ maxLength = 1000,
195
+ removeHTML = true,
196
+ removeScripts = true
197
+ } = options;
198
+
199
+ let sanitized = input.trim();
200
+
201
+ // Limit length
202
+ if (sanitized.length > maxLength) {
203
+ sanitized = sanitized.substring(0, maxLength);
204
+ }
205
+
206
+ // Remove HTML tags if requested
207
+ if (removeHTML) {
208
+ sanitized = sanitized.replace(/<[^>]*>/g, '');
209
+ }
210
+
211
+ // Remove script-like content
212
+ if (removeScripts) {
213
+ sanitized = sanitized.replace(/javascript:/gi, '');
214
+ sanitized = sanitized.replace(/on\w+\s*=/gi, '');
215
+ sanitized = sanitized.replace(/eval\s*\(/gi, '');
216
+ sanitized = sanitized.replace(/function\s*\(/gi, '');
217
+ }
218
+
219
+ // Check against allowed characters
220
+ if (!allowedChars.test(sanitized)) {
221
+ console.warn('Security: Input contains disallowed characters');
222
+ // Remove disallowed characters
223
+ sanitized = sanitized.replace(/[^a-zA-Z0-9\s\-_\.\,\!\?\(\)\[\]\{\}\:;"']/g, '');
224
+ }
225
+
226
+ return sanitized;
227
+ }
228
+
229
+ /**
230
+ * Validates command line arguments
231
+ * @param {object} args - Command line arguments
232
+ * @returns {object} - Validated arguments
233
+ */
234
+ static async validateCommandArgs(args) {
235
+ const validatedArgs = {};
236
+ const allowedArgs = [
237
+ 'source-dir', 'i18n-dir', 'output-dir', 'output-report',
238
+ 'help', 'language', 'strict-mode', 'exclude-files'
239
+ ];
240
+
241
+ for (const [key, value] of Object.entries(args)) {
242
+ if (allowedArgs.includes(key)) {
243
+ validatedArgs[key] = value;
244
+ } else {
245
+ console.warn(`${t('hardcodedTexts.securityUnknownCommandArg')}: ${key}`);
246
+ }
247
+ }
248
+
249
+ return validatedArgs;
250
+ }
251
+
252
+ /**
253
+ * Validates configuration object
254
+ * @param {object} config - Configuration object
255
+ * @returns {object|null} - Validated configuration or null if invalid
256
+ */
257
+ static validateConfig(config) {
258
+ if (!config || typeof config !== 'object') {
259
+ return null;
260
+ }
261
+
262
+ const validatedConfig = {};
263
+ const allowedKeys = [
264
+ 'sourceDir', 'outputDir', 'defaultLanguage', 'supportedLanguages',
265
+ 'filePattern', 'excludePatterns', 'reportFormat', 'logLevel',
266
+ 'i18nDir', 'sourceLanguage', 'excludeDirs', 'includeExtensions',
267
+ 'translationPatterns', 'notTranslatedMarker', 'excludeFiles', 'strictMode'
268
+ ];
269
+
270
+ for (const [key, value] of Object.entries(config)) {
271
+ if (!allowedKeys.includes(key)) {
272
+ console.warn(`${t('hardcodedTexts.securityUnknownConfigKey')}: ${key}`);
273
+ continue;
274
+ }
275
+
276
+ // Validate specific config values
277
+ switch (key) {
278
+ case 'sourceDir':
279
+ case 'outputDir':
280
+ if (typeof value === 'string') {
281
+ // Basic path validation - will be further validated when used
282
+ validatedConfig[key] = this.sanitizeInput(value, {
283
+ allowedChars: /^[a-zA-Z0-9\-_\.\,\/\\\:]+$/,
284
+ maxLength: 500
285
+ });
286
+ }
287
+ break;
288
+ case 'supportedLanguages':
289
+ if (Array.isArray(value)) {
290
+ validatedConfig[key] = value.filter(lang =>
291
+ typeof lang === 'string' && /^[a-z]{2}(-[A-Z]{2})?$/.test(lang)
292
+ );
293
+ }
294
+ break;
295
+ case 'defaultLanguage':
296
+ if (typeof value === 'string' && /^[a-z]{2}(-[A-Z]{2})?$/.test(value)) {
297
+ validatedConfig[key] = value;
298
+ }
299
+ break;
300
+ default:
301
+ if (typeof value === 'string') {
302
+ validatedConfig[key] = this.sanitizeInput(value);
303
+ } else if (typeof value === 'boolean' || typeof value === 'number') {
304
+ validatedConfig[key] = value;
305
+ }
306
+ }
307
+ }
308
+
309
+ return validatedConfig;
310
+ }
311
+
312
+ /**
313
+ * Generates a secure hash for file integrity checking
314
+ * @param {string} content - Content to hash
315
+ * @returns {string} - SHA-256 hash
316
+ */
317
+ static generateHash(content) {
318
+ return crypto.createHash('sha256').update(content).digest('hex');
319
+ }
320
+
321
+ /**
322
+ * Checks if a file path is safe for operations
323
+ * @param {string} filePath - File path to check
324
+ * @returns {boolean} - Whether the path is safe
325
+ */
326
+ static isSafePath(filePath) {
327
+ if (!filePath || typeof filePath !== 'string') {
328
+ return false;
329
+ }
330
+
331
+ // Check for dangerous patterns
332
+ const dangerousPatterns = [
333
+ /\.\./, // Parent directory traversal
334
+ /^\//, // Absolute path (Unix)
335
+ /^[A-Z]:\\/, // Absolute path (Windows)
336
+ /~/, // Home directory
337
+ /\$\{/, // Variable expansion
338
+ /`/, // Command substitution
339
+ /\|/, // Pipe
340
+ /;/, // Command separator
341
+ /&/, // Background process
342
+ />/, // Redirect
343
+ /</ // Redirect
344
+ ];
345
+
346
+ return !dangerousPatterns.some(pattern => pattern.test(filePath));
347
+ }
348
+
349
+ /**
350
+ * Logs security events for monitoring
351
+ * @param {string} event - Security event description
352
+ * @param {string} level - Log level (info, warn, error)
353
+ * @param {object} details - Additional details
354
+ */
355
+ static logSecurityEvent(event, level = 'info', details = {}) {
356
+ const timestamp = new Date().toISOString();
357
+ const logEntry = {
358
+ timestamp,
359
+ level,
360
+ event,
361
+ details: {
362
+ ...details,
363
+ pid: process.pid,
364
+ nodeVersion: process.version
365
+ }
366
+ };
367
+
368
+ // Only show security logs if debug mode is enabled and showSecurityLogs is true
369
+ try {
370
+ const settingsManager = SettingsManager;
371
+ if (settingsManager.shouldShowSecurityLogs()) {
372
+ console.log(`[SECURITY ${level.toUpperCase()}] ${timestamp}: ${event}`, details);
373
+ }
374
+ } catch (error) {
375
+ // Fallback: if settings can't be loaded, don't show security logs to maintain clean UI
376
+ // Only log critical security events in this case
377
+ if (event.includes('CRITICAL') || event.includes('BREACH') || event.includes('ATTACK')) {
378
+ console.log(`[SECURITY ALERT] ${timestamp}: ${event}`, details);
379
+ }
380
+ }
381
+ }
382
+ }
383
+
384
+ module.exports = SecurityUtils;