fix: assert which aws binary runs, because this script rewrites the whole config
Build and deploy / build-and-deploy (push) Failing after 4s

Round 2 returned an addendum after the previous commit, having re-checked its
own instrument. Two findings, one blocking on the next --apply.

This machine carries two AWS CLIs on PATH: /opt/homebrew/bin/aws 2.34.53 and
/usr/local/bin/aws 2.11.15 (April 2023). configure.mjs called bare `aws`, so
PATH decided. That matters because update-distribution is a full replace and
botocore parses a config against its own model, dropping members it does not
know — an old CLI reads a lossy config and writes the loss back, and --if-match
cannot catch it because the ETag is genuinely current.

Measured, which the review could not do: the older model is missing nine
members, and E1OK7G98KNKUTA carries two of them — GrpcConfig on the default
behaviour and on /api/*, both {Enabled: false}. So a round trip through the old
CLI would write back the same effective value and change nothing observable
today. That is precisely why it needed a guard rather than a look: nothing
reports it when that stops being true.

The script now resolves the binary, prints it and its version as the first line
of output, and exits 2 below a floor before any AWS call. Verified both ways.

Second finding: entry (av) claimed "no client-side validation of any kind"
stood between the 182-character Comment and the API, from reading one
validate.py without naming which CLI it came from — and the other install is
frozen, so it is unchecked rather than confirmed. That is CLAUDE.md's named
shape. Both records now name the instrument and version and lead with the claim
that needs no qualification: the Comment reached the API and came back
InvalidArgument, so nothing stopped it on the CLI that ran.

The second install's model independently confirms FunctionARN max 108, its
pattern, and the 128 on both Comment members.

Nothing was applied to the distribution and nothing was deployed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Md3GndFqWPzK78xAoebsg5
This commit is contained in:
Pouya Lajevardi
2026-09-04 14:32:11 -04:00
co-authored by Claude Opus 5
parent a07193d561
commit 07a8ff6989
4 changed files with 206 additions and 17 deletions
+93
View File
@@ -957,6 +957,99 @@ never being raised again.
# Change Log
## 2026-09-04 (aw) — Round 2's addendum: this machine has two AWS CLIs 23 minor versions apart, the script called whichever `PATH` chose, and it rewrites the whole distribution config
**Not a third round.** `adversarial-reviewer` returned an addendum to round 2
after entry (av) was committed, having gone back to check its own instrument. Two
findings, one blocking on the **next `--apply`** — which is why they are acted on
rather than deferred. Entry (av) stands; nothing in it is retracted.
---
### 1. 🛑 The script called bare `aws`, and there are two
`execFileSync('aws', …)` resolves from `PATH`. Measured 2026-09-04:
**`/opt/homebrew/bin/aws` = 2.34.53** and **`/usr/local/bin/aws` = 2.11.15**
(April 2023), both on `PATH`.
**Why that is not cosmetic here.** `configure.mjs` reads the whole distribution
config, mutates it, and posts the whole thing back — `update-distribution` is a
**full replace** — and botocore parses a response against **its own** model,
dropping members that model does not know. An old CLI therefore reads a lossy
config and writes the loss back. **`--if-match` cannot catch it**: the ETag is
genuinely current; only the parse was stale.
**The reviewer could not measure the blast radius and said so. It is now
measured.** Comparing the two installs' models for the same API version, the
older one does not know nine members across `DistributionConfig`,
`CacheBehavior` and `Origin` — and `E1OK7G98KNKUTA` **carries two of them**:
```
DefaultCacheBehavior.GrpcConfig = {"Enabled": false}
CacheBehaviors[0].GrpcConfig = {"Enabled": false}
```
⚠️ **Both are `{Enabled: false}`, which is the default — so a round trip through
2.11.15 would write back the same effective value and change nothing observable
today.** That is the whole reason it needed finding rather than noticing:
**nothing would report it when that stops being true**, and the seven others
(`ConnectionMode`, `VpcOriginConfig`, `CacheTagConfig`, `AnycastIpListId`,
`TenantConfig`, `ViewerMtlsConfig`, `ResponseCompletionTimeout`) are one console
change away from being present.
**The fix.** The script resolves the binary, reads `aws --version`, and prints
both as its **first line of output** — beside the two `resolved Managed-…` lines
that already exist for the same reason. Below a floor it **exits 2 before any AWS
call**. Verified both ways: `/opt/homebrew/bin/aws (2.34.53)` proceeds;
`PATH=/usr/local/bin:$PATH` prints `/usr/local/bin/aws (2.11.15)` and exits **2**
with **zero** `resolved Managed-` lines, i.e. no call was made. The floor is the
version this was verified against — older fails loudly, which is the safe
direction, and newer passes. **Lower it only with an entry here, never to make a
run go through.**
### 2. A universal claim from one unnamed instrument
Entry (av) recorded that *"no client-side validation of any kind stood between
that 182-character string and the CloudFront API"*, from reading
`botocore/validate.py`. Neither the runbook nor the module said **which** CLI
that was, and the second install is PyInstaller-frozen — its `validate.py` is not
extractable, so it is **unchecked, not confirmed**.
That is the shape `CLAUDE.md` corrected on 2026-09-01: *"no mechanism can X" is a
claim about every mechanism, including the ones you did not enumerate*, and the
preferred form was available. Both places now name the instrument and version —
`aws-cli/2.34.53`'s bundled `validate.py` — say plainly that the other is
unchecked, and lead with the claim that needs no qualification: **the
182-character `Comment` reached the API and came back `InvalidArgument`, so
nothing stopped it on the CLI that actually ran.**
The same edit carries finding 3's carve-out into the module: every entry **with a
cited AWS source** is enforced by the service and by nothing local; the two
`[assumed]` `Name` entries are not known to be enforced at all.
⚠️ **This does not weaken `PAYLOAD_LIMITS`** — the direct evidence for it is the
failed call, not the validator read.
### 3. Corroboration the addendum also produced
The second install's model independently confirms four constants entry (av)
sourced from one: `FunctionARN` `max: 108` and its pattern, and *"cannot be
longer than 128 characters"* on **both** `Comment` members. Two independent
models, same numbers.
### 4. Verification
`check` 0 · `build` 0 (23 pages) · `check:claims` 0 · `check:intake` 0 ·
`og:proof` 0 · `lint` 0 · `router.test` 0 (30/30) · `policy-shapes.test` 0
(53/53) · `spam-score.test` 0 (39/39) · minifier grep 1 (clean) · live dry run
**0**, 3 changes, 0 skips · stale-CLI refusal **2**, before any AWS call.
**Nothing was applied and nothing was deployed.** The distribution still shows
`/api/*` on `b689b0a8-…`; the account still holds **1** custom response-headers
policy (the orphan) and **0** origin request policies.
---
## 2026-09-04 (av) — The second `--apply` got one policy further and failed on a 128-character cap that nothing local enforces; the orphan it left is reused by name, measured; the dry run gains the only pre-flight this script has; and §7's own record broke the table it lives in
**Pouya's ruling of 2026-09-04**, after the second attempt: *"Fix: shorten BOTH
+34 -7
View File
@@ -329,6 +329,28 @@ dump** of the `*.pdf` behaviour it would add, 2 blank lines, the
`N change(s) to distribution …` header and the `DRY RUN — nothing was sent.`
footer `[measured 2026-09-04, exit 0, nothing written]`.
🛑 **THE FIRST LINE OF OUTPUT NAMES THE `aws` BINARY AND ITS VERSION. READ IT.**
This script **rewrites the whole distribution config**, and botocore drops
members its own model does not know — so an old CLI reads a lossy config and
writes the loss back, on a distribution serving 23 pages and the intake form.
`--if-match` cannot catch that: the ETag is genuinely current.
⚠️ **THIS MACHINE HAS CARRIED TWO CLIs**, `2.34.53` and `2.11.15` (April 2023),
both on `PATH` `[measured 2026-09-04]`. The older model does not know
`GrpcConfig`, and `E1OK7G98KNKUTA` carries it on **two** behaviours — both
`{Enabled: false}`, so the round trip is lossless *in effect* today, and nothing
would report it when that stops being true. It is also missing `ConnectionMode`,
`VpcOriginConfig`, `CacheTagConfig` and five more. The script now **refuses below
a floor** rather than leaving it to `which`:
```
resolved aws = /opt/homebrew/bin/aws (2.34.53)
```
If that line names `/usr/local/bin/aws` or a version below the floor, the run
exits **2 before any AWS call**. Run `which -a aws` and fix `PATH` — do not
lower the floor to get through.
🛑 **READ THE EXIT STATUS, AND IT HAS THREE VALUES.** `0` — everything this
script manages was applied or is already present. `2` — a usage error, before any
AWS call. **`3` — sections that could run did, and something was SKIPPED: read
@@ -507,13 +529,18 @@ adding `Comment` to the drift check would throw on this very policy and block
the run that attaches it.
**WHY NOTHING CAUGHT IT LOCALLY, AND THIS IS THE GENERAL LESSON.** Measured
2026-09-04: **`botocore/validate.py` checks neither `max` nor `pattern`** — its
`range_check()` reads only `min`, and the word `pattern` does not appear in the
file. And the 128 cap is not modelled as a constraint at all: on both policy
configs `Comment` is a bare `string`, and the number lives in the shape's
**`documentation` prose**. So **no client-side validation of any kind stood
between that 182-character string and the CloudFront API**, which is exactly why
the pre-flight below had to be built rather than relied upon.
2026-09-04 **against `aws-cli/2.34.53`'s bundled `botocore/validate.py`**: it
checks **neither `max` nor `pattern`** — `range_check()` reads only `min`, and
the word `pattern` does not appear in the file. And the 128 cap is not modelled
as a constraint at all: on both policy configs `Comment` is a bare `string`, and
the number lives in the shape's **`documentation` prose**.
⚠️ **THAT NAMES ONE VALIDATOR, DELIBERATELY.** This machine also carries
`aws-cli/2.11.15`, which is PyInstaller-frozen and whose `validate.py` cannot be
read — unchecked, not confirmed. The claim that holds without qualification is
the narrower and more useful one: **the 182-character `Comment` reached the API
and came back `InvalidArgument`, so nothing stopped it on the CLI that ran.**
That is why the pre-flight below had to be built rather than relied upon.
**BOTH POLICY COMMENTS ARE NOW UNDER 80 CHARACTERS**, and the dry run enforces
the limits it knows about — `infra/cloudfront/policy-shapes.mjs`,
+59
View File
@@ -131,6 +131,65 @@ if (args.includes('--function-arn')) {
}
}
/**
* 🛑 **WHICH `aws` RAN IS PART OF THE RESULT, BECAUSE THIS SCRIPT DOES A FULL
* READ-MODIFY-WRITE.** `update-distribution` replaces the whole config, and
* botocore parses the config it reads against **its own** model, silently
* dropping members that model does not know. So an old CLI reads a lossy config
* and writes the loss back — on a distribution serving 23 public pages and the
* intake form. `--if-match` cannot catch it: the ETag is genuinely current.
*
* ⚠️ NOT HYPOTHETICAL ON THIS MACHINE. Measured 2026-09-04: two CLIs on PATH,
* `2.34.53` and `2.11.15` (April 2023). The older model does not know
* `GrpcConfig`, and `E1OK7G98KNKUTA` carries it on **two** behaviours. Both are
* `{Enabled: false}` today — the default — so the round trip happens to be
* lossless in effect, and **nothing would report it when that stops being true.**
* The old model is also missing `ConnectionMode`, `VpcOriginConfig`,
* `CacheTagConfig` and five more.
*
* The floor is the version this was verified against. Older fails loudly, which
* is the safe direction; newer passes. Lower it deliberately, with a Change Log
* entry, never to make a run go through.
*/
const AWS_CLI_FLOOR = [2, 34, 53];
const awsBinary = execFileSync('command', ['-v', 'aws'], {
encoding: 'utf8',
shell: '/bin/sh',
stdio: ['ignore', 'pipe', 'inherit'],
}).trim();
const awsVersionLine = execFileSync('aws', ['--version'], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'inherit'],
}).trim();
const awsVersion = (awsVersionLine.match(/aws-cli\/(\d+)\.(\d+)\.(\d+)/) ?? [])
.slice(1)
.map(Number);
if (awsVersion.length !== 3) {
console.error(`could not read a version out of: ${awsVersionLine}`);
process.exit(2);
}
const belowFloor = (() => {
for (let i = 0; i < 3; i += 1) {
if (awsVersion[i] !== AWS_CLI_FLOOR[i])
return awsVersion[i] < AWS_CLI_FLOOR[i];
}
return false;
})();
console.log(
`resolved aws = ${awsBinary} (${awsVersion.join('.')})`,
);
if (belowFloor) {
console.error(
`\naws-cli ${awsVersion.join('.')} is below the floor ${AWS_CLI_FLOOR.join('.')}.\n` +
`This script rewrites the WHOLE distribution config, and botocore drops config ` +
`members its own model does not know — so an old CLI reads a lossy config and ` +
`writes the loss back. Measured 2026-09-04: 2.11.15 does not know GrpcConfig, ` +
`which this distribution carries on two behaviours.\n` +
`Resolved binary: ${awsBinary}. Check \`which -a aws\` — this machine has had two.`,
);
process.exit(2);
}
/* stderr is NEVER suppressed and the exit status is always read — the AWS CLI
reports an expired session, a missing permission and a typo'd id all on
stderr with a non-zero status, and swallowing that is how "it failed" becomes
+20 -10
View File
@@ -77,16 +77,26 @@ export function emptyObjectPaths(value, path = '') {
}
/**
* ⚠️ **NOTHING LOCAL ENFORCES ANY OF THESE, WHICH IS WHY THIS TABLE EXISTS.**
* Measured 2026-09-04: `botocore/validate.py` checks **neither `max` nor
* `pattern`** — `range_check()` reads only `min`, and the word `pattern` does
* not appear in the file — and the caps that matter are not modelled as
* constraints anyway. On both policy configs `Comment` is a bare `string`, and
* the 128 lives in the shape's **`documentation` prose**. So a 182-character
* `Comment` left the machine unremarked and came back `InvalidArgument`, after
* section 4 had already created its policy. **Every limit here is enforced by
* the service and by nothing else**, which makes the dry run the only
* pre-flight there is. `docs/09` Part 3 carries both attempts.
* ⚠️ **NO VALIDATOR I COULD READ ENFORCES ANY OF THESE, WHICH IS WHY THIS TABLE
* EXISTS.** Measured 2026-09-04 against **`aws-cli/2.34.53`'s bundled
* `botocore/validate.py`**: it checks **neither `max` nor `pattern`** —
* `range_check()` reads only `min`, and the word `pattern` does not appear in
* the file. And the caps that matter are not modelled as constraints anyway: on
* both policy configs `Comment` is a bare `string`, and the 128 lives in the
* shape's **`documentation` prose**.
*
* ⚠️ **THAT IS ONE INSTRUMENT, NAMED, NOT A CLAIM ABOUT EVERY MECHANISM.** This
* machine also carries `aws-cli/2.11.15`, whose install is PyInstaller-frozen
* and whose `validate.py` cannot be read, so it is **unchecked rather than
* confirmed**. `CLAUDE.md`: *"no mechanism can X" is a claim about every
* mechanism, including the ones you did not enumerate* — so the honest form is
* this one. What is **direct evidence** either way: the 182-character `Comment`
* reached the API and came back `InvalidArgument`, so nothing stopped it on the
* CLI that actually ran. `docs/09` Part 3 carries both attempts.
*
* Every entry below **with a cited AWS source** is therefore enforced by the
* service and by nothing local. The two `[assumed]` entries are not known to be
* enforced at all.
*
* ⚠️ **THE ENTRIES THAT MATTER MOST GUARD *CLONED* VALUES, NOT LITERALS THIS
* FILE AUTHORS.** A literal we write is reviewed when it is written; a value