The legend image was cancelled by the browser: "violates the resource's Cross-Origin-Resource-Policy response header". The app is cross-origin isolated — index.php and the container both send Cross-Origin-Embedder-Policy: require-corp, which SQLite in a worker needs — and under that policy a cross-origin image is blocked unless the provider sends Cross-Origin-Resource-Policy. Digital Earth Africa does not: checked by HEAD and GET, both origins, with and without a query string, always 200 with access-control-allow-origin: * and no CORP. A CORS request satisfies COEP on its own, which is why the base maps have always worked — every tile source in MapView.js already sets crossOrigin: 'anonymous', and OpenTopoMap sends only ACAO too. The legend <img> was the one cross-origin request in the app that did not ask for CORS. It now does, and the element is built through the DOM rather than innerHTML so the URL is not interpolated into an attribute. Because the browser I could test in still refused that particular host at the network layer — net::ERR_FAILED with no response, which curl never reproduces from the same machine — this also adds legend.php, a same-origin fallback. The client tries the provider directly first and falls back once; only if both fail is the entry hidden. legend.php is deliberately narrow, because it runs inside the Docker network where it can reach ApiEngine, MainBackend and the database. Hosts are allow-listed, the resolved address is rejected if it falls in a private or reserved range, redirects are not followed, the body is capped at 2 MB and must be an image, and nothing about the request is reflected back. Verified live: the allow-listed legend returns a 4,701-byte PNG, while ApiEngine, MainBackend, 169.254.169.254, loopback, an unlisted host and file:// are all refused, as is any method other than GET. Legend URLs come from the application's own layer definitions, never from user input, so the allow-list restricts nothing today. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
31 lines
4.3 KiB
JavaScript
31 lines
4.3 KiB
JavaScript
import{d as f,z as d}from"./index-DY549L0n.js";import"./vite-helpers-BlTxHScW.js";import"./bootstrap-D1-uvFxm.js";import"./openlayers-D5-VSKzq.js";import"./ol-ext-DV70ljNY.js";function p(e){return String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g,"")}function h(e){let l="",o=e;for(;o>=0;)l=String.fromCharCode(o%26+65)+l,o=Math.floor(o/26)-1;return l}function x(e){return e&&!Array.isArray(e)&&Array.isArray(e.cells)?{cells:e.cells,bold:!!e.bold}:{cells:Array.isArray(e)?e:[e],bold:!1}}function u(e){const l=[];return e.forEach((o,n)=>{const{cells:t,bold:a}=x(o),r=a?' s="1"':"",i=t.map((s,c)=>{const m=`${h(c)}${n+1}`;return s==null||s===""?`<c r="${m}"${r}/>`:typeof s=="number"&&Number.isFinite(s)?`<c r="${m}"${r}><v>${s}</v></c>`:`<c r="${m}"${r} t="inlineStr"><is><t xml:space="preserve">${p(s)}</t></is></c>`}).join("");l.push(`<row r="${n+1}">${i}</row>`)}),`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>${l.join("")}</sheetData></worksheet>`}const y=`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
|
|
<fonts count="2"><font><sz val="11"/><name val="Calibri"/></font><font><b/><sz val="11"/><name val="Calibri"/></font></fonts>
|
|
<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>
|
|
<borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders>
|
|
<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>
|
|
<cellXfs count="2"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/><xf numFmtId="0" fontId="1" fillId="0" borderId="0" xfId="0" applyFont="1"/></cellXfs>
|
|
<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>
|
|
</styleSheet>`;function g(e){const l=(e||[]).filter(t=>t&&Array.isArray(t.rows));if(l.length===0)throw new Error("No sheets to export.");const o={},n=l.map((t,a)=>{const r=`xl/worksheets/sheet${a+1}.xml`;o[r]=u(t.rows);const i=String(t.name||`Sheet${a+1}`).replace(/[:\\/?*[\]]/g," ").slice(0,31);return{id:a+1,name:i,file:r}});return o["[Content_Types].xml"]=`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
<Default Extension="xml" ContentType="application/xml"/>
|
|
<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
|
|
<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
|
|
${n.map(t=>`<Override PartName="/${t.file}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`).join(`
|
|
`)}
|
|
</Types>`,o["_rels/.rels"]=`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
|
|
</Relationships>`,o["xl/workbook.xml"]=`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
<sheets>${n.map(t=>`<sheet name="${p(t.name)}" sheetId="${t.id}" r:id="rId${t.id}"/>`).join("")}</sheets>
|
|
</workbook>`,o["xl/_rels/workbook.xml.rels"]=`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
${n.map(t=>`<Relationship Id="rId${t.id}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet${t.id}.xml"/>`).join(`
|
|
`)}
|
|
<Relationship Id="rIdStyles" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
|
|
</Relationships>`,o["xl/styles.xml"]=y,d(o,"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")}function S(e,l){f(g(e),l)}export{g as buildXlsxBlob,S as exportXlsx};
|
|
//# sourceMappingURL=xlsx-Xgu_2_hC.js.map
|