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:
pj committed 2026-08-18 17:17:03 +05:30
1 parent b1e95739ad
commit 38d328df90
12 files changed
+418 -16

No files matched your search

+2 -1
View File
@@ -78,7 +78,7 @@ s.ax.find({ testTag: "AccountCard", clickable: true })
Every key-value pair must match. A key means the same thing here as in the string form: `id`, `desc`, `idPrefix`, `descPrefix` and `tag` keep their matching rules, and every other key is an attribute name, with substring and boolean rules per attribute.
Known attribute names are typed; you get autocomplete on `testTag`, `text`, `content-desc`, the boolean states (`clickable`, `enabled`, `focused`, `checked`, `selected`), and the cross-platform aliases (`identifier`, `accessibilityIdentifier`, `accessibilityText`, `accessibilityLabel`, `label`, `resource-id`, `class`, `elementType`, `package`, `placeholderValue`, `hintText`). Boolean state attributes accept a native `true` / `false`. Other attribute keys still type-check as a string-valued fallback so raw driver attributes remain reachable.
Known attribute names are typed; you get autocomplete on `testTag`, `text`, `content-desc`, the boolean states (`clickable`, `enabled`, `focused`, `checked`, `selected`, `secure`), and the cross-platform aliases (`identifier`, `accessibilityIdentifier`, `accessibilityText`, `accessibilityLabel`, `label`, `resource-id`, `class`, `elementType`, `package`, `placeholderValue`, `hintText`). Boolean state attributes accept a native `true` / `false`. Other attribute keys still type-check as a string-valued fallback so raw driver attributes remain reachable.
A key that names neither an accepted selector key nor an attribute some element on screen carries fails the run, naming the key and the accepted list. Such a key can never match, and an empty result is indistinguishable from a screen with no matching element: the generator declines to act, the runner waits out the step, and the run ends clean having explored nothing. The string form keeps its open kind space, since `<attr>:<value>` is the documented way to reach a raw driver attribute.
@@ -126,6 +126,7 @@ Fields available on every element returned by `find` / `findAll`:
| `checked` | `boolean` | Checkbox or toggle state |
| `focused` | `boolean` | Element has input focus |
| `selected` | `boolean` | Selection state |
| `secure` | `boolean \| null` | Field masks what is typed into it; `null` where the platform does not report it (Android never does) |
| `bounds` | `{ left, top, right, bottom }` | Bounding box in device pixels |
| `x` | `number` | Center X (derived from bounds) |
| `y` | `number` | Center Y (derived from bounds) |