authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-20 12:05:35-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-20 12:05:35-07:00
log6888276f19d47b9a73546e92721a56abbc8ab6c8
tree2d4bde33a3070c409063396924de0360c386512f
parente06c33c38dba362e6a9a0733542cb37d56c024a3
signature Signed by PGP key B5690EEEBB952194

[codex] Sanitize YAML schema diagnostics (#3427)

Co-authored-by: codex <codex@users.noreply.github.com>

2 files changed, 50 insertions(+), 7 deletions(-)

packages/shared/src/schemaYaml.test.ts+38-2
......@@ -50,9 +50,45 @@ tags:
5050 expect(decodeYaml("answer: 42\n")).toEqual({ answer: 42 });
5151 });
5252
53 it("rejects malformed YAML", () => {
53 it("reports malformed YAML with safe structural diagnostics", () => {
5454 const decodeYaml = Schema.decodeUnknownSync(fromYaml(Schema.Unknown));
55 const secret = "credential=secret-value";
56 let error: unknown;
5557
56 expect(() => decodeYaml("name: ok\n bad-indent: nope\n")).toThrow();
58 try {
59 decodeYaml(`name: ${secret}\n bad-indent: nope\n`);
60 } catch (cause) {
61 error = cause;
62 }
63
64 expect(Schema.isSchemaError(error)).toBe(true);
65 if (!Schema.isSchemaError(error)) {
66 throw new Error("Expected a schema error");
67 }
68 expect(error.message).toBe("Invalid YAML (code=BLOCK_AS_IMPLICIT_KEY, line=1, column=7).");
69 expect(error.message).not.toContain(secret);
70 });
71
72 it("does not expose stringify failure details", () => {
73 const encodeYaml = Schema.encodeSync(fromYaml(Schema.Unknown));
74 const secret = "credential=secret-value";
75 let error: unknown;
76
77 try {
78 encodeYaml({
79 toJSON() {
80 throw new Error(secret);
81 },
82 });
83 } catch (cause) {
84 error = cause;
85 }
86
87 expect(Schema.isSchemaError(error)).toBe(true);
88 if (!Schema.isSchemaError(error)) {
89 throw new Error("Expected a schema error");
90 }
91 expect(error.message).toBe("Failed to stringify YAML.");
92 expect(error.message).not.toContain(secret);
5793 });
5894});
packages/shared/src/schemaYaml.ts+12-5
......@@ -5,6 +5,7 @@ import * as SchemaGetter from "effect/SchemaGetter";
55import * as SchemaIssue from "effect/SchemaIssue";
66import * as SchemaTransformation from "effect/SchemaTransformation";
77import {
8 YAMLParseError,
89 parse as parseYamlString,
910 stringify as stringifyYamlValue,
1011 type CreateNodeOptions,
......@@ -22,8 +23,14 @@ export type YamlStringifyOptions = DocumentOptions &
2223 CreateNodeOptions &
2324 ToStringOptions;
2425
25function formatYamlError(error: unknown): string {
26 return error instanceof Error ? error.message : String(error);
26function formatYamlParseError(error: unknown): string {
27 if (!(error instanceof YAMLParseError)) {
28 return "Invalid YAML.";
29 }
30
31 const position = error.linePos?.[0];
32 const location = position === undefined ? "" : `, line=${position.line}, column=${position.col}`;
33 return `Invalid YAML (code=${error.code}${location}).`;
2734}
2835
2936/**
......@@ -56,7 +63,7 @@ export function parseYaml<E extends string>(
5663 Effect.try({
5764 try: () => parseYamlString(input, options) as unknown,
5865 catch: (error) =>
59 new SchemaIssue.InvalidValue(Option.some(input), { message: formatYamlError(error) }),
66 new SchemaIssue.InvalidValue(Option.none(), { message: formatYamlParseError(error) }),
6067 }),
6168 );
6269}
......@@ -90,8 +97,8 @@ export function stringifyYaml(
9097 return SchemaGetter.transformOrFail((input: unknown) =>
9198 Effect.try({
9299 try: () => stringifyYamlValue(input, options),
93 catch: (error) =>
94 new SchemaIssue.InvalidValue(Option.some(input), { message: formatYamlError(error) }),
100 catch: () =>
101 new SchemaIssue.InvalidValue(Option.none(), { message: "Failed to stringify YAML." }),
95102 }),
96103 );
97104}