Skip to content

🚨 [security] [ruby] Update pagy 43.2.3 → 43.6.1 (minor) - #142

Open
depfu[bot] wants to merge 1 commit into
mainfrom
depfu/update/pagy-43.6.1
Open

🚨 [security] [ruby] Update pagy 43.2.3 → 43.6.1 (minor)#142
depfu[bot] wants to merge 1 commit into
mainfrom
depfu/update/pagy-43.6.1

Conversation

@depfu

@depfu depfu Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

🚨 Your current dependencies have known security vulnerabilities 🚨

This dependency update fixes known security vulnerabilities. Please see the details below and assess their impact carefully. We recommend to merge and deploy this as soon as possible!


Here is everything you need to know about this update. Please take a good look at what changed and the test results before merging this pull request.

What changed?

✳️ pagy (43.2.3 → 43.6.1) · Repo · Changelog

Security Advisories 🚨

🚨 Pagy I18n locale option is not validated before being used in a file path

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-existence / readability oracle for .yml paths, and the targeted
    file is read into the process during the attempt.

Impact

Information disclosure (CWE-22 / CWE-200): a file-existence / readability
oracle for .yml paths on the host, plus a server-side read of
attacker-chosen files into the process. The file contents are not
returned in the response.

Only applications that pass unsanitized end-user input into
Pagy::I18n.locale= are affected. Applications that set the locale from
trusted values are not affected.

Patched: pagy 43.5.6.
Workaround (if you cannot upgrade): validate the locale before
assigning it, e.g.
Pagy::I18n.locale = params[:locale].to_s[/\A[a-zA-Z]{2,8}(-[a-zA-Z0-9]{1,8})*\z/],
or restrict it to your known set of locales.

Release Notes

Too many releases to show here. View the full release notes.

Commits

See the full diff on Github. The new version differs by more commits than we can show here.


Depfu Status

Depfu will automatically keep this PR conflict-free, as long as you don't add any commits to this branch yourself. You can also trigger a rebase manually by commenting with @depfu rebase.

All Depfu comment commands
@​depfu rebase
Rebases against your default branch and redoes this update
@​depfu recreate
Recreates this PR, overwriting any edits that you've made to it
@​depfu merge
Merges this PR once your tests are passing and conflicts are resolved
@​depfu cancel merge
Cancels automatic merging of this PR
@​depfu close
Closes this PR and deletes the branch
@​depfu reopen
Restores the branch and reopens this PR (if it's closed)
@​depfu pause
Ignores all future updates for this dependency and closes this PR
@​depfu pause [minor|major]
Ignores all future minor/major updates for this dependency and closes this PR
@​depfu resume
Future versions of this dependency will create PRs again (leaves this PR as is)

@depfu depfu Bot added the depfu label Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants