{"id":"CVE-2026-57579","aliases":["GHSA-mqq5-j7w8-2hgh"],"url":"https://o3.security/vulnerability/CVE-2026-57579","summary":"Alchemy: 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)`.","published":"2026-09-14T16:54:19.932Z","modified":"2026-10-28T02:31:04.085707097Z","cvss":{"score":7.5,"severity":"HIGH","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"},"epss":null,"cisaKev":null,"exploitsKnown":null,"affectedPackages":[{"ecosystem":"RubyGems","name":"alchemy_cms","fixedVersion":"8.2.6"},{"ecosystem":"RubyGems","name":"alchemy_cms","fixedVersion":"8.1.14"},{"ecosystem":"RubyGems","name":"alchemy_cms","fixedVersion":"8.0.15"},{"ecosystem":"RubyGems","name":"alchemy_cms","fixedVersion":"7.4.15"}],"fix":{"url":"https://github.com/AlchemyCMS/alchemy_cms/commit/30888fb41d8b71fdfb3b116a7efccd851d73e1f3","label":"AlchemyCMS/alchemy_cms@30888fb"},"references":[{"type":"WEB","url":"https://github.com/AlchemyCMS/alchemy_cms/releases/tag/v7.4.15"},{"type":"WEB","url":"https://github.com/AlchemyCMS/alchemy_cms/releases/tag/v8.0.15"},{"type":"WEB","url":"https://github.com/AlchemyCMS/alchemy_cms/releases/tag/v8.1.14"},{"type":"WEB","url":"https://github.com/AlchemyCMS/alchemy_cms/releases/tag/v8.2.6"},{"type":"ADVISORY","url":"https://github.com/AlchemyCMS/alchemy_cms/security/advisories/GHSA-mqq5-j7w8-2hgh"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/57xxx/CVE-2026-57579.json"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-57579"},{"type":"FIX","url":"https://github.com/AlchemyCMS/alchemy_cms/commit/30888fb41d8b71fdfb3b116a7efccd851d73e1f3"},{"type":"FIX","url":"https://github.com/AlchemyCMS/alchemy_cms/commit/5620e76f0d274d6f69f34281b7e46d01879f6b99"},{"type":"FIX","url":"https://github.com/AlchemyCMS/alchemy_cms/commit/8417a2e9dcb00653c8b238f8a040b09532dd4c0f"},{"type":"FIX","url":"https://github.com/AlchemyCMS/alchemy_cms/commit/85702d9fe4e247649b1358900c5af853e83b4a3a"},{"type":"FIX","url":"https://github.com/AlchemyCMS/alchemy_cms/commit/a082dd0afe188d199f386a5e1db01987d0de807f"},{"type":"FIX","url":"https://github.com/AlchemyCMS/alchemy_cms/pull/3982"},{"type":"PACKAGE","url":"https://github.com/AlchemyCMS/alchemy_cms"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-28T02:31:04.085707097Z"}}