Conversation
Zod strips keys a schema does not declare, so a misspelled key in a JSON
flag body parses cleanly and then vanishes. On an update path the surviving
object replaces the stored configuration, so the typo destroys the value the
user was trying to edit rather than reporting an error.
The typo cannot be caught later either: the AWS SDK serializes only the
members its own schema declares, so the bad key never reaches the service
and no server-side validation can see it.
Applying strict in the helper rather than at each schema definition means a
schema author cannot forget it, and leaves the schemas in projectSchemas/
untouched.
Verified against the real CLI. `project add evaluator llm-as-a-judge
--rating-scale '{"numerical":[{"value":1,"label":"good","definition":"d"}],
"bogus":1}'` previously printed "added evaluator 'e9'" and wrote the
evaluator with "bogus" silently dropped; it now fails with
`Unrecognized key: "bogus"` and writes nothing.
|
Claude Security Review: no high-confidence findings. (run) |
There was a problem hiding this comment.
AgentCore Harness Review
Verdict: Looks good
The change is a small, well-scoped hardening: parseJsonFlagWithSchema now applies .strict() when the top-level schema is a ZodObject, so typos like modlId fail loudly instead of being silently dropped. Tests cover the important cases (valid input, unknown key rejected, refinements preserved, records left permissive), and strictened correctly skips non-object schemas so z.record(...), z.array(...), and custom-pipe wrappers (e.g. projectMemoryObject, componentMapSchema, TagsSchema) keep working.
I walked every existing callsite (gateway-connector, gateway-target, gateway, memory, evaluator/*, config-bundle, metadataFilters, online-eval/create) and none of them regress:
- Schemas that already call
.strict()(e.g.AgentCoreGatewayTargetSchema,GatewayAuthorizerConfigurationInputSchema) are unaffected —.strict()is idempotent. - Array/record schemas (indexed keys, strategies, tags, components, metadata filters) fall through
strictenedunchanged, as intended. - Zod v4's
.refine()/.superRefine()still returnZodObjectinstances, soRatingScaleSchemaand the testedModelSchemacorrectly get strictened.
The inline comment on strictened honestly calls out the "top level only" limitation, and the callers that care about deep validation (memory input via projectMemoryObject, gateway target via nested .strict() blocks) already provide their own deep checks. This is a pure utility change so no telemetry is needed, and there is no mocking in the added tests.
Nothing to change — good to merge.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## refactor #2379 +/- ##
============================================
- Coverage 97.25% 97.25% -0.01%
============================================
Files 613 613
Lines 41030 41033 +3
============================================
+ Hits 39905 39907 +2
- Misses 1125 1126 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Problem
Zod strips keys a schema does not declare. So a misspelled key in a JSON flag body parses cleanly, silently disappears, and on an update path the surviving object replaces the stored configuration — the typo destroys the value the user was trying to edit instead of reporting an error.
The typo cannot be caught any later. The AWS SDK serializes only the members its own schema declares, so the bad key never reaches the service and no server-side validation can see it:
Change
One helper,
parseJsonFlagWithSchema, applies.strict()to the schema it was handed:Applied in the helper rather than at each schema definition so a schema author cannot forget it, and so the schemas in
projectSchemas/stay untouched. Theinstanceof z.ZodObjectcheck is what leavesz.recordschemas (e.g.TagsSchema, where every key is legal by definition) and array-rooted schemas alone.Verified against the real CLI
Same command, same input, with the change stashed vs applied:
The before run wrote the evaluator to
agentcore.jsonand reported success.Blast radius
All 15 flags that route through this helper were exercised against the real CLI, before and after. Exactly one changes behaviour — the rest were already guarded:
project add evaluator llm-as-a-judge --rating-scalememory event list/memory record list --metadata-filters.strict()project add gateway --authorizer-configuration.strict()at call siteproject add config-bundle --components,eval config-bundle create --componentsproject add memory --strategies/--indexed-keys/--stream-delivery-resourcesproject add gateway-target --target-configuration,gateway-connector --connector-configuration--tags(×4)z.record— every key legalThe rest of the value is forward-looking: any schema passed to this helper from now on is strict without the author remembering.
Known limitation
Top level only, as the comment says. A nested typo (
model.bedrockModelConfig.modelIdd) still slips through, because zod's strictness is per-object. Recursing means rebuilding the schema tree, which drops the.refinechecksprojectSchemas/relies on — zod v4 throws on.extend()of a refined object for exactly that reason, and zod's maintainer rejects a built-indeepStricton the same grounds (colinhacks/zod#2062).Also out of scope: the ~90 call sites still using
parseJsonFlag/parseJsonObjectFlag/parseJsonArrayFlag, which cast without any schema and so have nothing to strictify.Testing
bun test— 3555 pass, 0 fail (242 files)tsc --noEmitclean,oxlintandprettiercleansrc/handlers/utils.test.tsx: typo rejected, schema refinements still enforced,z.recordstays permissive, valid input unchanged, omitted flag returns undefined