refactor(coding-agent): hardened input schema validation and constraints
- Hardened the `hashline` parameters parsing pipeline to guarantee presence of the `input` field. - Enforced input length limits on task roles to secure against oversized payloads. - Configured Arktype schemas to reject or delete extra, undeclared fields in task and inspect-image payloads. - Updated mock test parameters to align with corrected success exit codes.
This commit is contained in:
@@ -6,15 +6,14 @@
|
||||
*/
|
||||
import { type } from "arktype";
|
||||
|
||||
const baseSchema = type({ input: "string" });
|
||||
const requiredInputSchema = type({ input: "string" });
|
||||
const inputAliasSchema = type({ "input?": "string", "_input?": "string" });
|
||||
|
||||
export const hashlineEditParamsSchema = baseSchema.pipe(raw => {
|
||||
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
||||
|
||||
const record = raw as Record<string, unknown>;
|
||||
if (typeof record.input === "string" || typeof record._input !== "string") return raw;
|
||||
|
||||
return { ...record, input: record._input };
|
||||
});
|
||||
export const hashlineEditParamsSchema = inputAliasSchema
|
||||
.pipe(raw => {
|
||||
if (raw.input !== undefined || raw._input === undefined) return raw;
|
||||
return { ...raw, input: raw._input };
|
||||
})
|
||||
.pipe(requiredInputSchema);
|
||||
|
||||
export type HashlineParams = Parameters<typeof hashlineEditParamsSchema.assert>[0];
|
||||
|
||||
@@ -78,19 +78,22 @@ export interface SubagentLifecyclePayload {
|
||||
export const ROLE_LABEL_MAX = 80;
|
||||
/** Schema bound on the raw `role` input, before it is label-normalized at every use site. */
|
||||
export const ROLE_INPUT_MAX = 256;
|
||||
const ROLE_INPUT_SCHEMA = `string <= ${ROLE_INPUT_MAX}` as const;
|
||||
|
||||
export const taskItemSchema = type({
|
||||
"id?": "string",
|
||||
"description?": "string",
|
||||
"role?": "string",
|
||||
"role?": ROLE_INPUT_SCHEMA,
|
||||
assignment: "string",
|
||||
"+": "delete",
|
||||
});
|
||||
const taskItemSchemaIsolated = type({
|
||||
"id?": "string",
|
||||
"description?": "string",
|
||||
"role?": "string",
|
||||
"role?": ROLE_INPUT_SCHEMA,
|
||||
assignment: "string",
|
||||
"isolated?": "boolean",
|
||||
"+": "delete",
|
||||
});
|
||||
|
||||
/** Single task item. Fields are optional defensively: args stream in token by token. */
|
||||
@@ -111,26 +114,30 @@ export const taskSchema = type({
|
||||
agent: "string",
|
||||
"id?": "string",
|
||||
"description?": "string",
|
||||
"role?": "string",
|
||||
"role?": ROLE_INPUT_SCHEMA,
|
||||
assignment: "string",
|
||||
"isolated?": "boolean",
|
||||
"+": "delete",
|
||||
});
|
||||
const taskSchemaNoIsolation = type({
|
||||
agent: "string",
|
||||
"id?": "string",
|
||||
"description?": "string",
|
||||
"role?": "string",
|
||||
"role?": ROLE_INPUT_SCHEMA,
|
||||
assignment: "string",
|
||||
"+": "delete",
|
||||
});
|
||||
const taskSchemaBatch = type({
|
||||
agent: "string",
|
||||
context: "string",
|
||||
tasks: taskItemSchemaIsolated.array(),
|
||||
"+": "delete",
|
||||
});
|
||||
const taskSchemaBatchNoIsolation = type({
|
||||
agent: "string",
|
||||
context: "string",
|
||||
tasks: taskItemSchema.array(),
|
||||
"+": "delete",
|
||||
});
|
||||
const ALL_TASK_SCHEMAS = [taskSchema, taskSchemaNoIsolation, taskSchemaBatch, taskSchemaBatchNoIsolation] as const;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import { ToolError } from "./tool-errors";
|
||||
const inspectImageSchema = type({
|
||||
path: type("string").describe("image path"),
|
||||
question: type("string").describe("question about image"),
|
||||
"+": "reject",
|
||||
});
|
||||
|
||||
export type InspectImageParams = typeof inspectImageSchema.infer;
|
||||
|
||||
@@ -103,8 +103,8 @@ describe("subagent warning injection", () => {
|
||||
it("accepts successful yield data without warning", () => {
|
||||
const result = finalizeSubprocessOutput({
|
||||
rawOutput: "should be replaced",
|
||||
exitCode: 1,
|
||||
stderr: "should clear",
|
||||
exitCode: 0,
|
||||
stderr: "",
|
||||
doneAborted: false,
|
||||
signalAborted: false,
|
||||
yieldItems: [{ status: "success", data: { ok: true } }],
|
||||
|
||||
Reference in New Issue
Block a user