CVE-2026-84707: Medium severity Ansible automation-controller (AWX) vulnerability
A flaw was found in automation-controller (AWX). GET /api/controller/v2/hosts/ accepts a hostfilter query parameter that HostList.getqueryset (awx/api/views/ init.py:1938-1944) parses with SmartFilter.queryfromstring and intersects with the RBAC-filtered host queryset via qs &= filterqs; that intersection restricts only which Host rows are returned, not which related rows the lookup's JOINs traverse. SmartFilter.BoolOperand (awx/main/utils/filters.py:151-170) validates the user-supplied lookup PATH only by calling FieldLookupBackend(). getfieldfromlookup(Host, k) and then executes Host.objects.filter({k: v}) directly, including regex and icontains lookups. FieldLookupBackend (django- ansible-base ansiblebase/restfilters/utils.py:9-69) rejects only fields in PASSWORDFIELDS/encryptedfields, fields marked preventsearch, and traversal loops -- it performs no authorization check on the rows the relation traversal reaches. JobEvent.eventdata/stdout and AdHocCommandEvent.eventdata/stdout (awx/main/models/events.py:215,253,695,705) are not wrapped in preventsearch(), unlike extravars, jobenv, jobargs and surveypasswords which are. Consequently a user with only the Read role on an inventory can submit hostfilter values such as lastjobjobeventseventdataregex=<pattern>, jobeventsasprimaryhoststdouticontains=<substring>, or adhoccommandeventseventdataregex=<pattern> and read the returned host count as a one-bit oracle. Since regex is accepted, this allows efficient character-by- character extraction of the eventdata/stdout of jobs and ad-hoc commands the caller is explicitly denied (HTTP 403) via the direct API, for any job that ever ran against a host in a readable inventory -- including jobs in other organizations when a host spans inventories across organizations. Job output routinely contains plaintext credentials, API tokens, command output and host facts. This is a horizontal privilege escalation from inventory-read to read-arbitrary-job-output. Discovered internally (pentest-aap); verified live on AAP 2.7 / automation- controller 4.8.1 (the forbidden job's playbookuuid was extracted exactly); still present on devel. Upstream: github.com/ansible/awx (awx/api/views/init.py HostList.getqueryset; awx/main/utils/filters.py SmartFilter.BoolOperand; sensitive fields in awx/main/models/events.py) and github.com/ansible/django-ansible-base (ansiblebase/restfilters/utils.py getfieldsfrompath).