Spot the bug - #102: Password Strength Check

Password checker keeps approving passwords with no digits, help.

function validatePassword(pw) {
  const rules = [
    { test: /.{8,}/, msg: "At least 8 characters" },
    { test: /[A-Z]/, msg: "One uppercase letter" },
    { test: /[a-z]/, msg: "One lowercase letter" },
    { test: /[0-9]/g, msg: "One digit" },
    { test: /[!@#$%^&*]/, msg: "One special character" }
  ];

  const failed = rules.filter(rule => !rule.test.test(pw));
  return failed.length === 0
    ? "Password is strong!"
    : "Missing: " + failed.map(r => r.msg).join(", ");
}

console.log(validatePassword("Abcdefgh1!"));
console.log(validatePassword("Abcdefgh1!"));

Reply with what is broken and how you would fix it.

Something something something regex? :stuck_out_tongue:

Haha yeah, close. It’s the g flag on that regex, /[0-9]/g. . test() on a global regex keeps lastIndex state between calls, so it alternates true/false/true/false on repeated calls with the same input. That’s why two identical console. log calls disagree. Drop the g, problem gone.

Spot the Bug answer: The digit rule regex uses the global ‘g’ flag, so RegExp.test() keeps its lastIndex between calls and on repeated calls with matching strings it alternately returns false, causing passwords with digits to sometimes fail or later be wrongly approved for strings without digits since state gets out of sync.

The fix:
Remove the g flag: { test: /[0-9]/, msg: “One digit” }

Why:
Regex objects with the global flag are stateful: each call to test() advances lastIndex, so calling the same regex.test(pw) multiple times (as happens across the two console.log calls) toggles the result instead of re-evaluating from the start. Removing the g flag makes test() stateless and reliable.