[object Object]

← back to AsSeenInMovies

Add rel=noreferrer to every target=_blank link

694e783d60d26858f5408d3ea31d74559ff6b31d · 2026-05-19 15:15:08 -0700 · SteveStudio2

Eight inline anchor tags across SPOTTED rows, queue review, image credits,
movie-detail shop CTAs, and the TMDB attribution were opening in new tabs
with rel missing 'noreferrer'. Adding it (alongside the existing 'noopener')
closes the window.opener leak AND blanks the Referer header sent to the
target — matches the fleet standard.

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

Files touched

Diff

commit 694e783d60d26858f5408d3ea31d74559ff6b31d
Author: SteveStudio2 <steve@designerwallcoverings.com>
Date:   Tue May 19 15:15:08 2026 -0700

    Add rel=noreferrer to every target=_blank link
    
    Eight inline anchor tags across SPOTTED rows, queue review, image credits,
    movie-detail shop CTAs, and the TMDB attribution were opening in new tabs
    with rel missing 'noreferrer'. Adding it (alongside the existing 'noopener')
    closes the window.opener leak AND blanks the Referer header sent to the
    target — matches the fleet standard.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
 server.js | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/server.js b/server.js
index b415984..66eb933 100644
--- a/server.js
+++ b/server.js
@@ -432,7 +432,7 @@ app.get('/spotted', async (req, res) => {
           <h3 class="spot-title">${esc(s.product_name)}${s.brand ? ' <span class="muted">— '+esc(s.brand)+'</span>' : ''}</h3>
           ${s.product_category ? '<span class="chip">'+esc(s.product_category)+'</span>' : ''}
           ${s.scene_description ? '<p class="spot-desc">'+esc(s.scene_description)+'</p>' : ''}
-          ${s.shop_url ? '<a class="shop-btn" href="'+esc(s.shop_url)+'" target="_blank" rel="nofollow noopener">Where to find it ↗</a>' : ''}
+          ${s.shop_url ? '<a class="shop-btn" href="'+esc(s.shop_url)+'" target="_blank" rel="nofollow noopener noreferrer">Where to find it ↗</a>' : ''}
         </div>
       </article>`).join('');
     const pager = `
@@ -973,7 +973,7 @@ app.get('/spotted/queue', async (req, res) => {
       <article class="queue-row" id="row-${s.id}">
         <header>
           <span class="qid">#${s.id}</span>
-          <a class="movie-link" href="/movie/${s.movie_id}" target="_blank">${esc(s.movie_title || '')} (${esc(String(s.release_year || '?'))})</a>
+          <a class="movie-link" href="/movie/${s.movie_id}" target="_blank" rel="noopener noreferrer">${esc(s.movie_title || '')} (${esc(String(s.release_year || '?'))})</a>
           <time>${esc(new Date(s.created_at).toISOString().slice(0,16).replace('T',' '))} UTC</time>
         </header>
         <div class="qbody">
@@ -983,7 +983,7 @@ app.get('/spotted/queue', async (req, res) => {
             ${s.product_category ? `<div><strong>Category:</strong> ${esc(s.product_category)}</div>` : ''}
             ${s.scene_timecode ? `<div><strong>Timecode:</strong> ${esc(s.scene_timecode)}</div>` : ''}
             ${s.scene_description ? `<div><strong>Scene:</strong> ${esc(s.scene_description)}</div>` : ''}
-            ${s.shop_url ? `<div><strong>Shop:</strong> <a href="${esc(s.shop_url)}" rel="nofollow noopener" target="_blank">${esc(s.shop_url.slice(0,80))}</a></div>` : ''}
+            ${s.shop_url ? `<div><strong>Shop:</strong> <a href="${esc(s.shop_url)}" rel="nofollow noopener noreferrer" target="_blank">${esc(s.shop_url.slice(0,80))}</a></div>` : ''}
             <div class="submitter">
               <strong>By:</strong> ${esc(s.submitted_by_email || '(anon)')} · ${esc(s.submitted_by_role || 'fan')}
             </div>
@@ -1112,11 +1112,11 @@ function imageCreditHtml(credit, license, sourceUrl, sourceKind) {
   const lic = (license || '').toLowerCase();
   const isPd = lic === 'cc0' || lic === 'cc0-1.0' || lic.includes('public domain') || lic === 'pd';
   if (isPd) {
-    return `<span class="img-credit pd">Public domain · <a href="${esc(sourceUrl)}" rel="nofollow noopener" target="_blank">source</a></span>`;
+    return `<span class="img-credit pd">Public domain · <a href="${esc(sourceUrl)}" rel="nofollow noopener noreferrer" target="_blank">source</a></span>`;
   }
   const artistPart = credit ? `${esc(credit)} / ` : '';
   const licPart    = license ? `${esc(license)} / ` : '';
-  return `<span class="img-credit">Photo: ${artistPart}${licPart}<a href="${esc(sourceUrl)}" rel="nofollow noopener" target="_blank">source</a></span>`;
+  return `<span class="img-credit">Photo: ${artistPart}${licPart}<a href="${esc(sourceUrl)}" rel="nofollow noopener noreferrer" target="_blank">source</a></span>`;
 }
 
 function htmlShell(title, body, headExtras = '', canonical = '', metaDesc = '') {
@@ -1291,7 +1291,7 @@ app.get('/movie/:id', async (req, res) => {
               <div style="font-weight:500">${esc(s.product_name)}${s.brand ? ` <span style="color:var(--ink-faint);font-weight:400">— ${esc(s.brand)}</span>` : ''}</div>
               ${s.scene_timecode ? `<div style="color:var(--ink-faint);font-size:12px;margin-top:2px">${esc(s.scene_timecode)}</div>` : ''}
               <div class="desc">${esc(s.scene_description || '')}</div>
-              ${s.shop_url ? `<a class="shop" href="${esc(s.shop_url)}" target="_blank" rel="nofollow noopener">Where to find it ↗</a>` : ''}
+              ${s.shop_url ? `<a class="shop" href="${esc(s.shop_url)}" target="_blank" rel="nofollow noopener noreferrer">Where to find it ↗</a>` : ''}
             </div>
           </div>`).join('') : '<p style="color:var(--ink-faint);font-style:italic;padding:14px 0 4px;font-size:13.5px">No verified placements yet for this film. <a href="/spotted/submit?movie_id=' + id + '" style="color:var(--gold)">Submit one →</a></p>'}
       </section>`;
@@ -1423,7 +1423,7 @@ app.get('/person/:id', async (req, res) => {
             <div>
               <div style="font-weight:500">${esc(s.product_name)}${s.brand ? ' <span style="color:var(--ink-faint);font-weight:400">— '+esc(s.brand)+'</span>' : ''}</div>
               <div class="desc"><a href="/movie/${esc(s.movie_id)}#spotted">${esc(s.title)}${s.release_year ? ' ('+esc(s.release_year)+')' : ''}</a>${s.scene_timecode ? ' · ' + esc(s.scene_timecode) : ''}</div>
-              ${s.shop_url ? `<a class="shop" href="${esc(s.shop_url)}" target="_blank" rel="nofollow noopener">Where to find it ↗</a>` : ''}
+              ${s.shop_url ? `<a class="shop" href="${esc(s.shop_url)}" target="_blank" rel="nofollow noopener noreferrer">Where to find it ↗</a>` : ''}
             </div>
           </div>`).join('')}
       </section>` : '';
@@ -1934,7 +1934,7 @@ ${process.env.GA_ID ? `<script async src="https://www.googletagmanager.com/gtag/
   <a href="/terms" style="color:var(--gold)">Terms</a> ·
   <a href="/api" style="color:var(--gold)">API</a><br>
   <span style="font-size:10px;letter-spacing:0.1em;text-transform:uppercase;color:var(--muted)">
-    Movie metadata via <a href="https://www.themoviedb.org/" target="_blank" rel="noopener" style="color:var(--gold)">TMDB</a> — this product uses the TMDB API but is not endorsed or certified by TMDB.
+    Movie metadata via <a href="https://www.themoviedb.org/" target="_blank" rel="noopener noreferrer" style="color:var(--gold)">TMDB</a> — this product uses the TMDB API but is not endorsed or certified by TMDB.
   </span>
 </footer>
 </body></html>`);

← 2cee761 Guard .bak/.pre-/.orig paths in static middleware and gitign  ·  back to AsSeenInMovies  ·  card grid: universal hover-preview — wire ::after 3×3 tile-r 466b16e →