Skip to content

Conversation

@JoshFerge
Copy link
Member

@JoshFerge JoshFerge commented Nov 14, 2025

Summary

Implements email-based user mapping for Seer Autofix PRs to automatically request review from the triggering user, making it easier for users to track their Autofix PRs.

Fixes AIML-1597

Problem

Users lose track of Seer-authored Autofix PRs because Seer is the PR author. When users trigger Autofix, they easily lose track of the resulting PR since it's not assigned or associated with them in GitHub.

Solution

This PR enhances GitHub username resolution to check multiple sources, similar to how suspect commits maps users via email:

Changes

  1. Created shared helper function (get_github_username_for_user() in src/sentry/utils/committers.py)

    • Checks ExternalActor for direct user→GitHub mappings (existing behavior)
    • Falls back to CommitAuthor email matching (like suspect commits does)
    • Extracts GitHub username from CommitAuthor.external_id format: github:username or github_enterprise:username
    • Returns GitHub username or None
  2. Updated autofix (src/sentry/seer/autofix/autofix.py)

    • Refactored _get_github_username_for_user() to use shared helper
    • Removed duplicate code and unused imports
  3. Added comprehensive tests (tests/sentry/seer/autofix/test_autofix.py)

    • 6 new test cases for CommitAuthor fallback scenarios
    • Tests ExternalActor priority, GitHub Enterprise support, org scoping
    • All 48 related tests pass (11 new + 37 existing)

How It Works

When a user triggers Autofix:

  1. Sentry resolves their GitHub username using the new shared helper
  2. Helper checks ExternalActor first (fastest, most reliable)
  3. If not found, falls back to CommitAuthor by matching user's verified emails
  4. If matching CommitAuthor has GitHub external_id, extracts username
  5. Sentry passes GitHub username to Seer in the autofix request
  6. Seer creates PR and automatically requests review from that GitHub user

Expected Impact

Users who don't have an ExternalActor mapping but have committed code with their email to GitHub repos will now have Autofix PRs automatically request their review, making PRs easier to find and track.

Test Plan

  • All new tests pass (11 GitHub username tests)
  • All existing tests pass (37 suspect commits tests, no regressions)
  • Pre-commit hooks pass (black, isort, flake8, mypy)
  • Manual testing: Trigger Autofix and verify PR review request

Files Changed

  • src/sentry/utils/committers.py - Added shared helper function
  • src/sentry/seer/autofix/autofix.py - Updated to use shared helper
  • tests/sentry/seer/autofix/test_autofix.py - Added 6 new test cases

Users lose track of Seer-authored Autofix PRs because Seer is the PR author.
This change enables automatic PR review requests to the triggering user by
resolving their GitHub username from multiple sources.

Changes:
- Add shared `get_github_username_for_user()` helper in committers.py
- Check ExternalActor for direct user→GitHub mappings (existing behavior)
- Fall back to CommitAuthor email matching (like suspect commits)
- Extract GitHub username from CommitAuthor.external_id
- Add structured logging to track mapping sources
- Update autofix to use shared helper instead of duplicate code
- Add 6 new test cases for CommitAuthor fallback scenarios

When users trigger Autofix, if their email matches a CommitAuthor with a
GitHub external_id, Seer will automatically request their review on the PR,
making it easier to track and find their Autofix PRs.

Fixes AIML-1597
@linear
Copy link

linear bot commented Nov 14, 2025

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Nov 14, 2025
Comment on lines 514 to 519
CommitAuthor.objects.filter(
organization_id=organization_id,
email__in=[email.lower() for email in user_emails],
external_id__isnull=False,
)
.exclude(external_id="")
Copy link

@semgrep-code-getsentry semgrep-code-getsentry bot Nov 14, 2025

Choose a reason for hiding this comment

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

Risk: Affected versions of django are vulnerable to Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection'). The ORM methods QuerySet.filter(), QuerySet.exclude(), QuerySet.get() and the Q() class can be tricked into SQL injection when you pass a specially crafted dictionary via **kwargs that includes a malicious _connector entry. This bypasses the normal query parameterization and lets an attacker inject arbitrary SQL into the WHERE clause.

Fix: Upgrade this library to at least version 5.2.8 at sentry/uv.lock:305.

Reference(s): GHSA-frmv-pr5f-9mcr, CVE-2025-64459

Fixed in commit 91219f4

Comment on lines 467 to 474
ExternalActor.objects.filter(
user_id=user.id,
organization_id=organization_id,
provider__in=[
ExternalProviders.GITHUB.value,
ExternalProviders.GITHUB_ENTERPRISE.value,
],
)
Copy link

@semgrep-code-getsentry semgrep-code-getsentry bot Nov 14, 2025

Choose a reason for hiding this comment

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

Risk: Affected versions of django are vulnerable to Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection'). The ORM methods QuerySet.filter(), QuerySet.exclude(), QuerySet.get() and the Q() class can be tricked into SQL injection when you pass a specially crafted dictionary via **kwargs that includes a malicious _connector entry. This bypasses the normal query parameterization and lets an attacker inject arbitrary SQL into the WHERE clause.

Fix: Upgrade this library to at least version 5.2.8 at sentry/uv.lock:305.

Reference(s): GHSA-frmv-pr5f-9mcr, CVE-2025-64459

🎉 Removed in commit d3ede3f 🎉

@semgrep-code-getsentry
Copy link

Semgrep found 1 ssc-e23b0a49-86b3-48bc-b82c-03656ca7eaee finding:

Risk: Affected versions of django are vulnerable to Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection'). The ORM methods QuerySet.filter(), QuerySet.exclude(), QuerySet.get() and the Q() class can be tricked into SQL injection when you pass a specially crafted dictionary via **kwargs that includes a malicious _connector entry. This bypasses the normal query parameterization and lets an attacker inject arbitrary SQL into the WHERE clause.

Fix: Upgrade this library to at least version 5.2.8 at sentry/uv.lock:305.

Reference(s): GHSA-frmv-pr5f-9mcr, CVE-2025-64459

- Move all imports from inside get_github_username_for_user() to module level
- Remove all logging statements from the helper function
Comment on lines 498 to 503
CommitAuthor.objects.filter(
organization_id=organization_id,
email__in=[email.lower() for email in user_emails],
external_id__isnull=False,
)
.exclude(external_id="")
Copy link

@semgrep-code-getsentry semgrep-code-getsentry bot Nov 14, 2025

Choose a reason for hiding this comment

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

Risk: Affected versions of django are vulnerable to Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection'). The ORM methods QuerySet.filter(), QuerySet.exclude(), QuerySet.get() and the Q() class can be tricked into SQL injection when you pass a specially crafted dictionary via **kwargs that includes a malicious _connector entry. This bypasses the normal query parameterization and lets an attacker inject arbitrary SQL into the WHERE clause.

Fix: Upgrade this library to at least version 5.2.8 at sentry/uv.lock:305.

Reference(s): GHSA-frmv-pr5f-9mcr, CVE-2025-64459

🍰 Removed in commit d3ede3f 🍰

@semgrep-code-getsentry
Copy link

Semgrep found 1 ssc-e23b0a49-86b3-48bc-b82c-03656ca7eaee finding:

Risk: Affected versions of django are vulnerable to Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection'). The ORM methods QuerySet.filter(), QuerySet.exclude(), QuerySet.get() and the Q() class can be tricked into SQL injection when you pass a specially crafted dictionary via **kwargs that includes a malicious _connector entry. This bypasses the normal query parameterization and lets an attacker inject arbitrary SQL into the WHERE clause.

Fix: Upgrade this library to at least version 5.2.8 at sentry/uv.lock:305.

Reference(s): GHSA-frmv-pr5f-9mcr, CVE-2025-64459

- Move implementation from committers.py directly into _get_github_username_for_user in autofix.py
- Remove now-unused shared helper function from committers.py
- Remove unused imports from committers.py
- Keep all logic in autofix where it's actually used
Comment on lines +394 to +399
CommitAuthor.objects.filter(
organization_id=organization_id,
email__in=[email.lower() for email in user_emails],
external_id__isnull=False,
)
.exclude(external_id="")

Choose a reason for hiding this comment

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

Critical severity and reachable issue identified in your code:
Line 394 has a vulnerable usage of django, introducing a critical severity vulnerability.

ℹ️ Why this is reachable

A reachable issue is a real security risk because your project actually executes the vulnerable code. This issue is reachable because your code uses a certain version of django.
Affected versions of django are vulnerable to Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection'). The ORM methods QuerySet.filter(), QuerySet.exclude(), QuerySet.get() and the Q() class can be tricked into SQL injection when you pass a specially crafted dictionary via **kwargs that includes a malicious _connector entry. This bypasses the normal query parameterization and lets an attacker inject arbitrary SQL into the WHERE clause.

References: GHSA, CVE

To resolve this comment:
Upgrade this dependency to at least version 5.2.8 at uv.lock.

💬 Ignore this finding

To ignore this, reply with:

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

You can view more details on this finding in the Semgrep AppSec Platform here.

@semgrep-code-getsentry
Copy link

Semgrep found 1 ssc-e23b0a49-86b3-48bc-b82c-03656ca7eaee finding:

Risk: Affected versions of django are vulnerable to Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection'). The ORM methods QuerySet.filter(), QuerySet.exclude(), QuerySet.get() and the Q() class can be tricked into SQL injection when you pass a specially crafted dictionary via **kwargs that includes a malicious _connector entry. This bypasses the normal query parameterization and lets an attacker inject arbitrary SQL into the WHERE clause.

Fix: Upgrade this library to at least version 5.2.8 at sentry/uv.lock:305.

Reference(s): GHSA-frmv-pr5f-9mcr, CVE-2025-64459

@codecov
Copy link

codecov bot commented Nov 14, 2025

Codecov Report

❌ Patch coverage is 85.71429% with 2 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/sentry/seer/autofix/autofix.py 85.71% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           master   #103406      +/-   ##
===========================================
+ Coverage   78.11%    80.50%   +2.39%     
===========================================
  Files        9352      9352              
  Lines      400391    400407      +16     
  Branches    25700     25700              
===========================================
+ Hits       312778    322364    +9586     
+ Misses      87145     77575    -9570     
  Partials      468       468              

@getsantry getsantry bot added the Stale label Dec 6, 2025
@getsantry
Copy link
Contributor

getsantry bot commented Dec 6, 2025

This pull request has gone three weeks without activity. In another week, I will close it.

But! If you comment or otherwise update it, I will reset the clock, and if you add the label WIP, I will leave it alone unless WIP is removed ... forever!


"A weed is but an unloved flower." ― Ella Wheeler Wilcox 🥀

@shruthilayaj shruthilayaj marked this pull request as ready for review December 8, 2025 18:25
@shruthilayaj shruthilayaj requested a review from a team as a code owner December 8, 2025 18:25
@shruthilayaj
Copy link
Member

I tested this PR locally to the point where I ensure I make the seer call is made with the correct github user when no ExternalActor object is present but a CommitAuthor is present (I set up a github app integration to populate CommitAuthor). It looks good to me.

Copy link
Member Author

@JoshFerge JoshFerge left a comment

Choose a reason for hiding this comment

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

lgtm!

@shruthilayaj shruthilayaj merged commit abf2622 into master Dec 8, 2025
72 of 74 checks passed
@shruthilayaj shruthilayaj deleted the joshferge/aiml-1597-investigate-email-based-user-mapping-for-seer-autofix-prs branch December 8, 2025 19:59
ryan953 pushed a commit that referenced this pull request Dec 9, 2025
…w requests (#103406)

## Summary

Implements email-based user mapping for Seer Autofix PRs to
automatically request review from the triggering user, making it easier
for users to track their Autofix PRs.

Fixes AIML-1597

## Problem

Users lose track of Seer-authored Autofix PRs because Seer is the PR
author. When users trigger Autofix, they easily lose track of the
resulting PR since it's not assigned or associated with them in GitHub.

## Solution

This PR enhances GitHub username resolution to check multiple sources,
similar to how suspect commits maps users via email:

### Changes

1. **Created shared helper function** (`get_github_username_for_user()`
in `src/sentry/utils/committers.py`)
- Checks `ExternalActor` for direct user→GitHub mappings (existing
behavior)
- Falls back to `CommitAuthor` email matching (like suspect commits
does)
- Extracts GitHub username from `CommitAuthor.external_id` format:
`github:username` or `github_enterprise:username`
   - Returns GitHub username or None

2. **Updated autofix** (`src/sentry/seer/autofix/autofix.py`)
   - Refactored `_get_github_username_for_user()` to use shared helper
   - Removed duplicate code and unused imports

3. **Added comprehensive tests**
(`tests/sentry/seer/autofix/test_autofix.py`)
   - 6 new test cases for CommitAuthor fallback scenarios
- Tests ExternalActor priority, GitHub Enterprise support, org scoping
   - All 48 related tests pass (11 new + 37 existing)

## How It Works

When a user triggers Autofix:
1. Sentry resolves their GitHub username using the new shared helper
2. Helper checks `ExternalActor` first (fastest, most reliable)
3. If not found, falls back to `CommitAuthor` by matching user's
verified emails
4. If matching `CommitAuthor` has GitHub `external_id`, extracts
username
5. Sentry passes GitHub username to Seer in the autofix request
6. Seer creates PR and automatically requests review from that GitHub
user

## Expected Impact

Users who don't have an `ExternalActor` mapping but have committed code
with their email to GitHub repos will now have Autofix PRs automatically
request their review, making PRs easier to find and track.

## Test Plan

- [x] All new tests pass (11 GitHub username tests)
- [x] All existing tests pass (37 suspect commits tests, no regressions)
- [x] Pre-commit hooks pass (black, isort, flake8, mypy)
- [ ] Manual testing: Trigger Autofix and verify PR review request

## Files Changed

- `src/sentry/utils/committers.py` - Added shared helper function
- `src/sentry/seer/autofix/autofix.py` - Updated to use shared helper
- `tests/sentry/seer/autofix/test_autofix.py` - Added 6 new test cases

---------

Co-authored-by: Shruthilaya Jaganathan <shruthilaya.jaganathan@sentry.io>
@github-actions github-actions bot locked and limited conversation to collaborators Dec 24, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Scope: Backend Automatically applied to PRs that change backend components Stale

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants