b03051dec6a7b9d132995e809f378bc3277a987a
max
Thu Aug 27 01:18:06 2026 -0700
escape search terms and uploaded sequence names (XSS), refs #38057
#Preview2 week - bugs introduced now will need a build patch to fix
#Preview2 week - bugs introduced now will need a build patch to fix
Follow-up to the QA review of dbd10edb43b.
hgFileSearch and hgTracks track search: the name/description/simple search
boxes echoed the search term into a value attribute unescaped. The earlier
pass escaped the hgFileSearch simple box only, which sits inside a
USE_TABS block that is not compiled.
hgPhyloPlace: uploaded sequence names are echoed in the QC failure
messages from vcfFromFasta, in the subtree summary table, in the
placement and mutation-path headings, in the subtree download links and
in the no-reference-match list. All now go through htmlEncode.
hgLiftOver: the failure file was read back while its FILE* was still
open and unflushed, so the "Failed input regions" block always came out
empty. Close mapped and unmapped right after liftOver writes them.
diff --git src/hg/hgPhyloPlace/phyloPlace.c src/hg/hgPhyloPlace/phyloPlace.c
index d18a38cbb66..c12f47b7476 100644
--- src/hg/hgPhyloPlace/phyloPlace.c
+++ src/hg/hgPhyloPlace/phyloPlace.c
@@ -820,31 +820,31 @@
printf("%s: ", vpn->nodeName);
struct singleNucChange *snc;
for (snc = vpn->sncList; snc != NULL; snc = snc->next)
{
if (snc != vpn->sncList)
printf(", ");
printf("%c%d%c", snc->parBase, snc->chromStart+1, snc->newBase);
}
}
}
static void displayVariantPath(struct variantPathNode *variantPath, char *sampleId)
/* Display mutations on the path to this sample. */
{
printf("
Mutations along the path from the root of the phylogenetic tree to %s:\n",
- sampleId);
+ htmlEncode(sampleId)); // uploaded sample name, escape (XSS)
if (variantPath)
{
boolean makeCollapsible = (variantPathCountMuts(variantPath) > 20);
if (makeCollapsible)
beginCollapsibleSpan();
puts("
");
variantPathPrint(variantPath);
if (makeCollapsible)
endCollapsibleSpan();
puts("
");
}
else
puts("(None; your sample was placed at the root of the phylogenetic tree)");
puts("
");
}
@@ -1311,36 +1311,36 @@
struct placementInfo *info = ref->val;
if (clumpSize > 1)
{
// Sort identical samples alphabetically:
struct slName *sortedSamples = NULL;
int i;
for (i = 0, ref = refsToGo; ref != NULL && i < clumpSize; ref = ref->next, i++)
{
info = ref->val;
slNameAddHead(&sortedSamples, info->sampleId);
}
slNameSort(&sortedSamples);
printf("%d identical samples:\n\n", clumpSize);
struct slName *sln;
for (sln = sortedSamples; sln != NULL; sln = sln->next)
- printf("- %s\n", sln->name);
+ printf("
- %s\n", htmlEncode(sln->name)); // uploaded sample name, escape (XSS)
puts("
");
}
else
{
- printf("%s\n", info->sampleId);
+ printf("%s\n", htmlEncode(info->sampleId)); // uploaded sample name, escape (XSS)
ref = ref->next;
}
refsToGo = ref;
displaySampleMuts(info, refAcc);
if (info->imputedBases)
{
puts("Base values imputed by parsimony:\n
");
struct baseVal *bv;
for (bv = info->imputedBases; bv != NULL; bv = bv->next)
printf("- %d: %s\n", bv->chromStart+1, bv->val);
puts("
");
puts("");
}
displayVariantPath(info->variantPath, clumpSize == 1 ? info->sampleId : "samples");
displayNearestNeighbors(info, source, db);
@@ -2229,31 +2229,31 @@
else if (isRsv)
puts("RGCC 2023 clade (Nextclade)"
TOOLTIP("The RGCC clade assigned by Nextclade "
"to the nearest neighboring sample already in the tree")
" | ");
else
puts("Nextclade lineage"
TOOLTIP("The lineage assigned by Nextclade "
"to the nearest neighboring sample already in the tree")
" | ");
puts("subtree | ");
struct slName *si;
for (si = sampleIds; si != NULL; si = si->next)
{
puts("");
- printf("| %s", replaceChars(si->name, "|", " | "));
+ printf(" | %s", htmlEncode(replaceChars(si->name, "|", " | "))); // escape (XSS)
struct placementInfo *pi = hashFindVal(results->samplePlacements, si->name);
if (pi)
{
if (gotClades)
printf(" | %s | ", pi->nextClade ? pi->nextClade : "n/a");
if (gotLineages)
printLineageTd(pi->pangoLineage, "n/a", db);
}
else
{
if (gotClades)
printf("n/a | ");
if (gotLineages)
printf("n/a | ");
}
@@ -3568,39 +3568,39 @@
if (lfLooksLikeFasta(lf))
{
struct slPair *failedSeqs;
struct slPair *failedPsls;
struct hashOrMmHash *treeNames = NULL;
// We need to check uploaded names in fasta only for original usher, not usher-sampled(-server).
if (!serverIsConfigured(org) && !endsWith(usherPath, "-sampled"))
treeNames = getTreeNames(sampleNameFile, protobufPath, &bigTree, FALSE, &startTime);
vcfTn = vcfFromFasta(lf, org, refName, refGenome, maskSites, treeNames,
&sampleIds, &seqInfoList, &failedSeqs, &failedPsls, &startTime);
if (failedSeqs)
{
puts("");
struct slPair *fail;
for (fail = failedSeqs; fail != NULL; fail = fail->next)
- printf("%s
\n", fail->name);
+ printf("%s
\n", htmlEncode(fail->name)); // message quotes seq name, escape (XSS)
puts("
");
}
if (failedPsls)
{
puts("");
struct slPair *fail;
for (fail = failedPsls; fail != NULL; fail = fail->next)
- printf("%s
\n", fail->name);
+ printf("%s
\n", htmlEncode(fail->name)); // message quotes seq name, escape (XSS)
puts("
");
}
if (seqInfoList == NULL)
printf("Sorry, could not align any sequences to reference well enough to place in "
"the phylogenetic tree.
\n");
isFasta = TRUE;
}
else if (lfLooksLikeVcf(lf))
{
vcfTn = checkAndSaveVcf(lf, refGenome, maskSites, &seqInfoList, &sampleIds);
reportTiming(&startTime, "check uploaded VCF");
}
else
{
subtreesOnly = TRUE;
@@ -3826,49 +3826,49 @@
printf("phylogenetic tree "
"with your samples (Newick file)\n", results->bigTreePlusTn->forHtml);
printf("TSV summary of sequences and placements\n",
tsvTn->forHtml);
printf("TSV summary of S (Spike) gene changes\n",
sTsvTn->forHtml);
}
printf("ZIP archive of subtree Newick and JSON files\n",
zipTn->forHtml);
// For now, leave in the individual links so I don't break anybody's pipeline that's
// scraping this page...
for (ix = 0, ti = results->subtreeInfoList; ti != NULL; ti = ti->next, ix++)
{
int subtreeUserSampleCount = slCount(ti->subtreeUserSampleIds);
printf("Subtree with %s", ti->subtreeTn->forHtml,
- ti->subtreeUserSampleIds->name);
+ htmlEncode(ti->subtreeUserSampleIds->name)); // uploaded sample name, escape (XSS)
if (subtreeUserSampleCount > 10)
printf(" and %d other samples", subtreeUserSampleCount - 1);
else
{
struct slName *sln;
for (sln = ti->subtreeUserSampleIds->next; sln != NULL; sln = sln->next)
- printf(", %s", sln->name);
+ printf(", %s", htmlEncode(sln->name)); // uploaded sample name, escape (XSS)
}
puts(" (Newick file)");
printf("Auspice JSON for subtree with %s",
- jsonTns[ix]->forHtml, ti->subtreeUserSampleIds->name);
+ jsonTns[ix]->forHtml, htmlEncode(ti->subtreeUserSampleIds->name)); // escape (XSS)
if (subtreeUserSampleCount > 10)
printf(" and %d other samples", subtreeUserSampleCount - 1);
else
{
struct slName *sln;
for (sln = ti->subtreeUserSampleIds->next; sln != NULL; sln = sln->next)
- printf(", %s", sln->name);
+ printf(", %s", htmlEncode(sln->name)); // uploaded sample name, escape (XSS)
}
puts(" (JSON file)");
}
puts("");
if (ctTn != NULL)
{
// Notify in opposite order of custom track creation.
puts("Custom tracks for viewing in the Genome Browser
");
printf("Added custom track of uploaded samples.
\n");
if (subtreeCount > 0 && subtreeCount <= MAX_SUBTREE_CTS)
printf("Added %d subtree custom track%s.
\n",
subtreeCount, (subtreeCount > 1 ? "s" : ""));
ctFile = urlFromTn(ctTn);
cartSaveSession(cart);
@@ -4204,31 +4204,31 @@
if (! fileExists(nextcladeIndex))
errAbort("config for '%s' specifies nextcladeIndex file '%s' but it does not exist",
org, nextcladeIndex);
struct refMatch *refFiles = NULL;
struct slName *noMatches = NULL;
int startTime = clock1000();
if (lfLooksLikeFasta(lf))
refFiles = matchSamplesWithReferences(org, nextcladeIndex, lf, &noMatches, &startTime);
else
refFiles = matchNamesWithReference(org, lf, &noMatches, &startTime);
if (noMatches != NULL)
{
printf("
No reference was found for the following sequences:\n\n");
struct slName *noMatch;
for (noMatch = noMatches; noMatch != NULL; noMatch = noMatch->next)
- printf("- %s\n", noMatch->name);
+ printf("
- %s\n", htmlEncode(noMatch->name)); // uploaded sample name, escape (XSS)
puts("
");
}
int refCount = slCount(refFiles);
boolean doNav = (refCount > 1);
struct refMatch *ref;
if (doNav)
{
// Make some navigation links at the top
puts("");
printf("Your uploaded sequences matched %d different reference sequences. "
"Click on these links to jump to the results for each reference.\n",
refCount);
puts("
");
for (ref = refFiles; ref != NULL; ref = ref->next)
printf("- %s\n", ref->acc, ref->description);