@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,19 +1,18 @@
1
1
  import {
2
2
  installPackages
3
- } from "./chunk-6GV4NKUX.js";
3
+ } from "./chunk-V2IBYLVH.js";
4
4
  import {
5
5
  checkPathExists,
6
6
  ensureDirectory,
7
7
  normalizePath,
8
8
  readFileContent,
9
- readPackageJson,
10
9
  writeFileContent,
11
10
  writePackageJson
12
- } from "./chunk-FIB2J36N.js";
11
+ } from "./chunk-HI7RYD6W.js";
13
12
  import {
14
13
  getModuleLogger,
15
14
  logger
16
- } from "./chunk-QPEUT7QG.js";
15
+ } from "./chunk-VN4XTFDK.js";
17
16
 
18
17
  // src/types/index.ts
19
18
  var Category = /* @__PURE__ */ ((Category2) => {
@@ -31,6 +30,426 @@ var Category = /* @__PURE__ */ ((Category2) => {
31
30
  return Category2;
32
31
  })(Category || {});
33
32
 
33
+ // src/core/config-sanitizer.ts
34
+ import { parse } from "@babel/parser";
35
+ import { parseDocument } from "yaml";
36
+ import { parse as parseToml } from "@iarna/toml";
37
+ var ConfigSanitizer = class {
38
+ /**
39
+ * Validates and sanitizes JSON configuration
40
+ * Rejects if:
41
+ * - Invalid JSON syntax
42
+ * - Circular references
43
+ * - Prototype pollution attempts
44
+ *
45
+ * @param content - Raw JSON content
46
+ * @returns Parsed and validated JSON object
47
+ * @throws Error if JSON is invalid or contains injection attempts
48
+ */
49
+ static validateJSON(content) {
50
+ try {
51
+ const parsed = JSON.parse(content);
52
+ if (this.hasPrototypePollution(parsed)) {
53
+ throw new Error("Prototype pollution detected in JSON");
54
+ }
55
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
56
+ throw new Error("JSON must be an object");
57
+ }
58
+ return parsed;
59
+ } catch (error) {
60
+ if (error instanceof SyntaxError) {
61
+ throw new Error(`Invalid JSON: ${error.message}`);
62
+ }
63
+ throw error;
64
+ }
65
+ }
66
+ /**
67
+ * Validates JavaScript configuration file
68
+ * Rejects if:
69
+ * - Invalid JavaScript syntax
70
+ * - Contains eval/Function/require calls (except static imports)
71
+ * - Contains suspicious patterns
72
+ *
73
+ * @param content - Raw JavaScript content
74
+ * @returns Validated content (still as string, needs parsing via AST)
75
+ * @throws Error if JavaScript is invalid or contains suspicious code
76
+ */
77
+ static validateJavaScript(content) {
78
+ const ast = this.parseJavaScriptAst(content);
79
+ const blockedModules = /* @__PURE__ */ new Set([
80
+ "child_process",
81
+ "node:child_process",
82
+ "fs",
83
+ "node:fs",
84
+ "fs/promises",
85
+ "node:fs/promises"
86
+ ]);
87
+ this.walkAst(ast, (node, parent, parentKey) => {
88
+ if (node["type"] === "CallExpression") {
89
+ const callee = node["callee"];
90
+ if (callee?.["type"] === "Identifier" && callee["name"] === "eval") {
91
+ throw new Error("Dangerous pattern detected in JavaScript: eval()");
92
+ }
93
+ if (callee?.["type"] === "Identifier" && callee["name"] === "require") {
94
+ throw new Error("Dangerous pattern detected in JavaScript: require()");
95
+ }
96
+ const requireObject = callee?.["type"] === "MemberExpression" ? callee["object"] : void 0;
97
+ if (callee?.["type"] === "MemberExpression" && requireObject?.["type"] === "Identifier" && requireObject["name"] === "require") {
98
+ throw new Error("Dangerous pattern detected in JavaScript: require()");
99
+ }
100
+ }
101
+ if (node["type"] === "NewExpression") {
102
+ const callee = node["callee"];
103
+ if (callee?.["type"] === "Identifier" && callee["name"] === "Function") {
104
+ throw new Error(
105
+ "Dangerous pattern detected in JavaScript: new Function()"
106
+ );
107
+ }
108
+ }
109
+ if (node["type"] === "ImportExpression") {
110
+ throw new Error("Dangerous pattern detected in JavaScript: import()");
111
+ }
112
+ if (node["type"] === "ImportDeclaration") {
113
+ const source = node["source"];
114
+ const sourceValue = source?.["value"];
115
+ if (typeof sourceValue === "string" && blockedModules.has(sourceValue)) {
116
+ throw new Error(
117
+ `Dangerous pattern detected in JavaScript: import ${sourceValue}`
118
+ );
119
+ }
120
+ }
121
+ if (node["type"] === "MemberExpression") {
122
+ const objectNode = node["object"];
123
+ if (objectNode?.["type"] === "Identifier" && objectNode["name"] === "process") {
124
+ throw new Error(
125
+ "Dangerous pattern detected in JavaScript: process access"
126
+ );
127
+ }
128
+ }
129
+ if (node["type"] === "Identifier") {
130
+ const identifierName = node["name"];
131
+ const isObjectKey = parent?.["type"] === "ObjectProperty" && parentKey === "key" && parent["computed"] === false;
132
+ if (!isObjectKey && identifierName === "__dirname") {
133
+ throw new Error("Dangerous pattern detected in JavaScript: __dirname");
134
+ }
135
+ if (!isObjectKey && identifierName === "__filename") {
136
+ throw new Error(
137
+ "Dangerous pattern detected in JavaScript: __filename"
138
+ );
139
+ }
140
+ }
141
+ if (node["type"] === "TemplateLiteral") {
142
+ const expressions = node["expressions"];
143
+ if (Array.isArray(expressions) && expressions.length > 0) {
144
+ throw new Error(
145
+ "Dangerous pattern detected in JavaScript: template literal"
146
+ );
147
+ }
148
+ }
149
+ });
150
+ return content;
151
+ }
152
+ /**
153
+ * Validates YAML configuration
154
+ * Rejects if:
155
+ * - Invalid YAML syntax
156
+ * - Contains dangerous tags (!!, !!python, !!env)
157
+ * - Suspicious merge keys
158
+ *
159
+ * @param content - Raw YAML content
160
+ * @returns Validated content
161
+ * @throws Error if YAML is invalid or contains injection attempts
162
+ */
163
+ static validateYAML(content) {
164
+ const document = parseDocument(content, {
165
+ schema: "core"
166
+ });
167
+ if (document.errors.length > 0) {
168
+ throw new Error(
169
+ `Invalid YAML: ${document.errors[0]?.message ?? "unknown"}`
170
+ );
171
+ }
172
+ this.validateYamlNodes(document.contents);
173
+ const parsed = document.toJS({ maxAliasCount: 50 });
174
+ if (this.hasPrototypePollution(parsed)) {
175
+ throw new Error("Prototype pollution detected in YAML");
176
+ }
177
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
178
+ throw new Error("YAML must be an object");
179
+ }
180
+ if (this.hasSuspiciousStringValue(parsed)) {
181
+ throw new Error("Template syntax detected in YAML");
182
+ }
183
+ if (this.hasDangerousKey(parsed, /* @__PURE__ */ new Set(["exec", "eval", "require"]))) {
184
+ throw new Error("Dangerous key detected in YAML");
185
+ }
186
+ return content;
187
+ }
188
+ /**
189
+ * Validates TOML configuration
190
+ * Rejects if:
191
+ * - Invalid TOML syntax
192
+ * - Suspicious key names
193
+ * - Values that look like code injection
194
+ *
195
+ * @param content - Raw TOML content
196
+ * @returns Validated content
197
+ * @throws Error if TOML is invalid or contains injection attempts
198
+ */
199
+ static validateTOML(content) {
200
+ let parsed;
201
+ try {
202
+ parsed = parseToml(content);
203
+ } catch (error) {
204
+ const errorMessage = error instanceof Error ? error.message : String(error);
205
+ throw new Error(`Invalid TOML: ${errorMessage}`);
206
+ }
207
+ if (this.hasPrototypePollution(parsed)) {
208
+ throw new Error("Prototype pollution detected in TOML");
209
+ }
210
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
211
+ throw new Error("TOML must be an object");
212
+ }
213
+ if (this.hasSuspiciousStringValue(parsed)) {
214
+ throw new Error("Template syntax detected in TOML");
215
+ }
216
+ if (this.hasDangerousKey(parsed, /* @__PURE__ */ new Set(["exec", "eval", "require"]))) {
217
+ throw new Error("Dangerous key detected in TOML");
218
+ }
219
+ return content;
220
+ }
221
+ /**
222
+ * Escapes string values to prevent injection
223
+ * Used when inserting values into configs
224
+ *
225
+ * @param value - Value to escape
226
+ * @param format - Config format (json, js, yaml, toml)
227
+ * @returns Escaped value safe for insertion
228
+ */
229
+ static escapeValue(value, format = "json") {
230
+ if (!value) return value;
231
+ switch (format) {
232
+ case "json":
233
+ return JSON.stringify(value);
234
+ case "js":
235
+ return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
236
+ case "yaml":
237
+ if (value.includes(":") || value.includes("#") || value.includes("{")) {
238
+ return `"${value.replace(/"/g, '\\"')}"`;
239
+ }
240
+ return value;
241
+ case "toml":
242
+ return `"${value.replace(/"/g, '\\"')}"`;
243
+ default:
244
+ return value;
245
+ }
246
+ }
247
+ /**
248
+ * Detects prototype pollution attempts in objects
249
+ * Checks for dangerous keys like __proto__, constructor, prototype
250
+ *
251
+ * @param obj - Object to check
252
+ * @returns True if prototype pollution attempt detected
253
+ */
254
+ static hasPrototypePollution(obj) {
255
+ if (typeof obj !== "object" || obj === null) {
256
+ return false;
257
+ }
258
+ const dangerousKeys = ["__proto__", "constructor", "prototype"];
259
+ for (const [key, value] of Object.entries(obj)) {
260
+ if (dangerousKeys.includes(key)) {
261
+ return true;
262
+ }
263
+ if (typeof value === "object" && value !== null) {
264
+ if (this.hasPrototypePollution(value)) {
265
+ return true;
266
+ }
267
+ }
268
+ }
269
+ return false;
270
+ }
271
+ static parseJavaScriptAst(content) {
272
+ try {
273
+ return parse(content, {
274
+ sourceType: "module",
275
+ plugins: ["typescript", "jsx"]
276
+ });
277
+ } catch {
278
+ try {
279
+ return parse(content, {
280
+ sourceType: "script",
281
+ plugins: ["typescript", "jsx"]
282
+ });
283
+ } catch (error) {
284
+ const errorMessage = error instanceof Error ? error.message : String(error);
285
+ throw new Error(`Invalid JavaScript: ${errorMessage}`);
286
+ }
287
+ }
288
+ }
289
+ static walkAst(node, visitor, parent, parentKey) {
290
+ if (!node || typeof node !== "object") {
291
+ return;
292
+ }
293
+ const currentNode = node;
294
+ if (typeof currentNode["type"] !== "string") {
295
+ return;
296
+ }
297
+ visitor(currentNode, parent, parentKey);
298
+ for (const [key, value] of Object.entries(currentNode)) {
299
+ if (Array.isArray(value)) {
300
+ for (const item of value) {
301
+ this.walkAst(item, visitor, currentNode, key);
302
+ }
303
+ } else if (value && typeof value === "object") {
304
+ this.walkAst(value, visitor, currentNode, key);
305
+ }
306
+ }
307
+ }
308
+ static validateYamlNodes(node) {
309
+ if (!node || typeof node !== "object") {
310
+ return;
311
+ }
312
+ const currentNode = node;
313
+ if (currentNode.tag) {
314
+ const allowedTags = /* @__PURE__ */ new Set([
315
+ "tag:yaml.org,2002:map",
316
+ "tag:yaml.org,2002:seq",
317
+ "tag:yaml.org,2002:str",
318
+ "tag:yaml.org,2002:int",
319
+ "tag:yaml.org,2002:float",
320
+ "tag:yaml.org,2002:bool",
321
+ "tag:yaml.org,2002:null"
322
+ ]);
323
+ if (!allowedTags.has(currentNode.tag)) {
324
+ throw new Error(`Dangerous YAML tag detected: ${currentNode.tag}`);
325
+ }
326
+ }
327
+ if (Array.isArray(currentNode.items)) {
328
+ for (const item of currentNode.items) {
329
+ const pair = item;
330
+ const keyValue = pair?.key?.value;
331
+ if (keyValue === "<<") {
332
+ throw new Error("Dangerous YAML merge key detected");
333
+ }
334
+ this.validateYamlNodes(pair?.key);
335
+ this.validateYamlNodes(pair?.value);
336
+ }
337
+ }
338
+ this.validateYamlNodes(currentNode.key);
339
+ this.validateYamlNodes(currentNode.value);
340
+ }
341
+ static hasSuspiciousStringValue(value) {
342
+ if (typeof value === "string") {
343
+ return value.includes("${") || value.includes("`");
344
+ }
345
+ if (Array.isArray(value)) {
346
+ return value.some((item) => this.hasSuspiciousStringValue(item));
347
+ }
348
+ if (value && typeof value === "object") {
349
+ return Object.values(value).some(
350
+ (item) => this.hasSuspiciousStringValue(item)
351
+ );
352
+ }
353
+ return false;
354
+ }
355
+ static hasDangerousKey(value, dangerousKeys) {
356
+ if (Array.isArray(value)) {
357
+ return value.some((item) => this.hasDangerousKey(item, dangerousKeys));
358
+ }
359
+ if (value && typeof value === "object") {
360
+ for (const [key, item] of Object.entries(value)) {
361
+ if (dangerousKeys.has(key)) {
362
+ return true;
363
+ }
364
+ if (this.hasDangerousKey(item, dangerousKeys)) {
365
+ return true;
366
+ }
367
+ }
368
+ }
369
+ return false;
370
+ }
371
+ /**
372
+ * Validates that a config file can be safely modified
373
+ * Checks structure and ensures it matches expected format
374
+ *
375
+ * @param content - Config file content
376
+ * @param format - Expected config format
377
+ * @returns True if config is valid and safe to modify
378
+ * @throws Error if config is invalid
379
+ */
380
+ static canSafelyModify(content, format) {
381
+ try {
382
+ switch (format) {
383
+ case "json":
384
+ this.validateJSON(content);
385
+ break;
386
+ case "js":
387
+ this.validateJavaScript(content);
388
+ break;
389
+ case "yaml":
390
+ this.validateYAML(content);
391
+ break;
392
+ case "toml":
393
+ this.validateTOML(content);
394
+ break;
395
+ }
396
+ return true;
397
+ } catch {
398
+ return false;
399
+ }
400
+ }
401
+ /**
402
+ * Merges configurations safely
403
+ * Prevents injection by validating before merge
404
+ *
405
+ * @param original - Original config object
406
+ * @param updates - Updates to merge (user-provided)
407
+ * @param format - Config format
408
+ * @returns Merged configuration
409
+ * @throws Error if updates contain injection attempts
410
+ */
411
+ static mergeSafely(original, updates, format = "json") {
412
+ if (this.hasPrototypePollution(updates)) {
413
+ throw new Error("Prototype pollution detected in updates");
414
+ }
415
+ const result = { ...original };
416
+ for (const [key, value] of Object.entries(updates)) {
417
+ if (!this.isValidKey(key)) {
418
+ throw new Error(`Invalid key name: ${key}`);
419
+ }
420
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
421
+ result[key] = this.mergeSafely(
422
+ original[key] || {},
423
+ value,
424
+ format
425
+ );
426
+ } else {
427
+ result[key] = value;
428
+ }
429
+ }
430
+ return result;
431
+ }
432
+ /**
433
+ * Validates if a key name is safe to use
434
+ * Prevents prototype pollution and suspicious keys
435
+ *
436
+ * @param key - Key name to validate
437
+ * @returns True if key is valid and safe
438
+ */
439
+ static isValidKey(key) {
440
+ const dangerousKeys = [
441
+ "__proto__",
442
+ "constructor",
443
+ "prototype",
444
+ "constructor.prototype"
445
+ ];
446
+ if (dangerousKeys.includes(key)) {
447
+ return false;
448
+ }
449
+ return /^[a-zA-Z0-9_-]+$/.test(key);
450
+ }
451
+ };
452
+
34
453
  // src/plugins/animation/framer-motion.ts
35
454
  import { join } from "path";
36
455
 
@@ -258,6 +677,7 @@ var BackupManager = class {
258
677
 
259
678
  // src/core/config-writer.ts
260
679
  import { resolve as resolve2, dirname } from "path";
680
+ import { createHash } from "crypto";
261
681
  var ConfigWriter = class {
262
682
  /**
263
683
  * @param backupManager - Gestionnaire de backups à utiliser
@@ -268,6 +688,7 @@ var ConfigWriter = class {
268
688
  this.fsAdapter = fsAdapter;
269
689
  }
270
690
  logger = getModuleLogger();
691
+ jsonCache = /* @__PURE__ */ new Map();
271
692
  /**
272
693
  * Écrit ou modifie un fichier avec backup automatique
273
694
  *
@@ -359,24 +780,32 @@ var ConfigWriter = class {
359
780
  */
360
781
  async modifyPackageJson(projectRoot, modifier) {
361
782
  const fullPath = resolve2(projectRoot);
783
+ const packageJsonPath = resolve2(fullPath, "package.json");
362
784
  let pkg;
785
+ let existingContent;
363
786
  try {
364
- pkg = await readPackageJson(fullPath, this.fsAdapter);
787
+ existingContent = await readFileContent(
788
+ packageJsonPath,
789
+ "utf-8",
790
+ this.fsAdapter
791
+ );
365
792
  } catch (error) {
366
793
  const errorMessage = error instanceof Error ? error.message : String(error);
367
794
  throw new Error(
368
795
  `Failed to read package.json: ${errorMessage}. Make sure you're in a valid project directory.`
369
796
  );
370
797
  }
371
- const packageJsonPath = resolve2(fullPath, "package.json");
798
+ try {
799
+ pkg = this.parseJsonWithCache(packageJsonPath, existingContent);
800
+ } catch (error) {
801
+ const errorMessage = error instanceof Error ? error.message : String(error);
802
+ throw new Error(
803
+ `Failed to read package.json: ${errorMessage}. File may be invalid JSON.`
804
+ );
805
+ }
372
806
  if (this.backupManager.hasBackup(packageJsonPath)) {
373
807
  } else {
374
808
  try {
375
- const existingContent = await readFileContent(
376
- packageJsonPath,
377
- "utf-8",
378
- this.fsAdapter
379
- );
380
809
  this.backupManager.backup(packageJsonPath, existingContent);
381
810
  } catch (error) {
382
811
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -389,6 +818,7 @@ var ConfigWriter = class {
389
818
  try {
390
819
  await writePackageJson(fullPath, modifiedPkg, this.fsAdapter);
391
820
  this.logger.debug(`Modified package.json: ${packageJsonPath}`);
821
+ this.jsonCache.delete(packageJsonPath);
392
822
  } catch (error) {
393
823
  const errorMessage = error instanceof Error ? error.message : String(error);
394
824
  throw new Error(
@@ -497,6 +927,16 @@ var ConfigWriter = class {
497
927
  });
498
928
  this.logger.debug(`Injected import into ${fullPath}`);
499
929
  }
930
+ parseJsonWithCache(filePath, content) {
931
+ const hash = createHash("sha256").update(content).digest("hex");
932
+ const cached = this.jsonCache.get(filePath);
933
+ if (cached?.hash === hash) {
934
+ return cached.parsed;
935
+ }
936
+ const parsed = JSON.parse(content);
937
+ this.jsonCache.set(filePath, { hash, parsed });
938
+ return parsed;
939
+ }
500
940
  };
501
941
 
502
942
  // src/plugins/utils/plugin-services.ts
@@ -4707,6 +5147,12 @@ var nextjsImageOptimizationPlugin = {
4707
5147
  "utf-8",
4708
5148
  ctx.fsAdapter
4709
5149
  );
5150
+ const configFormat = extension === "ts" ? "js" : "js";
5151
+ if (!ConfigSanitizer.canSafelyModify(existingContent, configFormat)) {
5152
+ throw new Error(
5153
+ `Invalid or unsafe next.config.${extension} - cannot safely modify`
5154
+ );
5155
+ }
4710
5156
  const updatedContent = injectImageConfig(existingContent, extension);
4711
5157
  if (updatedContent !== existingContent) {
4712
5158
  await writer.writeFile(nextConfigPath, updatedContent, {
@@ -4802,6 +5248,9 @@ function injectImageConfig(content, _extension) {
4802
5248
  logger17.warn("Image configuration already exists in next.config");
4803
5249
  return content;
4804
5250
  }
5251
+ if (!ConfigSanitizer.canSafelyModify(content, "js")) {
5252
+ throw new Error("Invalid JavaScript in next.config file");
5253
+ }
4805
5254
  let modifiedContent = content;
4806
5255
  const configRegex = /(const\s+nextConfig\s*=\s*\{|nextConfig\s*=\s*\{)([\s\S]*?)(\})/m;
4807
5256
  if (configRegex.test(modifiedContent)) {
@@ -4811,24 +5260,29 @@ function injectImageConfig(content, _extension) {
4811
5260
  if (configContent.includes("images:")) {
4812
5261
  return match;
4813
5262
  }
5263
+ const imageConfig = buildImageConfig();
4814
5264
  const trimmed = configContent.trim();
4815
5265
  const hasTrailingComma = trimmed.endsWith(",");
4816
- const imageConfig = ` images: {
4817
- remotePatterns: [
4818
- {
4819
- protocol: 'https',
4820
- hostname: '**',
4821
- },
4822
- ],
4823
- },${hasTrailingComma ? "" : "\n"}`;
4824
5266
  return `${start}${trimmed}${hasTrailingComma ? "" : ","}
4825
5267
  ${imageConfig}
4826
5268
  }`;
4827
5269
  }
4828
5270
  );
4829
5271
  } else {
4830
- const imageConfig = `
4831
- images: {
5272
+ const imageConfig = buildImageConfig();
5273
+ modifiedContent = modifiedContent.replace(
5274
+ /(\}\s*)$/,
5275
+ (match) => ` ${imageConfig}
5276
+ ${match}`
5277
+ );
5278
+ }
5279
+ if (!ConfigSanitizer.canSafelyModify(modifiedContent, "js")) {
5280
+ throw new Error("Failed to safely inject image configuration");
5281
+ }
5282
+ return modifiedContent;
5283
+ }
5284
+ function buildImageConfig() {
5285
+ return ` images: {
4832
5286
  remotePatterns: [
4833
5287
  {
4834
5288
  protocol: 'https',
@@ -4836,13 +5290,6 @@ ${imageConfig}
4836
5290
  },
4837
5291
  ],
4838
5292
  },`;
4839
- modifiedContent = modifiedContent.replace(
4840
- /(\}\s*)$/,
4841
- (match) => `${imageConfig}
4842
- ${match}`
4843
- );
4844
- }
4845
- return modifiedContent;
4846
5293
  }
4847
5294
 
4848
5295
  // src/plugins/nextjs/middleware.ts
@@ -15321,6 +15768,7 @@ function getRecommendedPlugins(ctx) {
15321
15768
  export {
15322
15769
  BackupManager,
15323
15770
  ConfigWriter,
15771
+ ConfigSanitizer,
15324
15772
  pluginRegistry,
15325
15773
  getPluginsByCategory,
15326
15774
  getRecommendedPlugins
@@ -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
+ };