GHSA-mqq5-j7w8-2hgh: AlchemyCMS Unauthenticated Nested Page API Leaks Vulnerability

HIGH
CVSS 7.5
CWE-862
Published
Advisory Summary

GHSA-mqq5-j7w8-2hgh is a HIGH (CVSS 7.5) unauthenticated nested page api leaks vulnerability (CWE-862: Missing Authorization) affecting AlchemyCMS libraries in the RubyGems ecosystem. The issue involves unauthenticated nested page api leaks restricted & unpublished content. Remediation is available by upgrading affected packages to version 8.2.6, 8.1.14, 8.0.15, 7.4.15.

GHSA-mqq5-j7w8-2hgh at a Glance

CVE Identifier N/A
GHSA Identifier GHSA-mqq5-j7w8-2hgh
Severity HIGH
CVSS v3.1 Base Score 7.5
CWE Classification CWE-862: Missing Authorization
Affected Product AlchemyCMS
Package Ecosystem RubyGems
Published Date
Last Updated

Security Impact

CVSS: 7.5

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity None (I:N)
Availability None (A:N)

How to Fix GHSA-mqq5-j7w8-2hgh

To remediate this vulnerability, upgrade all affected AlchemyCMS dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

alchemy_cms fixed: 8.2.6
Upgrade alchemy_cms to 8.2.6
alchemy_cms fixed: 8.1.14
Upgrade alchemy_cms to 8.1.14
alchemy_cms fixed: 8.0.15
Upgrade alchemy_cms to 8.0.15
alchemy_cms fixed: 7.4.15
Upgrade alchemy_cms to 7.4.15
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for alchemy_cms

How to Check if You Are Affected

To determine whether your application is affected by GHSA-mqq5-j7w8-2hgh, inspect your project dependencies for AlchemyCMS:

inspect dependencies for alchemy_cms

Analyze the terminal output to locate any version of AlchemyCMS in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 8.2.6), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-862: Missing Authorization, where the product does not perform an authorization check when an actor attempts to access a resource or perform an action. In AlchemyCMS, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

# Unauthenticated nested page API leaks restricted & unpublished content - **Location:** `app/controllers/alchemy/api/pages_controller.rb:28` (`Api::PagesController#nested`) - **Affected version:** Alchemy CMS 8.3.0.dev (Rails 8.1.3) ## Description The unauthenticated `GET /api/pages/nested` endpoint returns the full page tree to any anonymous caller, including restricted (member-only) pages and unpublished/draft pages that should be hidden. Appending `?elements=true` additionally dumps the element/ingredient **content** of restricted pages, fully bypassing the access control the sibling `show` and `index` actions enforce. ## Root cause `Api::PagesController#nested` calls no `authorize!` and applies no `published`/`restricted` scoping, unlike `show` (`authorize! :show`) and `index` (`accessible_by(current_ability, :index)`). `PageTreePreloader` loads `page.self_and_descendants` unfiltered, and `PageTreeSerializer` emits every page's metadata (and, with `elements`, `public_version.elements`) with no ability check. ## Evidence An unauthenticated `GET /api/pages/nested` returns HTTP 200 with the restricted page (`"restricted":true`) and an unpublished draft (`"public":false`); `?elements=true` leaks its content (e.g. `TOPSECRET_RESTRICTED_BODY_proof123`). The same guest hitting `GET /api/pages/3` (`show`) gets HTTP **403** `{"error":"Not authorized"}`, proving `nested` returns what `show` correctly denies. ### Reproduction ```bash # 1) Metadata leak (guest, no auth) curl -s http://localhost:3000/api/pages/nested | python3 -m json.tool | grep -E '"name"|"restricted"|"public"' # 2) Content leak of restricted page curl -s "http://localhost:3000/api/pages/nested?elements=true" | grep -oE 'TOPSECRET_RESTRICTED_BODY_[A-Za-z0-9]+|RESTRICTED_RICHTEXT_[A-Za-z0-9]+' # 3) Contrast — show denies the same guest curl -s -o /dev/null -w "show /api/pages/3 -> HTTP %{http_code}\n" http://localhost:3000/api/pages/3 ``` ### Suggested fix ```ruby def nested @page = Page.find_by(id: params[:page_id]) || Language.current_root_page authorize! :show, @page preloaded_page = PageTreePreloader.new(page: @page, user: current_alchemy_user, ability: current_ability).call render json: PageTreeSerializer.new(preloaded_page, ability: current_ability, user: current_alchemy_user, elements: params[:elements]) end ``` Additionally scope `PageTreePreloader`'s `self_and_descendants` via `accessible_by(current_ability)` and gate element emission in `PageTreeSerializer#page_elements` behind `opts[:ability].can?(:show, page)`.

GHSA-mqq5-j7w8-2hgh Affected AlchemyCMS Versions

4 component(s)
Fixed Versions:
8.2.68.1.148.0.157.4.15
Product / Package Ecosystem Affected Versions Fixed Version
alchemy_cms
RubyGems
≥ 8.2.0
< 8.2.6
8.2.6
alchemy_cms
RubyGems
≥ 8.1.0
< 8.1.14
8.1.14
alchemy_cms
RubyGems
≥ 8.0.0.a
< 8.0.15
8.0.15
alchemy_cms
RubyGems
< 7.4.15
7.4.15

References

Frequently Asked Questions

What is GHSA-mqq5-j7w8-2hgh?
GHSA-mqq5-j7w8-2hgh is a security vulnerability in AlchemyCMS involving Unauthenticated nested page API leaks restricted & unpublished content under CWE-862: Missing Authorization.
Which AlchemyCMS versions are affected?
Affected versions include releases prior to 8.2.6, 8.1.14, 8.0.15, 7.4.15 across affected packages: alchemy_cms.
What version fixes GHSA-mqq5-j7w8-2hgh?
Upgrading to 8.2.6, 8.1.14, 8.0.15, 7.4.15 (or the latest patched release for your respective branch) resolves the vulnerability.
Is alchemy_cms affected?
Yes, alchemy_cms is affected in releases prior to 8.2.6, 8.1.14, 8.0.15, 7.4.15. Upgrade your dependency to 8.2.6, 8.1.14, 8.0.15, 7.4.15 or later.
What is the CVSS score of GHSA-mqq5-j7w8-2hgh?
GHSA-mqq5-j7w8-2hgh has a CVSS v3.1 base score of 7.5 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N.
What CWE is associated with GHSA-mqq5-j7w8-2hgh?
GHSA-mqq5-j7w8-2hgh is classified under CWE-862: Missing Authorization (The product does not perform an authorization check when an actor attempts to access a resource or perform an action.).
How can I check RubyGems dependencies for GHSA-mqq5-j7w8-2hgh?
Run the dependency tree command: 'inspect dependencies for alchemy_cms'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-mqq5-j7w8-2hgh",
  "modified" : "2026-06-19T17:47:13Z",
  "published" : "2026-06-19T17:47:13Z",
  "aliases" : [ ],
  "summary" : "AlchemyCMS: Unauthenticated nested page API leaks restricted & unpublished content",
  "details" : "# Unauthenticated nested page API leaks restricted & unpublished content\n\n- **Location:** `app/controllers/alchemy/api/pages_controller.rb:28` (`Api::PagesController#nested`)\n- **Affected version:** Alchemy CMS 8.3.0.dev (Rails 8.1.3)\n\n## Description\n\nThe unauthenticated `GET /api/pages/nested` endpoint returns the full page tree to any anonymous caller, including restricted (member-only) pages and unpublished/draft pages that should be hidden.\nAppending `?elements=true` additionally dumps the element/ingredient **content** of restricted pages, fully bypassing the access control the sibling `show` and `index` actions enforce.\n\n## Root cause\n\n`Api::PagesController#nested` calls no `authorize!` and applies no `published`/`restricted` scoping, unlike `show` (`authorize! :show`) and `index` (`accessible_by(current_ability, :index)`).\n`PageTreePreloader` loads `page.self_and_descendants` unfiltered, and `PageTreeSerializer` emits every page's metadata (and, with `elements`, `public_version.elements`) with no ability check.\n\n## Evidence\n\nAn unauthenticated `GET /api/pages/nested` returns HTTP 200 with the restricted page (`\"restricted\":true`) and an unpublished draft (`\"public\":false`); `?elements=true` leaks its content (e.g. `TOPSECRET_RESTRICTED_BODY_proof123`).\nThe same guest hitting `GET /api/pages/3` (`show`) gets HTTP **403** `{\"error\":\"Not authorized\"}`, proving `nested` returns what `show` correctly denies.\n\n### Reproduction\n\n```bash\n# 1) Metadata leak (guest, no auth)\ncurl -s http://localhost:3000/api/pages/nested | python3 -m json.tool | grep -E '\"name\"|\"restricted\"|\"public\"'\n\n# 2) Content leak of restricted page\ncurl -s \"http://localhost:3000/api/pages/nested?elements=true\" | grep -oE 'TOPSECRET_RESTRICTED_BODY_[A-Za-z0-9]+|RESTRICTED_RICHTEXT_[A-Za-z0-9]+'\n\n# 3) Contrast — show denies the same guest\ncurl -s -o /dev/null -w \"show /api/pages/3 -> HTTP %{http_code}\\n\" http://localhost:3000/api/pages/3\n```\n\n### Suggested fix\n\n```ruby\ndef nested\n  @page = Page.find_by(id: params[:page_id]) || Language.current_root_page\n  authorize! :show, @page\n  preloaded_page = PageTreePreloader.new(page: @page, user: current_alchemy_user, ability: current_ability).call\n  render json: PageTreeSerializer.new(preloaded_page, ability: current_ability,\n                                      user: current_alchemy_user, elements: params[:elements])\nend\n```\n\nAdditionally scope `PageTreePreloader`'s `self_and_descendants` via `accessible_by(current_ability)` and gate element emission in `PageTreeSerializer#page_elements` behind `opts[:ability].can?(:show, page)`.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "RubyGems",
      "name" : "alchemy_cms"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "8.2.0"
      }, {
        "fixed" : "8.2.6"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 8.2.5"
    }
  }, {
    "package" : {
      "ecosystem" : "RubyGems",
      "name" : "alchemy_cms"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "8.1.0"
      }, {
        "fixed" : "8.1.14"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 8.1.13"
    }
  }, {
    "package" : {
      "ecosystem" : "RubyGems",
      "name" : "alchemy_cms"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "8.0.0.a"
      }, {
        "fixed" : "8.0.15"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 8.0.14"
    }
  }, {
    "package" : {
      "ecosystem" : "RubyGems",
      "name" : "alchemy_cms"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "7.4.15"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 7.4.14"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/AlchemyCMS/alchemy_cms/security/advisories/GHSA-mqq5-j7w8-2hgh"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/AlchemyCMS/alchemy_cms"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-862" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-06-19T17:47:13Z",
    "nvd_published_at" : null
  }
}