← 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
M public/la-commercial.html
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,'"');
+// 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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
+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 →