Skip to content

Conversation

@dashed
Copy link
Member

@dashed dashed commented Jul 23, 2025

Take 2 of #95742

This fixes a critical bug where marketing promo codes (e.g., "fireship") were being
misinterpreted as OAuth authorization codes, causing authentication failures.

The issue occurred because the OAuth2LoginView checked for ANY request with a 'code'
parameter and treated it as an OAuth callback. This caused promo codes to trigger
token exchange attempts instead of starting a new OAuth flow.

Changes:

  • Enhanced OAuth callback detection to require both code/error AND matching state
  • Only requests with valid state parameters are now treated as OAuth callbacks
  • Added debug logging for OAuth callback detection
  • Added comprehensive test coverage for state validation scenarios

This ensures that:

  1. Marketing promo codes start new OAuth flows (not treated as callbacks)
  2. Valid OAuth callbacks with matching state are still processed correctly
  3. OAuth error responses with valid state are handled properly

dashed added 6 commits July 23, 2025 17:27
This fixes a critical bug where marketing promo codes (e.g., "fireship") were being
misinterpreted as OAuth authorization codes, causing authentication failures.

The issue occurred because the OAuth2LoginView checked for ANY request with a 'code'
parameter and treated it as an OAuth callback. This caused promo codes to trigger
token exchange attempts instead of starting a new OAuth flow.

Changes:
- Enhanced OAuth callback detection to require both code/error AND matching state
- Only requests with valid state parameters are now treated as OAuth callbacks
- Added debug logging for OAuth callback detection
- Added comprehensive test coverage for state validation scenarios

This ensures that:
1. Marketing promo codes start new OAuth flows (not treated as callbacks)
2. Valid OAuth callbacks with matching state are still processed correctly
3. OAuth error responses with valid state are handled properly
Fix SessionMiddleware type errors by returning HttpResponse instead of None
in the get_response lambda function. This ensures proper type compatibility
while maintaining test functionality.
Replace mocked `fetch_state` calls with proper Django session state management
in OAuth2 integration and state validation tests. This provides more reliable
testing that closely mirrors production behavior.

Key improvements:
- Add session state helper methods for storing/retrieving pipeline data
- Replace `pipeline.fetch_state()` mocks with real session persistence
- Enhance promo code preservation testing throughout OAuth flows
- Improve state validation testing with realistic session isolation
- Add comprehensive concurrent flow testing with proper session isolation
- Verify cross-session contamination prevention

Tests now use the same session mechanisms as the actual application,
providing stronger confidence in OAuth state validation, promo code
preservation, and session isolation.
- Replace Mock(status_code=200) with HttpResponse(status=200) in OAuth tests
- Add HttpResponseRedirect import for real response handling
- Add helper method _create_real_next_step_response for consistent response creation
- Add _mock_oauth_endpoints helper for OAuth endpoint mocking
- Remove unused Mock imports, keeping only patch from unittest.mock
- Improves test realism by using actual Django response objects instead of mocks
Adds comprehensive test coverage for OAuth security vulnerabilities to ensure
the state validation fix properly prevents known attack vectors.

Test Coverage:
- CSRF attacks with malicious authorization codes
- State parameter guessing/brute force attacks
- Replay attacks with captured authorization codes
- State fixation attacks
- Session hijacking attempts
- Timing attacks on state comparison
- Parameter pollution attacks
- Injection attacks via state parameter
- Concurrent session isolation

These tests validate that the OAuth implementation properly rejects malicious
requests and prevents common OAuth security vulnerabilities. All tests fail
against the vulnerable code (proving they detect real security issues) and
pass when proper state validation is implemented.

This comprehensive security test suite is especially important for open source
code where attack vectors are publicly visible.
This commit implements comprehensive protection against OAuth parameter
pollution attacks across all critical OAuth parameters (code, error, state).

Changes:
- Detect multiple parameter values using request.GET.getlist()
- Block requests with duplicate code/error/state parameters
- Log pollution attempts with security context for monitoring
- Maintain RFC 6749 compliance by rejecting code+error combinations
- Add comprehensive test coverage with 10 attack scenarios

Security Impact:
Previously, Django's "last parameter wins" behavior allowed attackers to
bypass state validation using URLs like:
/?code=malicious&state=wrong&state=legitimate

This fix prevents such attacks by detecting parameter pollution early
and starting a new OAuth flow instead of processing the malicious request.

All existing OAuth functionality remains intact while providing robust
protection against sophisticated parameter manipulation attacks.
@dashed dashed self-assigned this Jul 23, 2025
@dashed dashed requested a review from a team as a code owner July 23, 2025 21:41
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Jul 23, 2025
@codecov
Copy link

codecov bot commented Jul 24, 2025

Codecov Report

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

Files with missing lines Patch % Lines
...sts/sentry/identity/test_oauth2_integration_e2e.py 96.90% 7 Missing ⚠️
...ts/sentry/identity/test_oauth2_state_validation.py 99.06% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           master   #96264       +/-   ##
===========================================
+ Coverage   52.40%   84.92%   +32.52%     
===========================================
  Files       10568    11338      +770     
  Lines      610846   794965   +184119     
  Branches    24082    24082               
===========================================
+ Hits       320093   675158   +355065     
+ Misses     290466   119520   -170946     
  Partials      287      287               

@getsantry
Copy link
Contributor

getsantry bot commented Aug 14, 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 🥀

@getsantry getsantry bot added Stale and removed Stale labels Aug 14, 2025
@getsantry
Copy link
Contributor

getsantry bot commented Sep 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 🥀

@getsantry getsantry bot added Stale and removed Stale labels Sep 6, 2025
@dashed dashed closed this Oct 9, 2025
@vbro vbro reopened this Oct 16, 2025
@vbro vbro requested review from a team October 16, 2025 21:16
@vbro
Copy link
Contributor

vbro commented Oct 16, 2025

@getsantry
Copy link
Contributor

getsantry bot commented Nov 7, 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 🥀

@getsantry getsantry bot added the Stale label Nov 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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