Skip to content
Permalink
Browse files

Hardened endpoints with Joi

  • Loading branch information...
chen-robert committed Jul 3, 2019
1 parent 6529d5f commit 2163e5bfea4b690058248a8f8e1c789bfa1cbfff
Showing with 137 additions and 49 deletions.
  1. +41 −0 package-lock.json
  2. +2 −1 package.json
  3. +4 −1 server/admin/config.js
  4. +5 −1 server/routes/contest.js
  5. +58 −39 server/routes/grader.js
  6. +18 −6 server/routes/login.js
  7. +9 −1 views/pages/login.ejs

Some generated files are not rendered by default. Learn more.

@@ -9,12 +9,13 @@
"db:init": "node server/db/init.js",
"dev": "nodemon -e js,ejs,less,txt,in,out,json --watch index.js --watch server --watch problems index.js",
"heroku-postbuild": "npm run build",
"format": "prettier \"**/*.{js,less,html}\" --write",
"format": "prettier \"**/*.{js,less}\" --write",
"start": "node index.js"
},
"author": "Robert Chen",
"license": "MIT",
"dependencies": {
"@hapi/joi": "^15.1.0",
"async": "^3.1.0",
"autoprefixer": "^9.6.0",
"bcryptjs": "^2.4.3",
@@ -4,7 +4,10 @@ const { renderWithPopups } = require(__rootdir + "/server/util");

const fs = require("fs");
const updateConfigFile = () => {
fs.writeFileSync(__rootdir + "/config.json", JSON.stringify(config));
fs.writeFileSync(
__rootdir + "/config.json",
JSON.stringify(config, undefined, 2)
);
};

router.get("/", (req, res) =>
@@ -1,14 +1,18 @@
const loadProblems = require(__rootdir + "/server/problems");
const { renderWithPopups } = require(__rootdir + "/server/util");

const { languages } = require(__rootdir + "/config");
console.log(languages);

const md = new require("markdown-it")();

const router = require("express").Router();

router.get("/", (req, res) =>
renderWithPopups(req, res, "pages/contest/index", {
problems: loadProblems(),
md
md,
languages
})
);

@@ -1,4 +1,8 @@
const fs = require("fs");
const Joi = require("@hapi/joi");

const config = require(__rootdir + "/config");

const { testData } = require(__rootdir + "/server/problemData");

const { startGrading, finishGrading, getSolves } = require(__rootdir +
@@ -77,6 +81,14 @@ router.get("/submissions", (req, res) => {
});
router.get("/user", (req, res) => res.send({ uid: req.session.uid }));

const graderSchema = Joi.object().keys({
pid: Joi.string().required(),
lang: Joi.string()
.valid(config.languages)
.required(),
file: Joi.any()
});

router.post(
"/submit",
(req, res, next) => {
@@ -89,51 +101,58 @@ router.post(
});
},
(req, res) => {
const tests = testData[req.body.pid];

if (req.file === undefined) {
req.session.error = "Please upload a file";
} else if (tests === undefined) {
req.session.error = "Testcases not available. Please contact an admin.";
} else {
const data = fs.readFileSync(req.file.path, "utf8");
const expected = {};
for (let i = 0; i < tests.length; i++) {
expected[tests[i].name] = tests[i].stdout;
graderSchema.validate(req.body, (err, val) => {
if(err){
return res.status(400).send("Invalid parameters");
}
const {pid, lang} = req.body;

const tests = testData[pid];

if (req.file === undefined) {
req.session.error = "Please upload a file";
} else if (tests === undefined) {
req.session.error = "Testcases not available. Please contact an admin.";
} else {
const data = fs.readFileSync(req.file.path, "utf8");
const expected = {};
for (let i = 0; i < tests.length; i++) {
expected[tests[i].name] = tests[i].stdout;
}

const time = startGrading(req.session.uid, req.body.pid);

request(
{
method: "POST",
body: {
lang: req.body.lang,
source: data,
compile: compileLimits,
execute: execLimits,
tests
const time = startGrading(req.session.uid, pid);

request(
{
method: "POST",
body: {
lang: lang,
source: data,
compile: compileLimits,
execute: execLimits,
tests
},
json: true,
url: `${api}/run`
},
json: true,
url: `${api}/run`
},
(err, response, body) => {
let code;
if (err) {
code = "ENDPOINT_ERROR";
} else {
code = status(body, expected);
(err, response, body) => {
let code;
if (err) {
code = "ENDPOINT_ERROR";
} else {
code = status(body, expected);
}

finishGrading(req.session.uid, time, pid, code);
}
);

finishGrading(req.session.uid, time, req.body.pid, code);
}
req.session.message = "Successfully submitted";
}
return res.redirect(
"/contest#" + pid.toLowerCase().replace(/ /g, "-")
);

req.session.message = "Successfully submitted";
}
return res.redirect(
"/contest#" + req.body.pid.toLowerCase().replace(/ /g, "-")
);
});
}
);

@@ -1,4 +1,5 @@
const router = require("express").Router();
const Joi = require("@hapi/joi");

const { getPopups } = require(__rootdir + "/server/util");
const { checkLogin } = require(__rootdir + "/server/db");
@@ -12,16 +13,27 @@ router.get("/", (req, res) => {
});
});

const loginSchema = Joi.object().keys({
username: Joi.string().required(),
password: Joi.string().required()
});

router.post("/", (req, res) => {
checkLogin(req.body.username, req.body.password, (err, data) => {
loginSchema.validate(req.body, (err, val) => {
if (err) {
req.session.error = err;
return res.redirect("back");
return res.status(400).send("Invalid login parameters");
}
const { username, password } = val;
checkLogin(username, password, (err, data) => {
if (err) {
req.session.error = err;
return res.redirect("back");
}

req.session.uid = data.id;
req.session.username = req.body.username;
return res.redirect("/contest");
req.session.uid = data.id;
req.session.username = username;
return res.redirect("/contest");
});
});
});

@@ -8,9 +8,17 @@
<div class="login">
<form class="form form__centered" action="/login" method="POST">
<h1 class="login--title">IPC</h1>
<input autofocus required class="form--input" placeholder="Username" name="username" />
<input
autofocus
required
autocomplete="username"
class="form--input"
placeholder="Username"
name="username"
/>
<input
required
autocomplete="current-password"
class="form--input"
type="password"
placeholder="Password"

0 comments on commit 2163e5b

Please sign in to comment.
You can’t perform that action at this time.