Skip to content

fix(wafv2): decode SearchStringBase64 and rename reference-statement Arn to the SDK ARN - #1405

Merged
go-to-k merged 1 commit into
mainfrom
fix/1389-wafv2-search-string-base64
Aug 9, 2026
Merged

fix(wafv2): decode SearchStringBase64 and rename reference-statement Arn to the SDK ARN#1405
go-to-k merged 1 commit into
mainfrom
fix/1389-wafv2-search-string-base64

Conversation

@go-to-k

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

Copy link
Copy Markdown
Owner

Summary

WAFv2WebACLProvider forwarded the CFn Rules blob raw. The AWS SDK v3 serializer drops unknown members, and the Rules tree carries exactly two spellings CFn uses that the SDK model does not — all 154 CFn keys in the CfnWebACL tree were diffed against the SDK schema member set, so this is the complete list:

CFn key SDK member Effect
ByteMatchStatement.SearchStringBase64 (none — CFn-only; the SDK has a single SearchString blob) dropped, so CreateWebACL fails on the missing required SearchString
Arn on IPSetReferenceStatement / RegexPatternSetReferenceStatement / RuleGroupReferenceStatement ARN (required) dropped, so CreateWebACL fails on the missing required ARN

The second row was not in the filed issue — a reviewer found it by diffing the whole tree, and it is the wider breakage: any WebACL using an IPSet / RegexPatternSet / RuleGroup reference statement failed today, base64 or not. Both are the same class and both leaves are nestable, so one walk fixes them together.

What changed

  • One recursive statement walk covering every member the SDK Statement union declares as nested: NotStatement.Statement, AndStatement.Statements[], OrStatement.Statements[], and the ScopeDownStatement of both RateBasedStatement and ManagedRuleGroupStatement. RuleGroupReferenceStatement is deliberately not a recursion point (it declares no nested statement member); it appears only in the ARN rename list. Applied on create and update; every level is rebuilt, so the caller's properties object is never mutated (pinned by a test).
  • SearchStringBase64 decodes to a Uint8Array, matching the declared member type. The issue flagged a latent string-vs-Uint8Array risk; it is resolved from the resolved serializer source rather than assumed — the blob member is schema code 21, and @aws-sdk/core's JSON body serializer base64-encodes both a Uint8Array and a plain string at a blob member. Plain SearchString values are therefore correct as-is and left untouched.
  • Precedence when a template carries both search keys: SearchStringBase64 wins. CFn treats them as mutually exclusive and rejects such a template, so any choice is arbitrary; the explicit-encoding form is the only one that can express non-UTF-8 bytes, so honoring it never loses information.
  • Non-array Rules now passes through instead of defaulting to []. Defaulting a truthy non-array (an unresolved intrinsic) would make update() a silent UpdateWebACL that wipes every rule and reports success; the previous code handed the value to the SDK and failed loudly, and that is preserved. Absent Rules still defaults to [].
  • PreParseTextTransformations / Monetize / PriceMultiplier have no member in the installed SDK, so there is no mapping to write — the fix is an SDK bump, after which the spellings already match. They cannot go in unhandledByDesign, which is top-level property granularity, and all three nest inside the genuinely-handled Rules. Their drop is made loud with a warning naming them on create and update instead of staying silent.

Test plan

  • 25 unit tests, with fails-without-fix measured by a reviewer running surgical reverts rather than inferred: reverting the base64 decode turns 7 red, reverting the ARN rename 6, reverting the non-array Rules handling 1, and no-op'ing the warn helper 2. The remaining tests are deliberate construction-green guards (plain SearchString untouched, absent / falsy Rules, no mutation of the caller's object, no spurious warning, an already-ARN-spelled value left alone).
  • Full suite: 517 files / 8737 tests, no type errors.
  • Real AWS (/run-integ wafv2, us-east-1). The fixture now exercises all three divergence sites — a base64 ByteMatchStatement under the rate-based rule's ScopeDownStatement, one under a NotStatement, and an IPSetReferenceStatement under an AndStatement — so the deploy itself is the regression signal: pre-fix CreateWebACL rejects the stack. Deploy 9/9 created; GetWebACL round-tripped all three (Y2RrZC1zY29wZWQ=, Y2RrZC1ibG9ja2Vk, and the IPSet ARN under ARN); destroy 7 deleted / 2 DeletionPolicy: Retain / 0 errors; account swept clean.

Known gaps (reviewer-surfaced, deliberately not widened into this PR)

  • readCurrentState has no reverse mapping, so cdkd drift reports permanent phantom Rules drift — AWS returns ARN and a decoded Uint8Array SearchString, while state holds Arn / SearchStringBase64. Tracked in (AWS::WAFv2::WebACL: phantom drift on every ByteMatchStatement — readCurrentState returns the SDK Uint8Array SearchString unmapped #1403), which this work widened: the reverse map must be BASELINE-driven (only the template knows whether it used SearchString or SearchStringBase64), and it now also has to un-rename ARN. Not folded in here because the cheaper alternative — a getDriftUnknownPaths entry for Rules — trades a false positive for never reporting real rule drift, and the repo argues that trade deserves an explicit decision.
  • The integ asserts the missing-key class, not decode correctness. Deploy-success is a sound conjunctive assertion that all three divergence sites were accepted, but a hypothetical "renamed the key, forgot to decode" variant would send the base64 STRING at a blob member, which the SDK re-encodes and AWS accepts. Decode correctness is covered by unit tests instead (a Buffer-vs-Uint8Array mutation turns 7 red), and the exact bytes were confirmed by hand against GetWebACL on every verification run. Closing it mechanically means converting this standard-flow fixture to a verify.sh.
  • ARN-rename recursion coverage is unit-tested at top level and under RateBasedStatement.ScopeDownStatement; the other three recursion points are covered for the base64 conversion only. Both conversions ride the same toSdkStatement frame, so they cannot regress independently.

Follow-ups

Closes #1389

@go-to-k
go-to-k force-pushed the fix/1389-wafv2-search-string-base64 branch from bb7820d to e5ffba2 Compare August 9, 2026 05:58
@go-to-k go-to-k changed the title fix(wafv2): decode the CFn-only SearchStringBase64 into the SDK SearchString fix(wafv2): decode SearchStringBase64 and rename reference-statement Arn to the SDK ARN Aug 9, 2026
@go-to-k
go-to-k force-pushed the fix/1389-wafv2-search-string-base64 branch from e5ffba2 to 30eebbe Compare August 9, 2026 06:04
…Arn to the SDK ARN

WAFv2WebACLProvider forwarded the CFn Rules blob raw. The AWS SDK v3 serializer
drops unknown members, and the Rules tree carries exactly two spellings CFn uses
that the SDK model does not (all 154 CFn keys were diffed against the SDK member
set, so this is the complete list):

- ByteMatchStatement.SearchStringBase64 exists ONLY in CloudFormation; the SDK
  carries a single SearchString blob member. The key was dropped and
  CreateWebACL failed validation on the missing required SearchString.
- IPSetReferenceStatement / RegexPatternSetReferenceStatement /
  RuleGroupReferenceStatement spell the reference ARN `Arn` in CFn and `ARN` in
  the SDK, where it is also required. Same drop, same loud create failure — so
  any WebACL using a reference statement was broken regardless of base64.

Both leaves are nestable, so one recursive walk handles them, covering every
member the SDK Statement union declares as nested: NotStatement.Statement,
And/OrStatement.Statements[], and the ScopeDownStatement of both
RateBasedStatement and ManagedRuleGroupStatement. The walk rebuilds every level,
so the caller's properties object is never mutated. Plain SearchString values
are left untouched: the serializer accepts a string at a blob member and encodes
it, which is the existing working behavior.

A truthy non-array Rules value (an unresolved intrinsic) is now passed through
rather than defaulted to []. Defaulting it would make update() a silent
UpdateWebACL that wipes every rule and reports success, where the previous code
handed the value to the SDK and failed loudly.

PreParseTextTransformations, Monetize and PriceMultiplier have no member in the
installed SDK model, so no mapping exists to write -- the fix is an SDK bump,
after which the spellings already match. They cannot go in unhandledByDesign
either, which is top-level property granularity, and all three nest inside the
genuinely-handled Rules. Their drop is made loud with a warning naming them on
create and update instead of staying silent.

The wafv2 integ fixture now exercises all three divergence sites -- a base64
ByteMatchStatement under the rate-based rule's ScopeDownStatement, one under a
NotStatement, and an IPSetReferenceStatement under an AndStatement -- so the
deploy itself is the regression signal: pre-fix CreateWebACL rejects the stack.

Closes #1389
@go-to-k
go-to-k force-pushed the fix/1389-wafv2-search-string-base64 branch from 30eebbe to 6c24643 Compare August 9, 2026 06:53
@go-to-k
go-to-k merged commit 615c33b into main Aug 9, 2026
6 checks passed
@go-to-k
go-to-k deleted the fix/1389-wafv2-search-string-base64 branch August 9, 2026 06:58
github-actions Bot pushed a commit that referenced this pull request Aug 9, 2026
## [0.278.7](v0.278.6...v0.278.7) (2026-08-09)

### Bug Fixes

* **wafv2:** decode SearchStringBase64 and rename reference-statement Arn to the SDK ARN ([#1405](#1405)) ([615c33b](615c33b))
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 0.278.7 🎉

The release is available on:

Your semantic-release bot 📦🚀

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::WAFv2::WebACL: SearchStringBase64 not decoded into SDK SearchString — CreateWebACL fails; plus SDK-missing rule members pass through silently

1 participant