Skip to content

[INT-1091] Treat Square 403 as verified on INSUFFICIENT_SCOPES - #5367

Merged
MuneebUllahKhan222 merged 2 commits into
mainfrom
INT-1091
Sep 28, 2026
Merged

MuneebUllahKhan222 merged 2 commits into
mainfrom
INT-1091

Conversation

@MuneebUllahKhan222

@MuneebUllahKhan222 MuneebUllahKhan222 commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The Square detector treated every 403 from the verification endpoint as a live key:

if res.StatusCode == http.StatusOK || res.StatusCode == http.StatusForbidden {
    result.Verified = true
}

A customer reported a key that the detector marked as verified, but a direct curl to the Square API with the same key returned 401. A 403 doesn't have to come from Square's credential check. For example:

  • A TLS-terminating security appliance whose CA is in the host's trust store. It completes the handshake, so its 403 looks like Square's answer.
  • Square's own edge returning 403 for reasons unrelated to credential validity, such as rate limiting or bot protection.

The detector also closed the response body without reading it, so there was nothing to show where the 403 came from.

Changes (pkg/detectors/square/square.go)

  • Moved verification into verifyMatch(ctx, client, token), and added an injectable client on Scanner (defaults to common.SaneHttpClient()). Results now go through SetVerificationError.
  • For a 403, the body is parsed as Square's error envelope ({"errors":[{"category":...,"code":...,"detail":...}]}):
Response Verified Verification error
200 ✅ —
401 ❌ —
403 with errors[].code == "INSUFFICIENT_SCOPES" ✅ —
403 with any other body (HTML, other code, empty) ❌ unexpected 403 from Square: <body>
any other status (e.g. 429, 5xx) ❌ unexpected HTTP response status <code>: <body>
  • The body is read with a 4 KiB limit. In the error it's cut to 512 bytes with whitespace collapsed, so an HTML block page doesn't flood the output. The token is never included.

Other 403s are left indeterminate on purpose, not marked invalid. Marking them invalid would make a live key behind a blocking proxy look dead. With the body in the verification error, the user can see what actually answered.

Tests (pkg/detectors/square/square_test.go)

Added TestSquare_Verification, which runs FromData with a mocked HTTP client (common.ConstantResponseHttpClient) for these cases:

  • 200 → verified
  • 401 → unverified, no error
  • 403 + INSUFFICIENT_SCOPES → verified
  • 403 + HTML proxy page → indeterminate; the error contains the collapsed body
  • 403 + a different Square code (FORBIDDEN) → indeterminate
  • 403 + empty body → indeterminate (<empty body>)
  • 429 → indeterminate; the error contains the body
  • 500 + 10 KB body → the error is truncated to 512 bytes

Each error case also checks that the token doesn't appear in the error message.

Checklist:

  • Tests passing (make test-community)?
  • Lint passing (make lint this requires golangci-lint)?

Note

Medium Risk
Changes live/dead classification for Square API keys during scanning; behavior is more accurate but may alter verified counts for keys that previously matched any 403.

Overview
Tightens Square secret verification so a 403 no longer automatically counts as a live key. Verification is moved into verifyMatch, with an injectable http.Client on Scanner and errors surfaced via SetVerificationError.

A 403 is verified only when the response body parses as Square’s error JSON with errors[].code == "INSUFFICIENT_SCOPES". Other 403s (HTML from a proxy, empty body, other codes) stay unverified and return an indeterminate verification error with a truncated, whitespace-collapsed body snippet—without leaking the token. 401 stays unverified; unexpected statuses (e.g. 429, 5xx) also produce verification errors. Response bodies are read with a 4 KiB cap before close.

Tests: TestSquare_Verification mocks HTTP responses for 200, 401, scoped vs ambiguous 403s, rate limits, and large error bodies.

Reviewed by Cursor Bugbot for commit b779208. Bugbot is set up for automated code reviews on this repo. Configure here.

The Square detector treated every 403 as a live key. A 403 can also come
from a TLS-intercepting proxy or security appliance, or from Square's own
edge (rate limiting, bot protection). Those cases produced false "verified"
results. The response body was also discarded, so there was nothing to
diagnose them with.

- Move verification into verifyMatch with an injectable HTTP client
- Mark a 403 as verified only when Square's error body has code
  INSUFFICIENT_SCOPES
- Report other 403s and unexpected statuses as verification errors that
  include the response body (read up to 4 KiB, cut to 512 bytes in the
  error, whitespace collapsed)
- Add unit tests for 200, 401, 403 (scopes, HTML, other code, empty),
  429, and body truncation
@MuneebUllahKhan222
MuneebUllahKhan222 requested a review from a team September 28, 2026 06:03
@MuneebUllahKhan222
MuneebUllahKhan222 requested a review from a team as a code owner September 28, 2026 06:03
@github-actions

Copy link
Copy Markdown

Corpora Test Results

No detector regex or keyword changes in this PR. Bench skipped.

@MuneebUllahKhan222
MuneebUllahKhan222 merged commit 74d1c44 into main Sep 28, 2026
15 of 16 checks passed
@MuneebUllahKhan222
MuneebUllahKhan222 deleted the INT-1091 branch September 28, 2026 06:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants