Avo: Direct attachment upload endpoint lacks upload authorization and bypasses field-level upload policy
Affects avoSource GCVE Database
Not known to be exploited · CVSS 6.5 Medium : upgrade to 3.32.0 when convenient.
Data as of 26 Oct 2026, 14:05 UTC
- EPSS
- 0.4% chance of exploitation in 30 days – higher than 36% of CVEsExploit Prediction Scoring System: the likelihood attackers will use this flaw soon.
- Patch
- Available – fixed in 3.32.0
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:NExploitation evidence
t = 0 is the CVE’s publication on cve.org
Disclosed: evidence found.
CVE published 4 Sep 2026
Detectable: no evidence on record.
No public nuclei template indexed
Weaponised: evidence found.
CISA SSVC: proof of concept public
Exploited in the wildExploited: no evidence on record.
Not in CISA KEV and no source reports exploitation
Observed by OffSeq MirageSeen by Mirage: no evidence on record.
No attack traffic recorded by OffSeq Mirage sensors
Timeline
CVE ID reserved
by GitHub, Inc.
, 86 days before disclosure
CVE published
on cve.org
, t = 0
CISA SSVC assessment
proof of concept public
, 5 days after disclosure
A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), CISA SSVC, the Metasploit, Exploit-DB and nuclei-templates index as of 26 Oct 2026.
What to do
Upgrade avo to 3.32.0.
Affected software
| Product | Affected | Fixed in |
|---|---|---|
| RubyGemsavo | Affected
| Fixed in
|
Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.
Description
### Summary Avo's direct attachment upload endpoint lacks server-side upload authorization and bypasses the documented field-level upload policy methods such as `upload_{FIELD_ID}?`. An authenticated Avo user who can reach the Avo attachment upload endpoint can replace or add attachment content, including binary content, filename, and content-type metadata, on a resolved record even when both `update?` and `upload_<field>?` policies deny the operation. This primarily affects multi-role Avo Pro/Advanced-style deployments where non-administrator or restricted operator users can reach Avo and per-record or per-field operations are expected to be enforced by policies. ### Details Avo exposes a direct attachment upload endpoint: ```text POST /<avo-root>/avo_api/resources/:resource_name/:id/attachments/ ``` The vulnerable code path is `Avo::AttachmentsController#create`: - `app/controllers/avo/attachments_controller.rb:9-24` Current behavior: ```ruby def create blob = ActiveStorage::Blob.create_and_upload! io: params[:file].to_io, filename: params[:filename] association_name = BaseResource.valid_attachment_name(@record, params[:attachment_key]) if association_name @record.send(association_name).attach blob elsif params[:key].blank? raise ActionController::BadRequest.new(...) end render json: { url: main_app.url_for(blob), href: main_app.url_for(blob) } end ``` This endpoint creates an `ActiveStorage::Blob` before validating the requested attachment association and before any upload authorization could be enforced. If `attachment_key` resolves to an association, the blob is attached to the target record. If the request uses the key-based/Trix path, the endpoint can still persist the blob and return `url`/`href` even when no attachment association is resolved on the target record. The controller never calls: - `@resource.authorization.authorize_action("upload_<field>?", record: @record, ...)` - `@resource.authorization.authorize_action("update?", record: @record, ...)` This is inconsistent with the rest of Avo's file authorization implementation. The field-level file authorization concern defines upload authorization as: - `lib/avo/fields/concerns/file_authorization.rb:11-12` - `lib/avo/fields/concerns/file_authorization.rb:25-27` ```ruby def can_upload_file? authorize_file_action(:upload) end def authorize_file_action(action) authorize_action("#{action}_#{id}?", record: record, raise_exception: false) end ``` That upload policy is used by UI components to decide whether to render upload controls, but the server-side upload endpoint does not enforce the same policy. A user can therefore bypass the policy by directly POSTing to the endpoint. By contrast, attachment deletion does call attachment-specific authorization: - `app/controllers/avo/attachments_controller.rb:27-65` ```ruby def destroy if authorized_to :delete ... end end def authorized_to(action) @resource.authorization.authorize_action("#{action}_#{params[:attachment_name]}?", record: @record, raise_exception: false) end ``` The asymmetry is: - `destroy`: calls `delete_<attachment_name>?` - `create`: does not call `upload_<attachment_key>?` or `update?` The field-level upload authorization intent is also documented by Avo: - Issue #1624 requested the "Ability to police each file upload/download/delete". https://github.com/avo-hq/avo/issues/1624 - PR #1625 introduced `upload_cover_photo?` as an example upload policy method. https://github.com/avo-hq/avo/pull/1625 - PR #1667 clarified the policy semantics: "From now on, only field level authorization will be considered. If it is defined and returns true, the action will be granted; otherwise, it will not." https://github.com/avo-hq/avo/pull/1667 - The current Avo authorization documentation lists `upload_{FIELD_ID}?` as the policy method controlling whether a user can upload an attachment, stating: "Controls whether the user can upload the attachment." https://docs.avohq.io/4.0/authorization.html - The same documentation says the same `upload_file?` policy method will be used to "authorize the file upload" in action file fields. https://docs.avohq.io/4.0/authorization.html Affected versions observed: - PoC-confirmed: Avo `3.31.2`, commit `46aa6b3bc9e3283110c39e58cfec8bb95adc1897` - Same vulnerable code path by source inspection: `origin/main` HEAD as of 2026-05-29, `9e23ddc88f2b1e762e4a5ec35a6f86370ac16c73` - Same vulnerable code path by source inspection: Avo `v4.0.0.beta.26`, `6d339595a27f8779cb99b4aa38ddc97cb702b30f` - Same vulnerable code path by source inspection: `origin/4-dev` at version `4.0.0.beta.40`, `7ab9794f8b044b11b9677cdc57547d99cf96c3f3` Suggested affected range for the GitHub Security Advisory form: ```text >= 2.28.0 ``` Rationale: - The direct attachment upload endpoint exists without upload authorization from commit `ab5f5970e2aa76e6ca0a95bf04f510ba7ed5e858` (`feature: trix atta