CSIRTS // UNIFIED SECURITY ADVISORY FEEDSYS ● ONLINE · POWERED BY INTELFUSIONS.COM

GHSA-2xmw-f8j8-wfxc: Pagy I18n locale option is not validated before being used in a file path

mediumCVE-2026-54659
Summary Pagy::I18n.locale= did not validate its argument before using it as a path component to load the matching dictionary file (<locale>.yml). An application that assigns untrusted input to the locale — e.g. the common pattern Pagy::I18n.locale = params[:locale] — let that input influence which file Pagy attempted to load. Details The setter stored the value as-is, and the loader joined it into a path and read it: gem/lib/pagy/modules/i18n/i18n.rb def locale=(value) Thread.current[:pagy_locale] = value.to_s end ...later, when translating: path = pathnames.reverse.map { |p| p.join("#{locale}.yml") }.find(&:exist?) dictionary = YAML.load_file(path)[locale] Because the locale was used verbatim, a value such as an absolute path or a ../-style string redirected the lookup outside the locales directory. Pagy's subsequent structural check (dictionary['pagy']['p11n']) prevents the file's contents from being returned, so this is not a direct file read. Fixed in 43.5.6 by constraining the locale to a BCP 47 shape before use: LOCALE_PATTERN = /\A[a-zA-Z]{2,8}(-[a-zA-Z0-9]{1,8})*\z/ def locale=(value) Thread.current[:pagy_locale] = value.to_s[LOCALE_PATTERN] end Any non-matching value (including nil) resolves to the default locale and never reaches the file lookup. PoC In an application that sets Pagy::I18n.locale = params[:locale], the loader appends .yml and reads <locale>.yml, so the request param controls the target path. For example, pointing it at the app's config/database.yml: 1. Send a request with ?locale=../../../config/database (adjust the number of ../ to reach the app root from the gem's locales/ directory). 2. Pagy calls YAML.load_file on the resulting …/config/database.yml. 3. The outcome differs by whether that .yml exists, is readable, parses as YAML, and has Pagy's expected structure — an existing, readable config/database.yml raises a different error than a non-existent path (which silently falls back to the default locale). This yields a file

Details

Source
GitHub Security Advisories (INTL · database · site)
Severity
medium
Published
2026-07-28
Last updated
2026-07-28
Exploitation
Not in CISA KEV at last sync

Original advisory: https://github.com/advisories/GHSA-2xmw-f8j8-wfxc

Exploitation outlook

EPSS (FIRST.org) estimates each CVE’s probability of exploitation in the next 30 days — here is the CSIRTS.com read on those numbers.

Referenced CVEs

CVECSIRTS overviewExternal
CVE-2026-54659coverage & exploitation statusNVD · CVE.org

Same CVEs, other sources

How other CERTs, PSIRTs and databases cover the vulnerabilities in this advisory.

More from GitHub Security Advisories