<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[TeamCerts Engineering]]></title><description><![CDATA[TeamCerts Engineering]]></description><link>https://teamcerts-engineering.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 07 Nov 2026 11:45:03 GMT</lastBuildDate><atom:link href="https://teamcerts-engineering.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Uploading verified public images in a remote browser without file:// access]]></title><description><![CDATA[When browser automation runs through a browser extension on another machine, a server path such as `/srv/assets/logo.png` is not a path the browser can…]]></description><link>https://teamcerts-engineering.hashnode.dev/uploading-verified-public-images-in-a-remote-browser-without-file-access</link><guid isPermaLink="true">https://teamcerts-engineering.hashnode.dev/uploading-verified-public-images-in-a-remote-browser-without-file-access</guid><dc:creator><![CDATA[zijian xiao]]></dc:creator><pubDate>Sun, 25 Oct 2026 23:30:04 GMT</pubDate><content:encoded><![CDATA[<p>When browser automation runs through a browser extension on another machine, a server path such as `/srv/assets/logo.png` is not a path the browser can read. Treating it as a `file://` URL also requires a broad extension permission that is unnecessary for a normal website upload.</p>
<p>I ran into this while preparing public screenshots for TeamCerts, a training-record and certificate-expiry tracker I’m building. The safe approach was to keep the reviewed public asset on the server, verify it there, and send only its bytes to the already-authorized page.</p>
<p>## The pattern</p>
<p>1. Restrict files to one reviewed cache directory.</p>
<p>2. Require a PNG signature, a size limit, and a filename containing the expected SHA-256.</p>
<p>3. Pin the exact HTTPS page URL before touching the DOM.</p>
<p>4. Require exactly one intended file input.</p>
<p>5. Decode the bytes in the page, hash them again, create a `File`, and assign a `DataTransfer.files` list.</p>
<p>6. Dispatch `input` and `change`, then read the site’s own preview back. Never treat assignment alone as a successful upload or publication.</p>
<p>```js</p>
<p>async function attachVerifiedPng({ expectedUrl, selector, base64, name, size, sha256 }) {</p>
<p>if (location.href !== expectedUrl) throw new Error('page context changed');</p>
<p>const inputs = [...document.querySelectorAll(selector)];</p>
<p>if (inputs.length !== 1) throw new Error('file input is not unique');</p>
<p>const input = inputs[0];</p>
<p>if (!(input instanceof HTMLInputElement) || input.type !== 'file' || input.disabled) throw new Error('not an enabled file input');</p>
<p>const raw = atob(base64);</p>
<p>const bytes = Uint8Array.from(raw, ch =&gt; ch.charCodeAt(0));</p>
<p>const digest = [...new Uint8Array(await crypto.subtle.digest('SHA-256', bytes))].map(x =&gt; x.toString(16).padStart(2, '0')).join('');</p>
<p>if (bytes.length !== size || digest !== sha256) throw new Error('digest mismatch');</p>
<p>const transfer = new DataTransfer();</p>
<p>transfer.items.add(new File([bytes], name, { type: 'image/png', lastModified: 0 }));</p>
<p>input.files = transfer.files;</p>
<p>input.dispatchEvent(new Event('input', { bubbles: true }));</p>
<p>input.dispatchEvent(new Event('change', { bubbles: true }));</p>
<p>}</p>
<p>```</p>
<p>## Important boundaries</p>
<p>This does not bypass authentication, CAPTCHA, browser permissions, or the website’s submit button. Some sites reject synthetic events; in that case the correct result is to stop and ask for normal human input. The page URL must be read immediately before the operation, and a changed tab must fail closed.</p>
<p>After assigning the file, verify the preview’s count, filename, size, and—when possible—content hash. Final publishing should remain a separate, recorded action with duplicate checks and a public readback.</p>
<p>This article was prepared with AI assistance and reviewed against the implementation behavior described above. Project context: <a href="https://teamcerts.com">https://teamcerts.com</a></p>
<p>Originally published on DEV Community: <a href="https://dev.to/m4ra7h0n/uploading-verified-public-images-in-a-remote-browser-without-file-access-40lf">https://dev.to/m4ra7h0n/uploading-verified-public-images-in-a-remote-browser-without-file-access-40lf</a></p>
]]></content:encoded></item></channel></rss>