Skip to content

FIX: Add destination allowlist before forwarding Bearer credential to Graph in EntraAuthMiddleware#2183

Open
jsong468 wants to merge 1 commit into
microsoft:mainfrom
jsong468:icm123694
Open

FIX: Add destination allowlist before forwarding Bearer credential to Graph in EntraAuthMiddleware#2183
jsong468 wants to merge 1 commit into
microsoft:mainfrom
jsong468:icm123694

Conversation

@jsong468

Copy link
Copy Markdown
Contributor

Description

EntraAuthMiddleware._resolve_excess_groups_async forwards the user's Bearer credential to a host derived from token claims (_claim_sources.src1.endpoint) and from the response body (@odata.nextLink) without an explicit allowlist. There is no check that these values resolve to graph.microsoft.com before the credentialed request is made. This adds an explicit host + scheme allowlist before each request.

This is not an exploitable bug today: the endpoint originates from a signature-validated JWT and pagination links come from a TLS-authenticated Graph response. The allowlist makes the safety property local and robust against future changes and hardens the currently-dead path before it is re-enabled (see the existing NOTE about the Graph audience migration).

Changes

  • pyrit/backend/middleware/auth.py
    • Add _GRAPH_HOSTS class constant ({"graph.microsoft.com"}
    • Add _is_trusted_graph_url() helper using urlparse(...).hostname (correctly handles userinfo/port, so graph.microsoft.com@evil.com-style spoofs are rejected).
    • Guard the initial POST endpoint: refuse + log + return [] if untrusted.
    • Guard each @odata.nextLink in the pagination loop: refuse + log + break if untrusted.

Tests

  • tests/unit/backend/test_auth_middleware.py
    • Parametrized test of _is_trusted_graph_url (legit Graph URLs vs. http://, wrong host, suffix-spoof, userinfo-spoof, empty).
    • Async test asserting an untrusted endpoint returns [] and makes no HTTP request (token is never forwarded).

@hannahwestra25 hannahwestra25 self-assigned this Jul 14, 2026
}

# Hosts the user's Bearer token may be forwarded to
_GRAPH_HOSTS: ClassVar[set[str]] = {"graph.microsoft.com"}

@hannahwestra25 hannahwestra25 Jul 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

could we avoid sending the request to the endpoint supplied in the JWT? Entra adds _claim_sources when a user belongs to too many groups for all group IDs to fit in the token (typically more than 200). Since the Graph API path is known, constructing it from a trusted, deployment-configured base URL would prevent token data from controlling the request path or port. something like :

class EntraAuthMiddleware(BaseHTTPMiddleware):
    _GRAPH_HOSTS: ClassVar[set[str]] = {"graph.microsoft.com"}
    _GRAPH_MEMBER_OBJECTS_URL: ClassVar[str] = (
        "https://graph.microsoft.com/v1.0/me/getMemberObjects"
    )
...

claim_name = claims.get("_claim_names", {}).get("groups")
claim_sources = claims.get("_claim_sources", {})

# Confirm that Entra indicated the groups list did not fit in the token.
if not claim_name or claim_name not in claim_sources:
    logger.debug("No group overage claim source found")
    return []

endpoint = self._GRAPH_MEMBER_OBJECTS_URL

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