[object Object]

← back to Rentv

la-commercial: Cody gate — add esc() and escape all API-data interpolations (brief/name/address/city/type_label) going into innerHTML, closing an XSS vector on server/LLM-generated briefs + fixing raw '&' in firm names (e.g. 'Nourmand & Associates'). Kept the full-legal-name firm drill (verified it returns the exact firm, 1 result — more precise than a trimmed query)

4b0dfdfe46d811e19f5686fd89169a978268833e · 2026-08-05 17:10:40 -0700 · Steve

Files touched

Diff

commit 4b0dfdfe46d811e19f5686fd89169a978268833e
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Aug 5 17:10:40 2026 -0700

    la-commercial: Cody gate — add esc() and escape all API-data interpolations (brief/name/address/city/type_label) going into innerHTML, closing an XSS vector on server/LLM-generated briefs + fixing raw '&' in firm names (e.g. 'Nourmand & Associates'). Kept the full-legal-name firm drill (verified it returns the exact firm, 1 result — more precise than a trimmed query)
---
 public/la-commercial.html | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/public/la-commercial.html b/public/la-commercial.html
index b5e3ef17..f0bf60b1 100644
--- a/public/la-commercial.html
+++ b/public/la-commercial.html
@@ -86,19 +86,22 @@ function dstr(d){return d?new Date(d).toLocaleDateString(undefined,{year:'numeri
 // Every datum drills to its source (Steve's rule): assets/records → the LA County Assessor
 // parcel record (the cited source, keyed by AIN); firms → our CRE Directory filtered to that firm.
 const AIN=a=>'https://portal.assessor.lacounty.gov/parceldetail/'+encodeURIComponent(a);
-const attr=s=>String(s==null?'':s).replace(/"/g,'&quot;');
+// esc() for HTML text/attribute content — the API's brief/name/address strings are server/LLM-
+// generated, so they are untrusted and must be escaped before going into innerHTML (Cody gate).
+const esc=s=>String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
+const attr=esc;
 (async()=>{
   const d=await (await fetch('/api/la-commercial')).json();
   $('#fresh').textContent=d.fetched_at?('Data as of '+new Date(d.fetched_at).toLocaleString()):'';
   $('#marquee').innerHTML=(d.marquee||[]).slice(0,9).map(r=>`<a class="asset${r.ain?' ext':''}"${r.ain?` href="${AIN(r.ain)}" target="_blank" rel="noopener" title="LA County Assessor parcel ${attr(r.ain)}"`:''}>
-    <div class="tag">${r.type_label||''}</div>
+    <div class="tag">${esc(r.type_label||'')}</div>
     <div class="v">${usd(r.assessed_total)}</div>
-    <div class="a">${r.address||'—'}${r.city?', '+r.city:''}</div>
-    ${r.brief?`<div class="brief">${r.brief}</div>`:''}</a>`).join('');
+    <div class="a">${esc(r.address||'—')}${r.city?', '+esc(r.city):''}</div>
+    ${r.brief?`<div class="brief">${esc(r.brief)}</div>`:''}</a>`).join('');
   $('#recent').innerHTML='<table><thead><tr><th>Recorded</th><th>Address</th><th>Type</th><th>Assessed</th></tr></thead><tbody>'+
-    (d.recent||[]).map(r=>{const lbl=r.address?`${r.address}${r.city?', '+r.city:''}`:(r.ain?`Parcel ${r.ain}`:'—');const cell=r.ain?`<a class="ext" href="${AIN(r.ain)}" target="_blank" rel="noopener" title="LA County Assessor parcel ${attr(r.ain)}">${lbl}</a>`:lbl;return `<tr><td>${dstr(r.recording_date)}</td><td class="n">${cell}</td><td>${r.type_label||''}</td><td class="v">${usd(r.assessed_total)}</td></tr>`;}).join('')+'</tbody></table>';
+    (d.recent||[]).map(r=>{const lbl=r.address?`${esc(r.address)}${r.city?', '+esc(r.city):''}`:(r.ain?`Parcel ${esc(r.ain)}`:'—');const cell=r.ain?`<a class="ext" href="${AIN(r.ain)}" target="_blank" rel="noopener" title="LA County Assessor parcel ${attr(r.ain)}">${lbl}</a>`:lbl;return `<tr><td>${dstr(r.recording_date)}</td><td class="n">${cell}</td><td>${esc(r.type_label||'')}</td><td class="v">${usd(r.assessed_total)}</td></tr>`;}).join('')+'</tbody></table>';
   $('#firms').innerHTML=(d.firms||[]).slice(0,12).map(f=>`<a class="firm" href="/directory?tab=firms&q=${encodeURIComponent(f.name||'')}" title="See ${attr(f.name)} in the CRE Directory">
-    <div class="fn">${f.name}</div><div class="fa">${(f.agent_count||0).toLocaleString()} agents</div></a>`).join('');
+    <div class="fn">${esc(f.name)}</div><div class="fa">${(f.agent_count||0).toLocaleString()} agents</div></a>`).join('');
 })();
 </script>
 </body>

← c98cd596 la-commercial: wire every datum to deeper data per the all-d  ·  back to Rentv  ·  auto-save: 2026-08-05T17:14:13 (7 files) — data/deals-regist 15dedf60 →