CVE-2026-49976 is a medium-severity (CVSS 6.5) CWE-863 vulnerability in snipe/snipe-it. A fix is available for snipe/snipe-it — see the affected versions and patch details below.
Snipe-IT: User Account Escalation via CSV Import
Exploitation Status
No confirmed exploitation observed yet
- A successful exploit gives an attacker total control of the affected component, not partial access.
- CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-49976.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
Where this sits among everything scored
Real-World Exposure
snipe/snipe-itReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.
Description
Impact
The CSV user import in update mode bypasses user-edit authorization. A user with only the import permission can overwrite any non-admin user's email by uploading a CSV, then trigger a password reset to take over the account.
UserImporter.php checks the canEditAuthFields gate and tries to strip auth fields from the model:
// app/Importer/UserImporter.php:107-114
if (Auth::check() && (! Gate::allows('canEditAuthFields', $user))) {
unset($user->username);
unset($user->email);
unset($user->password);
unset($user->activated);
}
$user->update($this->sanitizeItemForUpdating($user));
The unset()s operate on the model, but sanitizeItemForUpdating() rebuilds its array from $this->item (the raw CSV row), not from the model:
// app/Importer/ItemImporter.php:135-149
protected function sanitizeItemForStoring($model, $updating = false)
{
$item = collect($this->item); // CSV data, not model attributes
$item = $item->only($model->getFillable());
if ($updating) {
$item = $item->reject(fn($v) => empty($v));
}
return $item->toArray();
}
The attacker's CSV values pass through untouched.
For non-admin attacker vs. non-admin, non-superuser target, the gate returns true at AuthServiceProvider.php:137, so the unset() block never executes. The entire import path checks only $this->authorize('import') (ImportController.php:196); no users.edit check anywhere. The normal API route PATCH /api/v1/users/{id} correctly returns 403 for the same user.
Attacker must have import privileges to exploit this, and that permission must be granted specifically and intentionally by a superadmin.
Patches
Patched in v8.6.0
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐘Packagist | snipe/snipe-it | all versions | 8.6.0composer require snipe/snipe-it:^8.6.0 |
Affected Products
snipe-itsnipeitappDetection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for snipe/snipe-it, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update snipe/snipe-it to 8.6.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-49976 is resolved across your whole dependency graph.
Workarounds
Put an independent control in front of the weakness: restrict the affected endpoint or interface to trusted networks, require an additional authentication factor or proxy-level check, and invalidate existing sessions and credentials in case the flaw has already been used.
Frequently Asked Questions
Is CVE-2026-49976 in your dependencies?
Find it across Packagist, including transitive dependencies.