@yrest/cli 0.8.0 → 0.8.1

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 CHANGED
@@ -10,7 +10,7 @@
10
10
  [![CI](https://github.com/aggiovato/yRest/actions/workflows/ci.yml/badge.svg)](https://github.com/aggiovato/yRest/actions)
11
11
  [![Node](https://img.shields.io/node/v/@yrest/cli)](https://www.npmjs.com/package/@yrest/cli)
12
12
  [![TypeScript](https://img.shields.io/badge/TypeScript-ready-blue)](https://www.typescriptlang.org/)
13
- [![Socket](https://badge.socket.dev/npm/package/@yrest/cli/0.8.0)](https://socket.dev/npm/package/@yrest/cli)
13
+ [![Socket](https://badge.socket.dev/npm/package/@yrest/cli/0.8.1)](https://socket.dev/npm/package/@yrest/cli)
14
14
 
15
15
  YAML-powered json-server alternative. Zero-config REST API mock server with full CRUD, relations, filters and snapshots from a `db.yml` file.
16
16
 
package/dist/cli/index.js CHANGED
@@ -292,8 +292,11 @@ var METHOD_COLOR = {
292
292
  DELETE: "#f85149",
293
293
  fn: "#f0883e"
294
294
  };
295
+ function escapeHtml(str) {
296
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
297
+ }
295
298
  function badge(label, color, bg) {
296
- return `<span class="badge" style="background:${bg};color:${color};border:1px solid ${color}40">${label}</span>`;
299
+ return `<span class="badge" style="background:${bg};color:${color};border:1px solid ${color}40">${escapeHtml(label)}</span>`;
297
300
  }
298
301
  function methodBadge(method) {
299
302
  const color = METHOD_COLOR[method] ?? "#7d8590";
@@ -303,7 +306,7 @@ function endpointRow(method, path, desc) {
303
306
  return `
304
307
  <tr>
305
308
  <td class="method-cell">${methodBadge(method)}</td>
306
- <td class="path-cell"><code>${path}</code></td>
309
+ <td class="path-cell"><code>${escapeHtml(path)}</code></td>
307
310
  <td class="desc-cell">${desc}</td>
308
311
  </tr>`;
309
312
  }
@@ -337,7 +340,7 @@ function resourceAccordion(name, base, isOpen) {
337
340
  return `
338
341
  <details class="resource-card" ${isOpen ? "open" : ""}>
339
342
  <summary>
340
- <span class="resource-name">/${name}</span>
343
+ <span class="resource-name">/${escapeHtml(name)}</span>
341
344
  <span class="route-count">6 routes</span>
342
345
  </summary>
343
346
  <table>
@@ -421,7 +424,7 @@ curl -X POST ${host}/_snapshot/reset`
421
424
  }
422
425
  if (firstCustomRoute) {
423
426
  const method = firstCustomRoute.method?.toUpperCase() ?? "GET";
424
- const fullPath = `${host}${base}${firstCustomRoute.path}`;
427
+ const fullPath = `${host}${base}${escapeHtml(firstCustomRoute.path ?? "")}`;
425
428
  const curlFlag = method === "GET" ? "" : `-X ${method} `;
426
429
  examples.push(`# Custom route
427
430
  curl ${curlFlag}${fullPath}`);
@@ -506,7 +509,8 @@ function generateAboutHtml(storage, options, handlers = /* @__PURE__ */ new Map(
506
509
  desc = `Error injection \u2014 <code>${r.error}</code>`;
507
510
  } else if (r.handler) {
508
511
  const found = handlers.has(r.handler);
509
- desc = found ? `Handler \u2014 <code>${r.handler}()</code>` : `Handler \u2014 <code>${r.handler}()</code> <span style="color:#f85149">(not loaded)</span>`;
512
+ const handlerName = escapeHtml(r.handler);
513
+ desc = found ? `Handler \u2014 <code>${handlerName}()</code>` : `Handler \u2014 <code>${handlerName}()</code> <span style="color:#f85149">(not loaded)</span>`;
510
514
  } else if (r.scenarios?.length) {
511
515
  const hasTemplateInScenarios = r.scenarios.some((s) => s.response.body != null && hasTemplates(s.response.body)) || r.otherwise?.body != null && hasTemplates(r.otherwise.body);
512
516
  desc = hasTemplateInScenarios ? `Scenarios \u2014 <code>{{\u2026}}</code>` : `Scenarios`;
@@ -779,6 +783,7 @@ function applyOperator(itemValue, op, filterValue) {
779
783
  case "_start":
780
784
  return strItem.toLowerCase().startsWith(filterValue.toLowerCase());
781
785
  case "_regex": {
786
+ if (filterValue.length > 200) return false;
782
787
  try {
783
788
  return new RegExp(filterValue, "i").test(strItem);
784
789
  } catch {
@@ -1439,8 +1444,15 @@ function loadConfigFile(configPath) {
1439
1444
 
1440
1445
  // src/utils/handlers.ts
1441
1446
  var import_node_fs5 = require("fs");
1447
+ var ALLOWED_EXTENSIONS = [".js", ".mjs", ".cjs"];
1442
1448
  async function loadHandlers(filePath) {
1443
1449
  if (!(0, import_node_fs5.existsSync)(filePath)) return /* @__PURE__ */ new Map();
1450
+ if (!ALLOWED_EXTENSIONS.some((ext) => filePath.endsWith(ext))) {
1451
+ console.error(
1452
+ ` \x1B[31m[handlers] ${filePath} \u2014 only .js, .mjs and .cjs files are allowed\x1B[0m`
1453
+ );
1454
+ return /* @__PURE__ */ new Map();
1455
+ }
1444
1456
  try {
1445
1457
  const mod = await import(filePath);
1446
1458
  const map = /* @__PURE__ */ new Map();
@@ -265,8 +265,11 @@ var METHOD_COLOR = {
265
265
  DELETE: "#f85149",
266
266
  fn: "#f0883e"
267
267
  };
268
+ function escapeHtml(str) {
269
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
270
+ }
268
271
  function badge(label, color, bg) {
269
- return `<span class="badge" style="background:${bg};color:${color};border:1px solid ${color}40">${label}</span>`;
272
+ return `<span class="badge" style="background:${bg};color:${color};border:1px solid ${color}40">${escapeHtml(label)}</span>`;
270
273
  }
271
274
  function methodBadge(method) {
272
275
  const color = METHOD_COLOR[method] ?? "#7d8590";
@@ -276,7 +279,7 @@ function endpointRow(method, path, desc) {
276
279
  return `
277
280
  <tr>
278
281
  <td class="method-cell">${methodBadge(method)}</td>
279
- <td class="path-cell"><code>${path}</code></td>
282
+ <td class="path-cell"><code>${escapeHtml(path)}</code></td>
280
283
  <td class="desc-cell">${desc}</td>
281
284
  </tr>`;
282
285
  }
@@ -310,7 +313,7 @@ function resourceAccordion(name, base, isOpen) {
310
313
  return `
311
314
  <details class="resource-card" ${isOpen ? "open" : ""}>
312
315
  <summary>
313
- <span class="resource-name">/${name}</span>
316
+ <span class="resource-name">/${escapeHtml(name)}</span>
314
317
  <span class="route-count">6 routes</span>
315
318
  </summary>
316
319
  <table>
@@ -394,7 +397,7 @@ curl -X POST ${host}/_snapshot/reset`
394
397
  }
395
398
  if (firstCustomRoute) {
396
399
  const method = firstCustomRoute.method?.toUpperCase() ?? "GET";
397
- const fullPath = `${host}${base}${firstCustomRoute.path}`;
400
+ const fullPath = `${host}${base}${escapeHtml(firstCustomRoute.path ?? "")}`;
398
401
  const curlFlag = method === "GET" ? "" : `-X ${method} `;
399
402
  examples.push(`# Custom route
400
403
  curl ${curlFlag}${fullPath}`);
@@ -479,7 +482,8 @@ function generateAboutHtml(storage, options, handlers = /* @__PURE__ */ new Map(
479
482
  desc = `Error injection \u2014 <code>${r.error}</code>`;
480
483
  } else if (r.handler) {
481
484
  const found = handlers.has(r.handler);
482
- desc = found ? `Handler \u2014 <code>${r.handler}()</code>` : `Handler \u2014 <code>${r.handler}()</code> <span style="color:#f85149">(not loaded)</span>`;
485
+ const handlerName = escapeHtml(r.handler);
486
+ desc = found ? `Handler \u2014 <code>${handlerName}()</code>` : `Handler \u2014 <code>${handlerName}()</code> <span style="color:#f85149">(not loaded)</span>`;
483
487
  } else if (r.scenarios?.length) {
484
488
  const hasTemplateInScenarios = r.scenarios.some((s) => s.response.body != null && hasTemplates(s.response.body)) || r.otherwise?.body != null && hasTemplates(r.otherwise.body);
485
489
  desc = hasTemplateInScenarios ? `Scenarios \u2014 <code>{{\u2026}}</code>` : `Scenarios`;
@@ -752,6 +756,7 @@ function applyOperator(itemValue, op, filterValue) {
752
756
  case "_start":
753
757
  return strItem.toLowerCase().startsWith(filterValue.toLowerCase());
754
758
  case "_regex": {
759
+ if (filterValue.length > 200) return false;
755
760
  try {
756
761
  return new RegExp(filterValue, "i").test(strItem);
757
762
  } catch {
@@ -1412,8 +1417,15 @@ function loadConfigFile(configPath) {
1412
1417
 
1413
1418
  // src/utils/handlers.ts
1414
1419
  import { existsSync as existsSync3 } from "fs";
1420
+ var ALLOWED_EXTENSIONS = [".js", ".mjs", ".cjs"];
1415
1421
  async function loadHandlers(filePath) {
1416
1422
  if (!existsSync3(filePath)) return /* @__PURE__ */ new Map();
1423
+ if (!ALLOWED_EXTENSIONS.some((ext) => filePath.endsWith(ext))) {
1424
+ console.error(
1425
+ ` \x1B[31m[handlers] ${filePath} \u2014 only .js, .mjs and .cjs files are allowed\x1B[0m`
1426
+ );
1427
+ return /* @__PURE__ */ new Map();
1428
+ }
1417
1429
  try {
1418
1430
  const mod = await import(filePath);
1419
1431
  const map = /* @__PURE__ */ new Map();
package/dist/index.js CHANGED
@@ -202,8 +202,15 @@ var import_node_path3 = require("path");
202
202
  // src/utils/handlers.ts
203
203
  init_cjs_shims();
204
204
  var import_node_fs = require("fs");
205
+ var ALLOWED_EXTENSIONS = [".js", ".mjs", ".cjs"];
205
206
  async function loadHandlers(filePath) {
206
207
  if (!(0, import_node_fs.existsSync)(filePath)) return /* @__PURE__ */ new Map();
208
+ if (!ALLOWED_EXTENSIONS.some((ext) => filePath.endsWith(ext))) {
209
+ console.error(
210
+ ` \x1B[31m[handlers] ${filePath} \u2014 only .js, .mjs and .cjs files are allowed\x1B[0m`
211
+ );
212
+ return /* @__PURE__ */ new Map();
213
+ }
207
214
  try {
208
215
  const mod = await import(filePath);
209
216
  const map = /* @__PURE__ */ new Map();
@@ -309,8 +316,11 @@ var METHOD_COLOR = {
309
316
  DELETE: "#f85149",
310
317
  fn: "#f0883e"
311
318
  };
319
+ function escapeHtml(str) {
320
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
321
+ }
312
322
  function badge(label, color, bg) {
313
- return `<span class="badge" style="background:${bg};color:${color};border:1px solid ${color}40">${label}</span>`;
323
+ return `<span class="badge" style="background:${bg};color:${color};border:1px solid ${color}40">${escapeHtml(label)}</span>`;
314
324
  }
315
325
  function methodBadge(method) {
316
326
  const color = METHOD_COLOR[method] ?? "#7d8590";
@@ -320,7 +330,7 @@ function endpointRow(method, path, desc) {
320
330
  return `
321
331
  <tr>
322
332
  <td class="method-cell">${methodBadge(method)}</td>
323
- <td class="path-cell"><code>${path}</code></td>
333
+ <td class="path-cell"><code>${escapeHtml(path)}</code></td>
324
334
  <td class="desc-cell">${desc}</td>
325
335
  </tr>`;
326
336
  }
@@ -354,7 +364,7 @@ function resourceAccordion(name, base, isOpen) {
354
364
  return `
355
365
  <details class="resource-card" ${isOpen ? "open" : ""}>
356
366
  <summary>
357
- <span class="resource-name">/${name}</span>
367
+ <span class="resource-name">/${escapeHtml(name)}</span>
358
368
  <span class="route-count">6 routes</span>
359
369
  </summary>
360
370
  <table>
@@ -438,7 +448,7 @@ curl -X POST ${host}/_snapshot/reset`
438
448
  }
439
449
  if (firstCustomRoute) {
440
450
  const method = firstCustomRoute.method?.toUpperCase() ?? "GET";
441
- const fullPath = `${host}${base}${firstCustomRoute.path}`;
451
+ const fullPath = `${host}${base}${escapeHtml(firstCustomRoute.path ?? "")}`;
442
452
  const curlFlag = method === "GET" ? "" : `-X ${method} `;
443
453
  examples.push(`# Custom route
444
454
  curl ${curlFlag}${fullPath}`);
@@ -523,7 +533,8 @@ function generateAboutHtml(storage, options, handlers = /* @__PURE__ */ new Map(
523
533
  desc = `Error injection \u2014 <code>${r.error}</code>`;
524
534
  } else if (r.handler) {
525
535
  const found = handlers.has(r.handler);
526
- desc = found ? `Handler \u2014 <code>${r.handler}()</code>` : `Handler \u2014 <code>${r.handler}()</code> <span style="color:#f85149">(not loaded)</span>`;
536
+ const handlerName = escapeHtml(r.handler);
537
+ desc = found ? `Handler \u2014 <code>${handlerName}()</code>` : `Handler \u2014 <code>${handlerName}()</code> <span style="color:#f85149">(not loaded)</span>`;
527
538
  } else if (r.scenarios?.length) {
528
539
  const hasTemplateInScenarios = r.scenarios.some((s) => s.response.body != null && hasTemplates(s.response.body)) || r.otherwise?.body != null && hasTemplates(r.otherwise.body);
529
540
  desc = hasTemplateInScenarios ? `Scenarios \u2014 <code>{{\u2026}}</code>` : `Scenarios`;
@@ -801,6 +812,7 @@ function applyOperator(itemValue, op, filterValue) {
801
812
  case "_start":
802
813
  return strItem.toLowerCase().startsWith(filterValue.toLowerCase());
803
814
  case "_regex": {
815
+ if (filterValue.length > 200) return false;
804
816
  try {
805
817
  return new RegExp(filterValue, "i").test(strItem);
806
818
  } catch {
package/dist/index.mjs CHANGED
@@ -168,8 +168,15 @@ import { resolve as resolve2 } from "path";
168
168
  // src/utils/handlers.ts
169
169
  init_esm_shims();
170
170
  import { existsSync } from "fs";
171
+ var ALLOWED_EXTENSIONS = [".js", ".mjs", ".cjs"];
171
172
  async function loadHandlers(filePath) {
172
173
  if (!existsSync(filePath)) return /* @__PURE__ */ new Map();
174
+ if (!ALLOWED_EXTENSIONS.some((ext) => filePath.endsWith(ext))) {
175
+ console.error(
176
+ ` \x1B[31m[handlers] ${filePath} \u2014 only .js, .mjs and .cjs files are allowed\x1B[0m`
177
+ );
178
+ return /* @__PURE__ */ new Map();
179
+ }
173
180
  try {
174
181
  const mod = await import(filePath);
175
182
  const map = /* @__PURE__ */ new Map();
@@ -275,8 +282,11 @@ var METHOD_COLOR = {
275
282
  DELETE: "#f85149",
276
283
  fn: "#f0883e"
277
284
  };
285
+ function escapeHtml(str) {
286
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
287
+ }
278
288
  function badge(label, color, bg) {
279
- return `<span class="badge" style="background:${bg};color:${color};border:1px solid ${color}40">${label}</span>`;
289
+ return `<span class="badge" style="background:${bg};color:${color};border:1px solid ${color}40">${escapeHtml(label)}</span>`;
280
290
  }
281
291
  function methodBadge(method) {
282
292
  const color = METHOD_COLOR[method] ?? "#7d8590";
@@ -286,7 +296,7 @@ function endpointRow(method, path2, desc) {
286
296
  return `
287
297
  <tr>
288
298
  <td class="method-cell">${methodBadge(method)}</td>
289
- <td class="path-cell"><code>${path2}</code></td>
299
+ <td class="path-cell"><code>${escapeHtml(path2)}</code></td>
290
300
  <td class="desc-cell">${desc}</td>
291
301
  </tr>`;
292
302
  }
@@ -320,7 +330,7 @@ function resourceAccordion(name, base, isOpen) {
320
330
  return `
321
331
  <details class="resource-card" ${isOpen ? "open" : ""}>
322
332
  <summary>
323
- <span class="resource-name">/${name}</span>
333
+ <span class="resource-name">/${escapeHtml(name)}</span>
324
334
  <span class="route-count">6 routes</span>
325
335
  </summary>
326
336
  <table>
@@ -404,7 +414,7 @@ curl -X POST ${host}/_snapshot/reset`
404
414
  }
405
415
  if (firstCustomRoute) {
406
416
  const method = firstCustomRoute.method?.toUpperCase() ?? "GET";
407
- const fullPath = `${host}${base}${firstCustomRoute.path}`;
417
+ const fullPath = `${host}${base}${escapeHtml(firstCustomRoute.path ?? "")}`;
408
418
  const curlFlag = method === "GET" ? "" : `-X ${method} `;
409
419
  examples.push(`# Custom route
410
420
  curl ${curlFlag}${fullPath}`);
@@ -489,7 +499,8 @@ function generateAboutHtml(storage, options, handlers = /* @__PURE__ */ new Map(
489
499
  desc = `Error injection \u2014 <code>${r.error}</code>`;
490
500
  } else if (r.handler) {
491
501
  const found = handlers.has(r.handler);
492
- desc = found ? `Handler \u2014 <code>${r.handler}()</code>` : `Handler \u2014 <code>${r.handler}()</code> <span style="color:#f85149">(not loaded)</span>`;
502
+ const handlerName = escapeHtml(r.handler);
503
+ desc = found ? `Handler \u2014 <code>${handlerName}()</code>` : `Handler \u2014 <code>${handlerName}()</code> <span style="color:#f85149">(not loaded)</span>`;
493
504
  } else if (r.scenarios?.length) {
494
505
  const hasTemplateInScenarios = r.scenarios.some((s) => s.response.body != null && hasTemplates(s.response.body)) || r.otherwise?.body != null && hasTemplates(r.otherwise.body);
495
506
  desc = hasTemplateInScenarios ? `Scenarios \u2014 <code>{{\u2026}}</code>` : `Scenarios`;
@@ -767,6 +778,7 @@ function applyOperator(itemValue, op, filterValue) {
767
778
  case "_start":
768
779
  return strItem.toLowerCase().startsWith(filterValue.toLowerCase());
769
780
  case "_regex": {
781
+ if (filterValue.length > 200) return false;
770
782
  try {
771
783
  return new RegExp(filterValue, "i").test(strItem);
772
784
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yrest/cli",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "YAML-powered json-server alternative. Zero-config REST API mock server with full CRUD, relations, filters and snapshots from a db.yml file.",
5
5
  "keywords": [
6
6
  "yrest",
@@ -77,7 +77,7 @@
77
77
  },
78
78
  "devDependencies": {
79
79
  "@eslint/js": "^10.0.1",
80
- "@types/node": "^22.19.20",
80
+ "@types/node": "^22.19.21",
81
81
  "eslint": "^10.4.1",
82
82
  "eslint-config-prettier": "^10.1.8",
83
83
  "prettier": "^3.8.3",
@@ -91,7 +91,8 @@
91
91
  },
92
92
  "socket": {
93
93
  "allow": {
94
- "filesystem": true
94
+ "filesystem": true,
95
+ "dynamic-require": true
95
96
  }
96
97
  }
97
98
  }