DumbAssets is a small self-hosted inventory manager written in Node.js. When you create an asset, the server records the request body as-is, without sanitizing the text fields (server.js):
const newAsset = req.body; // name, description, modelNumber, serialNumber, tags... bruts
if (!newAsset.name) {
return res.status(400).json({ error: 'Asset name is required' });
}
assets.push(newAsset);
writeJsonFile(assetsFilePath, assets); // stocké en l'état, aucun échappement
And on the browser side, those same fields are injected straight back into the page via innerHTML (public/script.js):
details.innerHTML = `
<div class="sub-asset-title">${subAsset.name}</div> // le nom part brut dans le DOM
`;
info.innerHTML = `
<span>${subAsset.modelNumber}</span>
<span>#${subAsset.serialNumber}</span>
${subAsset.tags.map(tag => `<span class="tag">${tag}</span>`).join('')}
`;
Nothing escapes the < and > characters, not at storage time, not at render time. So all it takes is to create an asset whose name is:
<img src=x onerror=alert(document.cookie)>
The browser does not display it as text: it interprets it as HTML and runs the script, for anyone who opens the asset list. The fields that flow through this rendering path are: name, description, modelNumber, serialNumber, and tags.
Links
- CVE: CVE-2026-45231
- Repository: DumbWareio/DumbAssets
- Advisory: VulnCheck
- Fix: PR #135