[AAASM-5237] 🐛 (agents): Key the enforcement lookup accumulator by Map - #1764
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Chisanan232
left a comment
There was a problem hiding this comment.
✅ LGTM — reviewed and approved by Claude Code
(Recorded as a review comment because GitHub blocks self-approval on an own-authored PR; this is the sign-off of record.)
Requirement correctness
Two independent fixes bundled correctly:
- AAASM-5237 (the ticket's own scope):
useAgentEnforcementQuery'sRecord<string, ...>accumulator →Map, closing the same prototype-pollution shape as every other site in Epic 5208. Correctly traced the read side (fleetTypes.ts:94enforcement?.[agent.id]→.get(agent.id)) and updated the type aliasAgentEnforcementLookupfromRecordtoReadonlyMap— confirmed viagh pr diffthat every consumer (OverviewPage.test.tsx's fixture,fleetTypes.test.ts) was updated coherently, not just the query hook. - Rebase-induced fix (AAASM-5209 fallout): 10
TS2322errors in 7 test files, all from the same class — test fixtures injecting inherited-prototype-member strings (constructor/__proto__/toString/retired) into fields AAASM-5209 closed to unions. Every cast site usesas unknown as <Type>with a one-line comment explaining it's a deliberate wire-value injection the type system no longer permits directly — mechanical, minimal, and does not touch any assertion/expectation. Confirmed this doesn't relitigate the unrelated tickets that own those test files (5227/5236/5232/5234/5233) — only the injection mechanism changed.
New test (apiHooks.test.tsx, fleetTypes.test.ts) explicitly covers constructor/__proto__ agent ids resolving via .get() without collision — the exact regression this closes.
CI — fully green
All 17 checks pass, including CI Success, Dashboard e2e (Playwright) (6m6s), Dashboard tests + coverage, aa-cli build compat (dashboard embed), SonarCloud, CodeQL.
Security
Prototype-pollution lookup path closed at the last remaining Epic 5208 site. No new risk — the cast sites are test-only, simulating wire input that already reaches these code paths at runtime regardless of the type system's compile-time narrowing.
Merging with a merge commit — this is the last blocker for AAASM-5256 (#1765), which is waiting to rebase onto this.
🤖 Reviewed with Claude Code
|
CI was failing here because this branch was cut before AAASM-5209 (#1751) merged into This PR's own test fixtures (proving the prototype-pollution guards added here work correctly) assign inherited- Fix applied:
Verified locally: CI is now green: |
useAgentEnforcementQuery folded GET /api/v1/analytics/agent-enforcement rows into a plain object keyed by the raw wire agent_id. A constructor or __proto__ agent_id would read back an inherited prototype member or write through the prototype setter instead of being stored as an ordinary entry -- the same prototype-pollution shape closed at every other site in the AAASM-5208 sweep (sibling AAASM-5236). Switch the accumulator to a Map so every agent_id is an ordinary key. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AgentEnforcementLookup is now a Map (see the api.ts accumulator fix), so the read side in toFleetAgent must use enforcement?.get(agent.id) instead of the plain-object index enforcement?.[agent.id]. The Story (AAASM-5213) originally scoped this call site as not needing a change when 5236 was fixed; that was only true while the lookup stayed a Record -- now that this ticket converts the type it reads, the read side does need to change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ent lookup Update the AgentEnforcementLookup fixtures in apiHooks.test.tsx, fleetTypes.test.ts, and OverviewPage.test.tsx from object literals to Map literals to match the new type, and add regression coverage proving a constructor/__proto__ agent_id round-trips through useAgentEnforcementQuery and toFleetAgent as an ordinary entry instead of colliding with an inherited prototype member. Verified red against the prior Record-based implementation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
04ef002 to
69492f2
Compare
|



Description
Closes a gap left by the AAASM-5208 sweep. That sweep converted every plain-object accumulator keyed by raw wire input to a
Map, closing a prototype-pollution shape across the codebase — butuseAgentEnforcementQueryindashboard/src/features/agents/api.tswas never done. Its sibling site,typeCountsindashboard/src/features/audit/export.ts(AAASM-5236), was already fixed and merged (#1737).Root cause:
useAgentEnforcementQueryfoldedGET /api/v1/analytics/agent-enforcementrows into a plain object keyed byrow.agent_id— raw wire data. Anagent_idofconstructor,__proto__,toString,hasOwnProperty, orvalueOfwould read back an inherited prototype member or write through the prototype setter instead of being stored as an ordinary entry, matching the pattern fixed at every other site in Epic AAASM-5208.Fix:
AgentEnforcementLookup(dashboard/src/features/agents/fleetTypes.ts) is nowReadonlyMap<string, AgentEnforcementCount>instead ofReadonly<Record<string, AgentEnforcementCount>>.useAgentEnforcementQuerybuilds aMapvia.set()instead of object-literal assignment.Read side (
fleetTypes.ts—toFleetAgent): The parent Story (AAASM-5213) originally scoped this call site as not needing a change when AAASM-5236 was fixed. Re-verified that claim while doing this conversion: it was only true because the lookup stayed aRecordat the time. Now that this ticket converts the typetoFleetAgentreads, the read side does need to change —enforcement?.[agent.id]becameenforcement?.get(agent.id).No other call site in
dashboard/srcindexes into, spreads, or iterates the lookup as a plain object (AgentDetailPage.tsx,OverviewPage.tsx,FleetPage.tsxall pass the value through opaquely intotoFleetAgent), so no further production call sites needed updating.Type of Change
Breaking Changes
AgentEnforcementLookupis not exported outside the dashboard package)Related Issues
Testing
dashboard/src/features/agents/apiHooks.test.tsx: updated the twouseAgentEnforcementQueryfixtures from object literals toMapliterals, and added a regression test asserting aconstructor/__proto__agent_idround-trips through.get()without colliding.dashboard/src/features/agents/fleetTypes.test.ts: updated the twotoFleetAgentenforcement fixtures toMapliterals, and added a regression test asserting aconstructor/__proto__agent id resolves correctly viatoFleetAgent, alongside a real agent id.dashboard/src/pages/OverviewPage.test.tsx: updated theFULL_ENFORCEMENTfixture to aMapliteral (existingOverviewPagetests exercise the sum path against it).api.ts) and the sole read site (fleetTypes.ts).Load-bearing confirmation: stashed the two source-file fixes (
api.ts,fleetTypes.ts) and reran the updated test suite against the priorRecord-based implementation — 6 tests failed as expected (the 3 new/updated regression tests inapiHooks.test.tsx/fleetTypes.test.ts, plus the twofleetTypes.test.tsfixture-based tests, plus an existingOverviewPage.test.tsxsum test that also caught the regression). All 71 tests pass with the fix restored.Gate results
pnpm type-check: 9 pre-existing errors, all in files untouched by this PR (AgentDecisionStream.test.tsx,export.test.ts,TeamMembersCard.test.tsx,TeamOrphanDetail.test.tsx,AuditLogPage.test.tsx,TeamDetailPage.test.tsx) — confirmed identical onorigin/mainbefore this change; no new errors introduced.pnpm lint: clean, zero violations.pnpm exec vitest run(full suite): 272 test files / 3016 tests passed.Checklist
AgentEnforcementLookupdoc comment)🤖 Generated with Claude Code