CVE-2025-49130
Description
Laravel Translation Manager is a package to manage Laravel translation files. Prior to version 0.6.8, the application is vulnerable to Cross-Site Scripting (XSS) attacks due to incorrect input validation and sanitization of user-input data. An attacker can inject arbitrary HTML code, including JavaScript scripts, into the page processed by the user's browser, allowing them to steal sensitive data, hijack user sessions, or conduct other malicious activities. Only authenticated users with access to the translation manager are impacted. The issue is fixed in version 0.6.8.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Laravel Translation Manager prior to 0.6.8 is vulnerable to stored XSS via unsanitized group/locale names, allowing authenticated attackers to execute arbitrary JavaScript.
Vulnerability
Overview Laravel Translation Manager, a package for managing Laravel translation files, is vulnerable to Cross-Site Scripting (XSS) in versions prior to 0.6.8. The root cause is insufficient input validation and sanitization of user-supplied data, specifically group and locale names, which are rendered in the web interface without proper escaping [1]. The commit that fixes the issue replaces raw echo statements with Laravel's e() helper, which HTML-encodes output [1].
Exploitation
Prerequisites An attacker must be an authenticated user with access to the translation manager's web interface [2]. The package provides a web-based editor for translation keys, and the vulnerable fields (group and locale) can be manipulated to inject arbitrary HTML or JavaScript [3]. No other special privileges or network position are required beyond authentication.
Impact
Successful exploitation allows the attacker to execute arbitrary JavaScript in the context of the victim's browser session. This can lead to theft of sensitive data, session hijacking, or other malicious actions performed on behalf of the authenticated user [4].
Mitigation
The vulnerability is fixed in version 0.6.8 of the package [1]. Users are advised to upgrade immediately. No workarounds are documented; the fix involves proper output escaping in the view templates.
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
barryvdh/laravel-translation-managerPackagist | < 0.6.8 | 0.6.8 |
Affected products
2v0.1.0, v0.1.1, v0.1.2, …+ 1 more
- (no CPE)range: v0.1.0, v0.1.1, v0.1.2, …
- (no CPE)range: <0.6.8
Patches
2527446ed419fFix group locale sanitization (#475)
2 files changed · +74 −57
resources/views/index.php+60 −54 modified@@ -1,3 +1,14 @@ +<?php +/** @see \Barryvdh\TranslationManager\Controller::getIndex() */ +/** @var array $translations */ +/** @var array|string[] $groups */ +/** @var array|string[] $locales */ +/** @var string $group */ +/** @var int $numTranslations */ +/** @var int $numChanged */ +/** @var string $editUrl */ +/** @var bool $deleteEnabled */ +?> <!DOCTYPE html> <html> <head> @@ -123,56 +134,51 @@ <p>Done searching for translations, found <strong class="counter">N</strong> items!</p> </div> <div class="alert alert-success success-publish" style="display:none;"> - <p>Done publishing the translations for group '<?php echo $group ?>'!</p> + <p>Done publishing the translations for group '<?php echo e($group) ?>'!</p> </div> <div class="alert alert-success success-publish-all" style="display:none;"> <p>Done publishing the translations for all group!</p> </div> - <?php if(Session::has('successPublish')) : ?> - <div class="alert alert-info"> - <?php echo Session::get('successPublish'); ?> - </div> - <?php endif; ?> <p> <?php if(!isset($group)) : ?> - <form class="form-import" method="POST" action="<?php echo action('\Barryvdh\TranslationManager\Controller@postImport') ?>" data-remote="true" role="form"> - <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>"> - <div class="form-group"> - <div class="row"> - <div class="col-sm-3"> - <select name="replace" class="form-control"> - <option value="0">Append new translations</option> - <option value="1">Replace existing translations</option> - </select> - </div> - <div class="col-sm-2"> + <form class="form-import" method="POST" action="<?php echo action('\Barryvdh\TranslationManager\Controller@postImport') ?>" data-remote="true" role="form"> + <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>"> + <div class="form-group"> + <div class="row"> + <div class="col-sm-3"> + <select name="replace" class="form-control"> + <option value="0">Append new translations</option> + <option value="1">Replace existing translations</option> + </select> + </div> + <div class="col-sm-2"> <button type="submit" class="btn btn-success btn-block" data-disable-with="Loading..">Import groups</button> - </div> </div> </div> + </div> + </form> + <form class="form-find" method="POST" action="<?php echo action('\Barryvdh\TranslationManager\Controller@postFind') ?>" data-remote="true" role="form" data-confirm="Are you sure you want to scan you app folder? All found translation keys will be added to the database."> + <div class="form-group"> + <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>"> + <button type="submit" class="btn btn-info" data-disable-with="Searching.." >Find translations in files</button> + </div> + </form> + <?php endif; ?> + <?php if(isset($group)) : ?> + <form class="form-inline form-publish" method="POST" action="<?php echo action('\Barryvdh\TranslationManager\Controller@postPublish', $group) ?>" data-remote="true" role="form" data-confirm="Are you sure you want to publish the translations group '<?php echo e($group) ?>? This will overwrite existing language files."> + <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>"> + <button type="submit" class="btn btn-info" data-disable-with="Publishing.." >Publish translations</button> + <a href="<?= action('\Barryvdh\TranslationManager\Controller@getIndex') ?>" class="btn btn-default">Back</a> </form> - <form class="form-find" method="POST" action="<?php echo action('\Barryvdh\TranslationManager\Controller@postFind') ?>" data-remote="true" role="form" data-confirm="Are you sure you want to scan you app folder? All found translation keys will be added to the database."> - <div class="form-group"> - <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>"> - <button type="submit" class="btn btn-info" data-disable-with="Searching.." >Find translations in files</button> - </div> - </form> - <?php endif; ?> - <?php if(isset($group)) : ?> - <form class="form-inline form-publish" method="POST" action="<?php echo action('\Barryvdh\TranslationManager\Controller@postPublish', $group) ?>" data-remote="true" role="form" data-confirm="Are you sure you want to publish the translations group '<?php echo $group ?>? This will overwrite existing language files."> - <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>"> - <button type="submit" class="btn btn-info" data-disable-with="Publishing.." >Publish translations</button> - <a href="<?= action('\Barryvdh\TranslationManager\Controller@getIndex') ?>" class="btn btn-default">Back</a> - </form> - <?php endif; ?> + <?php endif; ?> </p> <form role="form" method="POST" action="<?php echo action('\Barryvdh\TranslationManager\Controller@postAddGroup') ?>"> <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>"> <div class="form-group"> <p>Choose a group to display the group translations. If no groups are visisble, make sure you have run the migrations and imported the translations.</p> <select name="group" id="group" class="form-control group-select"> <?php foreach($groups as $key => $value): ?> - <option value="<?php echo $key ?>"<?php echo $key == $group ? ' selected':'' ?>><?php echo $value ?></option> + <option value="<?php echo e($key) ?>"<?php echo $key == $group ? ' selected':'' ?>><?php echo e($value) ?></option> <?php endforeach; ?> </select> </div> @@ -208,7 +214,7 @@ <label for="base-locale">Base Locale for Auto Translations</label> <select name="base-locale" id="base-locale" class="form-control"> <?php foreach ($locales as $locale): ?> - <option value="<?= $locale ?>"><?= $locale ?></option> + <option value="<?= e($locale) ?>"><?= e($locale) ?></option> <?php endforeach; ?> </select> </div> @@ -230,13 +236,13 @@ </div> </form> <hr> - <h4>Total: <?= $numTranslations ?>, changed: <?= $numChanged ?></h4> + <h4>Total: <?= $numTranslations ?>, changed: <?= $numChanged ?></h4> <table class="table"> <thead style="position: sticky; top: 0; background: #fff;"> <tr> <th width="15%">Key</th> <?php foreach ($locales as $locale): ?> - <th><?= $locale ?></th> + <th><?= e($locale) ?></th> <?php endforeach; ?> <?php if ($deleteEnabled): ?> <th> </th> @@ -246,26 +252,26 @@ <tbody> <?php foreach ($translations as $key => $translation): ?> - <tr id="<?php echo htmlentities($key, ENT_QUOTES, 'UTF-8', false) ?>"> - <td><?php echo htmlentities($key, ENT_QUOTES, 'UTF-8', false) ?></td> + <tr id="<?php echo e($key) ?>"> + <td><?php echo e($key) ?></td> <?php foreach ($locales as $locale): ?> <?php $t = isset($translation[$locale]) ? $translation[$locale] : null ?> <td> <a href="#edit" - class="editable status-<?php echo $t ? $t->status : 0 ?> locale-<?php echo $locale ?>" - data-locale="<?php echo $locale ?>" data-name="<?php echo $locale . "|" . htmlentities($key, ENT_QUOTES, 'UTF-8', false) ?>" - id="username" data-type="textarea" data-pk="<?php echo $t ? $t->id : 0 ?>" + class="editable status-<?php echo $t ? (int) $t->status : 0 ?> locale-<?php echo e($locale) ?>" + data-locale="<?php echo e($locale) ?>" data-name="<?php echo e($locale) . "|" . e($key) ?>" + id="username" data-type="textarea" data-pk="<?php echo $t ? (int) $t->id : 0 ?>" data-url="<?php echo $editUrl ?>" - data-title="Enter translation"><?php echo $t ? htmlentities($t->value, ENT_QUOTES, 'UTF-8', false) : '' ?></a> + data-title="Enter translation"><?php echo $t ? e($t->value, false) : '' ?></a> </td> <?php endforeach; ?> <?php if ($deleteEnabled): ?> <td> <a href="<?php echo action('\Barryvdh\TranslationManager\Controller@postDelete', [$group, $key]) ?>" class="delete-key" - data-confirm="Are you sure you want to delete the translations for '<?php echo htmlentities($key, ENT_QUOTES, 'UTF-8', false) ?>?"><span - class="glyphicon glyphicon-trash"></span></a> + data-confirm="Are you sure you want to delete the translations for '<?php echo e($key) ?>?"><span + class="glyphicon glyphicon-trash"></span></a> </td> <?php endif; ?> </tr> @@ -281,17 +287,17 @@ class="glyphicon glyphicon-trash"></span></a> <form class="form-remove-locale" method="POST" role="form" action="<?php echo action('\Barryvdh\TranslationManager\Controller@postRemoveLocale') ?>" data-confirm="Are you sure to remove this locale and all of data?"> <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>"> <ul class="list-locales"> - <?php foreach($locales as $locale): ?> - <li> - <div class="form-group"> - <button type="submit" name="remove-locale[<?php echo $locale ?>]" class="btn btn-danger btn-xs" data-disable-with="..."> - × - </button> - <?php echo $locale ?> + <?php foreach($locales as $locale): ?> + <li> + <div class="form-group"> + <button type="submit" name="remove-locale[<?php echo e($locale) ?>]" class="btn btn-danger btn-xs" data-disable-with="..."> + × + </button> + <?php echo e($locale) ?> - </div> - </li> - <?php endforeach; ?> + </div> + </li> + <?php endforeach; ?> </ul> </form> <form class="form-add-locale" method="POST" role="form" action="<?php echo action('\Barryvdh\TranslationManager\Controller@postAddLocale') ?>">
src/Controller.php+14 −3 modified@@ -48,7 +48,7 @@ public function getIndex($group = null) ->with('numTranslations', $numTranslations) ->with('numChanged', $numChanged) ->with('editUrl', $group ? action('\Barryvdh\TranslationManager\Controller@postEdit', [$group]) : null) - ->with('deleteEnabled', $this->manager->getConfig('delete_enabled')); + ->with('deleteEnabled', (bool) $this->manager->getConfig('delete_enabled')); } public function getView($group = null) @@ -141,7 +141,7 @@ public function postPublish($group = null) public function postAddGroup(Request $request) { - $group = str_replace(".", '', $request->input('new-group')); + $group = $this->sanitizeFilename($request->input('new-group')); if ($group) { return redirect()->action('\Barryvdh\TranslationManager\Controller@getView',$group); @@ -155,7 +155,7 @@ public function postAddGroup(Request $request) public function postAddLocale(Request $request) { $locales = $this->manager->getLocales(); - $newLocale = str_replace([], '-', trim($request->input('new-locale'))); + $newLocale = $this->sanitizeFilename($request->input('new-locale')); if (!$newLocale || in_array($newLocale, $locales)) { return redirect()->back(); } @@ -200,4 +200,15 @@ public function postTranslateMissing(Request $request){ } return redirect()->back(); } + + protected function sanitizeFilename($input) + { + $input = Str::ascii(trim($input)); + return str_replace( + ['<', '>', '%', '/', '\\', '.', ' ', '"', "'", '#', '?'], + '', + $input + ); + + } }
fe182d8b6e99Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-j226-63j7-qrqhghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-49130ghsaADVISORY
- github.com/barryvdh/laravel-translation-manager/commit/527446ed419f90f2319675fc5211cb8f851d7a1fnvdWEB
- github.com/barryvdh/laravel-translation-manager/pull/475nvdWEB
- github.com/barryvdh/laravel-translation-manager/releases/tag/v0.6.8nvdWEB
- github.com/barryvdh/laravel-translation-manager/security/advisories/GHSA-j226-63j7-qrqhnvdWEB
News mentions
0No linked articles in our index yet.