From 12dd3f9e53a7f71a4b5e01ab05bfcea3f7247d8f Mon Sep 17 00:00:00 2001 From: "j.mei7" Date: Sun, 3 Apr 2022 17:42:22 +0200 Subject: [PATCH] secure user input --- express_backend/database.db | Bin 212992 -> 212992 bytes express_backend/securePostData.js | 13 ++++ express_backend/server.js | 48 +++++++++++++-- react_frontend/src/IdeaContent.js | 46 ++++++++++---- react_frontend/src/css/IdeaContent.scss | 77 +++++++++++++++++------- 5 files changed, 144 insertions(+), 40 deletions(-) create mode 100644 express_backend/securePostData.js diff --git a/express_backend/database.db b/express_backend/database.db index 57d94fc3bc00baf92bcca2af31767df915018bbc..9e04cfcb5662b7f498ba847f8c9dc09d9235e143 100644 GIT binary patch delta 311 zcmZo@;B9E&ogl@;Y&22E2}o{CSj5j&%fQ5+%*nrnKY5~~uNZ2T8HtGl43i#T&IlGD~uxa+yH6^8B2fl2nD<#2kgx zq|`hG+<;$#iG@KNq93R#u{h=MN`;tG0|O&-{9lpaWdFdhXEDF}IZj{*Y{5EhpOdj;YYro$_n6xtLR+Z33VDKIMl0Fdxg AIsgCw delta 279 zcmZo@;B9E&ogl@;^k<@s6Oi1Pu!x`Q6$2B00vmrVf5JpZ$<3+)xA`V-kWsGZf5i|Y z+9)5AT3nJ?oB~1RDJjJ$fPsNw4uf`+cuGoI3K&4trARZeFes}!=B1<-r7C!)q^5?a z7AYiU7MCcbl-j2 { res.send({title: "Error", content: "Error fetching idea"}); }else{ if(rows.length > 0){ - res.json(rows[0]); + // De-Formatting + rows[0].title = securePostData.decode(rows[0].title); + rows[0].content = securePostData.decode(rows[0].content); + + res.json(rows[0]); }else{ res.send({title: "Error", content: "Idea not found"}); } @@ -42,6 +46,11 @@ app.get('/ideas', (req, res) => { if (err) { res.send({title: "Error", content: "Error fetching ideas"}); }else{ + for (let i = 0; i < rows.length; i++) { + // De-Formatting + rows[i].title = securePostData.decode(rows[i].title); + rows[i].content = securePostData.decode(rows[i].content); + } res.json(rows); } }); @@ -49,11 +58,40 @@ app.get('/ideas', (req, res) => { app.post('/idea/update/:id', (req, res) => { - db.run(`UPDATE ideas SET title = '${req.body.title}', content = '${req.body.content}' WHERE id = ${req.params.id}`, (err) => { + + // Validate POST + if(!req.body.title || req.body.title.replace(/\s/g, '').length === 0){ + res.send({title: "Error", type:"title", message: "Title is required"}); + return; + }else if(!req.body.content || req.body.content.replace(/\s/g, '').length === 0){ + res.send({title: "Error", type:"content", message: "Content is required"}); + return; + } + + let regexPattern = /^[a-zA-ZÀ-úÀ-ÿÀ-ÿÀ-ÖØ-öø-ÿ0-9ßäöüÄÖÜ!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?`´\s]*$/; + + + if(!regexPattern.test(req.body.title)){ + res.send({title: "Error", type:"title", message: "Title contains invalid characters"}); + return; + }else if(!regexPattern.test(req.body.content)){ + res.send({title: "Error", type:"content", message: "Content contains invalid characters"}); + return; + } + + // replace ' with \u0027 + let title = securePostData.secure(req.body.title); + let content = securePostData.secure(req.body.content); + + + + + + db.run(`UPDATE ideas SET title = '${title}', content = '${content}' WHERE id = ${req.params.id}`, (err) => { if (err) { - res.send({title: "Error", content: "Error updating idea"}); + res.send({title: "Error", type:"saving", message: "Error updating idea"}); }else{ - res.send({title: "Success", content: "Idea updated"}); + res.send({title: "Success", type:"saving", message: "Idea updated"}); } }); }); \ No newline at end of file diff --git a/react_frontend/src/IdeaContent.js b/react_frontend/src/IdeaContent.js index c6ea322..005d836 100644 --- a/react_frontend/src/IdeaContent.js +++ b/react_frontend/src/IdeaContent.js @@ -7,25 +7,28 @@ function IdeaContent(){ let [title, setTitle] = useState(""); let [content, setContent] = useState("") let [saved , setSaved] = useState(false); - let [seconds, setSeconds] = useState(0); + let [intervalTime, setintervalTime] = useState(0); + let [contentError, setContentError] = useState(""); + let [titleError, setTitleError] = useState(""); - let secondsSaveInterval = 3; + + //let secondsSaveInterval = 0; // TIMER useEffect(() => { const interval = setInterval(() => { - setSeconds(seconds => seconds + 1); + setintervalTime(intervalTime => intervalTime + 1); - if(seconds > secondsSaveInterval){ // save every > secondsSaveInterval seconds + if(intervalTime > 0){ saveData(); - setSeconds(0); + setintervalTime(0); } - }, 1000); + }, 500); return () => clearInterval(interval); }); - + const saveData = async () => { let ideaData = { title: title, @@ -44,7 +47,18 @@ function IdeaContent(){ if(res.title === "Success"){ setSaved(true); - } else { + setContentError(""); + setTitleError(""); + } else if(res.title === "Error"){ + if(res.type === "content"){ + setContentError(res.message); + setTitleError(""); + } else if(res.type === "title"){ + setTitleError(res.message); + setContentError(""); + } + setSaved(false); + }else{ setSaved(false); } } @@ -69,14 +83,20 @@ function IdeaContent(){ return(
- { setTitle(e.target.value); setSaved(false) }} - placeholder="Insert a title"/> + + {saved ? "Saved!": "Saving..."}
- + {contentError} +
) diff --git a/react_frontend/src/css/IdeaContent.scss b/react_frontend/src/css/IdeaContent.scss index 4ae7d5a..5db78b3 100644 --- a/react_frontend/src/css/IdeaContent.scss +++ b/react_frontend/src/css/IdeaContent.scss @@ -6,38 +6,46 @@ .head{ padding-top: 20px; - padding-bottom: 20px; + padding-bottom: 50px; margin-bottom: 20px; border-bottom: 2px solid rgba(87, 87, 87, 0.3); width: 100%; display: block; - input{ - background-color: transparent; - border: 0px; - border-bottom: 2px solid rgba(87, 87, 87, 0.5); - font-size: 24pt; - color: white; - outline:none; + label{ margin-left: 50px; - font-weight: bold; + margin-bottom: 20px; width: 60%; - transition: 100ms; - transition-timing-function: linear; + display: block; + float: left; - &::placeholder{ - color: rgba(255, 255, 255, 0.44); + input{ + background-color: transparent; + border: 0px; + border-bottom: 2px solid rgba(87, 87, 87, 0.5); + font-size: 24pt; + color: white; + outline:none; font-weight: bold; - } - - &:hover{ - border-color: rgb(80, 209, 160); - } - - &:focus{ - border-color: rgb(80, 209, 160); + width: 100%; + transition: 100ms; + transition-timing-function: linear; + + &::placeholder{ + color: rgba(255, 255, 255, 0.44); + font-weight: bold; + } + + &:hover{ + border-color: rgb(80, 209, 160); + } + + &:focus{ + border-color: rgb(80, 209, 160); + } } } + } .textAreaContainer{ @@ -48,7 +56,7 @@ textarea{ outline: 0; display: block; - width: calc(100% - 40px); + width: calc(100% - 43px); height: 100%; background-color: rgba(71, 71, 71, 0.171); border: 0px solid transparent; @@ -60,6 +68,31 @@ padding-top: 10px; padding-bottom: 10px; font-family: 'Roboto', sans-serif; + border-left: 3px solid transparent; + transition: 100ms; + transition-timing-function: linear; + + &:hover{ + border-color: rgb(80, 209, 160); + } + + &:focus{ + border-color: rgb(80, 209, 160); + } + } + + .error-text{ + padding-left: 23px; } } + + .input-error{ + border-color: rgb(255, 45, 45) !important; + } + + .error-text{ + color:rgb(255, 45, 45); + font-weight: bold; + cursor: text; + } } \ No newline at end of file