a8349f676d1dad81ee90c58605ecbd69453666b1 chmalee Mon Jul 27 15:14:21 2026 -0700 Fixed a bug in hubspace uploads where the unencoded parentDir was used by tusd for the upload location while the encoded path was set in the hubSpace table. Also validate and trim parentDir in the pre-create hook and in hgMyData.js, refs #34962 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> diff --git src/hg/js/hgMyData.js src/hg/js/hgMyData.js index 3f1ac0a5e3e..bdc7f01bdf4 100644 --- src/hg/js/hgMyData.js +++ src/hg/js/hgMyData.js @@ -113,30 +113,44 @@ generateDiv.style.display = "block"; let revokeDiv = document.getElementById("revokeDiv"); revokeDiv.style.display = "none"; }; let cartData = {revokeApiKey: {}}; cart.setCgiAndUrl(fileListEndpoint); cart.send(cartData, handleSuccess); cart.flush(); } const fileNameRegex = /[0-9a-zA-Z._]+/g; // allowed characters in file names const fileNameFixRegex = /[^0-9a-zA-Z_]+/g; // '.' get replaced to underbars in trackHub.c. Also any files uploaded from hubtools that may have weird chars need to be escaped const parentDirSegmentRegex = /^[0-9a-zA-Z._]+$/; // allowed characters in each hub-path segment +function normalizeParentDir(file) { + // Strip surrounding whitespace off a file's parentDir, writing the trimmed value back + // into the file metadata. A trailing space is invisible in the hub name field, so + // rejecting it outright gives the user an error they cannot see the cause of. Must be + // called before isValidParentDir so we validate what will actually be uploaded. + let parentDir = (file.meta && file.meta.parentDir) || ""; + let trimmed = parentDir.trim(); + if (trimmed !== parentDir) { + uppy.setFileMeta(file.id, {parentDir: trimmed}); + file.meta.parentDir = trimmed; + } + return trimmed; +} + function isValidParentDir(parentDir) { // Slash-separated path of segments matching parentDirSegmentRegex; no '..'. if (!parentDir) return false; if (parentDir.startsWith("/") || parentDir.endsWith("/")) return false; let segments = parentDir.split("/"); for (let seg of segments) { if (!seg || seg === "." || seg === "..") return false; if (!parentDirSegmentRegex.test(seg)) return false; } return true; } function getTusdEndpoint() { // this variable is set by hgHubConnect and comes from hg.conf value return tusdEndpoint; @@ -370,31 +384,31 @@ // Tag every file so pre-finish knows a user hub.txt is coming in // the same batch and can skip synthesizing its own. let hasHubTxt = Object.values(files).some(looksLikeHubTxt); for (let f of Object.values(files)) { f.meta.batchHasHubTxt = hasHubTxt ? "true" : "false"; } for (let [key, file] of Object.entries(files)) { let fileNameMatch = file.meta.name.match(fileNameRegex); if (!fileNameMatch || fileNameMatch[0] !== file.meta.name) { uppy.info(`Error: File name has special characters, please rename file: ${file.meta.name} to only include alpha-numeric characters, period, or underscore.`, 'error', 5000); doUpload = false; continue; } - if (!isValidParentDir(file.meta.parentDir)) { + if (!isValidParentDir(normalizeParentDir(file))) { uppy.info(`Error: Hub path has special characters, please rename hub: ${file.meta.parentDir} for file: ${file.meta.name} to a path of alpha-numeric / period / underscore segments separated by '/'.`, 'error', 5000); doUpload = false; continue; } // Hub-level files in a split-hub batch intentionally carry empty genome. if (!file.meta.genome && file.meta.batchSplitHub !== "true") { uppy.info(`Error: No genome selected for file ${file.meta.name}!`, 'error', 5000); doUpload = false; continue; } if (!file.meta.fileType) { uppy.info(`Error: File type not supported, file: ${file.meta.name}!`, 'error', 5000); doUpload = false; continue; } @@ -980,31 +994,31 @@ }); this.uppy.on("dashboard:file-edit-start", (file) => { autocompletes[`${file.name}DbInput`] = false; }); this.uppy.on("dashboard:file-edit-complete", (file) => { // check the filename and hubname metadata and warn the user // to edit them if they are wrong. unfortunately I cannot // figure out how to force the file card to re-toggle // and jump back into the editor from here if (file) { let fileNameMatch = file.meta.name.match(fileNameRegex); if (!fileNameMatch || fileNameMatch[0] !== file.meta.name) { uppy.info(`Error: File name has special characters, please rename file: '${file.meta.name}' to only include alpha-numeric characters, period, or underscore.`, 'error', 5000); } - if (!isValidParentDir(file.meta.parentDir)) { + if (!isValidParentDir(normalizeParentDir(file))) { uppy.info(`Error: Hub path '${file.meta.parentDir}' must be alpha-numeric / period / underscore segments separated by '/'.`, 'error', 5000); } } }); } uninstall() { // not really used because we aren't ever uninstalling the uppy instance this.uppy.off("file-added"); } } var hubCreate = (function() { let uiState = { // our object for keeping track of the current UI and what to do userUrl: "", // the web accesible path where the uploads are stored for this user hubNameDefault: "",