Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-57579

CVE-2026-57579: Alchemy CMS Authentication Bypass Vulnerability

CVE-2026-57579 is an authentication bypass vulnerability in Alchemy CMS that allows unauthorized access to restricted page data and unpublished content. This article covers the technical details, affected versions, and mitigation steps.

Published:

CVE-2026-57579 Overview

Alchemy is an open source content management system engine written in Ruby on Rails. CVE-2026-57579 is a missing authorization flaw [CWE-862] in the unauthenticated GET /api/pages/nested endpoint implemented by Api::PagesController#nested. The endpoint returns an unfiltered page tree because it performs no authorization and does not scope descendants by the caller's ability. Anonymous callers can retrieve restricted and unpublished page metadata that the sibling show action denies. When elements=true is supplied, PageTreeSerializer also returns element and ingredient content from restricted pages. The issue is fixed in versions 7.4.15, 8.0.15, 8.1.14, and 8.2.6.

Critical Impact

Unauthenticated remote attackers can enumerate and read restricted or unpublished page metadata, elements, and ingredient content from any affected AlchemyCMS deployment.

Affected Products

  • AlchemyCMS versions prior to 7.4.15
  • AlchemyCMS 8.0.x prior to 8.0.15, and 8.1.x prior to 8.1.14
  • AlchemyCMS 8.2.x prior to 8.2.6

Discovery Timeline

  • 2026-09-14 - CVE-2026-57579 published to NVD
  • 2026-09-14 - Last updated in NVD database

Technical Details for CVE-2026-57579

Vulnerability Analysis

The vulnerability resides in the nested action of Alchemy::Api::PagesController. The action loads a page by page_id and passes it to PageTreePreloader and PageTreeSerializer without invoking an ability check. In contrast, the sibling show action enforces authorization and rejects anonymous access to restricted pages. This inconsistency allows unauthenticated clients to bypass the intended access control by requesting the tree endpoint instead of the individual page endpoint.

The serializer amplifies the impact. PageTreeSerializer#pages calls object.self_and_descendants and returns the entire subtree. When callers pass elements=true, the preloader eagerly loads element and ingredient records for every node, exposing full content of restricted pages rather than just metadata.

Root Cause

The root cause is a missing authorization check [CWE-862] on both the controller entry point and the tree traversal logic. Neither authorize! nor an accessible_by scope was applied, so CanCan abilities that govern individual page reads were never evaluated for tree responses.

Attack Vector

An unauthenticated attacker issues a single HTTP GET request to /api/pages/nested against a vulnerable AlchemyCMS instance. Supplying elements=true returns the full element and ingredient payload for every descendant page, including drafts and pages otherwise restricted by role.

ruby
# Patched controller: app/controllers/alchemy/api/pages_controller.rb
def nested
  @page = Page.find_by(id: params[:page_id]) || Language.current_root_page

  authorize! :show, @page

  # Preload the full tree from this page, scoped to what the user may see
  preloaded_page = PageTreePreloader.new(
    page: @page,
    user: current_alchemy_user,
    ability: current_ability
  ).call

  render json: PageTreeSerializer.new(
    preloaded_page,
    # ...
  )
end

Source: AlchemyCMS Commit 5620e76

The serializer patch scopes descendants using CanCan's accessible_by and filters out orphaned branches whose parent was denied:

ruby
# Patched: app/serializers/alchemy/page_tree_serializer.rb
def pages
  tree = []
  path = [{id: object.parent_id, children: tree}]
  page_list = object.self_and_descendants
    .accessible_by(opts[:ability], :read)
    .includes(:public_version, {language: :site})
    .to_a
  # Drop pages whose parent was filtered out by the ability, so a
  # restricted or unpublished branch does not leak its accessible
  # descendants.
  kept_ids = Set.new([object.id])
  page_list = page_list.select do |page|
    next true if page.id == object.id
    kept_ids.include?(page.parent_id).tap do |kept|
      kept_ids << page.id if kept
    end
  end
end

Source: AlchemyCMS Commit 30888fb

Detection Methods for CVE-2026-57579

Indicators of Compromise

  • Unauthenticated HTTP GET requests to /api/pages/nested in web server access logs, especially with the elements=true query parameter.
  • Repeated tree API requests iterating through numeric page_id values from a single client IP.
  • API responses returning JSON payloads with page slugs or element ingredients not present in the public site map.

Detection Strategies

  • Inspect Rails and reverse-proxy logs for GET /api/pages/nested requests that lack an authenticated session cookie or API token.
  • Correlate anonymous tree requests with subsequent scraping patterns targeting draft or restricted page URLs.
  • Compare AlchemyCMS version strings advertised by the application against the fixed release list.

Monitoring Recommendations

  • Forward web access logs and Rails application logs to a centralized analytics platform and alert on anonymous /api/pages/* traffic volume anomalies.
  • Monitor egress bandwidth from AlchemyCMS hosts for large JSON responses that may indicate bulk content exfiltration.
  • Track deployed AlchemyCMS gem versions across environments and flag any host running an unpatched release.

How to Mitigate CVE-2026-57579

Immediate Actions Required

  • Upgrade AlchemyCMS to 7.4.15, 8.0.15, 8.1.14, or 8.2.6 as appropriate for the deployed branch.
  • Review web logs for prior anonymous requests to /api/pages/nested and treat retrieved draft or restricted content as disclosed.
  • Rotate any secrets, tokens, or unpublished business data that were stored inside restricted page elements or ingredients.

Patch Information

The fix is delivered in AlchemyCMS releases v7.4.15, v8.0.15, v8.1.14, and v8.2.6. Technical background is documented in AlchemyCMS Security Advisory GHSA-mqq5-j7w8-2hgh and Pull Request #3982. The controller now calls authorize! :show, @page, and PageTreePreloader and PageTreeSerializer accept an ability argument that scopes descendants via accessible_by(:read).

Workarounds

  • Block unauthenticated requests to /api/pages/nested at the reverse proxy or WAF until the upgrade is deployed.
  • Restrict access to the AlchemyCMS API path to authenticated internal networks or VPN clients only.
  • Temporarily disable the JSON API routes if the application does not require the nested pages endpoint.
bash
# Example NGINX rule to deny anonymous access to the vulnerable endpoint
location = /api/pages/nested {
    if ($http_authorization = "") { return 403; }
    if ($cookie__alchemy_session = "") { return 403; }
    proxy_pass http://alchemy_backend;
}

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.