Desired outcome
MCP031 finds unconstrained path arguments nested inside objects and arrays, not only at the top level of a tool's input schema.
Why it matters
pathTraversalArg in src/rules/secrets.ts looks exactly one level deep:
const props = tool.inputSchema?.properties ?? {};
for (const [name, schema] of Object.entries(props)) {
So a tool declaring { file: { type: "string" } } is flagged, but these are all missed:
{ target: { type: "object", properties: { path: { type: "string" } } } }
{ filePaths: { type: "array", items: { type: "string" } } }
- anything under
additionalProperties
Nesting one level is a completely ordinary way to write a tool schema, so the rule's coverage depends on schema style rather than on risk. For a high-severity traversal rule, a quiet miss is the worst outcome: the audit reports clean and the traversal is still there.
The codebase already has the right tool for this. walkSchema() in src/rules/helpers.ts recurses through properties, items (both forms), and additionalProperties, and yields a path string for each node. src/rules/schema.ts line 61 already uses it. secrets.ts does not.
Steps
- Rewrite the loop in
pathTraversalArg to use walkSchema(tool.inputSchema, (node, path) => ...).
- Derive the argument name from the last segment of
path for the PATH_ARG_NAMES check, and use the full path in the finding's location so the report points at the exact node, matching how schema.ts reports.
- Keep the existing
schema.type !== "string" and pattern/enum constraint logic.
- Add fixtures in
test/rules.test.ts for a nested object path arg and an array-of-strings path arg.
Claiming this
Comment below to claim it. A reply usually comes within a day.
Desired outcome
MCP031 finds unconstrained path arguments nested inside objects and arrays, not only at the top level of a tool's input schema.
Why it matters
pathTraversalArginsrc/rules/secrets.tslooks exactly one level deep:So a tool declaring
{ file: { type: "string" } }is flagged, but these are all missed:{ target: { type: "object", properties: { path: { type: "string" } } } }{ filePaths: { type: "array", items: { type: "string" } } }additionalPropertiesNesting one level is a completely ordinary way to write a tool schema, so the rule's coverage depends on schema style rather than on risk. For a high-severity traversal rule, a quiet miss is the worst outcome: the audit reports clean and the traversal is still there.
The codebase already has the right tool for this.
walkSchema()insrc/rules/helpers.tsrecurses throughproperties,items(both forms), andadditionalProperties, and yields a path string for each node.src/rules/schema.tsline 61 already uses it.secrets.tsdoes not.Steps
pathTraversalArgto usewalkSchema(tool.inputSchema, (node, path) => ...).pathfor thePATH_ARG_NAMEScheck, and use the fullpathin the finding'slocationso the report points at the exact node, matching howschema.tsreports.schema.type !== "string"andpattern/enumconstraint logic.test/rules.test.tsfor a nested object path arg and an array-of-strings path arg.Claiming this
Comment below to claim it. A reply usually comes within a day.