Skip to content

Guard MCP bounty search oversized numeric query#313

Merged
ramimbo merged 3 commits into
ramimbo:mainfrom
shootingallday:shootingallday/mcp-list-bounties-oversized-query-292
May 25, 2026
Merged

Guard MCP bounty search oversized numeric query#313
ramimbo merged 3 commits into
ramimbo:mainfrom
shootingallday:shootingallday/mcp-list-bounties-oversized-query-292

Conversation

@shootingallday
Copy link
Copy Markdown
Contributor

@shootingallday shootingallday commented May 25, 2026

Bounty #292

Summary

  • Guard MCP list_bounties issue-number search parsing against Python integer digit-limit failures.
  • Keep oversized all-digit q values on the same text-search/no-match path as already oversized SQLite integers instead of returning an MCP invalid-arguments error.
  • Add regression coverage for a 5000-digit MCP bounty query.

Repro before fix

Calling the MCP list_bounties tool with q = "9" * 5000 returned a JSON-RPC invalid tool arguments error because mcp_issue_number_search_value() attempted int(query_text) and Python raised a digit-limit ValueError.

Expected behavior: treat that query as text-only search, like the existing oversized numeric query case, and return an empty result set when nothing matches.

Validation

  • uv run --with pytest pytest tests/test_api_mcp.py::test_mcp_list_bounties_filters_status_query_and_limit -q -> 1 passed.
  • uv run --with ruff ruff check app/main.py tests/test_api_mcp.py -> passed.
  • uv run --with ruff ruff format --check app/main.py tests/test_api_mcp.py -> 2 files already formatted.
  • uv run --with mypy mypy app -> success.
  • git diff --check -> clean.

No secrets, wallet material, deployment credentials, private vulnerability details, payout details, or MRWK price claims are included.

Summary by CodeRabbit

  • Bug Fixes

    • Numeric inputs are now parsed safely so malformed values no longer raise errors; extremely long numeric queries are handled gracefully and capped where applicable.
  • Tests

    • Added tests validating numeric query length/format limits, ensuring oversized numeric strings return an empty result and endpoints remain stable.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

📝 Walkthrough

Walkthrough

Both the public _issue_number_search_value and MCP mcp_issue_number_search_value now catch ValueError from int(...) and return None on parse failure while retaining SQLITE_INTEGER_MAX checks; tests post 5000-digit numeric q to MCP and /api/v1/bounties and assert empty results.

Changes

Issue-number parsing robustness

Layer / File(s) Summary
Parse hardening in public and MCP handlers
app/main.py
_issue_number_search_value and mcp_issue_number_search_value wrap int(...) in try/except ValueError and return None on parse failure; SQLITE_INTEGER_MAX checks remain after successful parsing.
Tests exercising oversized numeric queries
tests/test_api_mcp.py, tests/test_bounty_pages.py
Added MCP tools/call test and /api/v1/bounties test that post a 5000-digit numeric q and assert HTTP 200 with empty JSON / empty MCP payload list results.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • ramimbo/mergework#286: Hardens _call_mcp_tool numeric q parsing and relates to list_bounties q-filter behavior.
  • ramimbo/mergework#300: Adds SQLITE_INTEGER_MAX checks for numeric ID parsing; related overflow-safeguarding changes.
  • ramimbo/mergework#296: Adds safeguards around parsing very large numeric issue-number strings in a different call site.

Suggested reviewers

  • weilixiong
  • MolhamHamwi
  • TateLyman
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: guarding MCP bounty search against oversized numeric queries, which is the primary focus of the changeset.
Description check ✅ Passed The description includes all key sections: Summary (with 3 clear bullets), Evidence (repro and expected behavior), comprehensive validation results, and explicit security disclosures.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/main.py`:
- Line 1723: Replace the hardcoded literal 2**63 - 1 with the named constant
SQLITE_INTEGER_MAX to match the other helper `_issue_number_search_value`;
locate the return in the function that currently does `return issue_number if
issue_number <= 2**63 - 1 else None` and change the comparison to use
`SQLITE_INTEGER_MAX` so the code is consistent and uses the shared constant
defined earlier.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fad1160d-d177-4e20-b72c-86eb5c03805d

📥 Commits

Reviewing files that changed from the base of the PR and between 4b8baf7 and ebd7e1c.

📒 Files selected for processing (2)
  • app/main.py
  • tests/test_api_mcp.py

Comment thread app/main.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/main.py (1)

123-129: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Apply the same ValueError protection for consistency.

The function _issue_number_search_value has the same vulnerability that was fixed in mcp_issue_number_search_value: calling int(query) on an oversized digit string (e.g., 5000 digits) will raise a ValueError before the SQLITE_INTEGER_MAX check. This affects the regular API endpoint /api/v1/bounties when filtering by query.

For consistency and robustness, wrap the int() call in a try/except block matching the pattern used in the MCP variant.

♻️ Proposed fix
 def _issue_number_search_value(query: str) -> int | None:
     if not query.isdigit():
         return None
-    issue_number = int(query)
-    if issue_number > SQLITE_INTEGER_MAX:
+    try:
+        issue_number = int(query)
+    except ValueError:
         return None
-    return issue_number
+    return issue_number if issue_number <= SQLITE_INTEGER_MAX else None
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/main.py` around lines 123 - 129, The _issue_number_search_value function
can raise ValueError when int(query) is called on extremely long digit strings;
update _issue_number_search_value to wrap the int(query) conversion in a
try/except ValueError block (matching the pattern used in
mcp_issue_number_search_value) so that on ValueError it returns None, then
proceed to check issue_number against SQLITE_INTEGER_MAX and return the integer
otherwise.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/main.py`:
- Around line 123-129: The _issue_number_search_value function can raise
ValueError when int(query) is called on extremely long digit strings; update
_issue_number_search_value to wrap the int(query) conversion in a try/except
ValueError block (matching the pattern used in mcp_issue_number_search_value) so
that on ValueError it returns None, then proceed to check issue_number against
SQLITE_INTEGER_MAX and return the integer otherwise.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 15e7d78c-2a9a-46f4-a0ca-5491a18ed1d6

📥 Commits

Reviewing files that changed from the base of the PR and between ebd7e1c and cab17ad.

📒 Files selected for processing (1)
  • app/main.py

Copy link
Copy Markdown

@weilixiong weilixiong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed:

Files: app/main.py (try/except int() in mcp_issue_number_search_value, using SQLITE_INTEGER_MAX), tests/test_api_mcp.py (oversized query regression test)

Checked: 9*5000 → ValueError → None → empty [] MCP response. Uses existing SQLITE_INTEGER_MAX constant from PR#300. Consistent with PR#312 approach.

CI: ✅ quality/readiness/docs/image

Risk: LOW — 2 files, MCP search hardening, +20/−2

@shootingallday
Copy link
Copy Markdown
Contributor Author

Follow-up pushed in 34de778 for the CodeRabbit outside-diff finding.

What changed:

  • _issue_number_search_value() now catches ValueError from oversized all-digit query strings before the SQLite integer limit check.
  • Added a public /api/v1/bounties?q=<5000 digits> regression alongside the existing oversized query coverage.

Validation:

  • uv run --with pytest pytest tests/test_bounty_pages.py::test_bounties_page_and_api_search_by_text_and_issue_number tests/test_api_mcp.py::test_mcp_list_bounties_filters_status_query_and_limit -q -> 2 passed.
  • uv run --with ruff ruff check app/main.py tests/test_bounty_pages.py tests/test_api_mcp.py -> passed.
  • uv run --with ruff ruff format --check app/main.py tests/test_bounty_pages.py tests/test_api_mcp.py -> passed.
  • git diff --check -> clean.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/main.py (1)

1717-1724: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Consider eliminating code duplication.

The nested mcp_issue_number_search_value duplicates the logic of the module-level _issue_number_search_value (lines 123-130). Both functions have identical parsing logic and constraints.

Consider refactoring to reuse the shared implementation:

♻️ Proposed refactor to eliminate duplication
     def mcp_issue_number_search_value(query_text: str) -> int | None:
-        if not query_text.isdigit():
-            return None
-        try:
-            issue_number = int(query_text)
-        except ValueError:
-            return None
-        return issue_number if issue_number <= SQLITE_INTEGER_MAX else None
+        return _issue_number_search_value(query_text)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/main.py` around lines 1717 - 1724, The mcp_issue_number_search_value
function duplicates parsing/validation logic already implemented in the
module-level _issue_number_search_value; update mcp_issue_number_search_value to
reuse that shared implementation (either by calling
_issue_number_search_value(query_text) or extracting the common logic into a new
helper used by both) so the int conversion, digit-check and SQLITE_INTEGER_MAX
bound are maintained via the single shared routine; ensure the function
signature and return type remain int | None and adjust any references
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/main.py`:
- Around line 1717-1724: The mcp_issue_number_search_value function duplicates
parsing/validation logic already implemented in the module-level
_issue_number_search_value; update mcp_issue_number_search_value to reuse that
shared implementation (either by calling _issue_number_search_value(query_text)
or extracting the common logic into a new helper used by both) so the int
conversion, digit-check and SQLITE_INTEGER_MAX bound are maintained via the
single shared routine; ensure the function signature and return type remain int
| None and adjust any references accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1663bd4a-8033-4bf1-9869-54c7d3134114

📥 Commits

Reviewing files that changed from the base of the PR and between cab17ad and 34de778.

📒 Files selected for processing (2)
  • app/main.py
  • tests/test_bounty_pages.py

@ramimbo ramimbo merged commit 7bd5799 into ramimbo:main May 25, 2026
2 checks passed
@ramimbo ramimbo added mrwk:accepted Maintainer accepted for payout mrwk:paid Ledger payment recorded labels May 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mrwk:accepted Maintainer accepted for payout mrwk:paid Ledger payment recorded

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants