← back to Approvals Viewer
chore: harden /api/undo (path-traversal guard + decision whitelist), session-close lint
a188fefe48acd6c79775811e5f36b04e3f9b2918 · 2026-08-11 13:37:08 -0700 · Steve Abrams
Files touched
Diff
commit a188fefe48acd6c79775811e5f36b04e3f9b2918
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Aug 11 13:37:08 2026 -0700
chore: harden /api/undo (path-traversal guard + decision whitelist), session-close lint
---
server.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server.js b/server.js
index fa67f7f..440a109 100644
--- a/server.js
+++ b/server.js
@@ -37,7 +37,7 @@ http.createServer((req,res)=>{
fs.appendFileSync(LOG,JSON.stringify({ts:new Date().toISOString(),file,decision})+'\n');send(res,200,'{"ok":true}');
}catch(e){send(res,400,JSON.stringify({error:String(e.message)}));}});return;}
if(req.url==='/api/undo'&&req.method==='POST'){let d='';req.on('data',c=>d+=c);req.on('end',()=>{try{
- const{file,decision}=JSON.parse(d);const from=decision==='approve'?APPROVED:REJECTED;const src=path.join(from,file);
+ const{file,decision}=JSON.parse(d);if(!/^[\w.\-]+\.md$/.test(file))throw new Error('bad file');if(decision!=='approve'&&decision!=='reject')throw new Error('bad decision');const from=decision==='approve'?APPROVED:REJECTED;const src=path.join(from,file);
if(fs.existsSync(src))fs.renameSync(src,path.join(QUEUE,file));
fs.appendFileSync(LOG,JSON.stringify({ts:new Date().toISOString(),file,decision:'undo:'+decision})+'\n');send(res,200,'{"ok":true}');
}catch(e){send(res,400,JSON.stringify({error:String(e.message)}));}});return;}
← f3b563d approvals-viewer UI: always show action buttons + fetch erro
·
back to Approvals Viewer
·
nav-agent: universal grid-controls drop-in on internal dashb af6b411 →