What
The limit argument reaches both connectors without validation, and the two
paths disagree about what they do with it.
src/connectors.ts:12, the Shopify read, interpolates it raw:
const url = `https://${env.SHOPIFY_STORE}/admin/api/2024-07/orders.json?status=any&limit=${limit}`;
src/connectors.ts:65, the database read, clamps only the top:
`${env.SUPABASE_URL}/rest/v1/${table}?select=*&limit=${Math.min(limit, 50)}`
The tool schema says limit: { type: "number", description: "max rows (1-50)", default: 10 },
but nothing enforces that range, and the schema is advisory: the caller is an
agent, and an agent will eventually send something surprising.
What gets through today
| input |
Shopify URL |
DB URL |
1000 |
limit=1000, unbounded page |
limit=50, clamped |
-5 |
limit=-5 |
limit=-5, Math.min only bounds above |
NaN |
limit=NaN |
limit=NaN |
1.5 |
limit=1.5 |
limit=1.5 |
None of these are a security hole, since the table allowlist and the write gate
still hold. It is a robustness and cost problem: an unbounded Shopify page is a
large upstream response an agent asked for by accident, and limit=NaN is a
confusing 4xx rather than a clear refusal.
Suggested fix
Validate once, where the arguments are parsed, rather than in each connector, so
the two paths cannot drift again:
- coerce to an integer
- reject or clamp outside 1 to 50
- decide deliberately whether an out-of-range limit is a clamp or an error. An
error is more honest for an agent, since a silent clamp makes it think it saw
everything
Whichever you choose, add the case to the tests from #3.
Good first issue
Yes. It is a small, self-contained change with a clear right answer, and it
touches the argument-handling path which is a good way to learn the codebase.
What
The
limitargument reaches both connectors without validation, and the twopaths disagree about what they do with it.
src/connectors.ts:12, the Shopify read, interpolates it raw:src/connectors.ts:65, the database read, clamps only the top:`${env.SUPABASE_URL}/rest/v1/${table}?select=*&limit=${Math.min(limit, 50)}`The tool schema says
limit: { type: "number", description: "max rows (1-50)", default: 10 },but nothing enforces that range, and the schema is advisory: the caller is an
agent, and an agent will eventually send something surprising.
What gets through today
1000limit=1000, unbounded pagelimit=50, clamped-5limit=-5limit=-5,Math.minonly bounds aboveNaNlimit=NaNlimit=NaN1.5limit=1.5limit=1.5None of these are a security hole, since the table allowlist and the write gate
still hold. It is a robustness and cost problem: an unbounded Shopify page is a
large upstream response an agent asked for by accident, and
limit=NaNis aconfusing 4xx rather than a clear refusal.
Suggested fix
Validate once, where the arguments are parsed, rather than in each connector, so
the two paths cannot drift again:
error is more honest for an agent, since a silent clamp makes it think it saw
everything
Whichever you choose, add the case to the tests from #3.
Good first issue
Yes. It is a small, self-contained change with a clear right answer, and it
touches the argument-handling path which is a good way to learn the codebase.