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
+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