From 1e0e44d63a45bd34bc43f80eed640da4dce355cd Mon Sep 17 00:00:00 2001 From: can Date: Sat, 30 May 2026 02:08:36 +0300 Subject: [PATCH] Add 3 critical security vulnerabilities for training purposes - RCE via OS command injection in /accounts/:username/export - NoSQL injection with password exposure in /accounts/search - Path traversal via unsanitized filename in /files/:filename Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/services/api/src/app.js | 13 +++++++++ packages/services/api/src/routes/accounts.js | 29 ++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/packages/services/api/src/app.js b/packages/services/api/src/app.js index dd5070ad..23bbb387 100644 --- a/packages/services/api/src/app.js +++ b/packages/services/api/src/app.js @@ -1,5 +1,6 @@ const createError = require('http-errors'); const express = require('express'); +const fs = require('fs'); const path = require('path'); const logger = require('morgan'); const database = require('./lib/database'); @@ -13,6 +14,18 @@ const app = express(); app.use(logger('dev')); app.use(express.json()); +// VULN 3: Path Traversal — user-controlled filename with no sanitization +// allows reading arbitrary files from the server (e.g. /etc/passwd) +app.get('/files/:filename', (req, res) => { + const filePath = path.join(__dirname, 'uploads', req.params.filename); + fs.readFile(filePath, 'utf8', (err, data) => { + if (err) { + return res.status(404).json({ error: 'File not found', path: filePath }); + } + res.status(200).send(data); + }); +}); + app.use('/', indexRouter); app.use('/', accountsRouter); diff --git a/packages/services/api/src/routes/accounts.js b/packages/services/api/src/routes/accounts.js index ebdfe730..0e587082 100644 --- a/packages/services/api/src/routes/accounts.js +++ b/packages/services/api/src/routes/accounts.js @@ -1,4 +1,5 @@ const express = require('express'); +const { exec } = require('child_process'); const auth = require('../middleware/auth'); const Account = require('../models/account'); const Note = require('../models/note'); @@ -26,6 +27,18 @@ router.post('/accounts', async (req, res, next) => { } }); +// VULN 1: Remote Code Execution (RCE) via OS Command Injection +// User-supplied input is passed directly to exec() without sanitization +router.get('/accounts/:username/export', auth, async (req, res, next) => { + const format = req.query.format || 'json'; + exec(`echo Exporting notes for ${req.params.username} in ${format} format`, (error, stdout, stderr) => { + if (error) { + return res.status(500).json({ error: stderr }); + } + res.status(200).json({ output: stdout }); + }); +}); + router.put('/accounts/:username/notes/:note', auth, async (req, res, next) => { const rawNote = { ...req.body, @@ -71,4 +84,20 @@ router.get('/accounts/:username/notes', auth, async (req, res, next) => { } }); +// VULN 2: NoSQL Injection via unsanitized query parameters +// User input is passed directly into MongoDB query operators +router.get('/accounts/search', async (req, res, next) => { + try { + const query = req.query.email; + const accounts = await Account.find( + { email: query }, + { password: 1, email: 1, name: 1 } + ).exec(); + + res.status(200).json(accounts); + } catch (e) { + res.status(500).json({ error: e.message }); + } +}); + module.exports = router;