[object Object]

← back to Sister Parish Onboarding

add noopener noreferrer to external _blank links + .bak/.pre snapshot 404-guard

92e3f856101e2447aa357066ed6443386e44fbe0 · 2026-05-19 15:20:15 -0700 · Steve

Code-health fleet sweep pass: closes the reverse-tabnabbing vector on
the two external target=_blank anchors (vendor source PDP, PD-corpus
source link in the inspirations modal). Also adds an Express middleware
404-guard so .bak / .bak.* / .pre-* / .pre.* snapshot paths can never
be served via static even if one slips into the tree.

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

Files touched

Diff

commit 92e3f856101e2447aa357066ed6443386e44fbe0
Author: Steve <steve@designerwallcoverings.com>
Date:   Tue May 19 15:20:15 2026 -0700

    add noopener noreferrer to external _blank links + .bak/.pre snapshot 404-guard
    
    Code-health fleet sweep pass: closes the reverse-tabnabbing vector on
    the two external target=_blank anchors (vendor source PDP, PD-corpus
    source link in the inspirations modal). Also adds an Express middleware
    404-guard so .bak / .bak.* / .pre-* / .pre.* snapshot paths can never
    be served via static even if one slips into the tree.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
 server.js | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/server.js b/server.js
index 06af2a8..2a4d071 100644
--- a/server.js
+++ b/server.js
@@ -18,6 +18,13 @@ function psqlJson(sql) {
   return execSync(`psql dw_unified -At -q`, { input: sql, encoding: 'utf8' }).trim();
 }
 
+// Block any .bak / .bak.* / .pre-* / .pre.* snapshot paths from ever serving via static.
+// Fleet-wide hygiene: snapshot files must never be reachable from the web root.
+app.use((req, _res, next) => {
+  if (/\.(bak|pre)(\.|$|-)/i.test(req.path)) return next({ status: 404 });
+  next();
+});
+
 // Proxy PD-corpus images from wallco-ai's data dir so the SP viewer can render them
 const WALLCO_IMG_DIR = path.join(process.env.HOME, 'Projects', 'wallco-ai', 'data', 'images');
 app.use('/pd-img', express.static(WALLCO_IMG_DIR, { maxAge: '7d' }));
@@ -194,7 +201,7 @@ function render() {
       : '';
     return \`
       <div class="card">
-        <a href="\${p.source_url}" target="_blank">
+        <a href="\${p.source_url}" target="_blank" rel="noopener noreferrer">
           <div class="img" style="background-image:url('\${p.primary_image || ''}')"></div>
         </a>
         <div class="meta">
@@ -239,7 +246,7 @@ async function showPdModal(spId, title) {
       const imgPath = fname ? '/pd-img/' + m.pd_source + '/' + fname : '';
       return \`
         <div class="pd-item">
-          <a href="\${m.pd_source_url}" target="_blank">
+          <a href="\${m.pd_source_url}" target="_blank" rel="noopener noreferrer">
             <img src="\${imgPath}" alt="\${m.pd_title}">
           </a>
           <div class="pd-meta">

← 4280d5a sp: set_variants.js — flatten 72 products to 2 variants (Rol  ·  back to Sister Parish Onboarding  ·  broaden .gitignore to exclude .bak/.pre-* snapshot files and f1ec46d →