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:
+1
-1
@@ -5,7 +5,7 @@
|
||||
<body>
|
||||
<header class="wrap nav"><div class="brand"><div class="brand-mark">RM</div><span><span id="brandName">Crypto Team Build</span><small>Getting Started</small></span></div><div class="nav-actions"><a class="btn btn-secondary" href="/">← Strategy</a><a class="btn btn-secondary" href="/training">Training</a></div></header>
|
||||
<main class="sponsor-shell"><div class="wrap"><div class="section-head"><div class="eyebrow">Current placement</div><h1 style="font-size:clamp(34px,5vw,54px);margin:8px 0 10px">Use the sponsor shown on <span class="gold">this page.</span></h1><p>Team placements rotate as positions become qualified. Do not use an old screenshot or saved referral link.</p></div>
|
||||
<div class="sponsor-grid"><aside class="sponsor-card"><span class="live-badge"><span class="dot"></span> Current team sponsor</span><div class="sponsor-id" id="sponsorId">—</div><div class="sponsor-name" id="sponsorName">Loading current placement…</div><div id="progressArea"><div style="display:flex;justify-content:space-between;color:var(--muted);font-size:13px"><span>Qualification progress</span><strong id="progressText">0 / 2</strong></div><div class="progress-line"><span id="progressBar" style="width:0%"></span></div></div><div class="facts"><div class="fact"><small>Program</small><strong>Premium</strong></div><div class="fact"><small>Entry</small><strong><span id="entryPol">362</span> POL</strong></div><div class="fact"><small>Network</small><strong>Polygon</strong></div><div class="fact"><small>Gas token</small><strong>POL</strong></div></div><a id="joinButton" class="btn btn-primary" style="width:100%" href="#">Join With Current Sponsor →</a><button id="copyButton" class="btn btn-secondary" style="width:100%;margin-top:9px">Copy Sponsor ID</button><p class="micro" id="queueText" style="text-align:center"></p></aside>
|
||||
<div class="sponsor-grid"><aside class="sponsor-card"><span class="live-badge"><span class="dot"></span> Current team sponsor</span><div class="sponsor-id" id="sponsorId">—</div><div class="sponsor-name" id="sponsorName">Loading current placement…</div><div id="progressArea"><div style="display:flex;justify-content:space-between;color:var(--muted);font-size:13px"><span>Qualification progress</span><strong id="progressText">0 / 2</strong></div><div class="progress-line"><span id="progressBar" style="width:0%"></span></div></div><div class="facts"><div class="fact"><small>Program</small><strong>Premium</strong></div><div class="fact"><small>Entry</small><strong><span id="entryPol">362</span> POL</strong></div><div class="fact"><small>Network</small><strong>Polygon</strong></div><div class="fact"><small>Gas token</small><strong>POL</strong></div></div><a id="joinButton" class="btn btn-primary" style="width:100%" href="#" target="_blank" rel="noopener noreferrer">Join With Current Sponsor →</a><button id="copyButton" class="btn btn-secondary" style="width:100%;margin-top:9px">Copy Sponsor ID</button><p class="micro" id="queueText" style="text-align:center"></p></aside>
|
||||
<section><div class="callout" style="margin-bottom:16px"><strong>First time?</strong> <a href="/training" style="color:var(--teal)">Watch the four training videos</a> — how the build works, creating your MetaMask wallet, funding it, and buying your position.</div><div class="instruction-list"><article class="instruction"><div class="num">1</div><div><h3>Install or open MetaMask</h3><p>Use the official MetaMask website or your device's official app store. Never install a wallet from a link sent by a stranger.</p><div style="margin-top:10px"><a class="btn btn-secondary btn-sm" href="https://metamask.io/" target="_blank" rel="noopener noreferrer">Official MetaMask Site ↗</a></div></div></article>
|
||||
<article class="instruction"><div class="num">2</div><div><h3>Use Polygon Mainnet</h3><p>In MetaMask, select Polygon. Polygon Mainnet uses chain ID <strong>137</strong>, and POL is the native gas token.</p></div></article>
|
||||
<article class="instruction"><div class="num">3</div><div><h3>Fund your wallet with POL</h3><p>Premium entry is <strong><span id="entryPol2">362</span> POL</strong>. Keep a small additional POL balance available for network gas. POL's USD value changes with the market.</p></div></article>
|
||||
|
||||
@@ -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]);
|
||||
|
||||
Reference in New Issue
Block a user