i18ntk 2.1.0 → 2.2.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 (53) hide show
  1. package/README.md +10 -6
  2. package/main/i18ntk-analyze.js +63 -63
  3. package/main/i18ntk-complete.js +75 -74
  4. package/main/i18ntk-fixer.js +3 -3
  5. package/main/i18ntk-init.js +5 -5
  6. package/main/i18ntk-scanner.js +2 -2
  7. package/main/i18ntk-sizing.js +35 -35
  8. package/main/i18ntk-summary.js +4 -4
  9. package/main/i18ntk-ui.js +95 -96
  10. package/main/i18ntk-usage.js +14 -14
  11. package/main/i18ntk-validate.js +6 -5
  12. package/main/manage/commands/AnalyzeCommand.js +67 -67
  13. package/main/manage/commands/FixerCommand.js +2 -2
  14. package/main/manage/commands/ScannerCommand.js +2 -2
  15. package/main/manage/commands/ValidateCommand.js +9 -9
  16. package/main/manage/index.js +7 -4
  17. package/main/manage/managers/LanguageMenu.js +7 -2
  18. package/main/manage/services/UsageService.js +7 -7
  19. package/package.json +20 -41
  20. package/settings/settings-cli.js +3 -3
  21. package/ui-locales/de.json +12 -11
  22. package/ui-locales/en.json +12 -11
  23. package/ui-locales/es.json +12 -11
  24. package/ui-locales/fr.json +12 -11
  25. package/ui-locales/ja.json +12 -11
  26. package/ui-locales/ru.json +12 -11
  27. package/ui-locales/zh.json +12 -11
  28. package/utils/i18n-helper.js +161 -166
  29. package/{scripts → utils}/locale-optimizer.js +61 -60
  30. package/main/i18ntk-go.js +0 -283
  31. package/main/i18ntk-java.js +0 -380
  32. package/main/i18ntk-js.js +0 -512
  33. package/main/i18ntk-manage.js +0 -1694
  34. package/main/i18ntk-php.js +0 -462
  35. package/main/i18ntk-py.js +0 -379
  36. package/main/i18ntk-settings.js +0 -23
  37. package/main/manage/index-fixed.js +0 -1447
  38. package/scripts/build-lite.js +0 -279
  39. package/scripts/deprecate-versions.js +0 -317
  40. package/scripts/export-translations.js +0 -84
  41. package/scripts/fix-all-i18n.js +0 -236
  42. package/scripts/fix-and-purify-i18n.js +0 -233
  43. package/scripts/fix-locale-control-chars.js +0 -110
  44. package/scripts/lint-locales.js +0 -80
  45. package/scripts/prepublish-dev.js +0 -221
  46. package/scripts/prepublish.js +0 -362
  47. package/scripts/security-check.js +0 -117
  48. package/scripts/sync-translations.js +0 -151
  49. package/scripts/sync-ui-locales.js +0 -20
  50. package/scripts/validate-all-translations.js +0 -195
  51. package/scripts/verify-deprecations.js +0 -157
  52. package/scripts/verify-translations.js +0 -63
  53. package/utils/security-fixed.js +0 -609
@@ -1,221 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * DEVELOPMENT-ONLY Prepublish Script
5
- * This script is for development use only and should NEVER be included in production packages
6
- * It provides additional development validation and cleanup functionality
7
- */
8
-
9
- const fs = require('fs');
10
- const path = require('path');
11
- const SecurityUtils = require('../utils/security');
12
-
13
- class DevelopmentPrepublishCleaner {
14
- constructor() {
15
- this.projectRoot = path.join(__dirname, '..');
16
- this.directories = [
17
- 'scripts/debug/logs',
18
- 'scripts/debug/reports',
19
- 'settings/backups',
20
- 'i18ntk-reports',
21
- 'reports',
22
- 'tests/temp'
23
- ];
24
- this.files = [
25
- 'settings/.i18n-admin-config.json',
26
- 'test-*.json',
27
- 'debug-*.log',
28
- 'npm-debug.log',
29
- 'yarn-error.log',
30
- '.env',
31
- '.env.test'
32
- ];
33
-
34
- // Essential files that must exist for development
35
- this.essentialFiles = [
36
- 'package.json',
37
- 'main/manage/index.js',
38
- 'main/i18ntk-init.js',
39
- 'main/i18ntk-analyze.js',
40
- 'main/i18ntk-validate.js',
41
- 'utils/security.js'
42
- ];
43
- }
44
-
45
- log(message) {
46
- console.log(`[Prepublish-Dev] ${message}`);
47
- }
48
-
49
- async clean() {
50
- this.log('Starting development pre-publish validation...');
51
-
52
- // Validate essential files exist
53
- await this.validateEssentialFiles();
54
-
55
- // Clean directories
56
- for (const dir of this.directories) {
57
- await this.cleanDirectory(path.join(this.projectRoot, dir));
58
- }
59
-
60
- // Clean files
61
- for (const file of this.files) {
62
- await this.cleanFile(file);
63
- }
64
-
65
- // Development-specific validations
66
- await this.devValidations();
67
-
68
- this.log('Development pre-publish validation completed successfully!');
69
- }
70
-
71
- async cleanDirectory(dirPath) {
72
- const validatedPath = SecurityUtils.validatePath(dirPath, this.projectRoot);
73
- if (!validatedPath || !SecurityUtils.safeExistsSync(validatedPath)) {
74
- return;
75
- }
76
-
77
- try {
78
- const files = SecurityUtils.safeReaddirSync(validatedPath, this.projectRoot);
79
- let deletedCount = 0;
80
-
81
- for (const file of files) {
82
- const filePath = path.join(validatedPath, file);
83
- const validatedFilePath = SecurityUtils.validatePath(filePath, this.projectRoot);
84
- if (!validatedFilePath) continue;
85
-
86
- const stat = SecurityUtils.safeStatSync(validatedFilePath, this.projectRoot);
87
- if (!stat) continue;
88
-
89
- if (stat.isFile()) {
90
- if (SecurityUtils.safeExistsSync(validatedFilePath)) {
91
- fs.unlinkSync(validatedFilePath);
92
- deletedCount++;
93
- }
94
- } else if (stat.isDirectory()) {
95
- // Recursively clean subdirectories
96
- await this.cleanDirectory(validatedFilePath);
97
- // Remove empty directories
98
- try {
99
- fs.rmdirSync(validatedFilePath);
100
- } catch (e) {
101
- // Directory not empty, skip
102
- }
103
- }
104
- }
105
-
106
- if (deletedCount > 0) {
107
- this.log(`Cleaned ${deletedCount} files from ${path.relative(this.projectRoot, validatedPath)}`);
108
- }
109
- } catch (error) {
110
- this.log(`Warning: Could not clean ${dirPath}: ${error.message}`);
111
- }
112
- }
113
-
114
- async cleanFile(pattern) {
115
- const searchPath = path.join(this.projectRoot, pattern);
116
- const validatedSearchPath = SecurityUtils.validatePath(searchPath, this.projectRoot);
117
- if (!validatedSearchPath) return;
118
-
119
- if (pattern.includes('*')) {
120
- // Handle glob patterns
121
- const dir = path.dirname(validatedSearchPath);
122
- const filenamePattern = path.basename(validatedSearchPath);
123
- const validatedDir = SecurityUtils.validatePath(dir, this.projectRoot);
124
- if (!validatedDir) return;
125
-
126
- if (SecurityUtils.safeExistsSync(validatedDir)) {
127
- const files = SecurityUtils.safeReaddirSync(validatedDir, this.projectRoot);
128
- const regex = new RegExp(filenamePattern.replace('*', '.*'));
129
-
130
- for (const file of files) {
131
- if (regex.test(file)) {
132
- const filePath = path.join(validatedDir, file);
133
- const validatedFilePath = SecurityUtils.validatePath(filePath, this.projectRoot);
134
- if (validatedFilePath && SecurityUtils.safeExistsSync(validatedFilePath)) {
135
- fs.unlinkSync(validatedFilePath);
136
- this.log(`Deleted ${path.relative(this.projectRoot, validatedFilePath)}`);
137
- }
138
- }
139
- }
140
- }
141
- } else {
142
- // Handle exact files
143
- const validatedFilePath = SecurityUtils.validatePath(searchPath, this.projectRoot);
144
- if (validatedFilePath && SecurityUtils.safeExistsSync(validatedFilePath)) {
145
- fs.unlinkSync(validatedFilePath);
146
- this.log(`Deleted ${path.relative(this.projectRoot, validatedFilePath)}`);
147
- }
148
- }
149
- }
150
-
151
- async validateEssentialFiles() {
152
- this.log('Validating essential development files...');
153
-
154
- let missingFiles = [];
155
- for (const file of this.essentialFiles) {
156
- const filePath = path.join(this.projectRoot, file);
157
- const validatedPath = SecurityUtils.validatePath(filePath, this.projectRoot);
158
- if (!validatedPath || !SecurityUtils.safeExistsSync(validatedPath)) {
159
- missingFiles.push(file);
160
- } else {
161
- const stat = SecurityUtils.safeStatSync(validatedPath, this.projectRoot);
162
- if (!stat || !stat.isFile()) {
163
- this.log(`❌ ${file} is not a file`);
164
- process.exit(1);
165
- }
166
- }
167
- }
168
-
169
- if (missingFiles.length > 0) {
170
- this.log(`❌ Missing essential files: ${missingFiles.join(', ')}`);
171
- process.exit(1);
172
- }
173
-
174
- this.log('✅ All essential development files present');
175
- }
176
-
177
- async devValidations() {
178
- this.log('Running development-specific validations...');
179
-
180
- // Check for test files
181
- const testFiles = [
182
- 'tests/security.test.js',
183
- 'tests/config-system.test.js',
184
- 'tests/comprehensive-test.js'
185
- ];
186
-
187
- for (const testFile of testFiles) {
188
- const testPath = path.join(this.projectRoot, testFile);
189
- const validatedPath = SecurityUtils.validatePath(testPath, this.projectRoot);
190
- if (!validatedPath || !SecurityUtils.safeExistsSync(validatedPath)) {
191
- this.log(`⚠️ Missing test file: ${testFile}`);
192
- }
193
- }
194
-
195
- // Check for development tools
196
- const devTools = [
197
- '.vscode/',
198
- '.idea/',
199
- 'node_modules/'
200
- ];
201
-
202
- for (const tool of devTools) {
203
- const toolPath = path.join(this.projectRoot, tool);
204
- const validatedPath = SecurityUtils.validatePath(toolPath, this.projectRoot);
205
- if (validatedPath && SecurityUtils.safeExistsSync(validatedPath)) {
206
- this.log(`ℹ️ Development tool found: ${tool}`);
207
- }
208
- }
209
- }
210
- }
211
-
212
- // Run if called directly
213
- if (require.main === module) {
214
- const cleaner = new DevelopmentPrepublishCleaner();
215
- cleaner.clean().catch(error => {
216
- console.error('Error during development cleanup:', error);
217
- process.exit(1);
218
- });
219
- }
220
-
221
- module.exports = DevelopmentPrepublishCleaner;
@@ -1,362 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Prepublish Script
5
- * Cleans up development artifacts before npm publish
6
- * Ensures fresh config and settings for public package
7
- * version 2.0.5
8
- */
9
-
10
- const fs = require('fs');
11
- const path = require('path');
12
- const SecurityUtils = require('../utils/security');
13
-
14
- class PrepublishCleaner {
15
- constructor() {
16
- this.projectRoot = path.join(__dirname, '..');
17
- this.directories = [
18
- 'scripts/debug/logs',
19
- 'scripts/debug/reports',
20
- 'settings/backups',
21
- 'i18ntk-reports',
22
- 'reports'
23
- ];
24
- this.files = [
25
- 'settings/.i18n-admin-config.json',
26
- 'test-*.json',
27
- 'debug-*.log',
28
- 'npm-debug.log',
29
- 'yarn-error.log'
30
- ];
31
-
32
- // Essential files that must exist for release
33
- this.essentialFiles = [
34
- 'package.json',
35
- 'main/manage/index.js',
36
- 'main/i18ntk-init.js',
37
- 'main/i18ntk-analyze.js',
38
- 'main/i18ntk-validate.js',
39
- 'main/i18ntk-usage.js',
40
- 'main/i18ntk-summary.js',
41
- 'main/i18ntk-sizing.js',
42
- 'main/i18ntk-complete.js',
43
- 'main/i18ntk-ui.js',
44
- 'main/i18ntk-autorun.js',
45
- 'utils/i18n-helper.js',
46
- 'utils/security.js',
47
- 'settings/settings-manager.js',
48
- 'settings/settings-cli.js',
49
- 'settings/i18ntk-config.json'
50
- ];
51
-
52
- // Essential locale files
53
- this.essentialLocales = [
54
- 'resources/i18n/ui-locales/en.json',
55
- 'resources/i18n/ui-locales/es.json',
56
- 'resources/i18n/ui-locales/fr.json',
57
- 'resources/i18n/ui-locales/de.json',
58
- 'resources/i18n/ui-locales/ja.json',
59
- 'resources/i18n/ui-locales/ru.json',
60
- 'resources/i18n/ui-locales/zh.json'
61
- ];
62
- }
63
-
64
- log(message) {
65
- console.log(`[Prepublish] ${message}`);
66
- }
67
-
68
- async clean() {
69
- this.log('Starting comprehensive pre-publish validation...');
70
-
71
- // Validate essential files exist
72
- await this.validateEssentialFiles();
73
-
74
- // Validate locale files
75
- await this.validateLocaleFiles();
76
-
77
- // Validate package.json
78
- await this.validatePackageJson();
79
-
80
- // Clean directories
81
- for (const dir of this.directories) {
82
- await this.cleanDirectory(path.join(this.projectRoot, dir));
83
- }
84
-
85
- // Clean files
86
- for (const file of this.files) {
87
- await this.cleanFile(file);
88
- }
89
-
90
- // Reset security settings
91
- await this.resetSecuritySettings();
92
-
93
- // Final validation
94
- await this.finalValidation();
95
-
96
- this.log('Pre-publish validation completed successfully!');
97
- }
98
-
99
- async cleanDirectory(dirPath) {
100
- const validatedPath = SecurityUtils.validatePath(dirPath, this.projectRoot);
101
- if (!validatedPath || !SecurityUtils.safeExistsSync(validatedPath)) {
102
- return;
103
- }
104
-
105
- try {
106
- const files = SecurityUtils.safeReaddirSync(validatedPath, this.projectRoot);
107
- let deletedCount = 0;
108
-
109
- for (const file of files) {
110
- const filePath = path.join(validatedPath, file);
111
- const validatedFilePath = SecurityUtils.validatePath(filePath, this.projectRoot);
112
- if (!validatedFilePath) continue;
113
-
114
- const stat = SecurityUtils.safeStatSync(validatedFilePath, this.projectRoot);
115
- if (!stat) continue;
116
-
117
- if (stat.isFile()) {
118
- if (SecurityUtils.safeExistsSync(validatedFilePath)) {
119
- fs.unlinkSync(validatedFilePath);
120
- deletedCount++;
121
- }
122
- } else if (stat.isDirectory()) {
123
- // Recursively clean subdirectories
124
- await this.cleanDirectory(validatedFilePath);
125
- // Remove empty directories
126
- try {
127
- fs.rmdirSync(validatedFilePath);
128
- } catch (e) {
129
- // Directory not empty, skip
130
- }
131
- }
132
- }
133
-
134
- if (deletedCount > 0) {
135
- this.log(`Cleaned ${deletedCount} files from ${path.relative(this.projectRoot, validatedPath)}`);
136
- }
137
- } catch (error) {
138
- this.log(`Warning: Could not clean ${dirPath}: ${error.message}`);
139
- }
140
- }
141
-
142
- async cleanFile(pattern) {
143
- const searchPath = path.join(this.projectRoot, pattern);
144
- const validatedSearchPath = SecurityUtils.validatePath(searchPath, this.projectRoot);
145
- if (!validatedSearchPath) return;
146
-
147
- if (pattern.includes('*')) {
148
- // Handle glob patterns
149
- const dir = path.dirname(validatedSearchPath);
150
- const filenamePattern = path.basename(validatedSearchPath);
151
- const validatedDir = SecurityUtils.validatePath(dir, this.projectRoot);
152
- if (!validatedDir) return;
153
-
154
- if (SecurityUtils.safeExistsSync(validatedDir)) {
155
- const files = SecurityUtils.safeReaddirSync(validatedDir, this.projectRoot);
156
- const regex = new RegExp(filenamePattern.replace('*', '.*'));
157
-
158
- for (const file of files) {
159
- if (regex.test(file)) {
160
- const filePath = path.join(validatedDir, file);
161
- const validatedFilePath = SecurityUtils.validatePath(filePath, this.projectRoot);
162
- if (validatedFilePath && SecurityUtils.safeExistsSync(validatedFilePath)) {
163
- fs.unlinkSync(validatedFilePath);
164
- this.log(`Deleted ${path.relative(this.projectRoot, validatedFilePath)}`);
165
- }
166
- }
167
- }
168
- }
169
- } else {
170
- // Handle exact files
171
- const validatedFilePath = SecurityUtils.validatePath(searchPath, this.projectRoot);
172
- if (validatedFilePath && SecurityUtils.safeExistsSync(validatedFilePath)) {
173
- fs.unlinkSync(validatedFilePath);
174
- this.log(`Deleted ${path.relative(this.projectRoot, validatedFilePath)}`);
175
- }
176
- }
177
- }
178
-
179
- async validateEssentialFiles() {
180
- this.log('Validating essential files...');
181
-
182
- let missingFiles = [];
183
- for (const file of this.essentialFiles) {
184
- const filePath = path.join(this.projectRoot, file);
185
- const validatedPath = SecurityUtils.validatePath(filePath, this.projectRoot);
186
- if (!validatedPath || !SecurityUtils.safeExistsSync(validatedPath)) {
187
- missingFiles.push(file);
188
- } else {
189
- const stat = SecurityUtils.safeStatSync(validatedPath, this.projectRoot);
190
- if (!stat || !stat.isFile()) {
191
- this.log(`❌ ${file} is not a file`);
192
- process.exit(1);
193
- }
194
- }
195
- }
196
-
197
- if (missingFiles.length > 0) {
198
- this.log(`❌ Missing essential files: ${missingFiles.join(', ')}`);
199
- process.exit(1);
200
- }
201
-
202
- this.log('✅ All essential files present');
203
- }
204
-
205
- async validateLocaleFiles() {
206
- this.log('Validating locale files...');
207
-
208
- let invalidFiles = [];
209
- for (const localeFile of this.essentialLocales) {
210
- const filePath = path.join(this.projectRoot, localeFile);
211
- const validatedPath = SecurityUtils.validatePath(filePath, this.projectRoot);
212
- if (!validatedPath || !SecurityUtils.safeExistsSync(validatedPath)) {
213
- invalidFiles.push(localeFile);
214
- continue;
215
- }
216
-
217
- try {
218
- const content = SecurityUtils.safeReadFileSync(validatedPath, this.projectRoot, 'utf8');
219
-
220
- // Validate structure
221
- if (typeof parsed !== 'object' || parsed === null) {
222
- invalidFiles.push(`${localeFile}: Invalid structure`);
223
- }
224
-
225
- // Check for required keys
226
- if (!parsed.settings || !parsed.settings.title) {
227
- invalidFiles.push(`${localeFile}: Missing required keys`);
228
- }
229
-
230
- } catch (e) {
231
- invalidFiles.push(`${localeFile}: ${e.message}`);
232
- }
233
- }
234
-
235
- if (invalidFiles.length > 0) {
236
- this.log(`❌ Invalid locale files: ${invalidFiles.join(', ')}`);
237
- process.exit(1);
238
- }
239
-
240
- this.log('✅ All locale files valid');
241
- }
242
-
243
- async validatePackageJson() {
244
- this.log('Validating package.json...');
245
-
246
- const packagePath = path.join(this.projectRoot, 'package.json');
247
- const validatedPath = SecurityUtils.validatePath(packagePath, this.projectRoot);
248
- if (!validatedPath) {
249
- this.log('❌ package.json not found or invalid path');
250
- process.exit(1);
251
- }
252
- try {
253
- const pkg = JSON.parse(SecurityUtils.safeReadFileSync(validatedPath, this.projectRoot, 'utf8'));
254
-
255
- // Validate required fields
256
- const requiredFields = ['name', 'version', 'description', 'main', 'bin', 'files'];
257
- for (const field of requiredFields) {
258
- if (!pkg[field]) {
259
- this.log(`❌ package.json missing required field: ${field}`);
260
- process.exit(1);
261
- }
262
- }
263
-
264
- // Validate version format
265
- if (!/^\d+\.\d+\.\d+/.test(pkg.version)) {
266
- this.log('❌ Invalid version format');
267
- process.exit(1);
268
- }
269
-
270
- // Validate bin entries
271
- const requiredBinEntries = [
272
- 'i18ntk', 'i18ntk-init', 'i18ntk-analyze', 'i18ntk-validate',
273
- 'i18ntk-usage', 'i18ntk-summary', 'i18ntk-sizing', 'i18ntk-complete',
274
- 'i18ntk-ui', 'i18ntk-autorun'
275
- ];
276
-
277
- for (const bin of requiredBinEntries) {
278
- if (!pkg.bin || !pkg.bin[bin]) {
279
- this.log(`❌ Missing bin entry: ${bin}`);
280
- process.exit(1);
281
- }
282
-
283
- const binPath = path.join(this.projectRoot, pkg.bin[bin]);
284
- if (!SecurityUtils.safeExistsSync(binPath)) {
285
- this.log(`❌ Missing bin script: ${pkg.bin[bin]}`);
286
- process.exit(1);
287
- }
288
- }
289
-
290
- this.log('✅ package.json validated');
291
- } catch (e) {
292
- this.log(`❌ Invalid package.json: ${e.message}`);
293
- process.exit(1);
294
- }
295
- }
296
-
297
- async finalValidation() {
298
- this.log('Running final validation checks...');
299
-
300
- // Check for development artifacts
301
- const devArtifacts = [
302
- 'dev/debug',
303
- 'benchmarks',
304
- '.github',
305
- 'test-usage-fix.html',
306
- '.i18ntk'
307
- ];
308
-
309
- for (const artifact of devArtifacts) {
310
- const artifactPath = path.join(this.projectRoot, artifact);
311
- const validatedPath = SecurityUtils.validatePath(artifactPath, this.projectRoot);
312
- if (validatedPath && SecurityUtils.safeExistsSync(validatedPath)) {
313
- this.log(`⚠️ Development artifact found: ${artifact}`);
314
- }
315
- }
316
-
317
- // Validate file permissions for executable scripts
318
- const scripts = [
319
- 'main/manage/index.js',
320
- 'main/i18ntk-init.js',
321
- 'main/i18ntk-analyze.js',
322
- 'main/i18ntk-validate.js',
323
- 'main/i18ntk-usage.js',
324
- 'main/i18ntk-summary.js',
325
- 'main/i18ntk-sizing.js',
326
- 'main/i18ntk-complete.js',
327
- 'main/i18ntk-ui.js',
328
- 'main/i18ntk-autorun.js'
329
- ];
330
-
331
- for (const script of scripts) {
332
- const scriptPath = path.join(this.projectRoot, script);
333
- const validatedPath = SecurityUtils.validatePath(scriptPath, this.projectRoot);
334
- if (validatedPath && SecurityUtils.safeExistsSync(validatedPath)) {
335
- try {
336
- fs.accessSync(validatedPath, fs.constants.X_OK);
337
- } catch (e) {
338
- this.log(`⚠️ Script not executable: ${script}`);
339
- }
340
- }
341
- }
342
-
343
- this.log('✅ Final validation complete');
344
- }
345
-
346
- async resetSecuritySettings() {
347
- // Remove security settings reset to prevent disabling security during publish
348
- // This script should not modify security configurations
349
- this.log('Skipping security settings reset to maintain security posture');
350
- }
351
- }
352
-
353
- // Run if called directly
354
- if (require.main === module) {
355
- const cleaner = new PrepublishCleaner();
356
- cleaner.clean().catch(error => {
357
- console.error('Error during cleanup:', error);
358
- process.exit(1);
359
- });
360
- }
361
-
362
- module.exports = PrepublishCleaner;
@@ -1,117 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Security Check Script
4
- * Validates that no child_process usage exists in production code
5
- */
6
-
7
- const fs = require('fs');
8
- const path = require('path');
9
-
10
- class SecurityCheck {
11
- constructor() {
12
- this.productionDirs = ['main', 'utils', 'settings', 'scripts'];
13
- this.forbiddenPatterns = [
14
- /require\(['"]child_process['"]\)/,
15
- /import.*child_process/,
16
- /execSync\(/,
17
- /spawnSync\(/,
18
- /execFileSync\(/,
19
- /spawn\(/,
20
- /exec\(/,
21
- /execFile\(/
22
- ];
23
- this.allowedFiles = [
24
- 'dev/', // Development files allowed to use child_process
25
- 'benchmarks/', // Benchmark scripts
26
- 'test/', // Test files
27
- 'scripts/deprecate-versions.js', // Allowed to use child_process for npm commands
28
- 'verify-package.js' // Package verification (development)
29
- ];
30
- this.violations = [];
31
- }
32
-
33
- async run() {
34
- console.log('🔒 i18ntk Security Check - Production Code Validation');
35
- console.log('═'.repeat(55));
36
-
37
- for (const dir of this.productionDirs) {
38
- await this.checkDirectory(dir);
39
- }
40
-
41
- if (this.violations.length > 0) {
42
- console.error('\n❌ SECURITY VIOLATIONS FOUND:');
43
- this.violations.forEach(violation => {
44
- console.error(` ${violation.file}:${violation.line} - ${violation.pattern}`);
45
- });
46
- console.error('\n💡 Production code must not use child_process');
47
- process.exit(1);
48
- } else {
49
- console.log('\n✅ All security checks passed - no child_process usage in production code');
50
- }
51
- }
52
-
53
- async checkDirectory(dir) {
54
- const dirPath = path.join(process.cwd(), dir);
55
-
56
- if (!SecurityUtils.safeExistsSync(dirPath)) {
57
- return;
58
- }
59
-
60
- const files = this.getAllFiles(dirPath);
61
-
62
- for (const file of files) {
63
- await this.checkFile(file);
64
- }
65
- }
66
-
67
- getAllFiles(dirPath) {
68
- const files = [];
69
- const entries = fs.readdirSync(dirPath, { withFileTypes: true });
70
-
71
- for (const entry of entries) {
72
- const fullPath = path.join(dirPath, entry.name);
73
-
74
- if (entry.isDirectory()) {
75
- files.push(...this.getAllFiles(fullPath));
76
- } else if (entry.isFile() && entry.name.endsWith('.js')) {
77
- files.push(fullPath);
78
- }
79
- }
80
-
81
- return files;
82
- }
83
-
84
- async checkFile(filePath) {
85
- const relativePath = path.relative(process.cwd(), filePath).replace(/\\/g, '/');
86
-
87
- for (const allowed of this.allowedFiles) {
88
- if (relativePath.startsWith(allowed)) {
89
- return;
90
- }
91
- }
92
-
93
- const content = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
94
- const lines = content.split('\n');
95
-
96
- for (let i = 0; i < lines.length; i++) {
97
- const line = lines[i];
98
-
99
- for (const pattern of this.forbiddenPatterns) {
100
- if (pattern.test(line)) {
101
- this.violations.push({
102
- file: relativePath,
103
- line: i + 1,
104
- pattern: pattern.toString()
105
- });
106
- }
107
- }
108
- }
109
- }
110
- }
111
-
112
- if (require.main === module) {
113
- const check = new SecurityCheck();
114
- check.run().catch(console.error);
115
- }
116
-
117
- module.exports = SecurityCheck;