mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
fix(verifier): a secure field's typed value never reaches the record
A folio login run wrote the account email and password in cleartext into llm-calls.jsonl, 166 times in one run, beside screenshots of the same screens. Three sites rendered it: the recent-action memory, the candidate list, and the trace. One helper now covers all three so a fourth cannot bypass it, and the driver still receives the real text. Android redacts every typed value because it cannot tell a secure field from any other. That asymmetry is deliberate and documented: safe by default on the target that cannot tell.
This commit is contained in:
1 parent
b1e95739ad
commit
38d328df90
12 files changed
+418
-16
No files matched your search
@@ -15,6 +15,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/priyanshujain/sanderling/internal/hierarchy"
|
||||
"github.com/priyanshujain/sanderling/internal/llmclient"
|
||||
"github.com/priyanshujain/sanderling/internal/trace"
|
||||
"github.com/priyanshujain/sanderling/internal/verifier"
|
||||
@@ -107,7 +108,7 @@ func (s *llmSource) NextAction(ctx context.Context, stepIndex int) (verifier.Act
|
||||
if err == nil {
|
||||
s.lastFromSetup = true
|
||||
s.record(stepIndex, trace.LLMCall{Outcome: trace.LLMOutcomeSetupAction})
|
||||
s.history.add(describeAction(action))
|
||||
s.history.add(describeAction(action, s.verifier.Tree()))
|
||||
return action, nil
|
||||
}
|
||||
if !errors.Is(err, verifier.ErrNoAction) {
|
||||
@@ -129,7 +130,7 @@ func (s *llmSource) NextAction(ctx context.Context, stepIndex int) (verifier.Act
|
||||
s.lastReasoning = selection.reasoning
|
||||
s.lastChoice = selection.choice
|
||||
s.lastChosenAction = selection.chosenAction
|
||||
s.history.add(describeAction(selection.action))
|
||||
s.history.add(describeAction(selection.action, s.verifier.Tree()))
|
||||
return selection.action, nil
|
||||
}
|
||||
|
||||
@@ -427,10 +428,13 @@ func parseChoice(content string) (choiceOutput, error) {
|
||||
}
|
||||
|
||||
// describeAction renders a short action summary for the recent-action memory.
|
||||
func describeAction(action verifier.Action) string {
|
||||
// The tree is the one the action was chosen against, which is what says whether
|
||||
// a typed value may be written into the memory at all.
|
||||
func describeAction(action verifier.Action, tree *hierarchy.Tree) string {
|
||||
switch action.Kind {
|
||||
case verifier.ActionKindInputText:
|
||||
return fmt.Sprintf("InputText %s = %q", actionTarget(action), action.Text)
|
||||
return fmt.Sprintf("InputText %s = %q",
|
||||
actionTarget(action), verifier.RecordedActionText(action, tree))
|
||||
case verifier.ActionKindScroll:
|
||||
// A builtin gesture carries endpoints rather than a selector, so name the
|
||||
// container by where the drag starts; that is what tells two scrollable
|
||||
|
||||
Reference in new issue
Block a user