@configjs/cli 1.1.16 → 1.1.17

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-WRBFDIV6.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-5QZOSS2F.js +168 -0
  5. package/dist/{chunk-NYCK4R4K.js → chunk-56AR5ZW2.js} +361 -96
  6. package/dist/{chunk-TN27AX4L.js → chunk-5FBN3L7R.js} +797 -18
  7. package/dist/{chunk-FIB2J36N.js → chunk-5NXT5HCC.js} +142 -20
  8. package/dist/chunk-EDCNW4UO.js +92 -0
  9. package/dist/{chunk-UKEHW2LH.js → chunk-F3VNGA2S.js} +17 -3
  10. package/dist/{chunk-JYWGJJ4M.js → chunk-IY4KX4KL.js} +476 -28
  11. package/dist/chunk-V2IBYLVH.js +932 -0
  12. package/dist/chunk-VN4XTFDK.js +315 -0
  13. package/dist/cli.js +31 -21
  14. package/dist/{installed-D6CUYQM5.js → installed-DI77OQIT.js} +4 -4
  15. package/dist/{list-VZDUWV5O.js → list-HWRVKXWR.js} +4 -4
  16. package/dist/{nextjs-command-WKKOAY7I.js → nextjs-command-H5WRXK5R.js} +8 -9
  17. package/dist/{nextjs-installer-2ZC5IWJ6.js → nextjs-installer-UM3YQT5Z.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-XNOHP6WY.js} +8 -9
  20. package/dist/{remove-ZY3MLPGN.js → remove-AJK2IHVJ.js} +4 -4
  21. package/dist/{svelte-command-B2DNNQ5Z.js → svelte-command-Q7UBBWXP.js} +8 -8
  22. package/dist/svelte-installer-WLGID6Z2.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-U7XUU3D7.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-FL6OQ6GV.js} +8 -9
  27. package/dist/{vue-installer-DGBBVF6F.js → vue-installer-BH2O2Y3M.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,115 @@
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 { pathExists, realpath } from "fs-extra";
8
+ import { z } from "zod";
9
+ var pathResolutionSchema = z.object({
10
+ projectRoot: z.string().min(1, "Project root cannot be empty").refine(
11
+ (path) => isAbsolute(path),
12
+ "Project root must be an absolute path"
13
+ ),
14
+ 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) => {
15
+ for (let i = 0; i < path.length; i++) {
16
+ const charCode = path.charCodeAt(i);
17
+ if (charCode >= 0 && charCode <= 31) {
18
+ return false;
19
+ }
20
+ }
21
+ return true;
22
+ }, "Path cannot contain control characters")
23
+ });
24
+ function validatePathInProject(projectRoot, userPath) {
25
+ const validated = pathResolutionSchema.parse({
26
+ projectRoot,
27
+ userPath
28
+ });
29
+ const normalizedRoot = normalize(resolve(validated.projectRoot));
30
+ const resolvedPath = normalize(resolve(normalizedRoot, validated.userPath));
31
+ const isWithinBoundary = resolvedPath === normalizedRoot || resolvedPath.startsWith(normalizedRoot + sep);
32
+ if (!isWithinBoundary) {
33
+ throw new Error(
34
+ `Path traversal detected: "${userPath}" attempts to escape project root. Resolved: "${resolvedPath}", allowed: "${normalizedRoot}"`
35
+ );
36
+ }
37
+ if (userPath.includes("..")) {
38
+ throw new Error(
39
+ `Path contains parent directory (..): "${userPath}". Only paths within project root allowed.`
40
+ );
41
+ }
42
+ if (isAbsolute(userPath)) {
43
+ throw new Error(
44
+ `Absolute paths not allowed: "${userPath}". Use path relative to project root.`
45
+ );
46
+ }
47
+ return resolvedPath;
48
+ }
49
+ async function findClosestExistingPath(boundaryRoot, resolvedPath) {
50
+ let currentPath = resolvedPath;
51
+ while (true) {
52
+ if (await pathExists(currentPath)) {
53
+ return currentPath;
54
+ }
55
+ if (currentPath === boundaryRoot) {
56
+ return null;
57
+ }
58
+ const parentPath = dirname(currentPath);
59
+ if (parentPath === currentPath) {
60
+ return null;
61
+ }
62
+ currentPath = parentPath;
63
+ }
64
+ }
65
+ async function validateSymlinkBoundary(projectRoot, resolvedPath) {
66
+ const normalizedRoot = normalize(resolve(projectRoot));
67
+ const realRoot = await realpath(normalizedRoot).catch(() => normalizedRoot);
68
+ const existingPath = await findClosestExistingPath(
69
+ normalizedRoot,
70
+ resolvedPath
71
+ );
72
+ if (!existingPath) {
73
+ return;
74
+ }
75
+ const realExisting = await realpath(existingPath).catch(() => existingPath);
76
+ const normalizedRealExisting = normalize(realExisting);
77
+ const normalizedRealRoot = normalize(realRoot);
78
+ const isWithinBoundary = normalizedRealExisting === normalizedRealRoot || normalizedRealExisting.startsWith(normalizedRealRoot + sep);
79
+ if (!isWithinBoundary) {
80
+ throw new Error(
81
+ `Symlink traversal detected: "${resolvedPath}" resolves outside project root. Resolved: "${normalizedRealExisting}", allowed: "${normalizedRealRoot}"`
82
+ );
83
+ }
84
+ }
85
+ async function validatePathInProjectWithSymlinks(projectRoot, userPath) {
86
+ const resolvedPath = validatePathInProject(projectRoot, userPath);
87
+ await validateSymlinkBoundary(projectRoot, resolvedPath);
88
+ return resolvedPath;
89
+ }
90
+ function getPathValidationErrorMessage(error) {
91
+ if (error instanceof z.ZodError) {
92
+ const issues = error.flatten().fieldErrors;
93
+ const messages = [];
94
+ for (const [field, msgs] of Object.entries(issues)) {
95
+ if (msgs && Array.isArray(msgs)) {
96
+ messages.push(`${field}: ${msgs.join(", ")}`);
97
+ }
98
+ }
99
+ return messages.join("; ") || "Invalid path format";
100
+ }
101
+ if (error instanceof Error) {
102
+ return error.message;
103
+ }
104
+ return "Path validation failed";
105
+ }
4
106
 
5
107
  // src/utils/fs-helpers.ts
6
- import { resolve as resolve2, dirname as dirname2, extname } from "path";
108
+ import { resolve as resolve3, dirname as dirname3, extname } from "path";
7
109
 
8
110
  // src/core/fs-adapter.ts
9
111
  import fs from "fs-extra";
10
- import { resolve, dirname } from "path";
112
+ import { resolve as resolve2, dirname as dirname2 } from "path";
11
113
  var FileSystemAdapter = class {
12
114
  /**
13
115
  * @param fsImpl - Implémentation du filesystem (memfs) ou undefined pour fs-extra
@@ -25,7 +127,7 @@ var FileSystemAdapter = class {
25
127
  * Normalise un chemin (résout les chemins relatifs)
26
128
  */
27
129
  normalizePath(path) {
28
- return resolve(path);
130
+ return resolve2(path);
29
131
  }
30
132
  // ===== Async Operations =====
31
133
  async readFile(path, encoding = "utf-8") {
@@ -50,7 +152,7 @@ var FileSystemAdapter = class {
50
152
  async writeFile(path, content, encoding = "utf-8") {
51
153
  const fullPath = this.normalizePath(path);
52
154
  const implementation = this.getFs();
53
- const parentDir = dirname(fullPath);
155
+ const parentDir = dirname2(fullPath);
54
156
  await this.mkdir(parentDir, { recursive: true });
55
157
  if (this.fsImpl) {
56
158
  await implementation.promises.writeFile(fullPath, content, encoding);
@@ -120,7 +222,7 @@ var FileSystemAdapter = class {
120
222
  }
121
223
  async writeJson(path, data, options) {
122
224
  const fullPath = this.normalizePath(path);
123
- const parentDir = dirname(fullPath);
225
+ const parentDir = dirname2(fullPath);
124
226
  await this.mkdir(parentDir, { recursive: true });
125
227
  if (this.fsImpl) {
126
228
  const content = JSON.stringify(data, null, options?.spaces ?? 2);
@@ -135,7 +237,7 @@ var FileSystemAdapter = class {
135
237
  async copyFile(src, dest) {
136
238
  const srcPath = this.normalizePath(src);
137
239
  const destPath = this.normalizePath(dest);
138
- const destDir = dirname(destPath);
240
+ const destDir = dirname2(destPath);
139
241
  await this.mkdir(destDir, { recursive: true });
140
242
  if (this.fsImpl) {
141
243
  const content = await this.readFile(srcPath);
@@ -184,7 +286,7 @@ var FileSystemAdapter = class {
184
286
  writeFileSync(path, content) {
185
287
  const fullPath = this.normalizePath(path);
186
288
  const implementation = this.getFs();
187
- const parentDir = dirname(fullPath);
289
+ const parentDir = dirname2(fullPath);
188
290
  if (!this.existsSync(parentDir)) {
189
291
  this.mkdirSync(parentDir, { recursive: true });
190
292
  }
@@ -229,7 +331,7 @@ function normalizePath(path) {
229
331
  }
230
332
  async function readPackageJson(root, fsAdapter) {
231
333
  const adapter = fsAdapter || createDefaultFsAdapter();
232
- const packageJsonPath = resolve2(root, "package.json");
334
+ const packageJsonPath = resolve3(root, "package.json");
233
335
  if (!await adapter.pathExists(packageJsonPath)) {
234
336
  throw new Error(`package.json not found at ${packageJsonPath}`);
235
337
  }
@@ -246,7 +348,7 @@ async function readPackageJson(root, fsAdapter) {
246
348
  }
247
349
  async function writePackageJson(root, pkg, fsAdapter) {
248
350
  const adapter = fsAdapter || createDefaultFsAdapter();
249
- const packageJsonPath = resolve2(root, "package.json");
351
+ const packageJsonPath = resolve3(root, "package.json");
250
352
  try {
251
353
  await adapter.writeJson(packageJsonPath, pkg, {
252
354
  spaces: 2,
@@ -261,9 +363,9 @@ async function writePackageJson(root, pkg, fsAdapter) {
261
363
  async function readTsConfig(root, fsAdapter) {
262
364
  const adapter = fsAdapter || createDefaultFsAdapter();
263
365
  const possiblePaths = [
264
- resolve2(root, "tsconfig.json"),
265
- resolve2(root, "tsconfig.app.json"),
266
- resolve2(root, "tsconfig.node.json")
366
+ resolve3(root, "tsconfig.json"),
367
+ resolve3(root, "tsconfig.app.json"),
368
+ resolve3(root, "tsconfig.node.json")
267
369
  ];
268
370
  for (const tsconfigPath of possiblePaths) {
269
371
  if (await adapter.pathExists(tsconfigPath)) {
@@ -285,12 +387,12 @@ async function readTsConfig(root, fsAdapter) {
285
387
  }
286
388
  async function checkPathExists(path, fsAdapter) {
287
389
  const adapter = fsAdapter || createDefaultFsAdapter();
288
- const fullPath = resolve2(path);
390
+ const fullPath = resolve3(path);
289
391
  return adapter.pathExists(fullPath);
290
392
  }
291
393
  async function ensureDirectory(path, fsAdapter) {
292
394
  const adapter = fsAdapter || createDefaultFsAdapter();
293
- const fullPath = resolve2(path);
395
+ const fullPath = resolve3(path);
294
396
  try {
295
397
  await adapter.mkdir(fullPath, { recursive: true });
296
398
  logger.debug(`Ensured directory exists: ${fullPath}`);
@@ -299,9 +401,18 @@ async function ensureDirectory(path, fsAdapter) {
299
401
  throw new Error(`Failed to create directory ${fullPath}: ${errorMessage}`);
300
402
  }
301
403
  }
302
- async function readFileContent(filePath, encoding = "utf-8", fsAdapter) {
404
+ async function readFileContent(filePath, encoding = "utf-8", fsAdapter, projectRoot) {
303
405
  const adapter = fsAdapter || createDefaultFsAdapter();
304
- const fullPath = resolve2(filePath);
406
+ let fullPath = resolve3(filePath);
407
+ if (projectRoot) {
408
+ try {
409
+ fullPath = await validatePathInProjectWithSymlinks(projectRoot, filePath);
410
+ } catch (error) {
411
+ const errorMsg = getPathValidationErrorMessage(error);
412
+ logger.error(`Path traversal attempt blocked: ${errorMsg}`);
413
+ throw new Error(`Invalid path: ${errorMsg}`);
414
+ }
415
+ }
305
416
  if (!await adapter.pathExists(fullPath)) {
306
417
  throw new Error(`File not found: ${fullPath}`);
307
418
  }
@@ -314,10 +425,19 @@ async function readFileContent(filePath, encoding = "utf-8", fsAdapter) {
314
425
  throw new Error(`Failed to read file ${fullPath}: ${errorMessage}`);
315
426
  }
316
427
  }
317
- async function writeFileContent(filePath, content, encoding = "utf-8", fsAdapter) {
428
+ async function writeFileContent(filePath, content, encoding = "utf-8", fsAdapter, projectRoot) {
318
429
  const adapter = fsAdapter || createDefaultFsAdapter();
319
- const fullPath = resolve2(filePath);
320
- const parentDir = dirname2(fullPath);
430
+ let fullPath = resolve3(filePath);
431
+ if (projectRoot) {
432
+ try {
433
+ fullPath = await validatePathInProjectWithSymlinks(projectRoot, filePath);
434
+ } catch (error) {
435
+ const errorMsg = getPathValidationErrorMessage(error);
436
+ logger.error(`Path traversal attempt blocked: ${errorMsg}`);
437
+ throw new Error(`Invalid path: ${errorMsg}`);
438
+ }
439
+ }
440
+ const parentDir = dirname3(fullPath);
321
441
  await ensureDirectory(parentDir, adapter);
322
442
  try {
323
443
  await adapter.writeFile(fullPath, content, encoding);
@@ -330,6 +450,8 @@ async function writeFileContent(filePath, content, encoding = "utf-8", fsAdapter
330
450
 
331
451
  export {
332
452
  createDefaultFsAdapter,
453
+ validatePathInProjectWithSymlinks,
454
+ getPathValidationErrorMessage,
333
455
  normalizePath,
334
456
  readPackageJson,
335
457
  writePackageJson,
@@ -0,0 +1,92 @@
1
+ // src/core/input-validator.ts
2
+ import { z } from "zod";
3
+ var projectNameSchema = z.string().min(1, "Project name cannot be empty").max(100, "Project name cannot exceed 100 characters").regex(
4
+ /^[a-zA-Z0-9._-]+$/,
5
+ "Project name can only contain letters, numbers, dots, dashes, and underscores"
6
+ ).refine(
7
+ (name) => !name.includes(".."),
8
+ "Project name cannot contain path traversal (..) patterns"
9
+ ).refine(
10
+ (name) => !name.includes("/") && !name.includes("\\"),
11
+ "Project name cannot contain path separators"
12
+ ).transform((name) => name.trim());
13
+ var svelteSetupSchema = z.object({
14
+ projectName: projectNameSchema,
15
+ useTypeScript: z.boolean()
16
+ });
17
+ var angularSetupSchema = z.object({
18
+ projectName: projectNameSchema,
19
+ useTypeScript: z.boolean(),
20
+ useRouting: z.boolean(),
21
+ useStylesheet: z.enum(["css", "scss", "sass", "less"])
22
+ });
23
+ var vueSetupSchema = z.object({
24
+ projectName: projectNameSchema,
25
+ typescript: z.boolean()
26
+ });
27
+ var nextjsSetupSchema = z.object({
28
+ projectName: projectNameSchema,
29
+ typescript: z.boolean(),
30
+ eslint: z.boolean(),
31
+ tailwind: z.boolean(),
32
+ srcDir: z.boolean(),
33
+ appRouter: z.boolean(),
34
+ importAlias: z.string().min(1, "Import alias cannot be empty").max(50, "Import alias cannot exceed 50 characters").regex(
35
+ /^@[a-zA-Z0-9_/*-]+$/,
36
+ "Import alias must start with @ and contain only valid characters"
37
+ ).default("@/*")
38
+ });
39
+ var viteSetupSchema = z.object({
40
+ projectName: projectNameSchema,
41
+ template: z.enum([
42
+ "react",
43
+ "react-ts",
44
+ "vue",
45
+ "vue-ts",
46
+ "svelte",
47
+ "svelte-ts"
48
+ ])
49
+ });
50
+ function validateInput(schema, data) {
51
+ try {
52
+ return schema.parse(data);
53
+ } catch (error) {
54
+ if (error instanceof z.ZodError) {
55
+ const messages = error.flatten().fieldErrors;
56
+ const errorMessages = [];
57
+ for (const [field, msgs] of Object.entries(messages)) {
58
+ if (msgs && Array.isArray(msgs)) {
59
+ errorMessages.push(`${field}: ${msgs.join(", ")}`);
60
+ }
61
+ }
62
+ throw new Error(`Input validation failed: ${errorMessages.join("; ")}`);
63
+ }
64
+ throw error;
65
+ }
66
+ }
67
+ function getValidationErrorMessage(error) {
68
+ if (error instanceof z.ZodError) {
69
+ const messages = error.flatten().fieldErrors;
70
+ const errorMessages = [];
71
+ for (const [field, msgs] of Object.entries(messages)) {
72
+ if (msgs && Array.isArray(msgs)) {
73
+ errorMessages.push(`${field}: ${msgs.join(", ")}`);
74
+ }
75
+ }
76
+ return errorMessages.join("; ");
77
+ }
78
+ if (error instanceof Error) {
79
+ return error.message;
80
+ }
81
+ return "Validation failed";
82
+ }
83
+
84
+ export {
85
+ svelteSetupSchema,
86
+ angularSetupSchema,
87
+ vueSetupSchema,
88
+ nextjsSetupSchema,
89
+ viteSetupSchema,
90
+ validateInput,
91
+ getValidationErrorMessage
92
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  detectPackageManager
3
- } from "./chunk-6GV4NKUX.js";
3
+ } from "./chunk-V2IBYLVH.js";
4
4
  import {
5
5
  checkPathExists,
6
6
  createDefaultFsAdapter,
@@ -8,10 +8,10 @@ import {
8
8
  readPackageJson,
9
9
  readTsConfig,
10
10
  writeFileContent
11
- } from "./chunk-FIB2J36N.js";
11
+ } from "./chunk-5NXT5HCC.js";
12
12
  import {
13
13
  getModuleLogger
14
- } from "./chunk-QPEUT7QG.js";
14
+ } from "./chunk-VN4XTFDK.js";
15
15
 
16
16
  // src/core/plugin-tracker.ts
17
17
  import { join } from "path";
@@ -200,6 +200,17 @@ var PluginTracker = class {
200
200
  import { resolve, join as join2 } from "path";
201
201
  import { platform, version } from "process";
202
202
  var detectionCache = /* @__PURE__ */ new Map();
203
+ var IGNORED_DIRS = /* @__PURE__ */ new Set([
204
+ "node_modules",
205
+ ".git",
206
+ ".next",
207
+ "dist",
208
+ "build",
209
+ "coverage",
210
+ ".nuxt",
211
+ ".vite",
212
+ "out"
213
+ ]);
203
214
  var DetectionError = class extends Error {
204
215
  constructor(message, context) {
205
216
  super(message);
@@ -410,6 +421,9 @@ async function detectVueApi(projectRoot, srcDir, fsAdapter) {
410
421
  async function findVueFiles(dir) {
411
422
  const entries = await adapter.readdir(dir);
412
423
  for (const entryName of entries) {
424
+ if (IGNORED_DIRS.has(entryName)) {
425
+ continue;
426
+ }
413
427
  const fullPath = join2(dir, entryName);
414
428
  const stat = await adapter.stat(fullPath);
415
429
  if (stat.isDirectory()) {