browserrot: login-before-change phase + curated Gitea recipe (M-B4 step 1)
Real account-settings pages (Gitea /user/settings/account, most dashboards) are reachable only inside an authenticated session, but the engine went straight to the change URL. Add Site.LoginBeforeChange: when set, Rotate authenticates through the Login flow with the OLD secret first, so the session cookie carries into the change-page fetch. Extract the login sequence into a shared r.login() helper reused by both the pre-change auth (old secret) and Verify (new secret). A login wall we cannot clear — including where MFA would appear — fails SAFE (old secret kept). GiteaSite() seeds a curated change-password recipe (LoginBeforeChange on, standard Gitea selectors, ProofUnproven until run against a real instance — self-heal repairs any selector drift on first run; SuccessText mismatch only fails safe). Proven LIVE-VM: TestIntegration_loginBeforeChangeRotation drives real Chromium through a Gitea-style cookie gate (change page 302s to /login unless authenticated): login(old)→settings→rotate→verify(new)→old-rejected. Non-browser TestRotate_loginBeforeChangeOrder asserts login precedes change navigation and the pre-change login uses the OLD secret. Full suite green. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
package browserrot
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"incredigo/internal/rotate"
|
||||
)
|
||||
|
||||
// Curated recipes for self-hosted software whose markup is stable and known. These
|
||||
// seed the Tier-1 selector table; on a first real run any miss is repaired by the
|
||||
// Tier-2 self-heal (Healer) and re-persisted (recipes.go), so a slightly-off seed
|
||||
// self-corrects rather than failing permanently. A recipe stays ProofUnproven until
|
||||
// it has actually driven a rotation on that host — same proof-as-data discipline as
|
||||
// the API drivers (rotate/proofs.go); only then does it promote to LIVE-REAL.
|
||||
|
||||
// GiteaSite builds a change-password recipe for a Gitea instance. baseURL is the
|
||||
// scheme+host (e.g. "https://git.churchofmalware.org"), username the account login.
|
||||
//
|
||||
// Gitea reaches the change-password controls only inside an authenticated session
|
||||
// (/user/settings/account 302s to /user/login otherwise), so LoginBeforeChange is on.
|
||||
// The password form on the account page carries old_password / password / retype; the
|
||||
// result renders in a flash message. These selectors match current Gitea markup but are
|
||||
// UNPROVEN against any specific instance/version — VERIFY on first run (self-heal will
|
||||
// rewrite any that drift). The SuccessText markers are the one part self-heal cannot fix
|
||||
// (they are content, not selectors): a wrong marker only makes Rotate fail SAFE (old
|
||||
// secret kept, human flagged), never a silent bad rotation.
|
||||
func GiteaSite(baseURL, username string) Site {
|
||||
base := strings.TrimRight(baseURL, "/")
|
||||
return Site{
|
||||
Host: strings.TrimPrefix(strings.TrimPrefix(base, "https://"), "http://"),
|
||||
ChangeURL: base + "/user/settings/account",
|
||||
Form: Form{
|
||||
CurrentSel: "#old_password",
|
||||
NewSel: "#password",
|
||||
ConfirmSel: "#retype",
|
||||
SubmitSel: "form[action$='/user/settings/account'] button[type=submit]",
|
||||
SuccessSel: ".ui.positive.message, .flash-success",
|
||||
SuccessText: "has been updated",
|
||||
},
|
||||
Login: Login{
|
||||
URL: base + "/user/login",
|
||||
UserSel: "#user_name",
|
||||
PassSel: "#password",
|
||||
SubmitSel: ".ui.primary.button[type=submit], button[type=submit]",
|
||||
// Post-login, Gitea lands on the dashboard. The sign-out control's text is a
|
||||
// stable "logged in" marker across themes; confirm on first run.
|
||||
SuccessSel: "body",
|
||||
SuccessText: username,
|
||||
},
|
||||
LoginBeforeChange: true,
|
||||
Proof: rotate.ProofUnproven,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user