Skip to content

Fbe.octo wrapper: .send / .public_send bypasses the off_quota? guard #449

@igor-belousov

Description

@igor-belousov

What

Calling .send(:method_name, args) or .public_send(:method_name, args) on the object returned by Fbe.octo bypasses the pre-call quota guard entirely. The call reaches the underlying Octokit client directly without going through the off_quota? check.

Why it happens

Fbe.octo wraps the Octokit client with intercepted (from the intercepted gem), which builds a BasicObject subclass and routes everything through method_missing. BasicObject does not define send or public_send, so:

  1. wrapper.send(:search_issues, 'q') triggers method_missing(:send, :search_issues, 'q').
  2. The intercepted block sees m = :send, which is NOT in Fbe::SEARCH_METHODS and is not off_quota?.
  3. Falls through to @o.__send__(:send, :search_issues, 'q') on the inner decoor object, which dispatches search_issues directly without the quota check.

Net effect: any caller using .send / .public_send on Fbe.octo (intentionally or via a meta-programming helper) skips the entire quota protection introduced by #447 and the original core check.

How I noticed

Discovered while writing tests for #447. The test test_search_methods_are_routed_through_search_quota_check initially used o.public_send(m, 'q') to iterate over Fbe::SEARCH_METHODS and assert each was blocked. The test failed for every method because public_send itself was being intercepted instead. Switched to o.__send__(m, 'q') to make the test work.

Reproduction

o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new)
# search quota is exhausted (remaining: 1, threshold: 5)
o.search_issues('q')           # raises Fbe::OffQuota (correct)
o.send(:search_issues, 'q')    # makes the HTTP call (bug)
o.public_send(:search_issues, 'q')  # also makes the HTTP call (bug)
o.__send__(:search_issues, 'q')     # raises Fbe::OffQuota (correct)

Scope

Affects both core and search quota paths. Predates #447 / PR #448; the search work just made it visible.

Possible fixes

  1. Define send and public_send on the intercepted wrapper that re-dispatch through the wrapper's own method_missing. Simplest, but requires upstream change to the intercepted gem (or a local override).
  2. Document that Fbe.octo callers must use direct method invocation, never .send / .public_send. Cheapest, but easy to forget.
  3. Replace intercepted with a different wrapping strategy that doesn't inherit from BasicObject.

Discovered while working on #447 / PR #448.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions