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.
- package/README.md +10 -6
- package/main/i18ntk-analyze.js +63 -63
- package/main/i18ntk-complete.js +75 -74
- package/main/i18ntk-fixer.js +3 -3
- package/main/i18ntk-init.js +5 -5
- package/main/i18ntk-scanner.js +2 -2
- package/main/i18ntk-sizing.js +35 -35
- package/main/i18ntk-summary.js +4 -4
- package/main/i18ntk-ui.js +95 -96
- package/main/i18ntk-usage.js +14 -14
- package/main/i18ntk-validate.js +6 -5
- package/main/manage/commands/AnalyzeCommand.js +67 -67
- package/main/manage/commands/FixerCommand.js +2 -2
- package/main/manage/commands/ScannerCommand.js +2 -2
- package/main/manage/commands/ValidateCommand.js +9 -9
- package/main/manage/index.js +7 -4
- package/main/manage/managers/LanguageMenu.js +7 -2
- package/main/manage/services/UsageService.js +7 -7
- package/package.json +20 -41
- package/settings/settings-cli.js +3 -3
- package/ui-locales/de.json +12 -11
- package/ui-locales/en.json +12 -11
- package/ui-locales/es.json +12 -11
- package/ui-locales/fr.json +12 -11
- package/ui-locales/ja.json +12 -11
- package/ui-locales/ru.json +12 -11
- package/ui-locales/zh.json +12 -11
- package/utils/i18n-helper.js +161 -166
- package/{scripts → utils}/locale-optimizer.js +61 -60
- package/main/i18ntk-go.js +0 -283
- package/main/i18ntk-java.js +0 -380
- package/main/i18ntk-js.js +0 -512
- package/main/i18ntk-manage.js +0 -1694
- package/main/i18ntk-php.js +0 -462
- package/main/i18ntk-py.js +0 -379
- package/main/i18ntk-settings.js +0 -23
- package/main/manage/index-fixed.js +0 -1447
- package/scripts/build-lite.js +0 -279
- package/scripts/deprecate-versions.js +0 -317
- package/scripts/export-translations.js +0 -84
- package/scripts/fix-all-i18n.js +0 -236
- package/scripts/fix-and-purify-i18n.js +0 -233
- package/scripts/fix-locale-control-chars.js +0 -110
- package/scripts/lint-locales.js +0 -80
- package/scripts/prepublish-dev.js +0 -221
- package/scripts/prepublish.js +0 -362
- package/scripts/security-check.js +0 -117
- package/scripts/sync-translations.js +0 -151
- package/scripts/sync-ui-locales.js +0 -20
- package/scripts/validate-all-translations.js +0 -195
- package/scripts/verify-deprecations.js +0 -157
- package/scripts/verify-translations.js +0 -63
- 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;
|
package/scripts/prepublish.js
DELETED
|
@@ -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;
|