CVE-2026-116199: The Snipe-IT Login Door That Forgot to Check if You Were Fired
The short version
Snipe-IT is an open source IT asset tracker that supports several ways to log in: a local password, LDAP, SAML, a remote-user header, and Google single sign-on. Every one of those paths checked whether the account was still active before letting you in, with one exception. The Google sign-in path didn’t. So a user who had been deactivated in Snipe-IT, the step you take when someone leaves or is removed, could still log back in through Google and land right back at their old permission level, up to and including admin, as long as their Google account hadn’t been shut off yet.
This is fixed now. It is tracked as CVE-2026-116199 (Moderate).
Some background
When someone leaves an organization, offboarding usually happens in stages. Often HR or an IT admin deactivates the person’s accounts in individual tools first, and the central identity, their Google Workspace account, gets revoked later, sometimes by a different team on a different schedule. The gap between those two moments is a well-known risk window, and it is exactly why every application is supposed to independently enforce its own “is this account still active?” check rather than assuming the identity provider has already cut access off.
Snipe-IT marks a disabled account with an activated flag set to zero. The rule is simple: no active flag, no login.
What went wrong
Four of Snipe-IT’s five login paths looked up the user with the active-account filter baked into the query. If the account was deactivated, the lookup returned nothing and the login was refused. Clean and correct.
The Google path looked the user up by email alone, with no active-account filter, and logged in whatever it found:
$user = User::where('username', $socialUser->getEmail())->first();
// ^ no ->where('activated', '=', '1')
if ($user) {
Auth::login($user, true); // logs in even if the account was deactivated
return redirect()->route('home');
}
For contrast, here is the LDAP path’s lookup, carrying the filter the Google path was missing:
$user = User::where('username', '=', $request->input('username'))
->whereNull('deleted_at')
->where('activated', '=', '1') // the check the Google path never made
->first();
So a deactivated user whose Google identity was still live could complete a Google sign-in and get an authenticated session at their original permission level. The deactivation step in the offboarding runbook silently failed to actually revoke Snipe-IT access.
One important narrowing condition: the Google lookup matched on username, and it compared against the Google email address. It only found a user when the account’s username was email-shaped. The default Snipe-IT install uses a firstname.lastname username format, and under that default the lookup returned nothing and the bug did not trigger. It bit deployments that use email as the username.
If you want the technical framing: this is improper authorization (CWE-285) and improper authentication (CWE-287). The authorization decision, is this account allowed to have a session at all, was simply not made on this one path.
How I found it and proved it
I do this kind of research in an isolated lab, never against a live company’s data. A full Google OAuth round-trip wasn’t needed to demonstrate the flaw, because it lives entirely in the lookup-then-login pair against a deactivated record. I seeded a victim account with an email-shaped username, the activated flag set to zero, and admin permissions, the worst case for an offboarding scenario, then exercised each path’s lookup against it:
Google path lookup: FOUND id=8 activated=0 -> Auth::login succeeds
LDAP / SAML / local path: NOT FOUND -> correctly rejected
Same deactivated account, same input. The Google path returned the row and authenticated it. Every other path’s lookup returned nothing.
Why this matters
Deactivating a departed employee’s account is supposed to mean they are out. This bug meant that, for a deployment using email usernames with Google SSO, “out” held only once Google itself had also revoked the person, and the window between the two could be days. During that window, a removed user, including one who had been an admin, could walk back in through the Google button and resume full access. And the symptom reads like a process failure (“why can the deactivated user still log in?”) rather than an authentication-control bug, which makes it easy to misdiagnose and slow to catch.
Timeline
I reported this to Snipe-IT’s maintainer, Grokability, on May 2, 2026, as part of a batch of findings against version 8.4.1. It was fixed in version 8.6.3. It is documented publicly as GHSA-xf36-wx48-5rqp, assigned CVE-2026-116199.
The fix
The fix adds the active-account check to the Google path, so it matches every other login path:
$user = User::where('username', $socialUser->getEmail())
->where('activated', '=', '1') // now enforced, like SAML / LDAP / remote-user / local
->whereNull('deleted_at')
->first();
Now a deactivated account is refused on the Google path too, and the controller redirects with an “account not activated” message instead of starting a session.
Takeaway
When the same security decision has to be made in several places, the real risk is the one place that forgets. Five login paths, four of them correct, is still a bypass if the fifth skips the check. The durable defense is to not duplicate the decision at all: push the active-account filter into a single shared lookup that every authentication path is forced to call, so a future sixth path cannot quietly omit it.
Reported by Brayden Arnold (@builtbybrayden). Snipe-IT is open source at github.com/grokability/snipe-it.