{"assessments":[],"deployments":[],"fuzz":[],"identity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"b1e24855-57e0-407c-9c7c-3037c7b4c077","kind":"skill:site-content-check","nodes":[{"acceptedSubmissionHash":"dfd4ded12d965ea826b4b6b87ad02b9120d1ad2f034bdbdaf9781acd298a3242","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"9724d22ee4afab47bcc23d9bcd121784b0de277f78f82806d4dc4e8aa62609bf","skillId":"site-content-check","tools":[]},"key":"site_content_check","kind":"code","role":"review","skillHash":"9724d22ee4afab47bcc23d9bcd121784b0de277f78f82806d4dc4e8aa62609bf","skillId":"site-content-check","state":"accepted"}],"objective":"Wheelcast: free OBS stream overlays driven by Squeekchain's live API (run alerts, wheel telemetry, block banners, Twitch chat predictions, stats ticker). Deploy the repository as-is.","parentJobId":null,"planHash":"8d157c07d86b7890a7833866611b73f59559bca617e2f0080d565320d876949c","previousHash":"7b98846ebf60083caa19aadccd143b16765a815ada768f71c8a4a8336d1b6689","projectId":"b1e24855-57e0-407c-9c7c-3037c7b4c077","publication":{"commit":null,"deliveredAt":null,"repoUrl":null},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[],"site":{"cid":"bafybeigxe4yqcway3nwf7yk3bavu3y5shap2if6te6tm7rhttzdkchzzqe","ensName":"wheelcast-broadcast-graphic-b1e2.identitymd.eth","namingTransaction":"0xe45f7221f956a86f63bde90fa1753ef5f053ebf3e8bc1b386f388a197de56589"},"snapshotHash":"45d02a663d1769ddd455c1b7ec907e4addec83b8afeb47b678bba4197c717d97","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"b8fd8054ea57c5d8","findings":[{"citation":"resolved","description":"overlay.html:9-11 and index.html:10-12 load stylesheets and font files from fonts.googleapis.com and fonts.gstatic.com. So each streamer's OBS browser source, and each visitor to the settings page, sends their IP address and user agent to Google whenever the page loads. The README ('No login, no keys, no server') and index.html:189 ('What Wheelcast keeps: Nothing on a server') list where data goes and mention only Squeekchain's API and Twitch chat. Google Fonts is not mentioned. This is third-party tracking without disclosure, which is advisory only (medium or lower). Wheelcast does not store anything on a server, but the request is made. The CSS font stacks already fall back to Bahnschrift, Arial Narrow and system-ui, so the fonts could be self-hosted under assets/.","line":11,"path":"overlay.html","reproduction":"Open overlay.html?w=all with DevTools > Network open. You will see requests to https://fonts.googleapis.com/css2?family=Barlow+Condensed... and to fonts.gstatic.com/*.woff2 alongside the api.squeekchain.com calls. index.html makes the same requests (line 12). Neither page nor the README names Google as a recipient. Expected: the fonts are self-hosted, or the disclosure at index.html:189 lists the request.","severity":"low","snippet":"<link href=\"https://fonts.googleapis.com/css2?family=Barlow+Condensed:ital,wght@0,500;0,600;0,700;0,800;1,700;1,800;1,900&family=Barlow:wght@500;600;700&display=swap\" rel=\"stylesheet\">","title":"Every page and OBS overlay loads Google Fonts from a third party, and the site does not say so"},{"citation":"resolved","description":"index.html:260-262 builds the program monitor from the same query string as the real links, including channel=<your channel>, and adds demo=1. It renders overlay.html inline through srcdoc, which inherits the settings page's origin. In demo mode, overlay.html still builds BKEY from CFG.channel (line 464). Demo votes (lines 546 and 550, names like 'wheelwatcher' and 'hamhodler'), demo rounds (lines 551-553, settled through onStatus -> onRounds -> resolvePoll) and the 'Settle the round' test button (line 536, random outcome) all call saveBoard(). That writes demo users and streaks into localStorage key 'wheelcast:board:<channel>', which is the key the live overlay reads for its top-three podium. If the streamer opens the copied overlay link in the same browser on the same origin (for example to preview it, or with a browser-based streaming tool), the 'Chat calls' podium shows invented viewers with streaks. The page says the board is kept from real chat calls settled on chain.","line":464,"path":"overlay.html","reproduction":"1) Open index.html from the hosted origin and type 'testchan' in Twitch channel. 2) Under Chat calls, press 'Add sample calls' and then 'Settle the round', or wait about 14 s for demo round t===7. 3) In DevTools > Application > Local Storage, key 'wheelcast:board:testchan' now holds demo names such as 'wheelwatcher' with w/s counts. 4) Open overlay.html?w=poll&channel=testchan in the same browser. After the first real settlement renders the podium (or immediately via renderPodium() at line 559), it lists the demo names. Expected: demo mode uses a separate key, e.g. 'wheelcast:board:demo', or does not persist.","severity":"low","snippet":"const BKEY = \"wheelcast:board:\" + (CFG.channel || \"local\");","title":"Demo monitor writes fake sample viewers into the real channel's streak board in localStorage"},{"citation":"resolved","description":"The message listener at overlay.html:529-537 does not check e.origin or e.source. Any window that holds a reference to the overlay, such as a page that iframes the hosted overlay.html or a window that opened it, can post {wheelcast:'test', ev:'resolve'}. That calls resolvePoll(Math.random()<.6, ...) (line 536), which updates and saves every voter's streak (lines 488-489) from a coin flip, not the chain. It can also fire run, nap or block alerts or hide sources ('show'). index.html:188 claims chat calls follow rounds settled on Sepolia 'so nobody can fake a result'. Inside OBS nothing else frames the source, so the exposure is small. The fix is to accept test messages only when demo mode is on, or only from e.source === window.parent with the hosted origin.","line":529,"path":"overlay.html","reproduction":"Host the site, then on any other origin create <iframe src='https://<host>/overlay.html?w=poll&channel=x'> and run frame.contentWindow.postMessage({wheelcast:'test',ev:'resolve'},'*'). Expected: the message is ignored outside demo mode. Actual: the poll panel stamps 'He ran' or 'He napped' at random with 'Round 512 settled', and the streaks in localStorage 'wheelcast:board:x' for that origin are updated.","severity":"low","snippet":"window.addEventListener(\"message\", (e) => {","title":"Overlay accepts 'wheelcast:test' postMessage commands from any origin, including settling a round with a random result"}],"hash":"dfd4ded12d965ea826b4b6b87ad02b9120d1ad2f034bdbdaf9781acd298a3242","nodeId":"14161c5a-0740-477c-85d6-8747f0faf8bb","outcome":"completed","summary":"I found nothing in Wheelcast that should stop it from being hosted: there are no critical or high findings. I read everything that will be served: `index.html`, `overlay.html`, both images in `assets/`, the README and the LICENSE. The three low-severity findings below are written to `.imd-findings.json`, and I changed no repository files.\n\n**What the scripts do:**\n- **Network calls:** they only read Squeekchain's API at `api.squeekchain.com/v1`, and Twitch chat anonymously (read-only `justinfan` login, never posts).\n- **Ruled out:** no wallet requests, no signature or approval prompts, no prompts for seed phrases or keys, no code fetched from elsewhere and run, no obfuscation, no redirects.\n- **The one iframe** is the settings page's visible demo monitor, built from the site's own `overlay.html`; it is not hidden.\n- **Squeekchain branding:** the site calls itself a community tool built on Squeekchain's public API, so it doesn't pass itself off as Squeekchain.\n- **Images:** the two images are an original cartoon hamster and a wooden wheel.\n- **Chat:** chat usernames are escaped before they are shown, and API values go through `Number()` before they reach the page, so I found no way to inject HTML.\n\n**Low-severity findings (advisory):**\n1. **Google Fonts isn't disclosed** (`overlay.html:11`, `index.html:12`). Both pages load fonts from Google, so every OBS source and visitor sends their IP address to Google. The site says \"Nothing on a server\" and lists only Squeekchain and Twitch as places it talks to.\n2. **The demo writes fake viewers into the real streak board** (`overlay.html:464`). Once a channel name is entered, the demo monitor saves its sample viewers and their streaks under the same browser-storage key the live overlay uses for its top three. To see it: enter a channel, press \"Add sample calls\" then \"Settle the round\", and open that channel's chat-calls overlay link in the same browser.\n3. **The overlay obeys test commands from any website** (`overlay.html:529`). Any page that embeds the overlay can fire alerts or settle the current round with a random result, which overwrites saved streaks. The site claims \"nobody can fake a result\". The risk is small because nothing else is embedded alongside an OBS source.\n\nNone of these findings is in Solidity, so none carries a Foundry test.","treeHash":null,"usage":{"cachedInputTokens":199653,"cost":{"source":"runtime","usdMicros":490722},"inputTokens":12,"model":"claude-opus-5-5","outputTokens":6546,"runtime":"claude","turns":10,"wallClockMs":74026}}],"verification":[]}