Skip to content

fix(credentials): raise when sentinel placeholder hits an invalid element#797

Open
leo-notte wants to merge 1 commit into
mainfrom
fix/raise-on-credential-validation-failure
Open

fix(credentials): raise when sentinel placeholder hits an invalid element#797
leo-notte wants to merge 1 commit into
mainfrom
fix/raise-on-credential-validation-failure

Conversation

@leo-notte
Copy link
Copy Markdown
Contributor

@leo-notte leo-notte commented May 4, 2026

Summary

When a known credential sentinel (e.g. PASSWORD = "mycoolpassword" from notte_core.credentials) was supplied for a FillAction but the targeted element failed the credential field's validate_element check, replace_credentials silently logged at trace level and returned the action unmodified — causing the literal placeholder string to be typed into the form.

This was easy to hit by targeting a <label> rather than its associated <input type="password"> (e.g. via observe IDs that point at the label):

  • UserNameField.validate_element returns True unconditionally → username substitution proceeded.
  • PasswordField.validate_element requires attrs.type == "password" → password substitution silently no-op'd while the fill itself reported success.

End result: mycoolpassword got typed into the password field as plaintext and the user only noticed because they manually re-read .value via eval-js.

Changes

  • notte_core.errors.processing: new CredentialFieldValidationError(NotteBaseError) with dev/agent/user messages naming the placeholder, credential key, and offending element attrs.
  • notte_core.credentials.base.replace_credentials: replaces the silent logger.trace with a raise. The branch only fires when the value already matched a known sentinel (lines 658–661 still raise InvalidPlaceholderError for unknown values), so raising here is the correct signal: the caller clearly intended a substitution and the literal sentinel must not leak into typing.
  • notte_browser.session._action_with_vault: adds a dedicated except CredentialFieldValidationError: raise ahead of the broad except ValueError, otherwise the new error would be swallowed (since NotteBaseError extends ValueError).
  • tests/test_session_vault_helpers.py: 4 new tests covering the new raise, the happy substitute path, the asymmetric username path, and propagation through the session-level catch.

Out of scope

  • The asymmetric design of validate_element (username unconditionally accepts, password requires type=="password") is preserved — pinned by the new test_username_placeholder_unaffected_by_validation_change. Tightening username validation would be a separate behavioural change.
  • Resolving label → associated input before reading LocatorAttributes would also fix the reported case but is a larger refactor; the raise is the safer first step because it surfaces the failure instead of corrupting the field.

Test plan

  • pytest tests/test_session_vault_helpers.py — 8 passed (4 existing + 4 new)
  • Manual: rerun the original repro (notte page fill M2 mycoolpassword on the-internet.herokuapp.com/login with a vault entry) and confirm the call now raises instead of silently typing mycoolpassword into the password field

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved credential field validation to detect and report targeting errors immediately. When credential placeholders target incompatible form fields (e.g., password credentials on non-password fields), the system now raises explicit validation errors instead of silently continuing and failing later, providing clearer error messages for debugging misconfigured credentials.
  • Tests

    • Added comprehensive validation test coverage for credential field targeting scenarios, including password and username field validation and error propagation.

Note

Replaces a silent logger.trace no-op in replace_credentials with a new CredentialFieldValidationError when a known credential sentinel targets an element that fails validate_element. Adds a dedicated except CredentialFieldValidationError: raise in _action_with_vault to prevent the broad except ValueError from swallowing it. Four new tests cover the error path, happy path, asymmetric username behaviour, and session-level propagation.

Written by Mendral for commit 52fbcdb.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

Walkthrough

This PR introduces explicit fail-fast behavior for credential placeholder validation. A new CredentialFieldValidationError exception signals when an LLM-provided placeholder targets an element that fails the expected credential field's validation. The credential replacement logic in BaseVault now raises this error instead of silently continuing, and NotteSession._action_with_vault catches and re-raises it to prevent generic error handling. Four new tests verify the error is raised on invalid targets, succeeds on valid ones, preserves behavior for credentials without strict validation, and propagates correctly through the session layer.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • nottelabs/notte#829: Both PRs modify NotteSession._action_with_vault credential replacement handling—this one adds fail-fast error propagation, while #829 refreshes the session snapshot before replacement.
  • nottelabs/notte#419: Both PRs extend the vault credential-substitution pipeline for form-fill actions; this PR adds validation error signaling to replace_credentials, while #419 extends NotteAgent.action_with_credentials to invoke credential replacement.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(credentials): raise when sentinel placeholder hits an invalid element' accurately describes the main change: introducing an error when credential validation fails instead of silently falling through.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/raise-on-credential-validation-failure

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 and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 4, 2026

Greptile Summary

This PR fixes a silent data-leak bug in credential substitution: when a known sentinel placeholder (e.g. PASSWORD) was used in a FillAction but the targeted element failed validate_element, replace_credentials silently skipped substitution and the literal placeholder string was typed into the form field.

  • Introduces CredentialFieldValidationError in notte_core.errors.processing and raises it in replace_credentials instead of the previous logger.trace no-op, ensuring the sentinel never leaks into the fill value.
  • Adds a dedicated except CredentialFieldValidationError: raise guard in _action_with_vault ahead of the existing broad except ValueError, preventing the new error from being swallowed (since NotteBaseError inherits from ValueError).
  • Adds four unit tests covering the error path, the happy substitution path, the asymmetric username behaviour, and session-level propagation.

Confidence Score: 5/5

Safe to merge — the change is narrowly scoped to the validation failure branch and the new guard is correctly ordered before the broad ValueError catch.

All changed paths are well-covered by new unit tests. The except CredentialFieldValidationError: raise guard is correctly placed before except ValueError, and the raise in replace_credentials only fires for the branch that previously no-op'd. The asymmetric username behaviour is explicitly pinned by a test. No security concerns or data-correctness regressions were identified.

No files require special attention.

Important Files Changed

Filename Overview
packages/notte-core/src/notte_core/errors/processing.py Adds CredentialFieldValidationError with well-structured dev/agent/user messages; no issues found
packages/notte-core/src/notte_core/credentials/base.py Replaces silent trace log with a CredentialFieldValidationError raise in the non-FormFillAction validation path; logic is correct
packages/notte-browser/src/notte_browser/session.py Adds dedicated except CredentialFieldValidationError: raise guard before the broad except ValueError to prevent the error from being swallowed
tests/test_session_vault_helpers.py 4 new unit tests covering the error path, happy path, asymmetric username behaviour, and session-level propagation; no integration tests added

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
tests/test_session_vault_helpers.py:115-190
**Missing integration test for a bug-fix PR**

The four new tests all use `MockVault` and `make_snapshot`, so they exercise only the in-process logic. Per the project's test guidelines, every bug-fix PR should include at least one integration test. The reported repro (target a label element on a real page with a live vault entry) is a good candidate — without it, a regression that re-introduces the silent no-op would not be caught automatically.
- Add a comment if the PR does n... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))

Reviews (2): Last reviewed commit: "fix(credentials): raise when sentinel pl..." | Re-trigger Greptile

mendral-app[bot]

This comment was marked as outdated.

…ment

When a known credential sentinel (e.g. PASSWORD = "mycoolpassword") was
supplied for a fill action but the targeted element failed the field's
validate_element check, replace_credentials silently logged at trace and
returned the action unmodified — causing the literal placeholder string
to be typed into the form. This was easy to hit by targeting a <label>
instead of its associated <input type="password">: UserNameField
validates True unconditionally, so username substitution worked, but
PasswordField requires attrs.type == "password", so password
substitution silently no-op'd while the fill itself appeared to succeed.

Replace the silent skip with a new CredentialFieldValidationError. Also
add a dedicated except in NotteSession._action_with_vault so the broad
`except ValueError` (which would otherwise swallow it via NotteBaseError)
doesn't suppress the new error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@leo-notte leo-notte force-pushed the fix/raise-on-credential-validation-failure branch from 4182b8e to 52fbcdb Compare May 19, 2026 01:06
Copy link
Copy Markdown
Contributor

@mendral-app mendral-app Bot left a comment

Choose a reason for hiding this comment

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

LGTM

No new issues introduced. The code and tests are correct and unchanged from the previous LGTM review.\n\nCI Failures: tests/test_trajectory.py::test_agent_observes_page_correctly failed with LLMParsingError — this is a flaky test that hits a live LLM and is unrelated to this PR's credential changes. Tracked in insight 01KRYWT00XT4JW0PFNHS8Y7KEN.

Tag @mendral-app with feedback or questions. View session

@leo-notte
Copy link
Copy Markdown
Contributor Author

@greptileai review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant