Skip to content

fix: strip multi-digit array indices from query string#5

Merged
JasonHolderness-bl merged 1 commit into
masterfrom
fix/query-string-cleaner-multi-digit-indices
May 20, 2026
Merged

fix: strip multi-digit array indices from query string#5
JasonHolderness-bl merged 1 commit into
masterfrom
fix/query-string-cleaner-multi-digit-indices

Conversation

@JasonHolderness-bl

Copy link
Copy Markdown

Problem

QueryStringCleanerMiddleware::buildQuery() and LeverClient::prepareOptions() both use:

preg_replace('/%5B[0-9]%5D/', '', $query)

That pattern matches %5B + exactly one digit + %5D, so it strips %5B0%5D through %5B9%5D but leaves %5B10%5D, %5B11%5D, ... intact.

When a caller chains the same builder method more than 10 times, e.g.:

$client = $lever->opportunities()->performAs($userId);
foreach ($postingIds as $id) {           // 12 posting IDs
    $client->posting($id);
}
$client->fetch();

http_build_query() indexes the array, the regex strips the first ten, and the final URL is a mix of clean and array-indexed parameters:

opportunities
  ?posting_id=...&posting_id=...&...&posting_id=...   ← first 10 cleaned
  &posting_id%5B10%5D=...&posting_id%5B11%5D=...      ← left behind

Lever's API rejects these requests, causing downstream production errors.

Fix

Switch both regexes to /%5B\d+%5D/ so any numeric index is stripped regardless of digit count. Behavior for batches ≤10 is unchanged.

Test

Added OpportunitiesTest::retrieve_opportunities_with_more_than_ten_postings_strips_all_array_indices, which calls ->posting() 12 times and asserts:

  • No %5B (URL-encoded [) anywhere in the URL
  • posting_id[ (decoded form) does not appear
  • Exactly 12 posting_id= parameters
  • Each provided UUID is present

I verified the new test fails against the old single-digit regex and passes with the multi-digit fix. Existing test suite (17 tests, 56 assertions) remains green.

Downstream context

Found while debugging Bugsnag errors in a Laravel app that batches up to ~30 posting_id parameters in a single opportunities request. The application now bypasses the fluent builder as a workaround; once this fix is released we can drop the workaround.

The array-bracket cleanup regex in QueryStringCleanerMiddleware and
LeverClient::prepareOptions() was '/%5B[0-9]%5D/', which only matches
single-digit indices. Calling the same builder method more than 10
times (e.g. ->posting($id) for 11+ posting IDs) accumulated indices
into the query string, but only %5B0%5D-%5B9%5D were stripped — leaving
posting_id[10]=..., posting_id[11]=..., etc. in the final URL, which
the Lever API rejects.

Switched the regex to '/%5B\d+%5D/' so all numeric indices are stripped
regardless of digit count.

Added a regression test that calls ->posting() 12 times and asserts the
resulting URL contains exactly 12 'posting_id=' parameters with no array
brackets or %5B in the output.
@JasonHolderness-bl
JasonHolderness-bl merged commit 38ed1fa into master May 20, 2026
3 checks passed
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.

1 participant