Skip to content

fix(#265): use invalid_credentials translation key for auth failures#270

Merged
DogukanUrker merged 2 commits into
mainfrom
fix/265-observable-response-discrepancy
Jun 27, 2026
Merged

fix(#265): use invalid_credentials translation key for auth failures#270
DogukanUrker merged 2 commits into
mainfrom
fix/265-observable-response-discrepancy

Conversation

@DogukanUrker

@DogukanUrker DogukanUrker commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Fix for #265: CWE-204 - Observable response discrepancy

What Changed

  • : Replaced 'password' with 'invalid_credentials' translation key
  • : Replaced 'not_found' and 'wrong' with 'invalid_credentials' translation key
  • All 13 translation files: Added 'invalid_credentials' key with localized text

Files Changed

Deliberately Left Untouched

  • Password reset code generation
  • Email sending logic
  • Password hash verification
  • Other flash messages (success, match, code, same)

Notes

  • This PR was opened as DRAFT for human review.
  • Local E2E tests could not run (no browser available).
  • All changed files pass py_compile verification.

Closes #265

Summary by CodeRabbit

  • Bug Fixes
    • Standardized authentication and password reset error flashes so invalid sign-in or reset details consistently display an “invalid credentials” message instead of more specific variants.
    • Updated localized text across supported languages to keep login and password reset feedback consistent and clearer when credentials/code are incorrect.

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@DogukanUrker, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 9 minutes and 46 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 26851122-ccef-4283-8fb2-cb65836e1835

📥 Commits

Reviewing files that changed from the base of the PR and between 1b0940d and 2ec426f.

📒 Files selected for processing (13)
  • app/routes/password_reset.py
  • app/translations/de.json
  • app/translations/en.json
  • app/translations/es.json
  • app/translations/fr.json
  • app/translations/hi.json
  • app/translations/ja.json
  • app/translations/pl.json
  • app/translations/pt.json
  • app/translations/ru.json
  • app/translations/tr.json
  • app/translations/uk.json
  • app/translations/zh.json
📝 Walkthrough

Walkthrough

Login and password reset now use invalid_credentials for authentication failures. All 13 locale files add matching translations under flash.login, flash.password_reset, and at the top level.

Changes

CWE-204 Fix: Unified Auth Error Messaging

Layer / File(s) Summary
Route flash message key unification
app/routes/login.py, app/routes/password_reset.py
Login and password reset failure branches now emit message="invalid_credentials" instead of distinct error keys.
Locale entries for invalid_credentials
app/translations/en.json, app/translations/de.json, app/translations/es.json, app/translations/fr.json, app/translations/hi.json, app/translations/ja.json, app/translations/pl.json, app/translations/pt.json, app/translations/ru.json, app/translations/tr.json, app/translations/uk.json, app/translations/zh.json
Adds invalid_credentials to flash.login, flash.password_reset, and as a top-level key in all locale files.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 Two paths once whispered different tales,
Now one clear message gently prevails.
Invalid creds, one hop, one sign,
Across the tongues, the keys align.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main auth-failure translation change and references the issue.
Description check ✅ Passed The description covers the issue, main changes, untouched areas, and verification notes, even if it doesn't match the template exactly.
Linked Issues check ✅ Passed The changes implement identical invalid-credential responses for login and password reset as required by #265.
Out of Scope Changes check ✅ Passed The PR stays focused on auth error messaging and translation updates without unrelated functional changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/265-observable-response-discrepancy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@DogukanUrker DogukanUrker marked this pull request as ready for review June 27, 2026 20:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
app/translations/uk.json (1)

250-264: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Localize the new flash strings.

At Lines 251 and 264 these entries are still English, while the helper flashes the nested translation value directly. Ukrainian users will get mixed-language auth errors here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/translations/uk.json` around lines 250 - 264, The new auth flash strings
in uk.json still contain English text, so Ukrainian users will see
mixed-language messages. Update the two invalid_credentials entries in the
translation object used by the auth/password reset flows to a proper Ukrainian
equivalent, keeping the key names unchanged so the helper that reads the nested
translation value continues to work.
app/translations/zh.json (1)

249-263: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Localize the new flash strings.

At Lines 250 and 263 these new values are still English, and the flash helper renders them as-is. Chinese users will see English auth errors in the updated flows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/translations/zh.json` around lines 249 - 263, The new flash strings in
the zh translation file are still hardcoded in English, so localize the
remaining auth error values used by the flash helper. Update the untranslated
entries in the translation JSON so the logout/password reset flows display
Chinese text consistently, and keep the existing key names and surrounding
structure intact.
app/translations/ru.json (1)

250-264: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Localize the new flash strings.

At Lines 251 and 264 these new values are still English, and flash_message() renders flash.<page>.<message> directly. Russian users will now see English auth errors on those paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/translations/ru.json` around lines 250 - 264, The new flash message
entries in ru.json still use English text for the auth errors, so update the
translation values for the relevant keys under the login/password_reset sections
to Russian. Make sure the strings returned by flash_message() for
invalid_credentials (and any other newly added flash.<page>.<message> keys in
this file) are fully localized, using the existing auth translation keys
structure as the guide.
app/translations/tr.json (1)

250-264: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Localize the new flash strings.

At Lines 251 and 264 these new values are still English, and the route helper flashes them verbatim. Turkish users will see English auth errors in the updated login and password-reset flows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/translations/tr.json` around lines 250 - 264, The new flash strings are
still hardcoded in English for the Turkish locale, so update the translated
values for the auth messages in the translation object and keep them consistent
with the other Turkish entries. Specifically, replace the English text used for
the shared invalid_credentials key in both the login and password_reset sections
of tr.json so the route helper flashes localized Turkish messages instead of
verbatim English.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/routes/password_reset.py`:
- Around line 170-176: The password_reset flow still reveals account existence
by taking different outward paths in the user-not-found branch versus the
user-found branch. Update the password reset handler in password_reset.py so the
branch around the user lookup in the reset request step always returns the same
visible response (same flash/redirect/page outcome) regardless of whether a user
was found, and only conditionally performs the email send internally in the
user-handling logic. Use the existing password_reset route logic, the user
lookup check, flash_message, and mail-sending code to keep the response uniform
and prevent enumeration.

In `@app/translations/de.json`:
- Line 251: Localize the new auth flash strings in the German translation file:
the entries at the two referenced keys are still English literals, so replace
them with German text to match the rest of the locale. Keep the namespaced keys
used by app/routes/login.py and app/routes/password_reset.py unchanged, and
align the wording with the existing German invalid_credentials entry already
present later in this file.

In `@app/translations/es.json`:
- Line 251: The new auth flash messages in es.json are still left in English, so
Spanish users will see untranslated login and password-reset errors. Update the
locale entries for the auth keys used by the login and password-reset routes,
especially invalid_credentials and the other affected key around the
password-reset message, so they match the Spanish text already added elsewhere
in the file.

In `@app/translations/fr.json`:
- Line 251: The auth flash messages in the French translations are still using
English values outside the flash namespace, so the login and password-reset
routes won’t pick up localized text. Update the `invalid_credentials` entries in
`fr.json` under `flash.*` to use the existing French translation already present
in the file, and make sure both auth lookups resolve to those `flash` keys
consistently.

In `@app/translations/hi.json`:
- Line 251: The Hindi auth flash messages are still using English text for the
keys resolved by the login/reset flows, so update the localized values in the
translation entries referenced by the auth routes to use Hindi copy instead.
Check the relevant keys in the Hindi translation map, including the one for
invalid_credentials and the other auth message at the nearby key used by the
reset flow, and replace the English strings with the existing Hindi equivalents
already present in the file.

In `@app/translations/ja.json`:
- Line 251: The auth flash strings in the Japanese translation map are still in
English, so update the active lookup entries in ja.json for the failed
login/password-reset messages to use the localized Japanese text already defined
elsewhere. Keep the values for invalid_credentials and the related
password-reset key in sync with the existing Japanese translation entry so the
auth flow returns localized copy.

In `@app/translations/pl.json`:
- Line 251: The new flash.*.invalid_credentials strings in the Polish
translation file are still in English, so update the corresponding entries used
by the login and password-reset handlers to proper Polish text. Make sure the
keys at the locations referenced by the auth flows match the existing localized
wording already present elsewhere in the file, and keep the translation
consistent across both invalid-credentials messages.

In `@app/translations/pt.json`:
- Line 251: Update the flashed auth message values in pt.json so the auth routes
use the Portuguese strings instead of English. Replace the values for the
affected auth keys referenced by invalid_credentials and the reset/login message
entries used by the updated auth flows, and align them with the existing
localized Portuguese text already present in this translation file. Keep the
same keys and only change the message values so the flashed authentication
errors are fully localized for Portuguese users.

---

Outside diff comments:
In `@app/translations/ru.json`:
- Around line 250-264: The new flash message entries in ru.json still use
English text for the auth errors, so update the translation values for the
relevant keys under the login/password_reset sections to Russian. Make sure the
strings returned by flash_message() for invalid_credentials (and any other newly
added flash.<page>.<message> keys in this file) are fully localized, using the
existing auth translation keys structure as the guide.

In `@app/translations/tr.json`:
- Around line 250-264: The new flash strings are still hardcoded in English for
the Turkish locale, so update the translated values for the auth messages in the
translation object and keep them consistent with the other Turkish entries.
Specifically, replace the English text used for the shared invalid_credentials
key in both the login and password_reset sections of tr.json so the route helper
flashes localized Turkish messages instead of verbatim English.

In `@app/translations/uk.json`:
- Around line 250-264: The new auth flash strings in uk.json still contain
English text, so Ukrainian users will see mixed-language messages. Update the
two invalid_credentials entries in the translation object used by the
auth/password reset flows to a proper Ukrainian equivalent, keeping the key
names unchanged so the helper that reads the nested translation value continues
to work.

In `@app/translations/zh.json`:
- Around line 249-263: The new flash strings in the zh translation file are
still hardcoded in English, so localize the remaining auth error values used by
the flash helper. Update the untranslated entries in the translation JSON so the
logout/password reset flows display Chinese text consistently, and keep the
existing key names and surrounding structure intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06c010a6-0251-4e6b-813f-3b0592d25c82

📥 Commits

Reviewing files that changed from the base of the PR and between 9c29e99 and 2a62f5c.

📒 Files selected for processing (14)
  • app/routes/login.py
  • app/routes/password_reset.py
  • app/translations/de.json
  • app/translations/en.json
  • app/translations/es.json
  • app/translations/fr.json
  • app/translations/hi.json
  • app/translations/ja.json
  • app/translations/pl.json
  • app/translations/pt.json
  • app/translations/ru.json
  • app/translations/tr.json
  • app/translations/uk.json
  • app/translations/zh.json

Comment on lines 170 to 176
Log.error(f'User: "{username}" with email: "{email}" not found')
flash_message(
page="password_reset",
message="wrong",
message="invalid_credentials",
category="error",
language=session.get("language", "en"),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

This still leaks whether the username/email pair exists.

At Line 173 the error text is unified, but the if user: branch still sends mail, flashes success, and redirects while this branch returns an error on the same page. That response split still enables account enumeration in the password-reset request step, so the CWE-204 fix is incomplete. Return the same outward response for both branches and only gate the actual email send internally.

Suggested shape
             if user:
                 context = ssl.create_default_context()
                 server = smtplib.SMTP(Settings.SMTP_SERVER, Settings.SMTP_PORT)
                 ...
                 server.send_message(message)
                 server.quit()
                 Log.success(
                     f'Password reset code: "{password_reset_code}" sent to "{email}" for user: "{username}"'
                 )
-                flash_message(
-                    page="password_reset",
-                    message="code",
-                    category="success",
-                    language=session.get("language", "en"),
-                )
-                return redirect("/password-reset/codesent=true")
             else:
-                Log.error(f'User: "{username}" with email: "{email}" not found')
-                flash_message(
-                    page="password_reset",
-                    message="invalid_credentials",
-                    category="error",
-                    language=session.get("language", "en"),
-                )
+                Log.error(f'Password reset requested for unknown credentials: "{username}" / "{email}"')
+
+            flash_message(
+                page="password_reset",
+                message="code",
+                category="success",
+                language=session.get("language", "en"),
+            )
+            return redirect("/password-reset/codesent=true")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/routes/password_reset.py` around lines 170 - 176, The password_reset flow
still reveals account existence by taking different outward paths in the
user-not-found branch versus the user-found branch. Update the password reset
handler in password_reset.py so the branch around the user lookup in the reset
request step always returns the same visible response (same flash/redirect/page
outcome) regardless of whether a user was found, and only conditionally performs
the email send internally in the user-handling logic. Use the existing
password_reset route logic, the user lookup check, flash_message, and
mail-sending code to keep the response uniform and prevent enumeration.

Comment thread app/translations/de.json Outdated
Comment thread app/translations/es.json Outdated
Comment thread app/translations/fr.json Outdated
Comment thread app/translations/hi.json Outdated
Comment thread app/translations/ja.json Outdated
Comment thread app/translations/pl.json Outdated
Comment thread app/translations/pt.json Outdated
- app/routes/login.py: Replace 'password' with 'invalid_credentials'
- app/routes/password_reset.py: Replace 'not_found' and 'wrong' with 'invalid_credentials'
- All 13 translation files: Add 'invalid_credentials' key with localized text

Closes #265
- flash.login.invalid_credentials
- flash.password_reset.invalid_credentials
- top-level invalid_credentials

All 13 languages now use proper localized text instead of 'Invalid username or password'
@DogukanUrker DogukanUrker force-pushed the fix/265-observable-response-discrepancy branch from 1b0940d to 2ec426f Compare June 27, 2026 21:23
@DogukanUrker DogukanUrker merged commit bf30bcb into main Jun 27, 2026
2 of 3 checks passed
@DogukanUrker DogukanUrker deleted the fix/265-observable-response-discrepancy branch June 27, 2026 21:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CWE-204: Observable response discrepancy (user enumeration)

1 participant