feat(19.4-03): tide captures a named input of an HTML response

- capture rule from: response.form reads the value of the <input> with
  the rule's name (a server-rendered form's one-shot token), so a parity
  flow can post a consent form back
- README and parity docs list the capture sources
This commit is contained in:
2026-10-19 19:59:24 +02:00
parent 1e37926ee9
commit bb4b8d448d
4 changed files with 79 additions and 3 deletions

View File

@@ -510,3 +510,42 @@ func TestScrubFormFieldDespiteSubstringSecrets(t *testing.T) {
t.Fatalf("code placeholder: %s", got)
}
}
func TestCaptureResponseFormField(t *testing.T) {
store, err := OpenStore("")
if err != nil {
t.Fatal(err)
}
const consent = "q8Zr2nV5xYk1Lw7pT3sA9dF6gH0jK4mN"
step := Step{
ID: "consent-page",
Response: Response{
Status: 200,
Headers: map[string]string{"Content-Type": "text/html; charset=UTF-8"},
Body: Body(`<form method="post" action="/oauth/authorize">` +
`<input type="hidden" name="request_id" value="0b9c2c7e-1d0f-4a54-8e0e-3f1c9b6f7a20">` +
`<input type=hidden name=consent_token value="` + consent + `"/>` +
`<button name="decision" value="allow">Allow</button></form>`),
},
Capture: []CaptureRule{
{From: "response.form", Name: "consent_token", As: "consent_token"},
{From: "response.form", Name: "request_id", As: "request_id"},
},
}
if err := CaptureStep(store, &step); err != nil {
t.Fatal(err)
}
if got, _ := store.Expand("{{consent_token}}|{{request_id}}"); got != consent+"|0b9c2c7e-1d0f-4a54-8e0e-3f1c9b6f7a20" {
t.Fatalf("captured %q", got)
}
if err := ScrubStep(store, &step); err != nil {
t.Fatal(err)
}
if strings.Contains(string(step.Response.Body), consent) || !strings.Contains(string(step.Response.Body), `value="{{consent_token}}"`) {
t.Fatalf("consent token not scrubbed: %s", step.Response.Body)
}
missing := Step{ID: "no-form", Response: Response{Body: "<p>nothing</p>"}, Capture: []CaptureRule{{From: "response.form", Name: "consent_token", As: "x"}}}
if err := CaptureStep(store, &missing); err == nil || !strings.Contains(err.Error(), "missing response form field consent_token") {
t.Fatalf("missing field: %v", err)
}
}