From f2aa3413fac9b1aa792ce0b1e95e60e15ed25077 Mon Sep 17 00:00:00 2001 From: soonnae Date: Sun, 27 Jul 2025 21:16:19 +0900 Subject: [PATCH 1/3] [ AutoFiC ] Create package.json and CI workflow --- .github/workflows/pr_notify.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/pr_notify.yml diff --git a/.github/workflows/pr_notify.yml b/.github/workflows/pr_notify.yml new file mode 100644 index 000000000..2b34036d0 --- /dev/null +++ b/.github/workflows/pr_notify.yml @@ -0,0 +1,20 @@ +name: PR Notifier + +on: + pull_request: + types: [opened, reopened, closed] + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Notify Discord + env: + DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} + run: | + curl -H "Content-Type: application/json" -d '{"content": "🔔 Pull Request [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }}) by ${{ github.event.pull_request.user.login }} - ${{ github.event.action }}"}' $DISCORD_WEBHOOK_URL + - name: Notify Slack + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + run: | + curl -H "Content-Type: application/json" -d '{"text": ":bell: Pull Request <${{ github.event.pull_request.html_url }}|${{ github.event.pull_request.title }}> by ${{ github.event.pull_request.user.login }} - ${{ github.event.action }}"}' $SLACK_WEBHOOK_URL From 011719f71859faa3aa00223c11767794d6d0f4e5 Mon Sep 17 00:00:00 2001 From: soonnae Date: Sun, 27 Jul 2025 21:16:23 +0900 Subject: [PATCH 2/3] [ AutoFiC ] 6 malicious code detected!! --- core/appHandler.js | 3 ++- core/authHandler.js | 8 +++++--- models/index.js | 5 +++-- routes/app.js | 13 ++++++++++--- routes/main.js | 18 ++++++++++++++++-- server.js | 12 ++++++++++-- 6 files changed, 46 insertions(+), 13 deletions(-) diff --git a/core/appHandler.js b/core/appHandler.js index 95e2befdd..5eca4f268 100644 --- a/core/appHandler.js +++ b/core/appHandler.js @@ -7,8 +7,9 @@ var serialize = require("node-serialize") const Op = db.Sequelize.Op module.exports.userSearch = function (req, res) { - var query = "SELECT name,id FROM Users WHERE login='" + req.body.login + "'"; + var query = "SELECT name,id FROM Users WHERE login=:login"; db.sequelize.query(query, { + replacements: { login: req.body.login }, model: db.User }).then(user => { if (user.length) { diff --git a/core/authHandler.js b/core/authHandler.js index f0e67ab5a..fe595efca 100644 --- a/core/authHandler.js +++ b/core/authHandler.js @@ -1,6 +1,6 @@ var db = require('../models') var bCrypt = require('bcrypt') -var md5 = require('md5') +var crypto = require('crypto') // Change from md5 to crypto for secure hashing module.exports.isAuthenticated = function (req, res, next) { if (req.isAuthenticated()) { @@ -46,7 +46,8 @@ module.exports.resetPw = function (req, res) { } }).then(user => { if (user) { - if (req.query.token == md5(req.query.login)) { + const token = crypto.createHash('sha256').update(req.query.login).digest('hex'); // Secure hashing + if (req.query.token == token) { res.render('resetpw', { login: req.query.login, token: req.query.token @@ -75,7 +76,8 @@ module.exports.resetPwSubmit = function (req, res) { } }).then(user => { if (user) { - if (req.body.token == md5(req.body.login)) { + const token = crypto.createHash('sha256').update(req.body.login).digest('hex'); // Secure hashing + if (req.body.token == token) { user.password = bCrypt.hashSync(req.body.password, bCrypt.genSaltSync(10), null) user.save().then(function () { req.flash('success', "Passowrd successfully reset") diff --git a/models/index.js b/models/index.js index 68067de3d..41ea2cebd 100644 --- a/models/index.js +++ b/models/index.js @@ -7,11 +7,12 @@ var env = process.env.NODE_ENV || "development"; var config = require("../config/db.js") if (process.env.DATABASE_URL) { - var sequelize = new Sequelize(process.env.DATABASE_URL); + var sequelize = new Sequelize(process.env.DATABASE_URL, { logging: false }); } else { var sequelize = new Sequelize(config.database, config.username, config.password, { host: config.host, - dialect: config.dialect + dialect: config.dialect, + logging: false }); } diff --git a/routes/app.js b/routes/app.js index 77832f9e7..49cb756f2 100644 --- a/routes/app.js +++ b/routes/app.js @@ -1,6 +1,13 @@ var router = require('express').Router() var appHandler = require('../core/appHandler') var authHandler = require('../core/authHandler') +var rateLimit = require('express-rate-limit') + +// Define a rate limiter for expensive operations +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}) module.exports = function () { router.get('/', authHandler.isAuthenticated, function (req, res) { @@ -19,7 +26,7 @@ module.exports = function () { }) }) - router.get('/bulkproducts', authHandler.isAuthenticated, function (req, res) { + router.get('/bulkproducts', authHandler.isAuthenticated, limiter, function (req, res) { res.render('app/bulkproducts',{legacy:req.query.legacy}) }) @@ -59,9 +66,9 @@ module.exports = function () { router.post('/calc', authHandler.isAuthenticated, appHandler.calc) - router.post('/bulkproducts',authHandler.isAuthenticated, appHandler.bulkProducts); + router.post('/bulkproducts',authHandler.isAuthenticated, limiter, appHandler.bulkProducts); - router.post('/bulkproductslegacy',authHandler.isAuthenticated, appHandler.bulkProductsLegacy); + router.post('/bulkproductslegacy',authHandler.isAuthenticated, limiter, appHandler.bulkProductsLegacy); return router } diff --git a/routes/main.js b/routes/main.js index b56db4040..71af0cf0a 100644 --- a/routes/main.js +++ b/routes/main.js @@ -1,6 +1,20 @@ var router = require('express').Router() var vulnDict = require('../config/vulns') var authHandler = require('../core/authHandler') +var rateLimit = require('express-rate-limit'); + +// Define a rate limiter for login and expensive operations +const loginLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 5, // limit each IP to 5 requests per windowMs + message: 'Too many login attempts from this IP, please try again after 15 minutes' +}); + +const generalLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, // limit each IP to 100 requests per windowMs + message: 'Too many requests from this IP, please try again after 15 minutes' +}); module.exports = function (passport) { router.get('/', authHandler.isAuthenticated, function (req, res) { @@ -11,7 +25,7 @@ module.exports = function (passport) { res.render('login') }) - router.get('/learn/vulnerability/:vuln', authHandler.isAuthenticated, function (req, res) { + router.get('/learn/vulnerability/:vuln', authHandler.isAuthenticated, generalLimiter, function (req, res) { res.render('vulnerabilities/layout', { vuln: req.params.vuln, vuln_title: vulnDict[req.params.vuln], @@ -48,7 +62,7 @@ module.exports = function (passport) { router.get('/resetpw', authHandler.resetPw) - router.post('/login', passport.authenticate('login', { + router.post('/login', loginLimiter, passport.authenticate('login', { successRedirect: '/learn', failureRedirect: '/login', failureFlash: true diff --git a/server.js b/server.js index 18c0e18db..4cb5d3813 100644 --- a/server.js +++ b/server.js @@ -19,12 +19,16 @@ app.use(fileUpload()); // Enable for Reverse proxy support // app.set('trust proxy', 1) +// Use Helmet to secure Express headers +const helmet = require('helmet'); +app.use(helmet()); + // Intialize Session app.use(session({ - secret: 'keyboard cat', + secret: process.env.SESSION_SECRET || 'defaultSecret', resave: true, saveUninitialized: true, - cookie: { secure: false } + cookie: { secure: true } })) // Initialize Passport @@ -34,6 +38,10 @@ app.use(passport.session()) // Initialize express-flash app.use(require('express-flash')()); +// CSRF protection +const csurf = require('csurf'); +app.use(csurf()); + // Routing app.use('/app',require('./routes/app')()) app.use('/',require('./routes/main')(passport)) From dce85c8817210806413aeab896f5cad15024ec45 Mon Sep 17 00:00:00 2001 From: soonnae Date: Sun, 27 Jul 2025 21:16:43 +0900 Subject: [PATCH 3/3] chore: remove CI workflow before upstream PR --- .github/workflows/pr_notify.yml | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 .github/workflows/pr_notify.yml diff --git a/.github/workflows/pr_notify.yml b/.github/workflows/pr_notify.yml deleted file mode 100644 index 2b34036d0..000000000 --- a/.github/workflows/pr_notify.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: PR Notifier - -on: - pull_request: - types: [opened, reopened, closed] - -jobs: - notify: - runs-on: ubuntu-latest - steps: - - name: Notify Discord - env: - DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} - run: | - curl -H "Content-Type: application/json" -d '{"content": "🔔 Pull Request [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }}) by ${{ github.event.pull_request.user.login }} - ${{ github.event.action }}"}' $DISCORD_WEBHOOK_URL - - name: Notify Slack - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - run: | - curl -H "Content-Type: application/json" -d '{"text": ":bell: Pull Request <${{ github.event.pull_request.html_url }}|${{ github.event.pull_request.title }}> by ${{ github.event.pull_request.user.login }} - ${{ github.event.action }}"}' $SLACK_WEBHOOK_URL