mirror of
https://github.com/aljazceru/CTFd.git
synced 2025-12-18 06:24:23 +01:00
Add length error content that is too long (#1787)
* Add length checking to some sensitive fields in Pages and Challenges. * Works on #1786 This is enough to fix most of the issues but this is really a systemic problem for most of the API endpoints. We should have something that verifies data consistency. Marshmallow is not good enough at this. Pydantic seems like it would be superior here.
This commit is contained in:
@@ -23,6 +23,7 @@ from CTFd.models import (
|
||||
db,
|
||||
)
|
||||
from CTFd.plugins.challenges import CHALLENGE_CLASSES, get_chal_class
|
||||
from CTFd.schemas.challenges import ChallengeSchema
|
||||
from CTFd.schemas.flags import FlagSchema
|
||||
from CTFd.schemas.hints import HintSchema
|
||||
from CTFd.schemas.tags import TagSchema
|
||||
@@ -223,6 +224,13 @@ class ChallengeList(Resource):
|
||||
)
|
||||
def post(self):
|
||||
data = request.form or request.get_json()
|
||||
|
||||
# Load data through schema for validation but not for insertion
|
||||
schema = ChallengeSchema()
|
||||
response = schema.load(data)
|
||||
if response.errors:
|
||||
return {"success": False, "errors": response.errors}, 400
|
||||
|
||||
challenge_type = data["type"]
|
||||
challenge_class = get_chal_class(challenge_type)
|
||||
challenge = challenge_class.create(request)
|
||||
@@ -427,6 +435,14 @@ class Challenge(Resource):
|
||||
},
|
||||
)
|
||||
def patch(self, challenge_id):
|
||||
data = request.get_json()
|
||||
|
||||
# Load data through schema for validation but not for insertion
|
||||
schema = ChallengeSchema()
|
||||
response = schema.load(data)
|
||||
if response.errors:
|
||||
return {"success": False, "errors": response.errors}, 400
|
||||
|
||||
challenge = Challenges.query.filter_by(id=challenge_id).first_or_404()
|
||||
challenge_class = get_chal_class(challenge.type)
|
||||
challenge = challenge_class.update(challenge, request)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from marshmallow import ValidationError, pre_load
|
||||
|
||||
from CTFd.models import Challenges, ma
|
||||
|
||||
|
||||
@@ -6,3 +8,30 @@ class ChallengeSchema(ma.ModelSchema):
|
||||
model = Challenges
|
||||
include_fk = True
|
||||
dump_only = ("id",)
|
||||
|
||||
@pre_load
|
||||
def validate_name(self, data):
|
||||
name = data.get("name", "")
|
||||
if len(name) > 80:
|
||||
raise ValidationError(
|
||||
"Challenge could not be saved. Challenge name too long",
|
||||
field_names=["name"],
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def validate_category(self, data):
|
||||
category = data.get("category", "")
|
||||
if len(category) > 80:
|
||||
raise ValidationError(
|
||||
"Challenge could not be saved. Challenge category too long",
|
||||
field_names=["category"],
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def validate_description(self, data):
|
||||
description = data.get("description", "")
|
||||
if len(description) >= 65536:
|
||||
raise ValidationError(
|
||||
"Challenge could not be saved. Challenge description is too long.",
|
||||
field_names=["description"],
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from marshmallow import pre_load
|
||||
from marshmallow import ValidationError, pre_load
|
||||
|
||||
from CTFd.models import Pages, ma
|
||||
from CTFd.utils import string_types
|
||||
@@ -10,11 +10,34 @@ class PageSchema(ma.ModelSchema):
|
||||
include_fk = True
|
||||
dump_only = ("id",)
|
||||
|
||||
@pre_load
|
||||
def validate_title(self, data):
|
||||
title = data.get("title", "")
|
||||
if len(title) > 128:
|
||||
raise ValidationError(
|
||||
"Page could not be saved. Your title is too long.",
|
||||
field_names=["title"],
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def validate_route(self, data):
|
||||
route = data.get("route")
|
||||
if route and route.startswith("/"):
|
||||
route = data.get("route", "")
|
||||
if route.startswith("/"):
|
||||
data["route"] = route.strip("/")
|
||||
if len(route) > 128:
|
||||
raise ValidationError(
|
||||
"Page could not be saved. Your route is too long.",
|
||||
field_names=["route"],
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def validate_content(self, data):
|
||||
content = data.get("content", "")
|
||||
if len(content) >= 65536:
|
||||
raise ValidationError(
|
||||
"Page could not be saved. Your content is too long.",
|
||||
field_names=["content"],
|
||||
)
|
||||
|
||||
def __init__(self, view=None, *args, **kwargs):
|
||||
if view:
|
||||
|
||||
@@ -149,6 +149,18 @@ function loadChalTemplate(challenge) {
|
||||
response.data.id
|
||||
);
|
||||
$("#challenge-create-options").modal();
|
||||
} else {
|
||||
let body = "";
|
||||
for (const k in response.errors) {
|
||||
body += response.errors[k].join("\n");
|
||||
body += "\n";
|
||||
}
|
||||
|
||||
ezAlert({
|
||||
title: "Error",
|
||||
body: body,
|
||||
button: "OK"
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -385,6 +397,18 @@ $(() => {
|
||||
title: "Success",
|
||||
body: "Your challenge has been updated!"
|
||||
});
|
||||
} else {
|
||||
let body = "";
|
||||
for (const k in response.errors) {
|
||||
body += response.errors[k].join("\n");
|
||||
body += "\n";
|
||||
}
|
||||
|
||||
ezAlert({
|
||||
title: "Error",
|
||||
body: body,
|
||||
button: "OK"
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import $ from "jquery";
|
||||
import CTFd from "core/CTFd";
|
||||
import CodeMirror from "codemirror";
|
||||
import "codemirror/mode/htmlmixed/htmlmixed.js";
|
||||
import { ezToast } from "core/ezq";
|
||||
import { ezAlert, ezToast } from "core/ezq";
|
||||
import Vue from "vue/dist/vue.esm.browser";
|
||||
import CommentBox from "../components/comments/CommentBox.vue";
|
||||
|
||||
@@ -35,6 +35,22 @@ function submit_form() {
|
||||
return response.json();
|
||||
})
|
||||
.then(function(response) {
|
||||
// Show errors reported by API
|
||||
if (response.success === false) {
|
||||
let body = "";
|
||||
for (const k in response.errors) {
|
||||
body += response.errors[k].join("\n");
|
||||
body += "\n";
|
||||
}
|
||||
|
||||
ezAlert({
|
||||
title: "Error",
|
||||
body: body,
|
||||
button: "OK"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (method === "PATCH" && response.success) {
|
||||
ezToast({
|
||||
title: "Saved",
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -162,7 +162,7 @@
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
;
|
||||
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _styles = __webpack_require__(/*! ../styles */ \"./CTFd/themes/admin/assets/js/styles.js\");\n\n__webpack_require__(/*! core/utils */ \"./CTFd/themes/core/assets/js/utils.js\");\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _codemirror = _interopRequireDefault(__webpack_require__(/*! codemirror */ \"./node_modules/codemirror/lib/codemirror.js\"));\n\n__webpack_require__(/*! codemirror/mode/htmlmixed/htmlmixed.js */ \"./node_modules/codemirror/mode/htmlmixed/htmlmixed.js\");\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nvar _vueEsm = _interopRequireDefault(__webpack_require__(/*! vue/dist/vue.esm.browser */ \"./node_modules/vue/dist/vue.esm.browser.js\"));\n\nvar _CommentBox = _interopRequireDefault(__webpack_require__(/*! ../components/comments/CommentBox.vue */ \"./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\n\nfunction submit_form() {\n // Save the CodeMirror data to the Textarea\n window.editor.save();\n var params = (0, _jquery[\"default\"])(\"#page-edit\").serializeJSON();\n var target = \"/api/v1/pages\";\n var method = \"POST\";\n var part = window.location.pathname.split(\"/\").pop();\n\n if (part !== \"new\") {\n target += \"/\" + part;\n method = \"PATCH\";\n }\n\n _CTFd[\"default\"].fetch(target, {\n method: method,\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(params)\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n if (method === \"PATCH\" && response.success) {\n (0, _ezq.ezToast)({\n title: \"Saved\",\n body: \"Your changes have been saved\"\n });\n } else {\n window.location = _CTFd[\"default\"].config.urlRoot + \"/admin/pages/\" + response.data.id;\n }\n });\n}\n\nfunction preview_page() {\n window.editor.save(); // Save the CodeMirror data to the Textarea\n\n (0, _jquery[\"default\"])(\"#page-edit\").attr(\"action\", _CTFd[\"default\"].config.urlRoot + \"/admin/pages/preview\");\n (0, _jquery[\"default\"])(\"#page-edit\").attr(\"target\", \"_blank\");\n (0, _jquery[\"default\"])(\"#page-edit\").submit();\n}\n\n(0, _jquery[\"default\"])(function () {\n window.editor = _codemirror[\"default\"].fromTextArea(document.getElementById(\"admin-pages-editor\"), {\n lineNumbers: true,\n lineWrapping: true,\n mode: \"htmlmixed\",\n htmlMode: true\n });\n (0, _jquery[\"default\"])(\"#media-button\").click(function (_e) {\n (0, _styles.showMediaLibrary)(window.editor);\n });\n (0, _jquery[\"default\"])(\"#save-page\").click(function (e) {\n e.preventDefault();\n submit_form();\n });\n (0, _jquery[\"default\"])(\".preview-page\").click(function () {\n preview_page();\n }); // Insert CommentBox element\n\n if (window.PAGE_ID) {\n var commentBox = _vueEsm[\"default\"].extend(_CommentBox[\"default\"]);\n\n var vueContainer = document.createElement(\"div\");\n document.querySelector(\"#comment-box\").appendChild(vueContainer);\n new commentBox({\n propsData: {\n type: \"page\",\n id: window.PAGE_ID\n }\n }).$mount(vueContainer);\n }\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/editor.js?");
|
||||
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _styles = __webpack_require__(/*! ../styles */ \"./CTFd/themes/admin/assets/js/styles.js\");\n\n__webpack_require__(/*! core/utils */ \"./CTFd/themes/core/assets/js/utils.js\");\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _codemirror = _interopRequireDefault(__webpack_require__(/*! codemirror */ \"./node_modules/codemirror/lib/codemirror.js\"));\n\n__webpack_require__(/*! codemirror/mode/htmlmixed/htmlmixed.js */ \"./node_modules/codemirror/mode/htmlmixed/htmlmixed.js\");\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nvar _vueEsm = _interopRequireDefault(__webpack_require__(/*! vue/dist/vue.esm.browser */ \"./node_modules/vue/dist/vue.esm.browser.js\"));\n\nvar _CommentBox = _interopRequireDefault(__webpack_require__(/*! ../components/comments/CommentBox.vue */ \"./CTFd/themes/admin/assets/js/components/comments/CommentBox.vue\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\n\nfunction submit_form() {\n // Save the CodeMirror data to the Textarea\n window.editor.save();\n var params = (0, _jquery[\"default\"])(\"#page-edit\").serializeJSON();\n var target = \"/api/v1/pages\";\n var method = \"POST\";\n var part = window.location.pathname.split(\"/\").pop();\n\n if (part !== \"new\") {\n target += \"/\" + part;\n method = \"PATCH\";\n }\n\n _CTFd[\"default\"].fetch(target, {\n method: method,\n credentials: \"same-origin\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(params)\n }).then(function (response) {\n return response.json();\n }).then(function (response) {\n // Show errors reported by API\n if (response.success === false) {\n var body = \"\";\n\n for (var k in response.errors) {\n body += response.errors[k].join(\"\\n\");\n body += \"\\n\";\n }\n\n (0, _ezq.ezAlert)({\n title: \"Error\",\n body: body,\n button: \"OK\"\n });\n return;\n }\n\n if (method === \"PATCH\" && response.success) {\n (0, _ezq.ezToast)({\n title: \"Saved\",\n body: \"Your changes have been saved\"\n });\n } else {\n window.location = _CTFd[\"default\"].config.urlRoot + \"/admin/pages/\" + response.data.id;\n }\n });\n}\n\nfunction preview_page() {\n window.editor.save(); // Save the CodeMirror data to the Textarea\n\n (0, _jquery[\"default\"])(\"#page-edit\").attr(\"action\", _CTFd[\"default\"].config.urlRoot + \"/admin/pages/preview\");\n (0, _jquery[\"default\"])(\"#page-edit\").attr(\"target\", \"_blank\");\n (0, _jquery[\"default\"])(\"#page-edit\").submit();\n}\n\n(0, _jquery[\"default\"])(function () {\n window.editor = _codemirror[\"default\"].fromTextArea(document.getElementById(\"admin-pages-editor\"), {\n lineNumbers: true,\n lineWrapping: true,\n mode: \"htmlmixed\",\n htmlMode: true\n });\n (0, _jquery[\"default\"])(\"#media-button\").click(function (_e) {\n (0, _styles.showMediaLibrary)(window.editor);\n });\n (0, _jquery[\"default\"])(\"#save-page\").click(function (e) {\n e.preventDefault();\n submit_form();\n });\n (0, _jquery[\"default\"])(\".preview-page\").click(function () {\n preview_page();\n }); // Insert CommentBox element\n\n if (window.PAGE_ID) {\n var commentBox = _vueEsm[\"default\"].extend(_CommentBox[\"default\"]);\n\n var vueContainer = document.createElement(\"div\");\n document.querySelector(\"#comment-box\").appendChild(vueContainer);\n new commentBox({\n propsData: {\n type: \"page\",\n id: window.PAGE_ID\n }\n }).$mount(vueContainer);\n }\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/editor.js?");
|
||||
|
||||
/***/ })
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user