Skip to content

GTM proxy resolves the tag from the request rather than from configuration #1115

Description

@prk-Jr

Description

In crates/trusted-server-core/src/integrations/google_tag_manager.rs, build_target_url consults the validated container_id only when the request carries no query string:

if let Some(query) = req.uri().query() {
    target_url = format!("{}?{}", target_url, query);
} else if path.ends_with("/gtm.js") {
    target_url = format!("{}?id={}", target_url, self.config.container_id);
}

Any query string therefore wins, and the response is re-served from the publisher's own origin as application/javascript with Cache-Control: public, max-age=<cache_max_age>. The same applies to /gtag/js, which answers 200 for tag ids that do not exist, so upstream cannot be relied on to reject an unknown one.

Related problems in the same file:

  • upstream_url is validated only as a URL. It may be plaintext, may embed a username and password, and may carry a wildcard host — and it is handed to the browser in the redirect for an unconfigured tag, so anything in it is disclosed. The proxy fetch is also unpinned: follow_redirects defaults on and an empty allowed_domains means any host.
  • Responses passed through unmodified may carry Set-Cookie, Strict-Transport-Security or Clear-Site-Data, all of which act against the serving origin.
  • GTM_URL_PATTERN matched on host alone, so any path on the Google hosts was rewritten to a first-party URL — but only five paths have routes, so the rest became first-party 404s. On the live GTM script this produced three broken rewrites, two of them landing inside minified JavaScript expressions.

Expected behavior

  • gtm.js serves the configured container regardless of what the request asks for.
  • gtag/js serves only tag ids the operator has configured; any other id is not served from this origin.
  • The upstream fetch is pinned to HTTPS and to the configured hosts.
  • Passed-through responses cannot set state against the publisher's origin.
  • Only routed paths are rewritten first-party.

Affected area

Integrations (prebid, lockr, permutive, etc.)

Done when

  • A client-supplied id cannot change which container gtm.js serves
  • gtag/js is limited to configured tag ids, with an operator-visible upgrade note
  • upstream_url must be HTTPS with a literal host and no credentials
  • Proxy fetches are pinned to the configured hosts
  • Set-Cookie / HSTS / Clear-Site-Data are stripped from passthrough responses
  • Rewriting is limited to routed paths, with tests covering prefix collisions and quoting styles

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions