dotCMS / dotCMS/core

Request-derived site is not authorized against the caller when spending AI credentials

Open
#37,624 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Type : Defect
Dominant language
Java
Stars
970
Forks
486
Avg merge
3d 33m
Merged PRs (30d)
170

Description

Description

The site whose AI credentials a request spends can be chosen by the caller through the Host header, and the resolved site is never checked against the caller's permissions.

Across the dotAI REST families, an explicitly named site is permission-checked and a request-derived one is not:

  • AiHostResolver.resolveHost / resolveHostStrict (used by CompletionsResource, AiProviderResource) check findHost(siteId, user) for an explicit siteId, then fall back to WebAPILocator.getHostWebAPI().getCurrentHostNoThrow(request) with no check on the result.
  • /api/inference/v1 follows the same shape: requireOverriddenHost(siteOverride, user) is checked, resolveFromRequest(request) resolves the server name as APILocator.systemUser() and is not.

So a token that cannot read site B can still direct a request at site B by sending Host: b.example, and B pays for the inference.

Why this is not a one-line fix. Applying the explicit path's READ check to the request path returns 403 for every frontend user, who are explicitly supported callers and generally hold no READ permission on the Host object — confirmed by InferenceAuthorizationTest.test_completions_withFrontendUser_isAccepted failing when it was tried. Passing respectFrontendRoles = true instead makes that test pass but defeats the control, since most sites are frontend-readable and the cross-site case sails through.

The question is what authorises spending a site's AI budget. READ on the site is either too strict or too weak depending on which flag is set, which suggests it is the wrong control. Options worth weighing:

  • scope an API token to the sites it may direct requests at
  • a distinct permission for spending AI credit, separate from READ
  • accept it explicitly for server-to-server use, and document that any authenticated caller may spend any site they can reach
Acceptance Criteria
  • A decision on what authorises AI spend on a site, recorded rather than implied by the resolver's use of systemUser
  • Applied consistently across /api/v1/ai/* and /api/inference/v1, so the two families do not carry different authorization models
  • Frontend users remain able to use the endpoints that support them
  • A test covering a caller directing a request at a site they have no business spending
Additional Context
  • Raised in review of #37559. Pre-existing across the dotAI families rather than introduced there.
  • Related and already fixed for the new family only: the host_id / Host request parameters were honoured on the unmatched-host fallback path (#37491 covers the shipped endpoints).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read AiHostResolver.resolveHost and resolveHostStrict, then compare requireOverriddenHost with resolveFromRequest across the dotAI REST families. Run InferenceAuthorizationTest.test_completions_withFrontendUser_isAccepted while evaluating the authorization options. Done means one recorded authorization model is applied consistently, frontend users remain supported, and a test prevents spending on an unauthorized site.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
api, authorization, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.