@configjs/cli 1.1.16 → 1.1.18

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 (34) hide show
  1. package/dist/{angular-command-XN26G6L3.js → angular-command-EOREU45Q.js} +8 -8
  2. package/dist/{angular-installer-FY43HE72.js → angular-installer-TKZDPFLD.js} +9 -1
  3. package/dist/angular-setup-QDTWXOB4.js +30 -0
  4. package/dist/check-KAPRT4FM.js +168 -0
  5. package/dist/{chunk-JYWGJJ4M.js → chunk-D7IWYKUX.js} +476 -28
  6. package/dist/chunk-EDCNW4UO.js +92 -0
  7. package/dist/{chunk-TN27AX4L.js → chunk-FJLN62L4.js} +797 -18
  8. package/dist/{chunk-FIB2J36N.js → chunk-HI7RYD6W.js} +161 -36
  9. package/dist/{chunk-NYCK4R4K.js → chunk-RIJNUJDC.js} +361 -96
  10. package/dist/chunk-V2IBYLVH.js +932 -0
  11. package/dist/chunk-VN4XTFDK.js +315 -0
  12. package/dist/{chunk-UKEHW2LH.js → chunk-Y4XYC7QV.js} +17 -3
  13. package/dist/cli.js +31 -21
  14. package/dist/{installed-D6CUYQM5.js → installed-QMJZIZNC.js} +4 -4
  15. package/dist/{list-VZDUWV5O.js → list-5T6VDDAO.js} +4 -4
  16. package/dist/{nextjs-command-WKKOAY7I.js → nextjs-command-C6PM7A5C.js} +8 -9
  17. package/dist/{nextjs-installer-2ZC5IWJ6.js → nextjs-installer-OFY5BQC4.js} +9 -2
  18. package/dist/{nextjs-setup-DYOFF72S.js → nextjs-setup-JIKPIJCX.js} +21 -9
  19. package/dist/{react-command-2T6IOTHB.js → react-command-JMK6VM4Q.js} +8 -9
  20. package/dist/{remove-ZY3MLPGN.js → remove-4ZNQR6ZR.js} +4 -4
  21. package/dist/{svelte-command-B2DNNQ5Z.js → svelte-command-YUSD55NO.js} +8 -8
  22. package/dist/svelte-installer-UP3KDZSY.js +105 -0
  23. package/dist/{svelte-setup-FWXLXJAC.js → svelte-setup-33E46IBT.js} +16 -5
  24. package/dist/{vite-installer-Y6VMFHIM.js → vite-installer-EE2LE76G.js} +9 -2
  25. package/dist/{vite-setup-JRELX6K2.js → vite-setup-VO5BOI46.js} +16 -4
  26. package/dist/{vue-command-IOTC32AI.js → vue-command-3CYWLLFQ.js} +8 -9
  27. package/dist/{vue-installer-DGBBVF6F.js → vue-installer-LEGLVD77.js} +9 -2
  28. package/dist/{vue-setup-G44DLT2U.js → vue-setup-FK5QT7AY.js} +16 -4
  29. package/package.json +12 -4
  30. package/dist/angular-setup-Z6TCKNBG.js +0 -18
  31. package/dist/check-KNGZSCMM.js +0 -131
  32. package/dist/chunk-6GV4NKUX.js +0 -122
  33. package/dist/chunk-QPEUT7QG.js +0 -157
  34. package/dist/svelte-installer-EOSC3EP3.js +0 -65
@@ -1,13 +1,118 @@
1
1
  import {
2
2
  getModuleLogger
3
- } from "./chunk-QPEUT7QG.js";
3
+ } from "./chunk-VN4XTFDK.js";
4
+
5
+ // src/core/path-validator.ts
6
+ import { resolve, normalize, sep, isAbsolute, dirname } from "path";
7
+ import { realpath as realpathCallback } from "fs/promises";
8
+ import fs from "fs-extra";
9
+ import { z } from "zod";
10
+ var pathExists = fs.pathExists;
11
+ var realpath = realpathCallback;
12
+ var pathResolutionSchema = z.object({
13
+ projectRoot: z.string().min(1, "Project root cannot be empty").refine(
14
+ (path) => isAbsolute(path),
15
+ "Project root must be an absolute path"
16
+ ),
17
+ userPath: z.string().min(1, "User path cannot be empty").max(1024, "Path exceeds maximum length (1024 characters)").refine((path) => !path.includes("\0"), "Path cannot contain null bytes").refine((path) => {
18
+ for (let i = 0; i < path.length; i++) {
19
+ const charCode = path.charCodeAt(i);
20
+ if (charCode >= 0 && charCode <= 31) {
21
+ return false;
22
+ }
23
+ }
24
+ return true;
25
+ }, "Path cannot contain control characters")
26
+ });
27
+ function validatePathInProject(projectRoot, userPath) {
28
+ const validated = pathResolutionSchema.parse({
29
+ projectRoot,
30
+ userPath
31
+ });
32
+ const normalizedRoot = normalize(resolve(validated.projectRoot));
33
+ const resolvedPath = normalize(resolve(normalizedRoot, validated.userPath));
34
+ const isWithinBoundary = resolvedPath === normalizedRoot || resolvedPath.startsWith(normalizedRoot + sep);
35
+ if (!isWithinBoundary) {
36
+ throw new Error(
37
+ `Path traversal detected: "${userPath}" attempts to escape project root. Resolved: "${resolvedPath}", allowed: "${normalizedRoot}"`
38
+ );
39
+ }
40
+ if (userPath.includes("..")) {
41
+ throw new Error(
42
+ `Path contains parent directory (..): "${userPath}". Only paths within project root allowed.`
43
+ );
44
+ }
45
+ if (isAbsolute(userPath)) {
46
+ throw new Error(
47
+ `Absolute paths not allowed: "${userPath}". Use path relative to project root.`
48
+ );
49
+ }
50
+ return resolvedPath;
51
+ }
52
+ async function findClosestExistingPath(boundaryRoot, resolvedPath) {
53
+ let currentPath = resolvedPath;
54
+ while (true) {
55
+ if (await pathExists(currentPath)) {
56
+ return currentPath;
57
+ }
58
+ if (currentPath === boundaryRoot) {
59
+ return null;
60
+ }
61
+ const parentPath = dirname(currentPath);
62
+ if (parentPath === currentPath) {
63
+ return null;
64
+ }
65
+ currentPath = parentPath;
66
+ }
67
+ }
68
+ async function validateSymlinkBoundary(projectRoot, resolvedPath) {
69
+ const normalizedRoot = normalize(resolve(projectRoot));
70
+ const realRoot = await realpath(normalizedRoot).catch(() => normalizedRoot);
71
+ const existingPath = await findClosestExistingPath(
72
+ normalizedRoot,
73
+ resolvedPath
74
+ );
75
+ if (!existingPath) {
76
+ return;
77
+ }
78
+ const realExisting = await realpath(existingPath).catch(() => existingPath);
79
+ const normalizedRealExisting = normalize(realExisting);
80
+ const normalizedRealRoot = normalize(realRoot);
81
+ const isWithinBoundary = normalizedRealExisting === normalizedRealRoot || normalizedRealExisting.startsWith(normalizedRealRoot + sep);
82
+ if (!isWithinBoundary) {
83
+ throw new Error(
84
+ `Symlink traversal detected: "${resolvedPath}" resolves outside project root. Resolved: "${normalizedRealExisting}", allowed: "${normalizedRealRoot}"`
85
+ );
86
+ }
87
+ }
88
+ async function validatePathInProjectWithSymlinks(projectRoot, userPath) {
89
+ const resolvedPath = validatePathInProject(projectRoot, userPath);
90
+ await validateSymlinkBoundary(projectRoot, resolvedPath);
91
+ return resolvedPath;
92
+ }
93
+ function getPathValidationErrorMessage(error) {
94
+ if (error instanceof z.ZodError) {
95
+ const issues = error.flatten().fieldErrors;
96
+ const messages = [];
97
+ for (const [field, msgs] of Object.entries(issues)) {
98
+ if (msgs && Array.isArray(msgs)) {
99
+ messages.push(`${field}: ${msgs.join(", ")}`);
100
+ }
101
+ }
102
+ return messages.join("; ") || "Invalid path format";
103
+ }
104
+ if (error instanceof Error) {
105
+ return error.message;
106
+ }
107
+ return "Path validation failed";
108
+ }
4
109
 
5
110
  // src/utils/fs-helpers.ts
6
- import { resolve as resolve2, dirname as dirname2, extname } from "path";
111
+ import { resolve as resolve3, dirname as dirname3, extname } from "path";
7
112
 
8
113
  // src/core/fs-adapter.ts
9
- import fs from "fs-extra";
10
- import { resolve, dirname } from "path";
114
+ import fs2 from "fs-extra";
115
+ import { resolve as resolve2, dirname as dirname2 } from "path";
11
116
  var FileSystemAdapter = class {
12
117
  /**
13
118
  * @param fsImpl - Implémentation du filesystem (memfs) ou undefined pour fs-extra
@@ -19,13 +124,13 @@ var FileSystemAdapter = class {
19
124
  * Obtient l'implémentation du filesystem
20
125
  */
21
126
  getFs() {
22
- return this.fsImpl || fs;
127
+ return this.fsImpl || fs2;
23
128
  }
24
129
  /**
25
130
  * Normalise un chemin (résout les chemins relatifs)
26
131
  */
27
132
  normalizePath(path) {
28
- return resolve(path);
133
+ return resolve2(path);
29
134
  }
30
135
  // ===== Async Operations =====
31
136
  async readFile(path, encoding = "utf-8") {
@@ -39,7 +144,7 @@ var FileSystemAdapter = class {
39
144
  const buffer = content;
40
145
  return buffer.toString(encoding);
41
146
  } else {
42
- const content = await fs.readFile(fullPath, encoding);
147
+ const content = await fs2.readFile(fullPath, encoding);
43
148
  if (typeof content === "string") {
44
149
  return content;
45
150
  }
@@ -50,12 +155,12 @@ var FileSystemAdapter = class {
50
155
  async writeFile(path, content, encoding = "utf-8") {
51
156
  const fullPath = this.normalizePath(path);
52
157
  const implementation = this.getFs();
53
- const parentDir = dirname(fullPath);
158
+ const parentDir = dirname2(fullPath);
54
159
  await this.mkdir(parentDir, { recursive: true });
55
160
  if (this.fsImpl) {
56
161
  await implementation.promises.writeFile(fullPath, content, encoding);
57
162
  } else {
58
- await fs.writeFile(fullPath, content, encoding);
163
+ await fs2.writeFile(fullPath, content, encoding);
59
164
  }
60
165
  }
61
166
  async mkdir(path, _options) {
@@ -64,7 +169,7 @@ var FileSystemAdapter = class {
64
169
  if (this.fsImpl) {
65
170
  await implementation.promises.mkdir(fullPath, { recursive: true });
66
171
  } else {
67
- await fs.ensureDir(fullPath);
172
+ await fs2.ensureDir(fullPath);
68
173
  }
69
174
  }
70
175
  async readdir(path) {
@@ -73,7 +178,7 @@ var FileSystemAdapter = class {
73
178
  const entries = this.fsImpl.readdirSync(fullPath);
74
179
  return Promise.resolve(entries.map((entry) => String(entry)));
75
180
  } else {
76
- return await fs.readdir(fullPath);
181
+ return await fs2.readdir(fullPath);
77
182
  }
78
183
  }
79
184
  async stat(path) {
@@ -87,7 +192,7 @@ var FileSystemAdapter = class {
87
192
  mtime: stats.mtime
88
193
  };
89
194
  } else {
90
- const stats = await fs.stat(fullPath);
195
+ const stats = await fs2.stat(fullPath);
91
196
  return {
92
197
  isFile: () => stats.isFile(),
93
198
  isDirectory: () => stats.isDirectory(),
@@ -106,7 +211,7 @@ var FileSystemAdapter = class {
106
211
  return false;
107
212
  }
108
213
  } else {
109
- return await fs.pathExists(fullPath);
214
+ return await fs2.pathExists(fullPath);
110
215
  }
111
216
  }
112
217
  async readJson(path) {
@@ -115,18 +220,18 @@ var FileSystemAdapter = class {
115
220
  const content = await this.readFile(fullPath);
116
221
  return JSON.parse(content);
117
222
  } else {
118
- return await fs.readJson(fullPath);
223
+ return await fs2.readJson(fullPath);
119
224
  }
120
225
  }
121
226
  async writeJson(path, data, options) {
122
227
  const fullPath = this.normalizePath(path);
123
- const parentDir = dirname(fullPath);
228
+ const parentDir = dirname2(fullPath);
124
229
  await this.mkdir(parentDir, { recursive: true });
125
230
  if (this.fsImpl) {
126
231
  const content = JSON.stringify(data, null, options?.spaces ?? 2);
127
232
  await this.writeFile(fullPath, content);
128
233
  } else {
129
- await fs.writeJson(fullPath, data, {
234
+ await fs2.writeJson(fullPath, data, {
130
235
  spaces: options?.spaces ?? 2,
131
236
  EOL: options?.EOL ?? "\n"
132
237
  });
@@ -135,13 +240,13 @@ var FileSystemAdapter = class {
135
240
  async copyFile(src, dest) {
136
241
  const srcPath = this.normalizePath(src);
137
242
  const destPath = this.normalizePath(dest);
138
- const destDir = dirname(destPath);
243
+ const destDir = dirname2(destPath);
139
244
  await this.mkdir(destDir, { recursive: true });
140
245
  if (this.fsImpl) {
141
246
  const content = await this.readFile(srcPath);
142
247
  await this.writeFile(destPath, content);
143
248
  } else {
144
- await fs.copyFile(srcPath, destPath);
249
+ await fs2.copyFile(srcPath, destPath);
145
250
  }
146
251
  }
147
252
  async remove(path) {
@@ -158,7 +263,7 @@ var FileSystemAdapter = class {
158
263
  } catch {
159
264
  }
160
265
  } else {
161
- await fs.remove(fullPath);
266
+ await fs2.remove(fullPath);
162
267
  }
163
268
  }
164
269
  // ===== Sync Operations (pour compatibilité) =====
@@ -173,7 +278,7 @@ var FileSystemAdapter = class {
173
278
  const buffer = content;
174
279
  return buffer.toString(encoding);
175
280
  } else {
176
- const content = fs.readFileSync(fullPath, encoding);
281
+ const content = fs2.readFileSync(fullPath, encoding);
177
282
  if (typeof content === "string") {
178
283
  return content;
179
284
  }
@@ -184,14 +289,14 @@ var FileSystemAdapter = class {
184
289
  writeFileSync(path, content) {
185
290
  const fullPath = this.normalizePath(path);
186
291
  const implementation = this.getFs();
187
- const parentDir = dirname(fullPath);
292
+ const parentDir = dirname2(fullPath);
188
293
  if (!this.existsSync(parentDir)) {
189
294
  this.mkdirSync(parentDir, { recursive: true });
190
295
  }
191
296
  if (this.fsImpl) {
192
297
  implementation.writeFileSync(fullPath, content);
193
298
  } else {
194
- fs.writeFileSync(fullPath, content);
299
+ fs2.writeFileSync(fullPath, content);
195
300
  }
196
301
  }
197
302
  existsSync(path) {
@@ -205,7 +310,7 @@ var FileSystemAdapter = class {
205
310
  return false;
206
311
  }
207
312
  } else {
208
- return fs.existsSync(fullPath);
313
+ return fs2.existsSync(fullPath);
209
314
  }
210
315
  }
211
316
  mkdirSync(path, _options) {
@@ -214,7 +319,7 @@ var FileSystemAdapter = class {
214
319
  if (this.fsImpl) {
215
320
  implementation.mkdirSync(fullPath, { recursive: true });
216
321
  } else {
217
- fs.ensureDirSync(fullPath);
322
+ fs2.ensureDirSync(fullPath);
218
323
  }
219
324
  }
220
325
  };
@@ -229,7 +334,7 @@ function normalizePath(path) {
229
334
  }
230
335
  async function readPackageJson(root, fsAdapter) {
231
336
  const adapter = fsAdapter || createDefaultFsAdapter();
232
- const packageJsonPath = resolve2(root, "package.json");
337
+ const packageJsonPath = resolve3(root, "package.json");
233
338
  if (!await adapter.pathExists(packageJsonPath)) {
234
339
  throw new Error(`package.json not found at ${packageJsonPath}`);
235
340
  }
@@ -246,7 +351,7 @@ async function readPackageJson(root, fsAdapter) {
246
351
  }
247
352
  async function writePackageJson(root, pkg, fsAdapter) {
248
353
  const adapter = fsAdapter || createDefaultFsAdapter();
249
- const packageJsonPath = resolve2(root, "package.json");
354
+ const packageJsonPath = resolve3(root, "package.json");
250
355
  try {
251
356
  await adapter.writeJson(packageJsonPath, pkg, {
252
357
  spaces: 2,
@@ -261,9 +366,9 @@ async function writePackageJson(root, pkg, fsAdapter) {
261
366
  async function readTsConfig(root, fsAdapter) {
262
367
  const adapter = fsAdapter || createDefaultFsAdapter();
263
368
  const possiblePaths = [
264
- resolve2(root, "tsconfig.json"),
265
- resolve2(root, "tsconfig.app.json"),
266
- resolve2(root, "tsconfig.node.json")
369
+ resolve3(root, "tsconfig.json"),
370
+ resolve3(root, "tsconfig.app.json"),
371
+ resolve3(root, "tsconfig.node.json")
267
372
  ];
268
373
  for (const tsconfigPath of possiblePaths) {
269
374
  if (await adapter.pathExists(tsconfigPath)) {
@@ -285,12 +390,12 @@ async function readTsConfig(root, fsAdapter) {
285
390
  }
286
391
  async function checkPathExists(path, fsAdapter) {
287
392
  const adapter = fsAdapter || createDefaultFsAdapter();
288
- const fullPath = resolve2(path);
393
+ const fullPath = resolve3(path);
289
394
  return adapter.pathExists(fullPath);
290
395
  }
291
396
  async function ensureDirectory(path, fsAdapter) {
292
397
  const adapter = fsAdapter || createDefaultFsAdapter();
293
- const fullPath = resolve2(path);
398
+ const fullPath = resolve3(path);
294
399
  try {
295
400
  await adapter.mkdir(fullPath, { recursive: true });
296
401
  logger.debug(`Ensured directory exists: ${fullPath}`);
@@ -299,9 +404,18 @@ async function ensureDirectory(path, fsAdapter) {
299
404
  throw new Error(`Failed to create directory ${fullPath}: ${errorMessage}`);
300
405
  }
301
406
  }
302
- async function readFileContent(filePath, encoding = "utf-8", fsAdapter) {
407
+ async function readFileContent(filePath, encoding = "utf-8", fsAdapter, projectRoot) {
303
408
  const adapter = fsAdapter || createDefaultFsAdapter();
304
- const fullPath = resolve2(filePath);
409
+ let fullPath = resolve3(filePath);
410
+ if (projectRoot) {
411
+ try {
412
+ fullPath = await validatePathInProjectWithSymlinks(projectRoot, filePath);
413
+ } catch (error) {
414
+ const errorMsg = getPathValidationErrorMessage(error);
415
+ logger.error(`Path traversal attempt blocked: ${errorMsg}`);
416
+ throw new Error(`Invalid path: ${errorMsg}`);
417
+ }
418
+ }
305
419
  if (!await adapter.pathExists(fullPath)) {
306
420
  throw new Error(`File not found: ${fullPath}`);
307
421
  }
@@ -314,10 +428,19 @@ async function readFileContent(filePath, encoding = "utf-8", fsAdapter) {
314
428
  throw new Error(`Failed to read file ${fullPath}: ${errorMessage}`);
315
429
  }
316
430
  }
317
- async function writeFileContent(filePath, content, encoding = "utf-8", fsAdapter) {
431
+ async function writeFileContent(filePath, content, encoding = "utf-8", fsAdapter, projectRoot) {
318
432
  const adapter = fsAdapter || createDefaultFsAdapter();
319
- const fullPath = resolve2(filePath);
320
- const parentDir = dirname2(fullPath);
433
+ let fullPath = resolve3(filePath);
434
+ if (projectRoot) {
435
+ try {
436
+ fullPath = await validatePathInProjectWithSymlinks(projectRoot, filePath);
437
+ } catch (error) {
438
+ const errorMsg = getPathValidationErrorMessage(error);
439
+ logger.error(`Path traversal attempt blocked: ${errorMsg}`);
440
+ throw new Error(`Invalid path: ${errorMsg}`);
441
+ }
442
+ }
443
+ const parentDir = dirname3(fullPath);
321
444
  await ensureDirectory(parentDir, adapter);
322
445
  try {
323
446
  await adapter.writeFile(fullPath, content, encoding);
@@ -330,6 +453,8 @@ async function writeFileContent(filePath, content, encoding = "utf-8", fsAdapter
330
453
 
331
454
  export {
332
455
  createDefaultFsAdapter,
456
+ validatePathInProjectWithSymlinks,
457
+ getPathValidationErrorMessage,
333
458
  normalizePath,
334
459
  readPackageJson,
335
460
  writePackageJson,