From 60b15a766ec746d057403823d1a24001b5ba35af Mon Sep 17 00:00:00 2001 From: nbriz Date: Fri, 8 May 2026 16:47:15 -0500 Subject: [PATCH 1/2] fixed shared sketches XSS risk --- my_modules/routes.js | 9 +- server.js | 5 +- www/core/main.js | 2 + www/core/netitor | 2 +- www/core/utils-convo.js | 12 ++- www/core/utils.js | 16 ++-- www/widgets/code-review/index.js | 95 ++++++++++++++++---- www/widgets/project-files/index.js | 4 + www/widgets/tutorial-maker/index.js | 6 +- www/widgets/tutorial-maker/popups/index.html | 2 +- 10 files changed, 123 insertions(+), 30 deletions(-) diff --git a/my_modules/routes.js b/my_modules/routes.js index 577c289a..425dc8a7 100644 --- a/my_modules/routes.js +++ b/my_modules/routes.js @@ -46,10 +46,14 @@ aliasRoutes.forEach(dep => { if (dep.url.includes('*')) { // for routes with wildcards router.get(dep.url, (req, res) => { // req.params[0] contains the wildcard path const filePath = path.join(__dirname, dep.loc, req.params[0]) + res.setHeader('Access-Control-Allow-Origin', '*') res.sendFile(filePath) }) } else { // for exact routes - router.get(dep.url, (req, res) => res.sendFile(path.join(__dirname, dep.loc))) + router.get(dep.url, (req, res) => { + res.setHeader('Access-Control-Allow-Origin', '*') + res.sendFile(path.join(__dirname, dep.loc)) + }) } }) @@ -133,7 +137,8 @@ router.get('/templates/*', (req, res) => { res.send(html) }) }) - } else { // it’s a file → serve it directly + } else { // it's a file → serve it directly + res.setHeader('Access-Control-Allow-Origin', '*') res.sendFile(requestedPath) } }) diff --git a/server.js b/server.js index 4ac93931..2853a49a 100644 --- a/server.js +++ b/server.js @@ -40,8 +40,9 @@ if (process.env.CURTAIN) { app.use('/api', utils.corsGate) app.use(ROUTES) app.use(GITHUB) -app.use(express.static(`${__dirname}/www`)) -app.use('/docs', express.static(`${__dirname}/docs`)) +const staticCORS = (res) => res.setHeader('Access-Control-Allow-Origin', '*') +app.use(express.static(`${__dirname}/www`, { setHeaders: staticCORS })) +app.use('/docs', express.static(`${__dirname}/docs`, { setHeaders: staticCORS })) app.use(ERRORS.notFound) app.use(ERRORS.errorHandler) diff --git a/www/core/main.js b/www/core/main.js index 4ac1ae62..47dd0cdc 100644 --- a/www/core/main.js +++ b/www/core/main.js @@ -87,6 +87,8 @@ nn.on('load', async () => { utils.loaderUpdate('ready') // setup custom renderer to catch errors (see on "message" below) utils.setCustomRenderer(null) + // sandbox for security + NNE.iframe.setAttribute('sandbox', 'allow-scripts allow-forms allow-popups allow-modals allow-pointer-lock') // ...check URL for params, && fade out load screen when ready if (utils.checkURL() === 'none') utils.loadDefault() }) diff --git a/www/core/netitor b/www/core/netitor index c6987751..4ef28d1e 160000 --- a/www/core/netitor +++ b/www/core/netitor @@ -1 +1 @@ -Subproject commit c69877518847a171eff9f85dfebe0b17f60d63b7 +Subproject commit 4ef28d1e2cd418e33e4d9b2c5d3dd5a09c03265a diff --git a/www/core/utils-convo.js b/www/core/utils-convo.js index 635431ba..a75b7bc6 100644 --- a/www/core/utils-convo.js +++ b/www/core/utils-convo.js @@ -77,9 +77,17 @@ window.CONVOS['utils-misc'] = (self) => { }, { before: () => { if (NNW.layout === 'welcome') NNW.layout = 'dock-left' }, id: 'agree-to-fork', - content: 'How exciting! In order to create your own remix of this project I\'m going to "fork" it to your GitHub. Forking creates an associated copy onto your account. Sounds good?', + content: 'How exciting! In order to create your own remix of this project I\'m going to "fork" it to your GitHub. Forking creates an associated copy in your account. One thing to keep in mind is that I\'ll be downloading this project\'s code and running it in your browser, so make sure you trust the creator of this repo. Sounds good?', options: { - 'let\'s do it!': (e) => utils.forkRepo(), + 'I trust it, let\'s do it!': (e) => utils.forkRepo(), + 'trust?': (e) => e.goTo('trust-code'), + 'oh, never mind': (e) => e.hide() + } + }, { + id: 'trust-code', + content: 'You didn\'t write this code, so it\'s important to trust the person who did. This is true anytime you run someone else\'s code in your environment, this could mean importing a library someone else wrote into your own project, installing a new extention in your browser or even installing an app you downloaded to your computer. If you didn\'t write the code, always make sure it\'s coming from a tusted source!', + options: { + 'I trust it, let\'s fork!': (e) => utils.forkRepo(), 'oh, never mind': (e) => e.hide() } }, { diff --git a/www/core/utils.js b/www/core/utils.js index 4ae154a1..67867378 100644 --- a/www/core/utils.js +++ b/www/core/utils.js @@ -180,10 +180,11 @@ window.utils = { function bgmovement (e) { // send mousemovments back up to netnet + // '*' required: iframe may be sandboxed (null origin) window.top.postMessage({ type: 'netnet-bg', data: { x: e.x, y: e.y } - }) + }, '*') } function reduceMotion () { @@ -209,7 +210,7 @@ window.utils = { NNE.iframe.contentWindow.postMessage({ type: 'netnet', data: { x: e.x, y: e.y, nomotion } - }) + }, '*') }, forkRepo: () => { @@ -756,9 +757,14 @@ window.utils = { // main.js listens for these errors + sends them to 'code-review' widget setCustomRenderer: (base, proxy) => { const errMsgr = `` if (!base) { NNE.customRender = function (event) { event.update(errMsgr + event.code) } diff --git a/www/widgets/code-review/index.js b/www/widgets/code-review/index.js index 81141ba3..c8a8d89c 100644 --- a/www/widgets/code-review/index.js +++ b/www/widgets/code-review/index.js @@ -13,6 +13,8 @@ class CodeReview extends Widget { this.tempCode = null this.issues = [] // issues netnet catches via linting this.error = {} // last error passed by browser console + this._resourceErrors = [] // failed resource URLs from iframe capture listener + this._runtimeError = null // last JS runtime error; persists across review() calls this._createHTML() this.on('open', () => this._opened()) @@ -21,18 +23,48 @@ class CodeReview extends Widget { Convo.load(this.key, () => { this.convos = window.CONVOS[this.key](this) }) } + /* + NOTE: this has gone through a lot of upates/changes over time, it may need + some refactoring at some point, when we do that, lets use this to test: + + http://localhost:8001/?layout=dock-left#code/eJxlUc1yhCAMvvsUGS+60y7cd9QH6KE9bF8AISqtgg1x3Z2dvntB2+l0ygFCku+HULXe3JosA6js1EMgXecD8xxOUq7rKqz2LphWaD/JLZYz4cXiKtfBMkpzMcfrdRSz63NQI9c5Y+B8Z5yUdY0QYlWWretjVMktt+t1pCb8lYyK6eh8FEDaFPOmknvbDgma7MwNxJWlLRliSJRQg/F6mdCx+FiQbmccUbOnskjl4rD1d8h6KAupZit7y8PSyiUguShQPMIdNKGJDFaN4QRFiOmjJ9tHPHzuDACCB3QlQd0AibfgXXn4WzKKVaomXWGdQ3rFK0eDT+eXZxGY4ihsd9v6fqFaJWuYgPf/0KIdvX5HE13BA+CPm/1R3pfpWsnv8WSV3H71Cw/vlGE= + + for security purposes, we've updated the way the render iframe works so that + it's now sandboxed (see main.js) which had some side-effects and required + updates to this widget and how it handled these special error cases. If we + update things in the future we need to first confirm that the sandbox is + still in place, the fetch request in the test sketch should update
+ with the "blocked" message (NOT a GitHub payload) + + we should also see 3 error markers in this sketch: + 1. a CORS error on line 3 for the + 2. a mixed-content warning for the