Date: 2025-10-28 Version: 1.0 Modules: ResponseBodyCapturer, RefreshTokenTracker
Based on adversarial analysis, the following critical bugs were fixed:
Problem: EvidenceCollector looked in this.responseCache, but ResponseBodyCapturer stored in authRequests
Fix: Modified processResponseBody() to accept authRequests Map as parameter
Files:
- evidence-collector.js:526 - Added
authRequestsparameter - response-body-capturer.js:222 - Pass
authRequeststo processResponseBody
Problem: Tokens were redacted BEFORE tracking, making rotation detection impossible Fix: Track tokens BEFORE redaction in ResponseBodyCapturer Files:
- response-body-capturer.js:215-230 - Track before redact
- background.js:252-253 - Pass refreshTokenTracker to ResponseBodyCapturer
Problem: handleAuthRequest() was called without .catch(), causing unhandled promise rejections
Fix: Added .catch() error handler
Files:
- webrequest-listeners.js:106-110 - Added error handling
Problem: Large responses could cause memory issues Fix: Added 1MB size limit with checks before and after fetching Files:
- response-body-capturer.js:184-209 - Size limits
Problem: Matched first URL, not necessarily the right request Fix: Best-match algorithm using timestamp proximity Files:
- response-body-capturer.js:313-342 - Timestamp matching
Objective: Verify DPoP token type detection from response body
Prerequisites:
- Microsoft account
- Test Azure AD application with OAuth2 configured
- Response body capture enabled
Test Steps:
-
Navigate to Microsoft login
https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize?... -
Complete authentication flow
- Enter credentials
- Complete MFA if required
- Wait for token endpoint request
-
Verify token endpoint captured
- Check console for:
[ResponseCapture] Auth response detected: https://login.microsoftonline.com/.../token - Verify no errors:
[ResponseCapture] Error capturing response body
- Check console for:
-
Verify response body captured
- Check console for:
[ResponseCapture] Captured response body for https://... - Open Hera popup/dashboard
- Find token endpoint request
- Verify
responseBodyCaptured: truein metadata
- Check console for:
-
Verify DPoP or Bearer detection
- Check console for:
[Evidence] Found X security findings in response body - Verify finding type:
- If DPoP:
DPOP_DETECTED(INFO severity) - If Bearer:
BEARER_TOKEN_USED(INFO severity)
- If DPoP:
- Check console for:
-
Verify token redaction
- Inspect
requestData.responseBody - Verify:
access_token: "[REDACTED_ACCESS_TOKEN length=XXX]" - Verify:
refresh_token: "[REDACTED_REFRESH_TOKEN length=XXX]" - Verify:
token_type: "Bearer"(NOT redacted) - Verify:
expires_in: 3600(NOT redacted)
- Inspect
Expected Results:
- ✅ Response body captured via debugger API
- ✅ Token type detected (DPoP or Bearer)
- ✅ Sensitive tokens redacted
- ✅ Metadata preserved for analysis
- ✅ Finding added to
metadata.responseBodyFindings
Debug Checklist:
- Debugger attached successfully? (Check for "DevTools is debugging" notification)
- DevTools NOT already open? (Conflicts with debugger API)
- Request matched by URL pattern? (Check
_isAuthResponse()) - RequestId matched correctly? (Check
_findWebRequestId()logs) - Response body is JSON? (Non-JSON skipped)
Objective: Verify refresh token rotation detection
Prerequisites:
- Google account
- Test application with refresh token flow
- Response body capture enabled
Test Steps:
-
Complete initial OAuth2 flow
- Authenticate with Google
- Obtain refresh token
- Verify token captured: Check console for
[ResponseCapture] Captured response body
-
Trigger refresh token usage (FIRST TIME)
- Use refresh token to get new access token
- Check console for:
[RefreshTokenTracker] No existing hash(implicit - no error) - Verify NO finding (rotation working correctly)
-
Trigger refresh token usage (SECOND TIME - Same token)
- Use SAME refresh token again
- Check console for:
[ResponseCapture] Refresh token rotation violation detected - Verify finding added:
REFRESH_TOKEN_NOT_ROTATED(HIGH severity)
-
Verify token tracking without plaintext
- Inspect
requestData.responseBody - Verify refresh_token is redacted:
[REDACTED_REFRESH_TOKEN...] - Open browser console
- Search memory for actual refresh token string
- Should NOT find plaintext token (only hash stored)
- Inspect
-
Verify finding evidence
- Check finding in
metadata.securityFindings - Verify evidence includes:
domain: "oauth2.googleapis.com"tokenHash: "a3f2c8d1..."(first 16 chars of SHA-256)useCount: 2(or higher)timeSinceFirstUse: XXXXX(milliseconds)
- Check finding in
Expected Results:
- ✅ First use: Token tracked, no finding
- ✅ Second use: Rotation violation detected (HIGH severity)
- ✅ Token hash stored (NOT plaintext)
- ✅ Evidence includes use count and timing
- ✅ Redacted token in storage
Debug Checklist:
- Response body parsed as JSON?
- URL matched
_isTokenResponse()pattern? - RefreshTokenTracker received BEFORE redaction?
- Hash collision? (Extremely unlikely, but check
tokenHash) - Token different but detected as same? (Hash collision - report to dev)
Objective: Verify basic OAuth2 flow without DPoP or rotation issues
Prerequisites:
- GitHub account
- Test OAuth2 application
- Response body capture enabled
Test Steps:
-
Complete GitHub OAuth2 flow
- Navigate to:
https://github.com/login/oauth/authorize?... - Authorize application
- Wait for token endpoint:
https://github.com/login/oauth/access_token
- Navigate to:
-
Verify basic capture
- Check console for:
[ResponseCapture] Auth response detected - Verify response body captured
- Verify no errors
- Check console for:
-
Verify token type detection
- Should detect:
BEARER_TOKEN_USED(INFO severity) - GitHub does NOT use DPoP
- Should detect:
-
Verify NO false positives
- Should NOT detect:
REFRESH_TOKEN_NOT_ROTATED - GitHub issues new token each time (rotation working)
- Should NOT detect:
-
Verify redaction
- access_token: Redacted
- token_type: Preserved ("bearer")
- scope: Preserved
Expected Results:
- ✅ Response body captured
- ✅ Bearer token detected
- ✅ No false positives
- ✅ Proper redaction
- ✅ No errors in console
Test:
- Open Chrome DevTools (F12)
- Navigate to OAuth2 flow
- Observe error:
[ResponseCapture] Failed to attach to tab: Another debugger is already attached
Expected:
- ❌ Response body capture disabled for this tab
- ✅ No crashes or uncaught exceptions
- ✅ Other detections still work (PKCE, state, headers)
Test:
- Configure server to return 2MB response
- Trigger OAuth2 token endpoint
- Observe warning:
[ResponseCapture] Response too large (2097152 bytes), skipping
Expected:
- ✅ Request skipped (no memory issues)
- ✅ No capture attempted
- ✅ Other requests still captured normally
Test:
- Start OAuth2 flow
- Close tab immediately after request sent
- Response arrives but tab is gone
Expected:
- ✅ Debug message:
[ResponseCapture] Tab closed before response captured - ✅ No errors thrown
- ✅ Debugger detached cleanly
Test:
- Server returns HTML error page instead of JSON
- Token endpoint returns 500 with HTML body
Expected:
- ✅ Debug message:
[ResponseCapture] Response body not JSON, skipping token tracking - ✅ No JSON parse errors
- ✅ No crash
Test:
- Make 2 requests to same token endpoint simultaneously
- Both responses arrive within 100ms
Expected:
- ✅ Both responses captured
- ✅ Matched to correct requests (timestamp-based)
- ✅ No response body swapping
Test:
- Complete 100 OAuth2 flows in a row
- Monitor extension memory usage
Expected:
- ✅ Memory stays under 50MB
- ✅ Old token hashes cleaned up (7 day TTL)
- ✅ No memory leaks
How to check:
- Open
chrome://extensions/ - Enable Developer mode
- Click "Inspect views: service worker"
- Go to Memory tab
- Take heap snapshot before/after 100 flows
Test:
- Measure token endpoint response time WITHOUT Hera
- Measure token endpoint response time WITH Hera
- Compare
Expected:
- ✅ Overhead < 50ms per request
- ✅ No visible slowdown to user
Before shipping P0 prerequisites, verify:
- Fix 1 (ResponseCache mismatch): processResponseBody() finds requests in authRequests
- Fix 2 (Track before redact): Token rotation detection works with redacted storage
- Fix 3 (Async error handling): No unhandled promise rejections in console
- Fix 4 (Error handling): Tab closure handled gracefully
- Fix 5 (Size limits): Large responses (>1MB) skipped
- Fix 6 (RequestId matching): Duplicate URLs matched correctly
- Microsoft OAuth2: DPoP or Bearer detected
- Google OAuth2: Refresh token rotation violations detected
- GitHub OAuth2: Basic flow works, no false positives
- Edge cases: All 5 edge cases handled correctly
- Performance: Memory < 50MB, overhead < 50ms
Add this to background.js:
// Enable verbose logging for P0 modules
if (!isProduction) {
window.DEBUG_RESPONSE_CAPTURE = true;
window.DEBUG_TOKEN_TRACKING = true;
}// In browser console
chrome.runtime.sendMessage({action: 'GET_DEBUGGER_STATUS'}, (response) => {
console.log('Debugger status:', response);
});// In Hera service worker console
refreshTokenTracker.getStats()
// Returns: { trackedTokens: 3, domains: ['oauth2.googleapis.com'], ... }// In Hera service worker console
for (const [id, req] of authRequests.entries()) {
if (req.responseBody) {
console.log(id, req.url, req.responseBody);
}
}## Test Results - [DATE]
**Tester:** [NAME]
**Browser:** Chrome [VERSION]
**Hera Version:** [VERSION]
### Test 1: Microsoft OAuth2
- [ ] Response body captured
- [ ] DPoP/Bearer detected
- [ ] Tokens redacted
- [ ] No errors
- **Notes:**
### Test 2: Google OAuth2
- [ ] First use: No finding
- [ ] Second use: Rotation violation
- [ ] Hash stored (not plaintext)
- [ ] No errors
- **Notes:**
### Test 3: GitHub OAuth2
- [ ] Response body captured
- [ ] Bearer detected
- [ ] No false positives
- [ ] No errors
- **Notes:**
### Edge Cases
- [ ] DevTools conflict handled
- [ ] Large response skipped
- [ ] Tab closure handled
- [ ] Non-JSON handled
- [ ] Duplicate requests matched
- **Notes:**
### Performance
- [ ] Memory < 50MB
- [ ] Overhead < 50ms
- **Notes:**
### Overall Status
- [ ] ✅ PASS - Ready for production
- [ ] ⚠️ PARTIAL - Minor issues found
- [ ] ❌ FAIL - Critical issues found
**Issues Found:**
1.
2.
3.
**Recommendations:**- Install Hera extension (load unpacked)
- Navigate to: https://github.com/login
- Click "Sign in with GitHub"
- Complete authentication
- Open browser console
- Search for:
[ResponseCapture] - Verify: "Captured response body"
- Open Hera popup
- Verify: Bearer token detected
- Run all 3 integration tests (Microsoft, Google, GitHub)
- Run all 5 edge case tests
- Run 2 performance tests
- Fill out test results template
- Report issues on GitHub
Test Plan Version: 1.0 Last Updated: 2025-10-28 Status: Ready for QA