From dfdc60a1537ccf2d5f5a32777bddd2724666502d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=86=E7=A0=810000?= Date: Thu, 28 May 2026 07:53:23 +0800 Subject: [PATCH 1/2] Guard webhook native bounty references --- app/webhooks/github.py | 3 +- tests/test_webhooks.py | 80 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/app/webhooks/github.py b/app/webhooks/github.py index 145085d9..c21b759e 100644 --- a/app/webhooks/github.py +++ b/app/webhooks/github.py @@ -19,7 +19,8 @@ ISSUE_NUMBER_BOUNDARY = r"(?![A-Za-z0-9_-])" MAX_SQLITE_INTEGER = 2**63 - 1 LINKED_ISSUE_RE = re.compile( - r"\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?|refs?|references?|bounty)\s+" + r"(?:(?[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+)#(?P\d+){ISSUE_NUMBER_BOUNDARY}" rf"|#(?P\d+){ISSUE_NUMBER_BOUNDARY})", re.IGNORECASE, diff --git a/tests/test_webhooks.py b/tests/test_webhooks.py index 030d3ca6..86aa256c 100644 --- a/tests/test_webhooks.py +++ b/tests/test_webhooks.py @@ -155,6 +155,86 @@ def test_accepted_pr_label_pays_pr_author_for_linked_bounty_issue(sqlite_url: st assert get_balance(session, "github:maintainer") == 0 +def test_accepted_pr_label_ignores_native_bounty_ids_before_issue_ref( + sqlite_url: str, +) -> None: + create_schema(sqlite_url) + cases = [ + ( + "delivery-pr-live-bounty-id", + "Evidence: live bounty #66 / issue #406 preflight returned status=open.", + ), + ( + "delivery-pr-native-bounty-id", + "Evidence: native bounty #66 maps to issue #406.", + ), + ( + "delivery-pr-internal-bounty-id", + "Evidence: internal bounty #66; issue #406 is the GitHub bounty issue.", + ), + ] + + with session_scope(sqlite_url) as session: + ensure_genesis(session) + create_bounty( + session, + repo="ramimbo/mergework", + issue_number=66, + issue_url="https://github.com/ramimbo/mergework/issues/66", + title="Wrong native-id lookalike", + reward_mrwk="1", + max_awards=len(cases), + acceptance="This should not be selected from native MRWK bounty text.", + ) + create_bounty( + session, + repo="ramimbo/mergework", + issue_number=406, + issue_url="https://github.com/ramimbo/mergework/issues/406", + title="Useful bug reports and small fixes", + reward_mrwk="50", + max_awards=len(cases), + acceptance="Accepted PR bodies may include native MRWK ids plus the GitHub issue.", + ) + + for pull_number, (delivery_id, pr_body) in enumerate(cases, start=406): + body = json.dumps( + { + "action": "labeled", + "label": {"name": "mrwk:accepted"}, + "pull_request": { + "number": pull_number, + "html_url": f"https://github.com/ramimbo/mergework/pull/{pull_number}", + "body": pr_body, + "user": {"login": "contributor"}, + }, + "repository": {"full_name": "ramimbo/mergework"}, + "sender": {"login": "maintainer"}, + }, + separators=(",", ":"), + ).encode() + + result = handle_github_webhook( + sqlite_url, + { + "X-GitHub-Delivery": delivery_id, + "X-GitHub-Event": "pull_request", + "X-Hub-Signature-256": _signature("secret", body), + }, + body, + "secret", + ) + + assert result["status"] == "paid" + + with session_scope(sqlite_url) as session: + assert get_balance(session, "github:contributor") == 150_000_000 + for delivery_id, _pr_body in cases: + event = session.get(WebhookEvent, delivery_id) + assert event is not None + assert event.processed_status == "paid" + + def test_accepted_issue_event_for_pull_request_does_not_pay_matching_bounty( sqlite_url: str, ) -> None: From 14679860a986e724a19df5b46fbf33d60371ffa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=86=E7=A0=810000?= Date: Thu, 28 May 2026 07:59:46 +0800 Subject: [PATCH 2/2] Cover MRWK native bounty webhook text --- tests/test_webhooks.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_webhooks.py b/tests/test_webhooks.py index 86aa256c..d474d04d 100644 --- a/tests/test_webhooks.py +++ b/tests/test_webhooks.py @@ -164,6 +164,10 @@ def test_accepted_pr_label_ignores_native_bounty_ids_before_issue_ref( "delivery-pr-live-bounty-id", "Evidence: live bounty #66 / issue #406 preflight returned status=open.", ), + ( + "delivery-pr-mrwk-bounty-id", + "Evidence: MRWK bounty #66 / issue #406 should resolve to the GitHub issue.", + ), ( "delivery-pr-native-bounty-id", "Evidence: native bounty #66 maps to issue #406.", @@ -228,7 +232,7 @@ def test_accepted_pr_label_ignores_native_bounty_ids_before_issue_ref( assert result["status"] == "paid" with session_scope(sqlite_url) as session: - assert get_balance(session, "github:contributor") == 150_000_000 + assert get_balance(session, "github:contributor") == len(cases) * 50_000_000 for delivery_id, _pr_body in cases: event = session.get(WebhookEvent, delivery_id) assert event is not None