← back to NationalPaperHangers
Constrain profile hero_url to the installer's own uploads, not an arbitrary URL
258f66afb1eaee516ae787c3d257c734c6d207a9 · 2026-05-19 08:00:18 -0700 · Steve
Files touched
Diff
commit 258f66afb1eaee516ae787c3d257c734c6d207a9
Author: Steve <steve@designerwallcoverings.com>
Date: Tue May 19 08:00:18 2026 -0700
Constrain profile hero_url to the installer's own uploads, not an arbitrary URL
---
routes/admin.js | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/routes/admin.js b/routes/admin.js
index b8f4024..2182cd7 100644
--- a/routes/admin.js
+++ b/routes/admin.js
@@ -850,9 +850,19 @@ router.post('/template', async (req, res, next) => {
}
// template_settings: keep what's there, overlay our editable fields.
const existing = req.installer.template_settings || {};
+ // hero_url must be one of THIS installer's own content-addressed uploads
+ // (/uploads/<their-id>/hero-<hash>.<ext>) — the hidden field is normally
+ // filled by the upload JS, but a hand-crafted POST could otherwise plant
+ // an arbitrary off-site URL that renders into the public profile's
+ // <img src>. Anything else is dropped (hero falls back to portfolio[0]).
+ const heroRaw = String(req.body.hero_url || '').slice(0, 500);
+ const heroOwnPathRe = new RegExp(
+ '^/uploads/' + req.installer.id + '/hero-[0-9a-f]{16}\\.(jpg|png|webp|avif)$'
+ );
+ const heroUrl = heroOwnPathRe.test(heroRaw) ? heroRaw : undefined;
const settings = {
...existing,
- hero_url: String(req.body.hero_url || '').slice(0, 500) || undefined,
+ hero_url: heroUrl,
accent_color: /^#[0-9a-f]{6}$/i.test(String(req.body.accent_color || '')) ? req.body.accent_color : undefined
};
// Strip undefineds.
← 50ae349 Validate the plan tier on billing checkout so installers can
·
back to NationalPaperHangers
·
JSON-serialise booking-page analytics values so a stray quot 098e20c →