Files
buzz/crates/buzz-push-gateway/tests
Tom Brow c477663211 fix(push-gateway): log which App Attest check rejected a device (#8630)
When the push gateway rejected a device's App Attest attestation or
assertion, it logged the reason only at debug level. In production that
left a bare `401 invalid_attestation` in the access logs, with no sign
of which check failed. We hit this diagnosing an internal iPhone that
never received push. Its enrollments failed with repeated `401`s, and
the gateway logs could not say why.

Rejections now log at warn level with:
- `operation`: `attestation` or `assertion`
- `reason`: the existing category, for example `certificate` or
`identity`
- `detail`: the specific failed check, for example `invalid AAGUID` or
`invalid App ID`

Malformed base64 now has its own `encoding` reason. The size, base64 and
challenge checks that run before full verification now take the same
path. Previously they rejected without logging anything. Without
`RUST_LOG`, the gateway now defaults to `error,buzz_push_gateway=warn`,
which keeps every target's errors plus the gateway's own warnings.
Previously the default showed only errors, and the Helm chart sets no
`RUST_LOG`, so a default deployment would have dropped these warnings.
The HTTP response is unchanged and stays a uniform
`invalid_attestation`. The logged fields are fixed strings, with no
device or request data.

Companion iOS PR [#8629](https://github.com/block/buzz/pull/8629) stops
a device from replaying the same rejected enrollment indefinitely.

### Validation
- Added tests: a single-test binary checks the emitted JSON warning
(level, operation, reason, detail) through
`AppAttestVerifier::verify_attestation`, for malformed base64, an empty
proof, and malformed CBOR. Another checks that the default filter keeps
gateway warnings and dependency errors, drops dependency warnings, and
still honors an explicit `RUST_LOG`. Both fail when the warning is
downgraded to debug or the default filter loses its `error` fallback.
- The existing push gateway suite passes locally, including the App
Attest rejection tests. Clippy is clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Tom Brow <tomb@block.xyz>
2026-10-27 12:59:44 -07:00
..