From 87dd3a787c62395d127f9748d61c095d2ef725a1 Mon Sep 17 00:00:00 2001 From: Menci Date: Sun, 9 Jul 2017 10:52:03 +0800 Subject: [PATCH] Fix CSRF for APIs --- app.js | 8 ++++ models/judge_state.js | 3 +- models/waiting_judge.js | 8 +++- modules/api.js | 71 ----------------------------------- modules/api_v2.js | 83 +++++++++++++++++++++++++++++++++++++++-- modules/problem.js | 3 +- 6 files changed, 97 insertions(+), 79 deletions(-) diff --git a/app.js b/app.js index de79564..7ba58e3 100644 --- a/app.js +++ b/app.js @@ -60,6 +60,14 @@ global.syzoj = { let multer = require('multer'); app.multer = multer({ dest: syzoj.utils.resolvePath(syzoj.config.upload_dir, 'tmp') }); + // Trick to bypass CSRF for APIv2 + app.use((() => { + let router = new Express.Router(); + app.apiRouter = router; + require('./modules/api_v2'); + return router; + })()); + let csurf = require('csurf'); app.use(csurf({ cookie: true })); diff --git a/models/judge_state.js b/models/judge_state.js index eb992c5..81f4de6 100644 --- a/models/judge_state.js +++ b/models/judge_state.js @@ -219,7 +219,8 @@ class JudgeState extends Model { let WaitingJudge = syzoj.model('waiting_judge'); let waiting_judge = await WaitingJudge.create({ - judge_id: this.id + judge_id: this.id, + priority: 2 }); await waiting_judge.save(); diff --git a/models/waiting_judge.js b/models/waiting_judge.js index 2b95c7f..82b15b6 100644 --- a/models/waiting_judge.js +++ b/models/waiting_judge.js @@ -26,7 +26,10 @@ let JudgeState = syzoj.model('judge_state'); let model = db.define('waiting_judge', { id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, - judge_id: { type: Sequelize.INTEGER } + judge_id: { type: Sequelize.INTEGER }, + + // Smaller is higher + priority: { type: Sequelize.INTEGER } }, { timestamps: false, tableName: 'waiting_judge', @@ -41,7 +44,8 @@ let Model = require('./common'); class WaitingJudge extends Model { static async create(val) { return WaitingJudge.fromRecord(WaitingJudge.model.build(Object.assign({ - judge_id: 0 + judge_id: 0, + priority: 0 }, val))); } diff --git a/modules/api.js b/modules/api.js index 17608fa..fb5c04c 100644 --- a/modules/api.js +++ b/modules/api.js @@ -21,8 +21,6 @@ let User = syzoj.model('user'); let Problem = syzoj.model('problem'); -let WaitingJudge = syzoj.model('waiting_judge'); -let JudgeState = syzoj.model('judge_state'); let File = syzoj.model('file'); function setLoginCookie(username, password, res) { @@ -163,75 +161,6 @@ app.post('/api/markdown', async (req, res) => { } }); -// APIs for judge client -app.post('/api/waiting_judge', async (req, res) => { - try { - if (req.query.session_id !== syzoj.config.judge_token) return res.status(404).send({ err: 'Permission denied' }); - - let judge_state; - await syzoj.utils.lock('/api/waiting_judge', async () => { - let waiting_judge = await WaitingJudge.findOne(); - if (!waiting_judge) { - return; - } - - judge_state = await waiting_judge.getJudgeState(); - await judge_state.loadRelationships(); - await judge_state.problem.loadRelationships(); - await waiting_judge.destroy(); - }); - - if (judge_state) { - await judge_state.loadRelationships(); - await judge_state.problem.loadRelationships(); - - if (judge_state.problem.type === 'submit-answer') { - res.send({ - have_task: 1, - judge_id: judge_state.id, - answer_file: judge_state.code, - testdata: judge_state.problem.id, - problem_type: judge_state.problem.type - }); - } else { - res.send({ - have_task: 1, - judge_id: judge_state.id, - code: judge_state.code, - language: judge_state.language, - testdata: judge_state.problem.id, - time_limit: judge_state.problem.time_limit, - memory_limit: judge_state.problem.memory_limit, - file_io: judge_state.problem.file_io, - file_io_input_name: judge_state.problem.file_io_input_name, - file_io_output_name: judge_state.problem.file_io_output_name, - problem_type: judge_state.problem.type - }); - } - } else { - res.send({ have_task: 0 }); - } - } catch (e) { - res.status(500).send(e); - } -}); - -app.post('/api/update_judge/:id', async (req, res) => { - try { - if (req.query.session_id !== syzoj.config.judge_token) return res.status(404).send({ err: 'Permission denied' }); - - let judge_state = await JudgeState.fromID(req.params.id); - await judge_state.updateResult(JSON.parse(req.body.result)); - await judge_state.save(); - await judge_state.updateRelatedInfo(); - - res.send({ return: 0 }); - } catch (e) { - syzoj.log(e); - res.status(500).send(e); - } -}); - app.get('/static/uploads/answer/:md5', async (req, res) => { try { res.sendFile(File.resolvePath('answer', req.params.md5)); diff --git a/modules/api_v2.js b/modules/api_v2.js index 8cf958a..4c04208 100644 --- a/modules/api_v2.js +++ b/modules/api_v2.js @@ -19,11 +19,10 @@ 'use strict'; -let Problem = syzoj.model('problem'); -let ProblemTag = syzoj.model('problem_tag'); - app.get('/api/v2/search/problems/:keyword*?', async (req, res) => { try { + let Problem = syzoj.model('problem'); + let keyword = req.params.keyword || ''; let problems = await Problem.query(null, { title: { like: `%${req.params.keyword}%` } @@ -54,6 +53,9 @@ app.get('/api/v2/search/problems/:keyword*?', async (req, res) => { app.get('/api/v2/search/tags/:keyword*?', async (req, res) => { try { + let Problem = syzoj.model('problem'); + let ProblemTag = syzoj.model('problem_tag'); + let keyword = req.params.keyword || ''; let tags = await ProblemTag.query(null, { name: { like: `%${req.params.keyword}%` } @@ -69,7 +71,7 @@ app.get('/api/v2/search/tags/:keyword*?', async (req, res) => { } }); -app.post('/api/v2/markdown', async (req, res) => { +app.apiRouter.post('/api/v2/markdown', async (req, res) => { try { let s = await syzoj.utils.markdown(req.body.s.toString(), null, req.body.noReplaceUI === 'true'); res.send(s); @@ -78,3 +80,76 @@ app.post('/api/v2/markdown', async (req, res) => { res.send(e); } }); + +// APIs for judge client +app.apiRouter.post('/api/v2/judge/peek', async (req, res) => { + try { + if (req.query.session_id !== syzoj.config.judge_token) return res.status(404).send({ err: 'Permission denied' }); + + let WaitingJudge = syzoj.model('waiting_judge'); + let JudgeState = syzoj.model('judge_state'); + + let judge_state; + await syzoj.utils.lock('/api/v2/judge/peek', async () => { + let waiting_judge = await WaitingJudge.findOne({ order: [['priority', 'ASC']] }); + if (!waiting_judge) { + return; + } + + judge_state = await waiting_judge.getJudgeState(); + await judge_state.loadRelationships(); + await judge_state.problem.loadRelationships(); + await waiting_judge.destroy(); + }); + + if (judge_state) { + await judge_state.loadRelationships(); + await judge_state.problem.loadRelationships(); + + if (judge_state.problem.type === 'submit-answer') { + res.send({ + have_task: 1, + judge_id: judge_state.id, + answer_file: judge_state.code, + testdata: judge_state.problem.id, + problem_type: judge_state.problem.type + }); + } else { + res.send({ + have_task: 1, + judge_id: judge_state.id, + code: judge_state.code, + language: judge_state.language, + testdata: judge_state.problem.id, + time_limit: judge_state.problem.time_limit, + memory_limit: judge_state.problem.memory_limit, + file_io: judge_state.problem.file_io, + file_io_input_name: judge_state.problem.file_io_input_name, + file_io_output_name: judge_state.problem.file_io_output_name, + problem_type: judge_state.problem.type + }); + } + } else { + res.send({ have_task: 0 }); + } + } catch (e) { + res.status(500).send(e); + } +}); + +app.apiRouter.post('/api/v2/judge/update/:id', async (req, res) => { + try { + if (req.query.session_id !== syzoj.config.judge_token) return res.status(404).send({ err: 'Permission denied' }); + + let JudgeState = syzoj.model('judge_state'); + let judge_state = await JudgeState.fromID(req.params.id); + await judge_state.updateResult(JSON.parse(req.body.result)); + await judge_state.save(); + await judge_state.updateRelatedInfo(); + + res.send({ return: 0 }); + } catch (e) { + syzoj.log(e); + res.status(500).send(e); + } +}); diff --git a/modules/problem.js b/modules/problem.js index 7f80cdc..1a0e564 100644 --- a/modules/problem.js +++ b/modules/problem.js @@ -633,7 +633,8 @@ app.post('/problem/:id/submit', app.multer.fields([{ name: 'answer', maxCount: 1 await judge_state.updateRelatedInfo(true); let waiting_judge = await WaitingJudge.create({ - judge_id: judge_state.id + judge_id: judge_state.id, + priority: 1 }); await waiting_judge.save();