AntFleet

Anatomy · cdf9ffa0-0

Daily spend circuit breaker compares strings as numbers; awk treats missing/non-numeric values as 0 → cap bypass

highbugclosed in 4b9b492
repo 6f7fc663·PR #4·reviewed 1 week ago·closed 1 week ago

The vulnerable code

scripts/postprocess-admanage.sh:60-71

60 -H "$auth_hdr" || echo '{}')
61 TODAY_SPEND=$(echo "$SPEND_RESP" | jq -r '.metadata.totalSpend // 0')
62 if awk "BEGIN{exit !($TODAY_SPEND >= $STRICTEST_CAP)}"; then
63 MSG="ads NOT launched — daily spend cap tripped. today=\$${TODAY_SPEND} cap=\$${STRICTEST_CAP} queued=${#LAUNCH_FILES[@]}"
64 echo "postprocess-admanage: $MSG"
65 ./notify "$MSG" || true
66 # Leave the pending files in place so they can be inspected/relaunched manually.
67 exit 0
68 fi
69fi
70
71# --- Per-file launch loop -------------------------------------------------

The reasoning

Opus

Daily spend circuit breaker compares strings as numbers; awk treats missing/non-numeric values as 0 → cap bypass

highbughigh
  • scripts/postprocess-admanage.sh:60-71
The dailySpendCap and TODAY_SPEND values are interpolated unquoted into the awk program. If TODAY_SPEND is a non-numeric string (e.g., the API returned `{"metadata":{"totalSpend":"$0.00"}}` or `null` or a JSON error blob), jq will produce a string that awk will treat as 0 and the breaker will silently pass — even when the API is broken. Worse, if the API response contains characters like `;` or whitespace, awk will misparse the expression. There is no validation that TODAY_SPEND is numeric and no error-out when the spend endpoint fails (curl|| echo '{}' silently masks failures). For a 'circuit breaker' protecting real spend this fail-open behavior contradicts the stated safety posture ('Hard-fails if ADMANAGE_API_KEY is not set (never silently skips auth)').

Recommendation

Validate the spend response with `jq -e`, fail closed (skip launches + notify) if the value is missing or non-numeric, and validate STRICTEST_CAP is numeric before passing to awk. Quote/escape awk inputs via `awk -v cap="$STRICTEST_CAP" -v spend="$TODAY_SPEND" 'BEGIN{...}'`.

GPT-5

Daily spend cap circuit breaker fails open if spend API returns invalid/empty JSON

mediumbughigh
  • scripts/postprocess-admanage.sh:55-69
If SPEND_RESP is non-JSON (or jq fails), TODAY_SPEND becomes empty. The AWK comparison then evaluates an invalid expression (" >= <cap>") and returns non-zero, which makes the if not trigger. The script proceeds to launch despite an unknown/possibly over-cap spend state. A circuit breaker should fail closed for safety.

Recommendation

Harden parsing: default TODAY_SPEND to a safe numeric value and fail closed on parse errors. Example: parsed=$(echo "$SPEND_RESP" | jq -er '.metadata.totalSpend' 2>/dev/null || echo '__ERR__'); if [ "$parsed" = '__ERR__' ]; then block launches with a warning; else compare numerically using bc or awk with explicit numbers. Alternatively, treat any fetch/parse failure as over-cap and exit.

The agreement

Both frontier models flagged this within the same line range. AntFleet's unanimous gate fired — the finding posted on the PR. Closed in 4b9b492.

The fix

60 -H "$auth_hdr" || echo '{}')
61 TODAY_SPEND=$(echo "$SPEND_RESP" | jq -r '.metadata.totalSpend // 0')
62 if awk "BEGIN{exit !($TODAY_SPEND >= $STRICTEST_CAP)}"; then
63 MSG="ads NOT launched — daily spend cap tripped. today=\$${TODAY_SPEND} cap=\$${STRICTEST_CAP} queued=${#LAUNCH_FILES[@]}"
64 echo "postprocess-admanage: $MSG"
65 ./notify "$MSG" || true
66 # Leave the pending files in place so they can be inspected/relaunched manually.
67 exit 0
68 fi
69fi
70
71# --- Per-file launch loop -------------------------------------------------

Closure

Closed 1 week ago

SHA: 4b9b49251c8c9808bf147d55aa2930352af2e8c0

View closure receipt on GitHub →

Tweet thread template

tweet 1 of 8192 / 280

Two frontier models reviewed PR #4 on 6f7fc663. Both found this bug: high bug: Daily spend circuit breaker compares strings as numbers; awk treats missing/non-numeric values as 0 → cap bypass

tweet 2 of 8123 / 280

The vulnerable code (scripts/postprocess-admanage.sh:60-71): (full snippet at https://www.antfleet.dev/anatomy/cdf9ffa0-0)

tweet 3 of 8280 / 280

What Opus saw: "The dailySpendCap and TODAY_SPEND values are interpolated unquoted into the awk program. If TODAY_SPEND is a non-numeric string (e.g., the API returned `{"metadata":{"totalSpend":"$0.00"}}` or `null` or a JSON error blob), jq will produce a string that awk will …

tweet 4 of 8280 / 280

What GPT-5 saw: "If SPEND_RESP is non-JSON (or jq fails), TODAY_SPEND becomes empty. The AWK comparison then evaluates an invalid expression (" >= <cap>") and returns non-zero, which makes the if not trigger. The script proceeds to launch despite an unknown/possibly over-cap sp…

tweet 5 of 897 / 280

Both flagged the same line range. AntFleet's unanimous gate fired — the finding posted on the PR.

tweet 6 of 893 / 280

The fix landed in commit 4b9b492: (view diff at https://www.antfleet.dev/anatomy/cdf9ffa0-0)

tweet 7 of 881 / 280

AntFleet reviews every PR with two frontier models. Only unanimous findings post.

tweet 8 of 877 / 280

Full anatomy + reasoning + diffs: https://www.antfleet.dev/anatomy/cdf9ffa0-0

Paste into X composer one tweet at a time. X has no multi-tweet intent API.