CVE-2026-84711 automation-controller: automation-controller: Project scm_branch/scm_refspec argument injection into git during project sync allows arbitrary file read on the sync host (control-plane ServiceAccount token, SECRET_KEY, and DB credentials ...
A flaw was found in Red Hat Ansible Automation Platform's automation-controller. The
Project SCM branch and SCM refspec fields are accepted without rejecting values that begin
with a dash and are passed to the git module during project synchronization, where they
reach a `git checkout`/`git fetch` command line as bare arguments with no end-of-options
separator. An authenticated user permitted to create or edit a project can set the SCM
branch to a git option such as `--pathspec-from-file=<path>`, causing git to read an
arbitrary file on the synchronization host and reflect its contents back through the
project-update output. Because project synchronization runs on the control-plane host on
default OpenShift Operator deployments, an attacker can read the control-plane Kubernetes
ServiceAccount token, the Controller SECRET_KEY, and the database credentials, leading to
full compromise of the Automation Platform and its Kubernetes namespace. System
administrator privileges are not required and the impact crosses tenants.
A flaw was found in automation-controller (AWX). Project.scm_branch
(awx/main/models/credential path awx/main/models/projects.py:98-111, a CharField with no
validators) and scm_refspec are accepted as free text with no rejection of a leading dash
(no validate_scm_branch/validate_scm_refspec and no leading-dash guard anywhere in
awx/api/serializers.py, awx/main, or awx/playbooks). During project sync,
RunProjectUpdate.build_extra_vars_file (awx/main/tasks/jobs.py:1481-1518) copies scm_branch
(:1486/:1508) and scm_refspec (:1517-1518) verbatim into the project-update extra_vars, and
awx/playbooks/project_update.yml (`- hosts: localhost`, `connection: local`, :28-30) passes
them to the ansible.builtin.git module as `version: "{{ scm_branch | quote }}"` (:50) and
`refspec: "{{ scm_refspec | default(omit) }}"` (:51). The git module's switch_version()
builds `[/usr/bin/git, checkout, --force, <version>]` with no `--` end-of-options sentinel
(upstream lib/ansible/modules/git.py), so a value beginning with `--` is parsed by git as
an option flag. The `| quote` filter is ansible's shlex.quote and does NOT defend against
this: an all-shell-safe string such as `--pathspec-from-file=/etc/passwd` is returned
unchanged (matching the live proof, whose command line shows no quotes). Setting scm_branch
to `--pathspec-from-file=/var/run/secrets/kubernetes.io/serviceaccount/token` makes git read
the file line-by-line as pathspecs and echo each line on stderr as
`error: pathspec '<line>' did not match any file(s) known to git`; ansible-runner captures
stderr into the job event stream, returned via GET
/api/controller/v2/project_updates/{id}/stdout/?format=txt — an arbitrary-file-read oracle
on the sync host. Because project_update.yml runs on localhost and, on the tested
OpenShift Operator deployment, that host is the automation-controller-task control-plane
pod (confirmed: the leaked token's pod name matches a control node in
/api/controller/v2/ping/), the read reaches control-plane secrets: the
system:serviceaccount:ansible-automation-platform:automation-controller JWT (pod/secret
CRUD in the AAP namespace -> full platform takeover), /etc/tower/SECRET_KEY (offline
decryption of every stored Credential), and /etc/tower/conf.d/*.py (PostgreSQL
credentials). Any user able to create or edit a project (Organization project_admin, or
Project Admin on a single project) can trigger this on project create/edit auto-sync;
system administrator is not required, and any org's admin reads secrets governing all
tenants on the control plane. scm_refspec (fed to `git fetch`, no quote at all) is a second
option-injection sink to fix together. On deployments where project updates run in an
isolated execution environment without control-plane secrets, the primitive is limited to
that context. Discovered internally (pentest-aap); verified live on AAP 2.7 /
automation-controller 4.8.1; still present on devel (fix prepared, not merged)
Upstream: github.com/ansible/awx (awx/main/models/projects.py scm_branch/scm_refspec;
awx/api/serializers.py Project serializers; awx/main/tasks/jobs.py
RunProjectUpdate.build_extra_vars_file; awx/playbooks/project_update.yml) and
github.com/ansible/ansible (ansible.builtin.git switch_version -- add `--`).
This issue has been addressed in the following products:
Red Hat Ansible Automation Platform 2.4 for RHEL 8
Red Hat Ansible Automation Platform 2.4 for RHEL 9
Via RHSA-2026:71115 https://access.redhat.com/errata/RHSA-2026:71115
This issue has been addressed in the following products:
Red Hat Ansible Automation Platform 2.6 for RHEL 10
Red Hat Ansible Automation Platform 2.6 for RHEL 9
Via RHSA-2026:71113 https://access.redhat.com/errata/RHSA-2026:71113
This issue has been addressed in the following products:
Red Hat Ansible Automation Platform 2.5 for RHEL 9
Red Hat Ansible Automation Platform 2.5 for RHEL 8
Via RHSA-2026:71114 https://access.redhat.com/errata/RHSA-2026:71114
A flaw was found in automation-controller (AWX). Project.scm_branch (awx/main/models/credential path awx/main/models/projects.py:98-111, a CharField with no validators) and scm_refspec are accepted as free text with no rejection of a leading dash (no validate_scm_branch/validate_scm_refspec and no leading-dash guard anywhere in awx/api/serializers.py, awx/main, or awx/playbooks). During project sync, RunProjectUpdate.build_extra_vars_file (awx/main/tasks/jobs.py:1481-1518) copies scm_branch (:1486/:1508) and scm_refspec (:1517-1518) verbatim into the project-update extra_vars, and awx/playbooks/project_update.yml (`- hosts: localhost`, `connection: local`, :28-30) passes them to the ansible.builtin.git module as `version: "{{ scm_branch | quote }}"` (:50) and `refspec: "{{ scm_refspec | default(omit) }}"` (:51). The git module's switch_version() builds `[/usr/bin/git, checkout, --force, <version>]` with no `--` end-of-options sentinel (upstream lib/ansible/modules/git.py), so a value beginning with `--` is parsed by git as an option flag. The `| quote` filter is ansible's shlex.quote and does NOT defend against this: an all-shell-safe string such as `--pathspec-from-file=/etc/passwd` is returned unchanged (matching the live proof, whose command line shows no quotes). Setting scm_branch to `--pathspec-from-file=/var/run/secrets/kubernetes.io/serviceaccount/token` makes git read the file line-by-line as pathspecs and echo each line on stderr as `error: pathspec '<line>' did not match any file(s) known to git`; ansible-runner captures stderr into the job event stream, returned via GET /api/controller/v2/project_updates/{id}/stdout/?format=txt — an arbitrary-file-read oracle on the sync host. Because project_update.yml runs on localhost and, on the tested OpenShift Operator deployment, that host is the automation-controller-task control-plane pod (confirmed: the leaked token's pod name matches a control node in /api/controller/v2/ping/), the read reaches control-plane secrets: the system:serviceaccount:ansible-automation-platform:automation-controller JWT (pod/secret CRUD in the AAP namespace -> full platform takeover), /etc/tower/SECRET_KEY (offline decryption of every stored Credential), and /etc/tower/conf.d/*.py (PostgreSQL credentials). Any user able to create or edit a project (Organization project_admin, or Project Admin on a single project) can trigger this on project create/edit auto-sync; system administrator is not required, and any org's admin reads secrets governing all tenants on the control plane. scm_refspec (fed to `git fetch`, no quote at all) is a second option-injection sink to fix together. On deployments where project updates run in an isolated execution environment without control-plane secrets, the primitive is limited to that context. Discovered internally (pentest-aap); verified live on AAP 2.7 / automation-controller 4.8.1; still present on devel (fix prepared, not merged) Upstream: github.com/ansible/awx (awx/main/models/projects.py scm_branch/scm_refspec; awx/api/serializers.py Project serializers; awx/main/tasks/jobs.py RunProjectUpdate.build_extra_vars_file; awx/playbooks/project_update.yml) and github.com/ansible/ansible (ansible.builtin.git switch_version -- add `--`).