Some checks failed
Caterium QA / qa (push) Has been cancelled
Inline handlers built as onclick="fn('${esc(id)}')" were injectable:
esc() turns ' into ', which the browser decodes back to ' before
the JS runs, so an id like x');alert(1);// broke out of the string.
Ids can come from a restored backup file or a synced catalog. Add
SunSafe.jsArg (JSON.stringify + HTML escape) and use it in all 23
handlers in app-runtime.js and index.html. Verified in a browser: a
payload id is passed through as a plain string and nothing executes.
Also replace the Settings version label that still showed
v17.6.0 · 2026.09.07, and bump the cache-busting version of the two
changed scripts (sun-safe.js, app-runtime.js).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
44 lines
2.2 KiB
JavaScript
44 lines
2.2 KiB
JavaScript
(()=>{
|
|
'use strict';
|
|
const escapeHTML=value=>String(value??'').replace(/[&<>"']/g,ch=>({
|
|
'&':'&','<':'<','>':'>','"':'"',"'":'''
|
|
})[ch]);
|
|
const escapeAttr=escapeHTML;
|
|
const idToken=value=>String(value??'').replace(/[^a-zA-Z0-9_-]/g,'');
|
|
const jsArg=value=>escapeHTML(JSON.stringify(String(value??'')));
|
|
// Existing trial databases and saved offers still reference the PNG originals.
|
|
// Resolve only our ten bundled demo assets; never rewrite customer photos.
|
|
const demoNames='berry-dessert|bruschetta-tomato|caprese|cheese-fruit|chicken-sandwich|meat-assortment|mushroom-tartlet|salmon-cream|turkey-wrap|vegetables-hummus';
|
|
const demoPath=new RegExp('^/(demo/images/(?:'+demoNames+'))\\.png$');
|
|
const imageAssetSrc=value=>{
|
|
const src=String(value??'').trim();
|
|
try{const url=new URL(src,document.baseURI);const match=url.pathname.match(demoPath);if(url.origin===location.origin&&match)return match[1]+'.webp'+url.search+url.hash;}catch(_){}
|
|
return src;
|
|
};
|
|
const safeImageSrc=value=>{
|
|
const s=imageAssetSrc(value);
|
|
if(!s)return '';
|
|
if(/^data:image\/(?:png|jpe?g|webp|gif);base64,[a-z0-9+/=\s]+$/i.test(s))return s;
|
|
if(/^(?:\.\/|\.\.\/|\/)?[a-z0-9_./-]+\.(?:png|jpe?g|webp|gif)(?:[?#][^\s]*)?$/i.test(s))return s;
|
|
if(/^blob:[a-z0-9-]+:/i.test(s))return s;
|
|
return '';
|
|
};
|
|
const setText=(node,value)=>{if(node)node.textContent=String(value??'');return node};
|
|
const insertBefore=(parent,node,reference=null)=>{
|
|
if(!parent||!node)return node;
|
|
if(reference&&reference.parentNode===parent)parent.insertBefore(node,reference);else parent.appendChild(node);
|
|
return node;
|
|
};
|
|
window.SunSafe=Object.freeze({escapeHTML,escapeAttr,idToken,jsArg,safeImageSrc,imageAssetSrc,setText,insertBefore});
|
|
|
|
// Small bootstrap for account/profile UI. Keeping it here makes the account
|
|
// center available on every Caterium screen without touching the legacy monolith.
|
|
if(!document.getElementById('cateriumAccountCenterV1780Script')){
|
|
const script=document.createElement('script');
|
|
script.id='cateriumAccountCenterV1780Script';
|
|
script.src='core/account-center-v1780.js?v=20260912-v17-8-0-account-center-2';
|
|
script.async=true;
|
|
document.head.appendChild(script);
|
|
}
|
|
})();
|