d338d5080783d2ac5828658fe17160668bf64cdb
chmalee
  Thu May 7 15:05:24 2026 -0700
Fixes from code review, refs #37500

- Revalidate shared myVariants tracks against hgcentral on every read
path (hgTracks, hgc, hgTables); cart-supplied owner/db/project no
longer trusted. New myVariantsResolveSharedTrack helper.
- Scope shared-track UPDATE statements by share->project/db so a
recipient can't edit rows outside the granted scope.
- Add hgsid CSRF check to myVariantsJsCommand; pass hgsid in the
hgTracks.js highlight Add-Annotation POST.
- HTML-escape owner-controlled fields in the canEdit branch of
doMyVariantsDetails (Chromosome, Project, project select options,
hidden text input).
- Validate targetUser against gbMembers when creating a share; return
a clear 400 on typos.
- Replace the concat(id,' ',name)='%s' lookup with parsed-id +
name verification.
- Remove cgiMakeColorVar / cgiMakeColorVarWithLabel; the canEdit form
uses spectrum.js (already loaded for the create dialog).
- Strip _hidden_* columns from hgTables field lists for shared tracks,
both the display path and the selected-fields read path.
- Make the per-assembly invariant explicit: myVariantsLoadItems and
doMyVariantsDetails bail out if share->db != current database.
- Memoize myVariantsSharedScopeWhere to avoid per-region hgcentral
round-trips on genome-wide hgTables queries.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js
index 9245b74d78b..faf1f7b5ee4 100644
--- src/hg/js/hgTracks.js
+++ src/hg/js/hgTracks.js
@@ -638,31 +638,31 @@
         });
         $(positionDialog).dialog('open');
     }
 
 };
 
   /////////////////////////////////////
  //// Creating items             /////
 /////////////////////////////////////
 var myVariants = {
     createBedForm: function(dialogEle) {
         // Simple fields shown by default (matches hgc edit form style)
         const simpleFields = [
             { label: "Label", id: "name", type: "text", placeholder: "Optional item label",
               info: "A short label for this annotation, displayed in the browser." },
-            { label: "Color", id: "color", type: "color" },
+            { label: "Color", id: "color", type: "text" },
             { label: "Ref", id: "ref", type: "text", placeholder: "Optional reference allele sequence",
               info: "Reference allele sequence at this position." },
             { label: "Alt", id: "alt", type: "text", placeholder: "Optional alternate allele sequence",
               info: "Alternate (variant) allele sequence." },
             { label: "Mouseover", id: "mouseover", type: "text", placeholder: "Short text shown on hover",
               info: "Short text shown when hovering over this item. If empty, the label and alleles are displayed." },
             { label: "Description", id: "description", type: "textarea", placeholder: "Longer description/notes",
               info: "Longer notes or comments about this annotation. Displayed on the details page." },
             { label: "Project", id: "project", type: "text", placeholder: "Optional project name",
               info: "Group annotations by project. Projects with no annotations are automatically removed from the list." },
         ];
 
         // Advanced fields hidden by default
         const advancedFields = [
             { label: "Chromosome", id: "chrom", type: "text" },
@@ -830,31 +830,30 @@
                 if (field.id === "start" || field.id === "thickStart") {
                     input.value = hgTracks.winStart;
                 }
                 if (field.id === "end" || field.id === "thickEnd") {
                     input.value = hgTracks.winEnd;
                 }
                 if (field.id === "score") {
                     input.value = 0;
                     input.max = 1000;
                 }
             }
             if (field.id === "strand") {
                 input.value = ".";
             }
             if (field.id === "color") {
-                input.type = "text";
                 input.value = "#000000";
                 input.style.width = "70px";
             }
             if (field.placeholder) {
                 input.placeholder = field.placeholder;
             }
 
             wrapper.appendChild(label);
             wrapper.appendChild(input);
             if (field.info) {
                 wrapper.appendChild(createInfoIcon(field.info));
             }
             container.appendChild(wrapper);
 
             return input;
@@ -2131,31 +2130,31 @@
                 data.end = data.thickEnd = pos.end.toString();
                 data.score = "0";
                 data.strand = ".";
                 data.color = $("#hlColorInput").val();
                 data.name = "";
                 data.description = "";
                 data.ref = "";
                 data.alt = "";
                 data.trackName = "myVariants";
                 $(this).dialog("close");
                 let req = encodeURIComponent(`myVariants myVariants ${JSON.stringify(data)}`);
                 jQuery('body').css('cursor', 'wait');
                 $.ajax({
                         type: "POST",
                         url: "../cgi-bin/hgTracks",
-                        data: cart.addUpdatesToUrl(`hgt_doJsCommand=${req}&trackName=myVariants`),
+                        data: cart.addUpdatesToUrl(`hgt_doJsCommand=${req}&trackName=myVariants&hgsid=${getHgsid()}`),
                         dataType: "html",
                         trueSuccess: imageV2.updateImgAndMap,
                         success: catchErrorOrDispatch,
                         error: errorHandler,
                         cmd: 'wholeImage',
                         loadingId: showLoadingImage("imgTbl"),
                         cache: false
                     });
             };
         }
         dragSelectButtons.Cancel = function() {
             $(this).dialog("close");
         };
         $(dragSelectDialog).dialog({
                 modal: true,