Skip to content

fix(ecr): wire ImageTagMutabilityExclusionFilters onto the create and mutability calls - #1406

Merged
go-to-k merged 1 commit into
mainfrom
fix/1392-ecr-tag-mutability-exclusion
Aug 9, 2026
Merged

fix(ecr): wire ImageTagMutabilityExclusionFilters onto the create and mutability calls#1406
go-to-k merged 1 commit into
mainfrom
fix/1392-ecr-tag-mutability-exclusion

Conversation

@go-to-k

@go-to-k go-to-k commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

ECRProvider declared ImageTagMutabilityExclusionFilters in handledProperties but sent it on no API call: CreateRepository omitted it entirely and update's PutImageTagMutability sent only imageTagMutability. A repository using IMMUTABLE_WITH_EXCLUSION therefore lost its exclusions silently, while the property-coverage pre-flight passed on the strength of the false handled claim.

The member names diverge — not just their casing — so the blob cannot be forwarded verbatim: CFn {ImageTagMutabilityExclusionFilterType, ImageTagMutabilityExclusionFilterValue} vs SDK {filterType, filter} (@aws-sdk/client-ecr models_0.d.ts:591-603).

What changed

  • Create + update wired. The property attaches to CreateRepositoryRequest (:663) and to PutImageTagMutabilityRequest (:3549), both verified against the installed model. Absent / empty lists omit the key rather than sending [], matching the provider's existing spread idiom.
  • The update now fires on a filters-only change. Both members ride the same PutImageTagMutability call, and the old gate only compared ImageTagMutability — so a filters-only edit (exclusion mode throughout, only the patterns changing) never reached AWS at all. imageTagMutability is a required member of that request, so it is re-sent alongside the new filters.
  • Drift. DescribeRepositories returns the value on the Repository shape (:725), so readCurrentState reads it back through the inverse mapping and compares against a template-shaped baseline. The stale comment claiming the property was "not part of the persisted AWS state" is corrected; EmptyOnDelete stays in the omitted list with an accurate reason.

Test plan

  • 12 unit tests. Fails-without-fix probe: reverting the update gate to its old mutability-only form fails the three that isolate the filters half (filters-only change, removal with the mode unchanged, add with the mode unchanged); reverting the mapping fails create / update / readCurrentState. The remaining tests assert absence and pass either way by construction — they pin the "omit, don't send []" contract.
  • Full suite: 516 files / 8725 tests, no type errors.
  • Real AWS (/run-integ ecr-scanning, us-east-1). The fixture now deploys IMMUTABLE_WITH_EXCLUSION with one filter (dev-*), then changes only the filters on the UPDATE phase (two different patterns, mutability unchanged) — precisely the case the old gate skipped. Phase 1 asserted imageTagMutability, the filter pattern and the WILDCARD filter type all reached AWS; Phase 2 asserted the changed patterns landed. Destroy 2 deleted / 0 errors / 0 orphans.

Won't-do (recorded)

Removal semantics — dropping the property omits the key rather than sending [] — are documented in an in-code comment but not covered by the integ. PutImageTagMutability is a full-replace setter, and the only reachable removal path is a mutability change away from an exclusion mode (AWS rejects an exclusion mode with no filters), which already fires the call with the key omitted. Building a fixture phase for the unreachable case is not worth the extra deploy cycle.

Known gap

A repository deployed by a pre-fix binary carries ImageTagMutabilityExclusionFilters in its state properties while AWS has none, and its observedProperties were captured by the old reader that omitted the key — so neither a no-change redeploy nor cdkd drift heals it. Any template edit that reaches PutImageTagMutability does. Not worth a migration for a property this new.

Follow-ups

Closes #1392

… mutability calls

ECRProvider declared ImageTagMutabilityExclusionFilters in handledProperties
but never sent it: CreateRepository omitted it and the update path's
PutImageTagMutability sent only imageTagMutability. A repository using
IMMUTABLE_WITH_EXCLUSION therefore lost its exclusions silently, while the
property-coverage pre-flight passed on the false handled claim.

The member names diverge (not just their casing): CFn
{ImageTagMutabilityExclusionFilterType, ImageTagMutabilityExclusionFilterValue}
vs SDK {filterType, filter}, so the blob cannot be forwarded verbatim.

- Map CFn to SDK on create and on PutImageTagMutability.
- Fire the update on a filters-only change (both members ride the same call,
  and imageTagMutability is a required member, so it is re-sent).
- Read the filters back off DescribeRepositories through the inverse mapping
  so drift compares against a template-shaped baseline, and correct the
  readCurrentState comment that claimed the property was not readable.
- Extend the ecr-scanning integ fixture to deploy IMMUTABLE_WITH_EXCLUSION
  with a filter, then change ONLY the filters on the UPDATE phase, asserting
  both reach AWS.

Closes #1392
@go-to-k
go-to-k force-pushed the fix/1392-ecr-tag-mutability-exclusion branch from 9d71e62 to 48983bf Compare August 9, 2026 06:01
@go-to-k
go-to-k merged commit 5518934 into main Aug 9, 2026
6 checks passed
@go-to-k
go-to-k deleted the fix/1392-ecr-tag-mutability-exclusion branch August 9, 2026 06:42
github-actions Bot pushed a commit that referenced this pull request Aug 9, 2026
## [0.278.6](v0.278.5...v0.278.6) (2026-08-09)

### Bug Fixes

* **ecr:** wire ImageTagMutabilityExclusionFilters onto the create and mutability calls ([#1406](#1406)) ([5518934](5518934))
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 0.278.6 🎉

The release is available on:

Your semantic-release bot 📦🚀

go-to-k added a commit that referenced this pull request Aug 9, 2026
…first run caught

gen-property-coverage verifies every CFn property is ACCOUNTED FOR (declared in
handledProperties or unhandledByDesign); gen-nested-key-coverage audits
spellings INSIDE a forwarded blob. Neither checks that a handledProperties
entry is actually WIRED. ECRProvider declared ImageTagMutabilityExclusionFilters
handled while the property appeared on NO API call, so the pre-flight passed on
the declaration alone and the value silently vanished (#1392, fixed in #1406) --
a FALSE handled claim, the exact thing the declaration system exists to prevent.

The critic requires per-property read evidence in one of four AST shapes
(element-read, property-read, destructure, table-loop), plus a delegated tag
when the read happens in a callable reached by a call edge. Evidence is
CLASS-SCOPED via a taint walk seeded from each method's desired-state parameter
and propagated only through calls that pass the bag WHOLE, so a sibling class in
the same file, a comment, a getDriftUnknownPaths entry, the declaration itself,
and a readCurrentState write-back all fail to vouch for a property.

Two strictness decisions were forced by the real tree, not by fixtures:

- A whole-bag forward does NOT blanket-excuse un-read declarations. The first
  draft's excuse silenced the very #1392 property via ECRProvider's
  hasCdkAutoDeleteTag(properties) call in delete(); measured across the tree it
  rescued 0 of 1063 properties. Blind spots are recorded for visibility only.
- A read of previousProperties is NOT evidence: a diff-only read proves change
  DETECTION, not delivery. Also 0 of 1063 today, so the strictness is free now
  and closes the disguise later.

Coverage floors are per SHAPE rather than a grand total (84 classes / 1063
properties), with property-read and destructure pinned === 0 so a future first
user cannot regress those recognizers silently.

The first real-tree run found two live gaps, seeded as KNOWN GAP allow-list
entries and FILED rather than fixed here (this PR introduces the tool; the
fixes are separate): AWS::EC2::NatGateway.MaxDrainDurationSeconds (#1411) and
AWS::Logs::LogGroup.ResourcePolicyDocument (#1412). IAMAccessKeyProvider#Serial
and NestedStackProvider#TemplateURL are rationale'd NOT-A-BUG entries. Stale
entries fail in both modes, so wiring a property forces its entry's removal.

Real-code fail probe (the repo's checker rule, re-run independently): reverting
the real ecr-provider.ts to its pre-#1406 state makes --check exit 1 naming
ECRProvider#ImageTagMutabilityExclusionFilters, and the file restores
byte-identical. A first probe that stripped only the lowercase-p reads PASSED --
the surviving previousProperties read cleared it -- and that false clean is what
drove the previousProperties exclusion; both variants are now automated.

Closes #1404
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AWS::ECR::Repository: ImageTagMutabilityExclusionFilters declared handled but never sent on any API call

1 participant