Skip to content

RSPEED-2651: enforce max header size on x-rh-identity before base64 decode#1352

Merged
tisnik merged 1 commit intolightspeed-core:mainfrom
major:rspeed-2651/rh-identity-header-size-limit
Mar 19, 2026
Merged

RSPEED-2651: enforce max header size on x-rh-identity before base64 decode#1352
tisnik merged 1 commit intolightspeed-core:mainfrom
major:rspeed-2651/rh-identity-header-size-limit

Conversation

@major
Copy link
Copy Markdown
Contributor

@major major commented Mar 18, 2026

Description

The rh-identity auth module passes the x-rh-identity header value directly to base64.b64decode() with no size check. An attacker can send a multi-megabyte header and force memory allocation for decoding, JSON parsing, and dict traversal (DoS vector).

This PR adds a configurable max_header_size field to RHIdentityConfiguration (default 8KB) and enforces a size check on the raw base64-encoded header before any decoding occurs. Oversized headers are rejected with HTTP 400 and a warning is logged with size details.

Changes

  • src/constants.py: add DEFAULT_RH_IDENTITY_MAX_HEADER_SIZE = 8192
  • src/models/config.py: add max_header_size: PositiveInt field to RHIdentityConfiguration
  • src/authentication/rh_identity.py: add max_header_size constructor param and size check before base64.b64decode()
  • src/authentication/__init__.py: wire max_header_size from config into the auth dependency
  • Unit tests for config validation and auth header size enforcement

Type of change

  • Bug fix

Tools used to create PR

  • Assisted-by: Claude (OpenCode)
  • Generated by: N/A

Related Tickets & Documents

Checklist before requesting a review

  • I have performed a self-review of my code.
  • PR has passed all pre-merge test jobs.
  • If it is a core feature, I have added thorough tests.

Testing

  1. uv run make verify passes all linters (pylint 10/10, pyright 0 errors, ruff clean)
  2. uv run make test-unit passes all 1765 tests with 60%+ coverage
  3. rh_identity.py has 100% test coverage
  4. New tests verify:
    • Oversized headers (9000 bytes, 101 bytes, 200 bytes) rejected with HTTP 400
    • Header at exact limit is accepted and auth succeeds
    • Custom max_header_size is respected
    • Warning logged with actual header size and configured limit
    • Config field defaults to 8192, rejects 0 and negative values via PositiveInt

Summary by CodeRabbit

  • New Features
    • Added header size validation for authentication headers. The x-rh-identity header now enforces a configurable maximum size limit (default: 8192 bytes), rejecting requests exceeding this threshold with a 400 error.

…ecode

Reject x-rh-identity headers exceeding configurable max_header_size
(default 8KB) before base64 decoding to prevent DoS from oversized
payloads.

Resolves: RSPEED-2651
Signed-off-by: Major Hayden <major@redhat.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 18, 2026

Walkthrough

This change adds header size validation to the RH Identity authentication mechanism. A new max_header_size parameter is introduced across the configuration layer, authentication dependency, and constants, enforcing a maximum limit on the x-rh-identity header size before processing, returning a 400 error if exceeded.

Changes

Cohort / File(s) Summary
Configuration & Constants
src/constants.py, src/models/config.py
Added DEFAULT_RH_IDENTITY_MAX_HEADER_SIZE constant (8192) and wired it into RHIdentityConfiguration as a new max_header_size field with PositiveInt validation.
Authentication Logic
src/authentication/rh_identity.py, src/authentication/__init__.py
Updated RHIdentityAuthDependency to accept and store max_header_size parameter, added header length validation before decoding that raises HTTP 400 if exceeded with warning log.
Tests
tests/unit/authentication/test_rh_identity.py, tests/unit/models/config/test_authentication_configuration.py
Added TestRHIdentityHeaderSizeLimit class with tests for headers at exact limit (accepted) and exceeding limit (rejected with 400 error and log verification), plus validation tests for the config field.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically identifies the main change: enforcing a maximum header size check on the x-rh-identity header before base64 decoding, which is the primary security improvement in this changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can generate a title for your PR based on the changes.

Add @coderabbitai placeholder anywhere in the title of your PR and CodeRabbit will replace it with a title based on the changes in the PR. You can change the placeholder by changing the reviews.auto_title_placeholder setting.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/authentication/rh_identity.py (1)

205-218: Add a defensive constructor guard for non-positive max_header_size.

RHIdentityConfiguration validates this already, but direct instantiation of RHIdentityAuthDependency can still pass invalid values and cause accidental auth outages.

🛡️ Suggested hardening
     def __init__(
         self,
         required_entitlements: Optional[list[str]] = None,
         virtual_path: str = DEFAULT_VIRTUAL_PATH,
         max_header_size: int = DEFAULT_RH_IDENTITY_MAX_HEADER_SIZE,
     ) -> None:
@@
-        self.required_entitlements = required_entitlements
+        if max_header_size <= 0:
+            raise ValueError("max_header_size must be greater than 0")
+
+        self.required_entitlements = required_entitlements
         self.virtual_path = virtual_path
         self.max_header_size = max_header_size
         self.skip_userid_check = False
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/authentication/rh_identity.py` around lines 205 - 218, Add a defensive
check in the RHIdentityAuthDependency.__init__ to guard against non-positive
max_header_size: validate the incoming max_header_size parameter (e.g., if
max_header_size is None or <= 0) and either raise a clear ValueError or default
it to DEFAULT_RH_IDENTITY_MAX_HEADER_SIZE; update the assignment of
self.max_header_size to use the validated value. This targets the
RHIdentityAuthDependency constructor and references
DEFAULT_RH_IDENTITY_MAX_HEADER_SIZE so direct instantiation cannot set an
invalid header size.
tests/unit/authentication/test_rh_identity.py (1)

573-576: Avoid hardcoded default limit in the parametrized test.

Using the shared constant instead of 8192 keeps the test resilient if the default changes later.

♻️ Suggested update
-from constants import NO_USER_TOKEN
+from constants import DEFAULT_RH_IDENTITY_MAX_HEADER_SIZE, NO_USER_TOKEN
...
         "header_size,max_size",
         [
-            (9000, 8192),  # Well over default limit
+            (9000, DEFAULT_RH_IDENTITY_MAX_HEADER_SIZE),  # Well over default limit
             (101, 100),  # One byte over custom limit
             (200, 100),  # Well over custom limit
         ],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/authentication/test_rh_identity.py` around lines 573 - 576, The
parametrized test hardcodes 8192 as the default limit; replace that magic number
by importing and using the shared default constant from the production module
(e.g., DEFAULT_LIMIT or DEFAULT_OIDC_TOKEN_LIMIT) used by the rh identity code
and use that constant in the tuple (instead of 8192) so the test follows the
real default and stays correct if the default changes; update the import at the
top of tests/unit/authentication/test_rh_identity.py and the tuple list where
(9000, 8192) appears to use the shared constant.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/authentication/rh_identity.py`:
- Around line 205-218: Add a defensive check in the
RHIdentityAuthDependency.__init__ to guard against non-positive max_header_size:
validate the incoming max_header_size parameter (e.g., if max_header_size is
None or <= 0) and either raise a clear ValueError or default it to
DEFAULT_RH_IDENTITY_MAX_HEADER_SIZE; update the assignment of
self.max_header_size to use the validated value. This targets the
RHIdentityAuthDependency constructor and references
DEFAULT_RH_IDENTITY_MAX_HEADER_SIZE so direct instantiation cannot set an
invalid header size.

In `@tests/unit/authentication/test_rh_identity.py`:
- Around line 573-576: The parametrized test hardcodes 8192 as the default
limit; replace that magic number by importing and using the shared default
constant from the production module (e.g., DEFAULT_LIMIT or
DEFAULT_OIDC_TOKEN_LIMIT) used by the rh identity code and use that constant in
the tuple (instead of 8192) so the test follows the real default and stays
correct if the default changes; update the import at the top of
tests/unit/authentication/test_rh_identity.py and the tuple list where (9000,
8192) appears to use the shared constant.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ee70f774-1826-49bd-a7e8-adbe4c869cd9

📥 Commits

Reviewing files that changed from the base of the PR and between 840bcba and 0073c36.

📒 Files selected for processing (6)
  • src/authentication/__init__.py
  • src/authentication/rh_identity.py
  • src/constants.py
  • src/models/config.py
  • tests/unit/authentication/test_rh_identity.py
  • tests/unit/models/config/test_authentication_configuration.py

Copy link
Copy Markdown
Contributor

@tisnik tisnik left a comment

Choose a reason for hiding this comment

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

LGTM

@tisnik tisnik merged commit bb0f23a into lightspeed-core:main Mar 19, 2026
21 of 22 checks passed
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.

2 participants