mirror of
https://github.com/DerTyp7/notes-react.git
synced 2025-10-29 20:42:09 +01:00
secure user input
This commit is contained in:
Binary file not shown.
13
express_backend/securePostData.js
Normal file
13
express_backend/securePostData.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
function secure(text){
|
||||||
|
text = text.replace(/'/g, "\\u0027");
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decode(text){
|
||||||
|
text = text.replace(/\\u0027/g, "'");
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
secure, decode
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ const app = express();
|
|||||||
const sqlite3 = require('sqlite3');
|
const sqlite3 = require('sqlite3');
|
||||||
const db = new sqlite3.Database("database.db")
|
const db = new sqlite3.Database("database.db")
|
||||||
const port = process.env.PORT || 5000;
|
const port = process.env.PORT || 5000;
|
||||||
|
const securePostData = require('./securePostData');
|
||||||
// body parser
|
// body parser
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
@@ -29,7 +29,11 @@ app.get('/idea/:id', (req, res) => {
|
|||||||
res.send({title: "Error", content: "Error fetching idea"});
|
res.send({title: "Error", content: "Error fetching idea"});
|
||||||
}else{
|
}else{
|
||||||
if(rows.length > 0){
|
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{
|
}else{
|
||||||
res.send({title: "Error", content: "Idea not found"});
|
res.send({title: "Error", content: "Idea not found"});
|
||||||
}
|
}
|
||||||
@@ -42,6 +46,11 @@ app.get('/ideas', (req, res) => {
|
|||||||
if (err) {
|
if (err) {
|
||||||
res.send({title: "Error", content: "Error fetching ideas"});
|
res.send({title: "Error", content: "Error fetching ideas"});
|
||||||
}else{
|
}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);
|
res.json(rows);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -49,11 +58,40 @@ app.get('/ideas', (req, res) => {
|
|||||||
|
|
||||||
|
|
||||||
app.post('/idea/update/:id', (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) {
|
if (err) {
|
||||||
res.send({title: "Error", content: "Error updating idea"});
|
res.send({title: "Error", type:"saving", message: "Error updating idea"});
|
||||||
}else{
|
}else{
|
||||||
res.send({title: "Success", content: "Idea updated"});
|
res.send({title: "Success", type:"saving", message: "Idea updated"});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -7,25 +7,28 @@ function IdeaContent(){
|
|||||||
let [title, setTitle] = useState("");
|
let [title, setTitle] = useState("");
|
||||||
let [content, setContent] = useState("")
|
let [content, setContent] = useState("")
|
||||||
let [saved , setSaved] = useState(false);
|
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
|
// TIMER
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
setSeconds(seconds => seconds + 1);
|
setintervalTime(intervalTime => intervalTime + 1);
|
||||||
|
|
||||||
if(seconds > secondsSaveInterval){ // save every > secondsSaveInterval seconds
|
if(intervalTime > 0){
|
||||||
saveData();
|
saveData();
|
||||||
setSeconds(0);
|
setintervalTime(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}, 1000);
|
}, 500);
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
});
|
});
|
||||||
|
|
||||||
const saveData = async () => {
|
const saveData = async () => {
|
||||||
let ideaData = {
|
let ideaData = {
|
||||||
title: title,
|
title: title,
|
||||||
@@ -44,7 +47,18 @@ function IdeaContent(){
|
|||||||
|
|
||||||
if(res.title === "Success"){
|
if(res.title === "Success"){
|
||||||
setSaved(true);
|
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);
|
setSaved(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,14 +83,20 @@ function IdeaContent(){
|
|||||||
return(
|
return(
|
||||||
<div className="ideaContent">
|
<div className="ideaContent">
|
||||||
<div className="head">
|
<div className="head">
|
||||||
<input type="text" name="title"
|
<label htmlFor="title">
|
||||||
value={title}
|
<input type="text" name="title"
|
||||||
onChange={(e) => { setTitle(e.target.value); setSaved(false) }}
|
className={titleError ? "input-error" : ""}
|
||||||
placeholder="Insert a title"/>
|
value={title}
|
||||||
|
onChange={(e) => { setTitle(e.target.value); setSaved(false) }}
|
||||||
|
placeholder="Insert a title"/>
|
||||||
|
<small className="error-text">{titleError}</small>
|
||||||
|
</label>
|
||||||
|
|
||||||
<small>{saved ? "Saved!": "Saving..."}</small>
|
<small>{saved ? "Saved!": "Saving..."}</small>
|
||||||
</div>
|
</div>
|
||||||
<div className="textAreaContainer">
|
<div className="textAreaContainer">
|
||||||
<textarea value={content} onChange={(e) => { setContent(e.target.value); setSaved(false)}}></textarea>
|
<small className="error-text">{contentError}</small>
|
||||||
|
<textarea className={contentError ? "input-error" : ""} value={content} onChange={(e) => { setContent(e.target.value); setSaved(false)}}></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,38 +6,46 @@
|
|||||||
|
|
||||||
.head{
|
.head{
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 50px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
border-bottom: 2px solid rgba(87, 87, 87, 0.3);
|
border-bottom: 2px solid rgba(87, 87, 87, 0.3);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
input{
|
label{
|
||||||
background-color: transparent;
|
|
||||||
border: 0px;
|
|
||||||
border-bottom: 2px solid rgba(87, 87, 87, 0.5);
|
|
||||||
font-size: 24pt;
|
|
||||||
color: white;
|
|
||||||
outline:none;
|
|
||||||
margin-left: 50px;
|
margin-left: 50px;
|
||||||
font-weight: bold;
|
margin-bottom: 20px;
|
||||||
width: 60%;
|
width: 60%;
|
||||||
transition: 100ms;
|
display: block;
|
||||||
transition-timing-function: linear;
|
float: left;
|
||||||
|
|
||||||
&::placeholder{
|
input{
|
||||||
color: rgba(255, 255, 255, 0.44);
|
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;
|
font-weight: bold;
|
||||||
}
|
width: 100%;
|
||||||
|
transition: 100ms;
|
||||||
&:hover{
|
transition-timing-function: linear;
|
||||||
border-color: rgb(80, 209, 160);
|
|
||||||
}
|
&::placeholder{
|
||||||
|
color: rgba(255, 255, 255, 0.44);
|
||||||
&:focus{
|
font-weight: bold;
|
||||||
border-color: rgb(80, 209, 160);
|
}
|
||||||
|
|
||||||
|
&:hover{
|
||||||
|
border-color: rgb(80, 209, 160);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus{
|
||||||
|
border-color: rgb(80, 209, 160);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.textAreaContainer{
|
.textAreaContainer{
|
||||||
@@ -48,7 +56,7 @@
|
|||||||
textarea{
|
textarea{
|
||||||
outline: 0;
|
outline: 0;
|
||||||
display: block;
|
display: block;
|
||||||
width: calc(100% - 40px);
|
width: calc(100% - 43px);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: rgba(71, 71, 71, 0.171);
|
background-color: rgba(71, 71, 71, 0.171);
|
||||||
border: 0px solid transparent;
|
border: 0px solid transparent;
|
||||||
@@ -60,6 +68,31 @@
|
|||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
font-family: 'Roboto', sans-serif;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user