Skip to content

Conversation

@garland3
Copy link
Collaborator

WebSocket connections were falling back to test user instead of reading the authenticated user from the configured authentication header set by the reverse proxy. This caused file access checks to fail with Access denied: [email protected] attempted to access users/{actual_user}/... when users tried to load their own files.

Changes

backend/main.py

  • WebSocket endpoint now uses config.app_settings.auth_user_header to read the authentication header (configurable via AUTH_USER_HEADER environment variable, default: X-User-Email)
  • Consistent with HTTP endpoints via AuthMiddleware which also uses the configurable header
  • Falls back to query parameter for dev/test compatibility
  • Falls back to test user as final fallback
  • Added logging to identify authentication source and header name used

Tests

backend/tests/test_websocket_auth_header.py

  • Test header-based authentication with configurable header
  • Test query parameter fallback
  • Test header precedence over query param
  • Test user fallback

backend/tests/test_issue_access_denied_fix.py

  • Integration test demonstrating the exact issue scenario
  • Verifies file access works when WebSocket uses correct authenticated user

backend/tests/test_security_header_injection.py

  • Security tests for header injection prevention
  • Documents why reverse proxy must strip client headers
  • Includes production verification test

Documentation

docs/example/nginx.config

  • Production-ready nginx configuration
  • Includes critical header stripping for security
  • WebSocket support with proper authentication

docs/example/reverse-proxy-examples.md

  • Comprehensive reverse proxy setup guide
  • Nginx and Apache configuration examples
  • Security testing procedures
  • Deployment checklist

docs/archive/security_architecture.md

  • Added header injection prevention section
  • Updated WebSocket authentication flow
  • Enhanced security checklist

Compatibility

This fix works seamlessly with the configurable AUTH_USER_HEADER feature, supporting custom header names for different reverse proxy setups (e.g., X-User-Email, X-Remote-User, X-Authenticated-User) without code changes.

Fixes #45

WebSocket connections were falling back to test user instead of reading
the authenticated user from the configured authentication header set by
the reverse proxy. This caused file access checks to fail with 'Access
denied: [email protected] attempted to access users/{actual_user}/...' when
users tried to load their own files.

Changes:
- WebSocket endpoint now uses config.app_settings.auth_user_header
- Consistent with HTTP endpoints via AuthMiddleware
- Falls back to query parameter for dev/test compatibility
- Added logging to identify authentication source

Tests:
- test_websocket_auth_header.py: Header-based auth, query param fallback
- test_issue_access_denied_fix.py: Integration test for the bug scenario
- test_security_header_injection.py: Header injection security tests

Documentation:
- docs/example/nginx.config: Production-ready nginx configuration
- docs/example/reverse-proxy-examples.md: Nginx/Apache setup guide
- Updated security architecture with header injection prevention

Fixes #45
@garland3
Copy link
Collaborator Author

@ktpedre Just so you are tracking these changes.

if not user_email:
user_email = websocket.query_params.get('user')
if user_email:
logger.info("WebSocket authenticated via query parameter: %s", sanitize_for_logging(user_email))

Check failure

Code scanning / CodeQL

Log Injection High

This log entry depends on a
user-provided value
.

Copilot Autofix

AI 7 days ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

Copilot finished reviewing on behalf of garland3 November 21, 2025 04:59
@github-actions
Copy link

🔒 Security Scan Results

Security Scan Summary

Scan Results

Python SAST (Bandit)

⚠️ Security issues found in Python code

Recommendations

  • Review all SARIF files uploaded to GitHub Security tab
  • Address high and critical severity vulnerabilities immediately
  • Run npm audit fix and pip-audit locally to fix dependencies
  • Consider implementing additional security controls

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR fixes a critical authentication bug where WebSocket connections were falling back to the test user instead of reading the authenticated user from the configurable authentication header set by the reverse proxy. This caused file access permission errors when users tried to access their own files.

The fix ensures WebSocket authentication is consistent with HTTP endpoint authentication by reading from config.app_settings.auth_user_header (configurable via AUTH_USER_HEADER environment variable, default: X-User-Email). The implementation maintains backward compatibility with query parameter fallback for development/testing and includes comprehensive security documentation about header injection prevention.

  • WebSocket authentication now uses configurable header (consistent with AuthMiddleware)
  • Comprehensive test coverage added for header authentication, fallback mechanisms, and security scenarios
  • Production-ready reverse proxy configuration examples (nginx and Apache) with critical security requirements documented

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
backend/main.py Updated WebSocket endpoint to extract authenticated user from configurable auth header; added proper fallback chain (header → query param → test user) with logging for each authentication source
backend/tests/test_websocket_auth_header.py Unit tests validating header-based authentication, query parameter fallback, test user fallback, and header precedence
backend/tests/test_issue_access_denied_fix.py Integration test demonstrating the exact issue scenario and verifying the fix works correctly
backend/tests/test_security_header_injection.py Security tests documenting header injection vulnerabilities and production verification procedures
docs/example/nginx.config Production-ready nginx configuration with critical header stripping to prevent injection attacks
docs/example/reverse-proxy-examples.md Comprehensive reverse proxy setup guide with nginx/Apache examples, security testing procedures, and deployment checklist
docs/archive/security_architecture.md Updated WebSocket authentication flow documentation and enhanced security requirements with header injection prevention details
.github/copilot-instructions.md Updated developer guide to reflect X-User-Email header with header stripping requirement

# Last Updated: 2025-11-10
#
# SECURITY REQUIREMENTS:
# - This configuration includes header stripping to prevent header injection attacks
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

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

Double space found in comment. Should be "includes header" instead of "includes header".

Suggested change
# - This configuration includes header stripping to prevent header injection attacks
# - This configuration includes header stripping to prevent header injection attacks

Copilot uses AI. Check for mistakes.
# Fallback to test user or require auth
config_manager = app_factory.get_config_manager()
user_email = config_manager.app_settings.test_user or '[email protected]'
logger.info(f"WebSocket using fallback test user: {sanitize_for_logging(user_email)}")
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

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

[nitpick] Inconsistent logging style within the same code block. Lines 234 and 240 use logger format placeholders (logger.info("message: %s", value)), while this line uses an f-string. For consistency, consider changing to:

logger.info("WebSocket using fallback test user: %s", sanitize_for_logging(user_email))
Suggested change
logger.info(f"WebSocket using fallback test user: {sanitize_for_logging(user_email)}")
logger.info("WebSocket using fallback test user: %s", sanitize_for_logging(user_email))

Copilot uses AI. Check for mistakes.
Add mandatory proxy secret check in websocket_endpoint before accepting WebSocket connections, ensuring consistency with HTTP AuthMiddleware security. Raise WebSocketException for invalid secrets to reject unauthorized access. Introduce WebSocketException import and sanitize logging for auth headers and user emails to prevent potential information leakage. This enhances overall system security in production environments.
@garland3 garland3 merged commit 5af78b6 into main Nov 21, 2025
6 checks passed
@garland3 garland3 deleted the fix-websocket-auth-squashed branch November 21, 2025 05:43
logger.info(
"WebSocket authenticated via %s header: %s",
sanitize_for_logging(auth_header_name),
sanitize_for_logging(user_email)

Check failure

Code scanning / CodeQL

Log Injection High

This log entry depends on a
user-provided value
.

Copilot Autofix

AI 7 days ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.

@github-actions
Copy link

🔒 Security Scan Results

Security Scan Summary

Scan Results

Python SAST (Bandit)

⚠️ Security issues found in Python code

Recommendations

  • Review all SARIF files uploaded to GitHub Security tab
  • Address high and critical severity vulnerabilities immediately
  • Run npm audit fix and pip-audit locally to fix dependencies
  • Consider implementing additional security controls

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unable to load previous files into current session.

2 participants