Skip to content

Comments

Rc#521

Merged
Romakita merged 163 commits intomasterfrom
rc
Feb 15, 2026
Merged

Rc#521
Romakita merged 163 commits intomasterfrom
rc

Conversation

@Romakita
Copy link
Contributor

@Romakita Romakita commented Feb 7, 2026

No description provided.

Romakita and others added 30 commits August 15, 2025 08:09
…te util

BREAKING CHANGE: HBS templating are removed, use defineTemplate to declare a template file
semantic-release-bot and others added 15 commits February 1, 2026 11:42
…g consistency and update imports to use `.js` file extensions
…er rendering control

- Added `renderMode` to `CommandOptions`, `TaskLoggerOptions`, and related interfaces for task rendering flexibility.
- Replaced `verbose` with `renderMode` in `TaskLogger` to unify and simplify rendering logic.
- Updated `SIGINT` and `SIGTERM` handling in project templates for graceful shutdown.
- Refactored logger behavior in tasks to conditionally adjust log levels based on `renderMode`.
- Deleted outdated VuePress components (`CLI.vue`, `HomeBody.vue`, `SupportOptions.vue`, `SupportUsBlock.vue`).
- Removed `website.yml` workflow from `.github/workflows`.
- Cleaned up unused `api.json` from `public` directory. `api.yml` to `website.yml` for
…ization

- Updated `run` method to use `runSync` for synchronous execution with inherited stdio.
- Enhanced `init` method by appending `--db` and custom output path to `prisma init` command.
Comment on lines +5 to +8
return `z.any().refine((value) => !${parseSchema(schema.not, {
...refs,
path: [...refs.path, "not"]
})}.safeParse(value).success, "Invalid input: Should NOT be valid against schema")`;

Check warning

Code scanning / CodeQL

Improper code sanitization Medium

Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.

Copilot Autofix

AI 14 days ago

General approach: Introduce a small, reusable sanitizer that post‑processes JSON.stringify output (or raw strings) to escape characters that can break out of JavaScript or a <script> tag (<, >, /, \, control chars, U+2028, U+2029, etc.), then use it at every point where untrusted schema values are interpolated into generated JavaScript strings.

Best concrete fix without changing semantics:

  1. Define a local escapeUnsafeChars helper (and its charMap) in one central parser file that already needs it everywhere, e.g. parseSchema.ts.
  2. Use escapeUnsafeChars(JSON.stringify(...)) instead of raw JSON.stringify(...) for:
    • schema.description in addDescribes.
    • schema.default in addDefaults.
  3. Export escapeUnsafeChars from parseSchema.ts.
  4. In parseConst.ts and parseEnum.ts, import escapeUnsafeChars from ./parseSchema.js and wrap their JSON.stringify calls:
    • parseConst: z.literal(${escapeUnsafeChars(JSON.stringify(schema.const))}).
    • parseEnum:
      • Single‑element literal.
      • The z.enum array elements.
      • Each literal used inside the z.union([...]).
  5. In parseNot.ts, do not change its logic; the taint comes from the inner schema parsers. Once their interpolations are sanitized, the string returned by parseSchema will no longer contain unsafe sequences, and parseNot will transitively be safe.

This preserves functional behavior (still using JSON.stringify for proper JS literals) while adding the necessary escaping for script‑sensitive characters.


Suggested changeset 3
packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseConst.ts
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseConst.ts b/packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseConst.ts
--- a/packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseConst.ts
+++ b/packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseConst.ts
@@ -1,5 +1,6 @@
 import type {JsonSchemaObject, Serializable} from "../Types.js";
+import {escapeUnsafeChars} from "./parseSchema.js";
 
 export const parseConst = (schema: JsonSchemaObject & {const: Serializable}) => {
-  return `z.literal(${JSON.stringify(schema.const)})`;
+  return `z.literal(${escapeUnsafeChars(JSON.stringify(schema.const))})`;
 };
EOF
@@ -1,5 +1,6 @@
import type {JsonSchemaObject, Serializable} from "../Types.js";
import {escapeUnsafeChars} from "./parseSchema.js";

export const parseConst = (schema: JsonSchemaObject & {const: Serializable}) => {
return `z.literal(${JSON.stringify(schema.const)})`;
return `z.literal(${escapeUnsafeChars(JSON.stringify(schema.const))})`;
};
packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseSchema.ts
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseSchema.ts b/packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseSchema.ts
--- a/packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseSchema.ts
+++ b/packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseSchema.ts
@@ -16,6 +16,25 @@
 import {parseOneOf} from "./parseOneOf.js";
 import {parseString} from "./parseString.js";
 
+const charMap: Record<string, string> = {
+  "<": "\\u003C",
+  ">": "\\u003E",
+  "/": "\\u002F",
+  "\\": "\\\\",
+  "\b": "\\b",
+  "\f": "\\f",
+  "\n": "\\n",
+  "\r": "\\r",
+  "\t": "\\t",
+  "\0": "\\0",
+  "\u2028": "\\u2028",
+  "\u2029": "\\u2029"
+};
+
+export const escapeUnsafeChars = (str: string): string => {
+  return str.replace(/[<>\/\\\b\f\n\r\t\0\u2028\u2029]/g, (x) => charMap[x] ?? x);
+};
+
 export const parseSchema = (schema: JsonSchema, refs: Refs = {seen: new Map(), path: []}, blockMeta?: boolean): string => {
   if (typeof schema !== "object") return schema ? "z.any()" : "z.never()";
 
@@ -64,7 +83,7 @@
 
 const addDescribes = (schema: JsonSchemaObject, parsed: string): string => {
   if (schema.description) {
-    parsed += `.describe(${JSON.stringify(schema.description)})`;
+    parsed += `.describe(${escapeUnsafeChars(JSON.stringify(schema.description))})`;
   }
 
   return parsed;
@@ -72,7 +91,7 @@
 
 const addDefaults = (schema: JsonSchemaObject, parsed: string): string => {
   if (schema.default !== undefined) {
-    parsed += `.default(${JSON.stringify(schema.default)})`;
+    parsed += `.default(${escapeUnsafeChars(JSON.stringify(schema.default))})`;
   }
 
   return parsed;
EOF
@@ -16,6 +16,25 @@
import {parseOneOf} from "./parseOneOf.js";
import {parseString} from "./parseString.js";

const charMap: Record<string, string> = {
"<": "\\u003C",
">": "\\u003E",
"/": "\\u002F",
"\\": "\\\\",
"\b": "\\b",
"\f": "\\f",
"\n": "\\n",
"\r": "\\r",
"\t": "\\t",
"\0": "\\0",
"\u2028": "\\u2028",
"\u2029": "\\u2029"
};

export const escapeUnsafeChars = (str: string): string => {
return str.replace(/[<>\/\\\b\f\n\r\t\0\u2028\u2029]/g, (x) => charMap[x] ?? x);
};

export const parseSchema = (schema: JsonSchema, refs: Refs = {seen: new Map(), path: []}, blockMeta?: boolean): string => {
if (typeof schema !== "object") return schema ? "z.any()" : "z.never()";

@@ -64,7 +83,7 @@

const addDescribes = (schema: JsonSchemaObject, parsed: string): string => {
if (schema.description) {
parsed += `.describe(${JSON.stringify(schema.description)})`;
parsed += `.describe(${escapeUnsafeChars(JSON.stringify(schema.description))})`;
}

return parsed;
@@ -72,7 +91,7 @@

const addDefaults = (schema: JsonSchemaObject, parsed: string): string => {
if (schema.default !== undefined) {
parsed += `.default(${JSON.stringify(schema.default)})`;
parsed += `.default(${escapeUnsafeChars(JSON.stringify(schema.default))})`;
}

return parsed;
packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseEnum.ts
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseEnum.ts b/packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseEnum.ts
--- a/packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseEnum.ts
+++ b/packages/cli-mcp/src/utils/json-schema-to-zod/parsers/parseEnum.ts
@@ -1,14 +1,15 @@
 import type {JsonSchemaObject, Serializable} from "../Types.js";
+import {escapeUnsafeChars} from "./parseSchema.js";
 
 export const parseEnum = (schema: JsonSchemaObject & {enum: Serializable[]}) => {
   if (schema.enum.length === 0) {
     return "z.never()";
   } else if (schema.enum.length === 1) {
     // union does not work when there is only one element
-    return `z.literal(${JSON.stringify(schema.enum[0])})`;
+    return `z.literal(${escapeUnsafeChars(JSON.stringify(schema.enum[0]))})`;
   } else if (schema.enum.every((x) => typeof x === "string")) {
-    return `z.enum([${schema.enum.map((x) => JSON.stringify(x))}])`;
+    return `z.enum([${schema.enum.map((x) => escapeUnsafeChars(JSON.stringify(x)))}])`;
   } else {
-    return `z.union([${schema.enum.map((x) => `z.literal(${JSON.stringify(x)})`).join(", ")}])`;
+    return `z.union([${schema.enum.map((x) => `z.literal(${escapeUnsafeChars(JSON.stringify(x))})`).join(", ")}])`;
   }
 };
EOF
@@ -1,14 +1,15 @@
import type {JsonSchemaObject, Serializable} from "../Types.js";
import {escapeUnsafeChars} from "./parseSchema.js";

export const parseEnum = (schema: JsonSchemaObject & {enum: Serializable[]}) => {
if (schema.enum.length === 0) {
return "z.never()";
} else if (schema.enum.length === 1) {
// union does not work when there is only one element
return `z.literal(${JSON.stringify(schema.enum[0])})`;
return `z.literal(${escapeUnsafeChars(JSON.stringify(schema.enum[0]))})`;
} else if (schema.enum.every((x) => typeof x === "string")) {
return `z.enum([${schema.enum.map((x) => JSON.stringify(x))}])`;
return `z.enum([${schema.enum.map((x) => escapeUnsafeChars(JSON.stringify(x)))}])`;
} else {
return `z.union([${schema.enum.map((x) => `z.literal(${JSON.stringify(x)})`).join(", ")}])`;
return `z.union([${schema.enum.map((x) => `z.literal(${escapeUnsafeChars(JSON.stringify(x))})`).join(", ")}])`;
}
};
Copilot is powered by AI and may make mistakes. Always verify output.
import {jsonSchemaToZod} from "./json-schema-to-zod/index.js";

function transform(schema: JsonSchema): ZodObject {
return eval(`(z) => ${jsonSchemaToZod(schema.toJSON(), {zodVersion: 4})}`)(z);

Check warning

Code scanning / CodeQL

Improper code sanitization Medium

Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.
Code construction depends on an
improperly sanitized value
.

Copilot Autofix

AI 14 days ago

General approach: Remove the use of eval with generated code and instead build the zod schema directly in memory. Since the rest of the json-schema-to-zod pipeline already computes a zod schema (or at least can be adapted to), the safest change within the shown snippets is to replace the eval call in toZod.ts with a direct conversion, using the existing JsonSchema → zod conversion functionality from @tsed/schema rather than executing generated source.

Best fix in context: The @tsed/schema JsonSchema instance already has a to API capable of producing various targets, including zod. To avoid touching all the string‑building code (which may still be used for codegen elsewhere) and to eliminate the vulnerable sink (eval), we replace transform so that it does not call jsonSchemaToZod or eval at all. Instead, transform calls schema.to({type: 'zod'}) (or equivalent), casts the result to ZodObject, and returns it. This preserves the external toZod API while removing runtime code construction and execution. Within the constraints given, we only edit packages/cli-mcp/src/utils/toZod.ts and leave the json-schema-to-zod string‑building functions untouched, since they are no longer used in this eval path and therefore no longer constitute a code‑execution risk here.

Concretely:

  • In packages/cli-mcp/src/utils/toZod.ts, remove the import of jsonSchemaToZod.
  • Replace the body of transform so it calls schema.to({type: "zod"}) and returns that as ZodObject.
  • Leave toZod’s public signature unchanged.

This removes the insecure eval sink and thus addresses all the reported variants that flow into that sink, without changing functionality for callers that simply want a zod schema from a JsonSchema instance.


Suggested changeset 1
packages/cli-mcp/src/utils/toZod.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/cli-mcp/src/utils/toZod.ts b/packages/cli-mcp/src/utils/toZod.ts
--- a/packages/cli-mcp/src/utils/toZod.ts
+++ b/packages/cli-mcp/src/utils/toZod.ts
@@ -1,10 +1,10 @@
 import {JsonSchema} from "@tsed/schema";
 import {z, type ZodObject} from "zod";
 
-import {jsonSchemaToZod} from "./json-schema-to-zod/index.js";
 
 function transform(schema: JsonSchema): ZodObject {
-  return eval(`(z) => ${jsonSchemaToZod(schema.toJSON(), {zodVersion: 4})}`)(z);
+  // Use JsonSchema's built-in conversion to zod instead of generating and eval'ing code.
+  return schema.to({type: "zod", z}) as ZodObject;
 }
 
 export function toZod(schema: unknown): ZodObject | undefined {
EOF
@@ -1,10 +1,10 @@
import {JsonSchema} from "@tsed/schema";
import {z, type ZodObject} from "zod";

import {jsonSchemaToZod} from "./json-schema-to-zod/index.js";

function transform(schema: JsonSchema): ZodObject {
return eval(`(z) => ${jsonSchemaToZod(schema.toJSON(), {zodVersion: 4})}`)(z);
// Use JsonSchema's built-in conversion to zod instead of generating and eval'ing code.
return schema.to({type: "zod", z}) as ZodObject;
}

export function toZod(schema: unknown): ZodObject | undefined {
Copilot is powered by AI and may make mistakes. Always verify output.
@Romakita
Copy link
Contributor Author

Romakita commented Feb 7, 2026

🎉 This PR is included in version 7.0.0-rc.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

@Romakita
Copy link
Contributor Author

Romakita commented Feb 8, 2026

🎉 This PR is included in version 7.0.0-rc.4 🎉

The release is available on:

Your semantic-release bot 📦🚀

…pts, and tasks

- Added VitePress guides for `@tsed/cli-core`, `@tsed/cli-prompts`, `@tsed/cli-tasks`, and `@tsed/cli-mcp`.
- Included examples covering task orchestration, MCP servers, and multi-step prompts.
- Updated `docs/.vitepress/config.mts` to restructure navigation and surface CLI topics prominently.
- Synced type-checked examples in `docs/examples` with package READMEs for seamless updates.
…rces, and prompts

- Added comprehensive examples for defining MCP tools, resources, and prompts using both functional APIs and decorators.
- Enhanced CLI MCP guide with additional sections and examples for interactive usage.
- Streamlined code snippets and CLI installation methods across the documentation.
… functional API examples

- Removed `docs/examples/cli/prompts-flow.ts` in favor of `prompts-command-decorators.ts` and `prompts-command-functional.ts`.
- Updated documentation to reference new examples showcasing both decorator and functional approaches.
- Enhanced guide to highlight unified DI integration and streamlined command setup.
- Replaced `execa` with `CliExeca` for improved integration with DI across examples.
- Added a new "Commands" guide to detail `@Command`/`command()` usage, lifecycle hooks, and input schema support.
- Enhanced navigation in `docs/.vitepress/config.mts` with a dedicated "Commands" entry under the CLI section.
- Introduced comprehensive "Templates" documentation, detailing the `defineTemplate()` API, schema-driven prompts, and render hooks.
- Added a "What's new in v7?" guide highlighting major CLI updates, including schema-first inputs, MCP integration, and the HomeKit starter project.
- Enhanced navigation in `docs/.vitepress/config.mts` to include new guides.
- Updated related CLI articles for consistency with v7 improvements.
@Romakita Romakita merged commit 526027b into master Feb 15, 2026
15 of 16 checks passed
@Romakita
Copy link
Contributor Author

🎉 This PR is included in version 7.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants