From f326d1123247831eff13ade79b04afeb31f81886 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 13 Feb 2025 00:05:30 -0500 Subject: [PATCH] Added input validation (allows Share ID or Share URL) --- .../editor/metadataEditor/metadataEditor.jsx | 13 ++++++------- .../homebrew/editor/metadataEditor/validations.js | 13 +++++++++++++ client/homebrew/pages/editPage/editPage.jsx | 4 ++-- server/app.js | 1 + 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx index 141e1ab92..0656b08e7 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx +++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx @@ -77,6 +77,7 @@ const MetadataEditor = createClass({ ...this.props.metadata, [name] : e.target.value }); + return true; } else { // if validation issues, display built-in browser error popup with each error. const errMessage = validationErr.map((err)=>{ @@ -85,6 +86,7 @@ const MetadataEditor = createClass({ callIfExists(e.target, 'setCustomValidity', errMessage); callIfExists(e.target, 'reportValidity'); + return false; } }, @@ -120,9 +122,8 @@ const MetadataEditor = createClass({ }, handleThemeWritein : function(e) { - this.props.metadata.theme = e.target.value; - - + const shareId = e.target.value.split('/').pop(); //Extract just the ID if a URL was pasted in + this.props.metadata.theme = shareId; this.props.onChange(this.props.metadata, 'theme'); }, @@ -225,8 +226,6 @@ const MetadataEditor = createClass({ }); }; - console.log(this.props.themeBundle); - const currentRenderer = this.props.metadata.renderer; const currentTheme = mergedThemes[`${_.upperFirst(this.props.metadata.renderer)}`][this.props.metadata.theme] ?? { name: `${this.props.themeBundle?.name || ''}`, author: `${this.props.themeBundle?.author || ''}` }; @@ -247,8 +246,8 @@ const MetadataEditor = createClass({ onSelect={(value)=>this.handleTheme(value)} onEntry={(e)=>{ e.target.setCustomValidity(''); //Clear the validation popup while typing - //debouncedHandleFieldChange('theme', e); - this.handleThemeWritein(e); + if(this.handleFieldChange('theme', e)) + this.handleThemeWritein(e); }} options={listThemes(currentRenderer)} autoSuggest={{ diff --git a/client/homebrew/editor/metadataEditor/validations.js b/client/homebrew/editor/metadataEditor/validations.js index 32c8131f6..b475783a4 100644 --- a/client/homebrew/editor/metadataEditor/validations.js +++ b/client/homebrew/editor/metadataEditor/validations.js @@ -27,6 +27,19 @@ module.exports = { (value)=>{ return new RegExp(/^([a-zA-Z]{2,3})(-[a-zA-Z]{4})?(-(?:[0-9]{3}|[a-zA-Z]{2}))?$/).test(value) === false && (value.length > 0) ? 'Invalid language code.' : null; } + ], + theme: [ + (value) => { + const URL = global.config.baseUrl.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); //Escape any regex characters + const shareIDPattern = '[a-zA-Z0-9-_]{12}'; + const shareURLRegex = new RegExp(`^${URL}\\/share\\/${shareIDPattern}$`); + const shareIDRegex = new RegExp(`^${shareIDPattern}$`); + if (value?.length === 0) return null; + if (shareURLRegex.test(value)) return null; + if (shareIDRegex.test(value)) return null; + + return 'Must be a valid Share URL or a 12-character ID.'; + } ] }; diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index c784edc8e..bee813f46 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -379,7 +379,7 @@ const EditPage = createClass({ const title = `${this.props.brew.title} ${systems}`; const text = `Hey guys! I've been working on this homebrew. I'd love your feedback. Check it out. -**[Homebrewery Link](${global.config.publicUrl}/share/${shareLink})**`; +**[Homebrewery Link](${global.config.baseUrl}/share/${shareLink})**`; return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(title.toWellFormed())}&text=${encodeURIComponent(text)}`; }, @@ -410,7 +410,7 @@ const EditPage = createClass({ view - {navigator.clipboard.writeText(`${global.config.publicUrl}/share/${shareLink}`);}}> + {navigator.clipboard.writeText(`${global.config.baseUrl}/share/${shareLink}`);}}> copy url diff --git a/server/app.js b/server/app.js index 4dec6b4c4..76caf6fed 100644 --- a/server/app.js +++ b/server/app.js @@ -552,6 +552,7 @@ const renderPage = async (req, res)=>{ const configuration = { local : isLocalEnvironment, publicUrl : config.get('publicUrl') ?? '', + baseUrl : `${req.protocol}://${req.get('host')}`, environment : nodeEnv, deployment : config.get('heroku_app_name') ?? '' };