5a249cd50f592a3b7598110eec792cfc942fab3f max Wed Sep 9 08:52:06 2026 -0700 Address the v504 code review hgSession: an anonymous share name that arrives with the request is saved only when it is not already in the table. Every anonymous link sits under the one reserved user "l", so a name already there stays as it is and the caller is told so. The top-right Share dialog is unaffected, since it passes a name it has just reserved and such a name does not exist yet. Snapshot names are now left out of both My Sessions listings, which is what their "__" prefix has claimed all along. Share dialog: "Create link & copy" reports the copy instead of promising it. copyToClipboard says whether the text reached the clipboard, the dialog passes that on when a browser refuses, and it tries the asynchronous clipboard API before giving up. The preview is built with the same encoding the server uses, so a name holding a hyphen or a slash previews as the link that really gets made. Cancelling out of the name editor no longer copies a second time, and a reply with no link in it says so rather than showing "undefined". hgBlat: a second click on the share button while the first request is still out no longer mints a second snapshot session, and a box dismissed during the wait stays closed. Also: a snapshot moves a cart value into durable storage only when it is a trash path, the way sessionData's own callers check; sqlAddressMatch keeps to its own documented precondition when handed an empty address; alphaGenomeToWig compares its output with its input rather than with itself, rejects a position that is not all digits and skips an empty score; and hgc's default iframe width reaches the browser as one percent sign. refs #38294 diff --git src/hg/hgLogin/hgLogin.c src/hg/hgLogin/hgLogin.c index 50db23eebf0..62daed5ebcc 100644 --- src/hg/hgLogin/hgLogin.c +++ src/hg/hgLogin/hgLogin.c @@ -518,33 +518,39 @@ char *remoteAddr=getenv("REMOTE_ADDR"); safef(subject, sizeof(subject),"Your username at the %s", brwName); safef(msg, sizeof(msg), " Someone (probably you, from IP address %s) has requested username(s) associated with this email address at the %s: \n\n %s\n\n%s\n%s", remoteAddr, brwName, users, signature, returnAddr); sendMailOut(email, subject, msg); } static char *sqlAddressMatch(char *email) /* Return a SQL fragment matching the gbMembers rows that belong to whoever controls email: the * accounts carrying it as their primary address, plus the accounts carrying it as a *confirmed* * recovery address. An unconfirmed recovEmail is only a string that a signup form typed in -- * nobody ever proved they can read mail there -- so matching it would let someone who registered * with a victim's address as their recovery address capture that victim's login (see - * confirmRecovEmail). Callers must pass a non-empty email, or rows with a blank recovEmail - * match. Result is allocd and carries the sqlSafef prefix; embed it with %-s. */ + * confirmRecovEmail). An empty email matches nothing: rows with a blank recovEmail would + * otherwise all match, which is every account on a mirror that has just added the column. + * Result is allocd and carries the sqlSafef prefix; embed it with %-s. */ { +if (isEmpty(email)) + { + struct dyString *dyNone = sqlDyStringCreate("(0)"); + return dyStringCannibalize(&dyNone); + } struct dyString *dy = sqlDyStringCreate("(email='%s'", email); if (recovEmailVerifyOk) sqlDyStringPrintf(dy, " OR (recovEmail='%s' AND recovEmailVerified='Y')", email); else /* A mirror whose gbMembers predates the column: we cannot tell confirmed from unconfirmed, * so keep the old behavior rather than locking those users out of their own accounts. */ sqlDyStringPrintf(dy, " OR recovEmail='%s'", email); sqlDyStringPrintf(dy, ")"); return dyStringCannibalize(&dy); } void sendUsername(struct sqlConnection *conn, char *email) /* email user username(s) */ { struct sqlResult *sr; @@ -1121,33 +1127,35 @@ "%s).\n\nIf you made this change, nothing more is needed. If you did NOT, please reply to " "this message right away so we can help you secure the account.\n\n%s\n%s", brwName, user, newEmail, emptyForNull(remoteAddr), signature, returnAddr); sendActMailOut(oldEmail, subject, message); } static char *recovEmailSig(char *user, char *newRecov, char *curRecov, char *curVerified, char *expStr) /* HMAC-MD5 over a pending recovery address, keyed by the secret login.cookieSalt. It goes in * the link mailed to that address, so that opening the link -- and only opening it -- puts the * address on the account and marks it confirmed, proving the mailbox really does reach the * person who claimed it. One signature serves both cases: the address given at signup (where * newRecov is already stored, unconfirmed) and a later change (where it is not stored at all * until the link is opened, so a typo cannot cost the user a working recovery address). * curRecov and curVerified are the account's stored address and flag when the link was minted; - * because confirmRecovEmail recomputes the signature from what is on the account now, a link - * stops validating once it has been used, so each link works exactly once and a stale link - * cannot quietly undo a newer change. Result is allocd. */ + * because confirmRecovEmail recomputes the signature from what is on the account now, applying a + * link stops it validating, so a stale link cannot quietly undo a newer change. Note this is a + * check on the account's state, not a one-time token: put the account back the way it was when the + * link was minted and, within the week, the same link applies again. That only ever moves the + * owner between addresses they have already confirmed for themselves. Result is allocd. */ { char *salt = cfgOption(CFG_LOGIN_COOKIE_SALT); if (isEmpty(salt)) errAbort("Confirming a recovery email address requires %s in hg.conf, set to a secret random " "string. Without a secret we cannot sign the confirmation link.", CFG_LOGIN_COOKIE_SALT); char buf[1024]; safef(buf, sizeof(buf), "recovEmail|%s|%s|%s|%s|%s", emptyForNull(user), emptyForNull(newRecov), emptyForNull(curRecov), emptyForNull(curVerified), emptyForNull(expStr)); return hmacMd5(salt, buf); } static void sendRecovEmailConfirmMail(char *recovEmail, char *user, char *curRecov, char *curVerified) /* Email a one-time link to recovEmail that, when opened, puts it on account user as a confirmed