mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
fix(runner): a secure field's value does not reach state.lastAction either
folio extracts lastAction, and extractor values are persisted as extractor_changes, so the password still reached the run directory through the spec after the three render sites were closed. The wrap sits in the runner rather than in lastActionFields because the hosts hold the next step's tree, not the one the action was chosen against: a field that stops being secure between the two would publish what the trace withheld. Live and replay now agree byte for byte.
This commit is contained in:
1 parent
38d328df90
commit
66fd5bce5d
7 files changed
+181
-8
No files matched your search
+1
-1
@@ -31,7 +31,7 @@ The trace is written incrementally. An interrupted run is complete up to the ste
|
||||
|
||||
## Typed values in the record
|
||||
|
||||
A run types into whatever the app puts on screen, login forms included, and the trace and the model call record are both shared. So a typed value is written down as `[redacted]` whenever the target may be a credential entry: the trace action, the recent-action memory the prompt carries, and the numbered candidate list all render it that way. The app still receives the real keystrokes; only the record is redacted, and the record still names the field that was typed into.
|
||||
A run types into whatever the app puts on screen, login forms included, and the trace and the model call record are both shared. So a typed value is written down as `[redacted]` whenever the target may be a credential entry: the trace action, the recent-action memory the prompt carries, the numbered candidate list, and the `state.lastAction` a spec reads (and can extract into the trace) all render it that way. The app still receives the real keystrokes; only the record is redacted, and the record still names the field that was typed into.
|
||||
|
||||
Which values that covers differs by platform, because the platforms differ in what they report. iOS and web state on every editable field whether it masks its input, so only the fields that do are redacted and the rest of the memory keeps its values. Android reports nothing: uiautomator's password attribute is dropped by the native tree mapper before the driver sees it, a password field is indistinguishable from a search box there, and so every typed value on Android is redacted.
|
||||
|
||||
|
||||
@@ -47,6 +47,8 @@ interface State {
|
||||
|
||||
`lastAction.applied` is `true` when the runner saw the dispatch succeed and `null` when the apply call failed with the action possibly already delivered: an RPC deadline can fire after the tap reached the app, and nothing can find out afterwards. So there are three states, not two. `state.lastAction === null` means no action ran; `applied === null` means one ran whose fate is unknown. A property that attributes an effect to the action ("this submit must move the balance by the typed amount") has to decline unless `applied` is `true`, or a timeout convicts a healthy app. A property that counts what the app COULD have done should include it: an unconfirmed submit belongs in an upper bound on how many submits a window holds.
|
||||
|
||||
`lastAction.text` carries the value as the record renders it, not the value the app received. A typed value reads `[redacted]` wherever the target may be a credential entry, which on iOS and web means the fields that report themselves secure and on Android means all of them, since Android reports nothing either way. A spec extracting `state.lastAction` writes it to the trace, so it is redacted for the same reason the trace action is; see [typed values in the record](../runs/#typed-values-in-the-record). A property that has to reason about what a field holds should read the field off `ax`.
|
||||
|
||||
## Selectors
|
||||
|
||||
Selectors are passed to `ax.find()`, `ax.findAll()`, and element-scoped `.find()` / `.findAll()`. A tree-level lookup scans the whole hierarchy, the root element included; an element-scoped one scans that element's descendants. Both selector forms scan the same set, so `ax.find("id:page")` and `ax.find({ id: "page" })` return the same element.
|
||||
|
||||
Reference in new issue
Block a user