criticalLogins, sessions and access

Password Stored or Compared in Plain Text

Passwords are saved or checked as plain text.

Why it matters

If the database is ever copied, dumped by a bug, or read through one of the other issues in this scan, every user's real password is exposed, including the ones they reuse for email and banking. Hashing means a leak gives the attacker nothing usable. Comparing plain text on login also means the password had to be stored plain in the first place.

What it looks like

This is the shape of code that triggers the rule. AI tools produce it because it works, and nothing tells them it is unsafe.

Password saved straight from the request
app.post('/signup', async (req, res) => {
  await User.create({ email: req.body.email, password: req.body.password });
  res.json({ ok: true });
});

The smallest fix

minimal patch
import bcrypt from 'bcrypt';

// Signup: store a hash, never the password
const passwordHash = await bcrypt.hash(req.body.password, 12);
await User.create({ email, passwordHash });

// Login: compare with the library, never with ===
const user = await User.findOne({ email });
const ok = user && (await bcrypt.compare(req.body.password, user.passwordHash));
if (!ok) return res.status(401).json({ error: 'Invalid email or password' });

The better fix

If you have a few more minutes, this is the approach that holds up as the app grows.

safe alternative
// Let an auth provider own passwords entirely: Supabase Auth, Clerk,
// Auth0 or NextAuth. Your database then never contains a password column.

Let your AI tool fix it

When the scanner finds this in your project, it fills in the file and line for you. This is the prompt it gives you to paste into Claude Code, Cursor or whatever you use.

In [the file] at line [the line number], a password is stored or compared in plain text: [the flagged code]. Hash passwords with bcrypt (cost 12) or argon2 on signup, store only the hash, and use the library's compare function on login. Return the same generic error for a wrong email and a wrong password. Add a migration plan for existing plain-text rows.

How to check the fix worked

1. Create an account, then look at the row in the database: the password
   column must be a hash, not the typed value.
2. Log in with the right password: success. With a wrong one: 401 and the
   same message as an unknown email.

Further reading