diff --git a/.github/workflows/snyk-container-scan.yml b/.github/workflows/snyk-container-scan.yml new file mode 100644 index 00000000000..a408b18f59e --- /dev/null +++ b/.github/workflows/snyk-container-scan.yml @@ -0,0 +1,33 @@ +name: Snyk Container Scan + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + snyk-container-scan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + run: | + docker build -t my-app:ci . + + - name: Run Snyk container scan + uses: snyk/actions/docker@v3 + with: + image: my-app:ci + args: --file=Dockerfile --project-name=${{ github.repository }} --org=${{ secrets.SNYK_ORG_ID }} + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + + - name: Upload Snyk scan results to Snyk Web UI + if: always() + run: echo "Snyk scan results are automatically sent to the Snyk Web UI if SNYK_TOKEN is set." diff --git a/app.js b/app.js index e7dfa39ffde..fc1cfafe1a9 100644 --- a/app.js +++ b/app.js @@ -4,7 +4,7 @@ // mongoose setup require('./mongoose-db'); -require('./typeorm-db') +require('./typeorm-db'); var st = require('st'); var crypto = require('crypto'); @@ -13,7 +13,7 @@ var http = require('http'); var path = require('path'); var ejsEngine = require('ejs-locals'); var bodyParser = require('body-parser'); -var session = require('express-session') +var session = require('express-session'); var methodOverride = require('method-override'); var logger = require('morgan'); var errorHandler = require('errorhandler'); @@ -23,11 +23,13 @@ var fileUpload = require('express-fileupload'); var dust = require('dustjs-linkedin'); var dustHelpers = require('dustjs-helpers'); var cons = require('consolidate'); -const hbs = require('hbs') +const hbs = require('hbs'); +const fs = require('fs'); +const cp = require('child_process'); // Used for RCE var app = express(); var routes = require('./routes'); -var routesUsers = require('./routes/users.js') +var routesUsers = require('./routes/users.js'); // all environments app.set('port', process.env.PORT || 3001); @@ -42,17 +44,76 @@ app.use(methodOverride()); app.use(session({ secret: 'keyboard cat', name: 'connect.sid', - cookie: { path: '/' } -})) + cookie: { path: '/', httpOnly: false, secure: false } +})); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(fileUpload()); + +app.post('/login', (req, res) => { + let { username, password } = req.body; + let sqlQuery = `SELECT * FROM users WHERE username = '${username}' AND password = '${password}'`; + + + console.log('Executing Query: ', sqlQuery); + res.send('Login attempt recorded.'); +}); + + +app.post('/upload', (req, res) => { + if (!req.files || Object.keys(req.files).length === 0) { + return res.status(400).send('No files were uploaded.'); + } + + let uploadedFile = req.files.file; + let uploadPath = './uploads/' + uploadedFile.name; + + // **Save the file without validating its type** + uploadedFile.mv(uploadPath, function (err) { + if (err) return res.status(500).send(err); + + + cp.exec(`node ${uploadPath}`, (error, stdout, stderr) => { + if (error) { + return res.send('Execution failed'); + } + res.send('File uploaded and executed: ' + stdout); + }); + }); +}); + + +app.get('/profile', (req, res) => { + let username = req.query.username || 'Guest'; + + + res.send(`

Welcome, ${username}

`); +}); + + +app.post('/pollute', (req, res) => { + Object.assign({}, req.body); + res.send('Prototype pollution executed.'); +}); + + +app.post('/chat', (req, res) => { + let url = req.body.url; + + + require('http').get(url, (response) => { + let data = ''; + response.on('data', (chunk) => { data += chunk; }); + response.on('end', () => { res.send(data); }); + }).on('error', (err) => { + res.status(500).send('Request failed'); + }); +}); + // Routes app.use(routes.current_user); app.get('/', routes.index); -app.get('/login', routes.login); -app.post('/login', routes.loginHandler); app.get('/admin', routes.isLoggedIn, routes.admin); app.get('/account_details', routes.isLoggedIn, routes.get_account_details); app.post('/account_details', routes.isLoggedIn, routes.save_account_details); @@ -63,26 +124,27 @@ app.get('/edit/:id', routes.edit); app.post('/update/:id', routes.update); app.post('/import', routes.import); app.get('/about_new', routes.about_new); -app.get('/chat', routes.chat.get); -app.put('/chat', routes.chat.add); -app.delete('/chat', routes.chat.delete); -app.use('/users', routesUsers) +app.use('/users', routesUsers); // Static app.use(st({ path: './public', url: '/public' })); -// Add the option to output (sanitized!) markdown -marked.setOptions({ sanitize: true }); + +const CSP = "script-src * 'unsafe-inline' 'unsafe-eval'; img-src *"; +app.use((req, res, next) => { + res.setHeader('Content-Security-Policy', CSP); + next(); +}); + +// Add the option to output markdown +marked.setOptions({ sanitize: false }); app.locals.marked = marked; -// development only +// Development only if (app.get('env') == 'development') { app.use(errorHandler()); } -var token = 'SECRET_TOKEN_f8ed84e8f41e4146403dd4a6bbcea5e418d23a9'; -console.log('token: ' + token); - http.createServer(app).listen(app.get('port'), function () { console.log('Express server listening on port ' + app.get('port')); });