Allow public pages to render in safelist/traffic-exchange iframes

Framing was blocked site-wide (X-Frame-Options DENY + frame-ancestors
none), so safelists and traffic exchanges showed a blank panel. Public
pages now send frame-ancestors *; admin.html keeps the strict lockdown.
Join button opens the dApp in a new tab so it escapes the exchange
iframe (dApps typically refuse to render framed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-08-13 08:37:56 -05:00
parent fe357dd361
commit 89a84aa7ca
2 changed files with 7 additions and 4 deletions
+6 -3
View File
@@ -164,14 +164,17 @@ function publicSponsorPayload(sponsor, config) {
if(!sponsor)return null;
return {id:sponsor.id,name:config.showSponsorName?sponsor.name:null,directs:sponsor.directs,goal:2,level:sponsor.level,referralUrl:`${config.dappReferralBaseUrl}${encodeURIComponent(sponsor.id)}`};
}
const CSP_BASE="default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; font-src 'self' data:; form-action 'self'; frame-src https://www.youtube-nocookie.com";
function securityHeaders(extra={}) {
// Public pages must render inside safelist / traffic-exchange iframes, so framing stays open here; admin.html re-locks it via ADMIN_FRAME_HEADERS.
return {
'X-Content-Type-Options':'nosniff','X-Frame-Options':'DENY','Referrer-Policy':'strict-origin-when-cross-origin',
'X-Content-Type-Options':'nosniff','Referrer-Policy':'strict-origin-when-cross-origin',
'Permissions-Policy':'camera=(), microphone=(), geolocation=()',
'Content-Security-Policy':"default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; font-src 'self' data:; form-action 'self'; frame-src https://www.youtube-nocookie.com; frame-ancestors 'none'",
'Content-Security-Policy':`${CSP_BASE}; frame-ancestors *`,
...extra
};
}
const ADMIN_FRAME_HEADERS={'X-Frame-Options':'DENY','Content-Security-Policy':`${CSP_BASE}; frame-ancestors 'none'`};
function send(res,status,body,headers={}) { res.writeHead(status,securityHeaders(headers));res.end(body); }
function json(res,status,obj,headers={}) { send(res,status,JSON.stringify(obj),{'Content-Type':'application/json; charset=utf-8',...headers}); }
function parseCookies(req){const out={};for(const p of (req.headers.cookie||'').split(';')){const i=p.indexOf('=');if(i>0)out[p.slice(0,i).trim()]=decodeURIComponent(p.slice(i+1).trim())}return out}
@@ -182,7 +185,7 @@ function contentType(file){const ext=path.extname(file);return ({'.html':'text/h
function staticFile(req,res,file,status=200){
if(!fs.existsSync(file)||!fs.statSync(file).isFile())return false;
const size=fs.statSync(file).size;
const base={'Content-Type':contentType(file),'Accept-Ranges':'bytes','Cache-Control':['.html','.css','.js'].includes(path.extname(file))?'no-cache':'public, max-age=3600'};
const base={'Content-Type':contentType(file),'Accept-Ranges':'bytes','Cache-Control':['.html','.css','.js'].includes(path.extname(file))?'no-cache':'public, max-age=3600',...(path.basename(file)==='admin.html'?ADMIN_FRAME_HEADERS:{})};
const m=status===200&&req.headers.range?String(req.headers.range).match(/^bytes=(\d*)-(\d*)$/):null;
if(m&&(m[1]!==''||m[2]!=='')){
const start=m[1]===''?Math.max(0,size-Number(m[2])):Number(m[1]);