discord.js SQL command issue - mysql

I am trying to make a rep command for my discord bot but im running into issues, This is the code i was using
con.query(`SELECT * FROM rep WHERE UserID = '${member.id}'`, (err, rows) => {
if (err) {
throw err;
}
if (!rows[0]) {
$sql = sql = `INSERT INTO rep(UserID, rep) VALUES(${ member.id }, 0)`;
connection.query(sql)
} else {
}
The issue was that it was not adding someone to the table, Basically It would add both the person that typed the command and the mentioned person into the table if they were not added in
At this point i keep messing up, Can someone lead me to a tutorial on making a rep command?

I saw problem, that some database systems are auto-converting string to int, when it is possible. But Node.JS can't hold int 64 (and ids are bigger than int 32). So try putting something as id: before the id, when you are storing it.

I saw some problems with your code that typically are wrong but I don't know in your case because I don't know how you declared the member variable or its ID, si try this and tell me what's going on, maybe a little more bit of code would help or a console error. Thanks.
con.query(`SELECT * FROM rep WHERE UserID = '${member.user.id}'`, (err, rows) => {
if (err) {
throw err;
}
if (rows.length < 1) {
$sql = `INSERT INTO rep(UserID, rep) VALUES ('${member.user.id}', '1')`; // Because he got the $rep #mention command in the first place, he got 1 rep, not 0, I guess.
connection.query(sql)
} else {
}

Related

Preventing SQL Injection from node.js using mysql driver with multipleStatements: true

I've been developing an "Employee leave management" web app project for our internal use using node.js with express and ejs template. Now, my employer wants me to make the app accessible through internet and I'm worried about SQL injection.
Let's say I have a button like this in html:
Edit
This will GET from index.js file:
const { edit } = require("./request");
app.get("/edit/:ReqID", edit);
This will then go to module edit in request.js file:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
let squery = `SELECT * FROM table1 WHERE ReqID="${ReqID}";
SELECT * FROM table2 WHERE ReqID="${ReqID}";`;
db.query(squery, function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
There might be two or more queries in there and I'm using mysql driver for node.js with multipleStatements: true and I'm aware of warning "Support for multiple statements is disabled for security reasons (it allows for SQL injection attacks if values are not properly escaped)." This will return something like http://localhost:port/edit/reqid on the browser address box. I saw a video from youtube that says SQL Injection can be done through the browser's address box like http://localhost:port/edit/reqid;";SELECT * FROM users; so I did that and for sure I can see that syntax being send to the server. So I follow the suggestion in the video to do a placeholder like this:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
let squery = `SELECT * FROM table1 WHERE ReqID= ?;
SELECT * FROM table2 WHERE ReqID= ?;`;
db.query(squery, [ReqID, ReqID], function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
Then I try the extreme http://localhost:port/edit/reqid;";DELETE FROM users; and http://localhost:port/edit/reqid;";DROP TABLE users; separately and it works! First it deletes data from users tble and for sure the second drop table command also worked. After the first attempt, I refresh the browser with the same sql injection syntax and I've got this message:
{"code":"ER_BAD_TABLE_ERROR","errno":1051,"sqlMessage":"Unknown table 'users'","sqlState":"42S02","index":1,"sql":"SELECT * FROM table1 WHERE ReqID= "ReqID;";drop table users;";SELECT * FROM table1 WHERE ReqID= "ReqID;";drop table users;";"}
So, the table users clearly have been dropped from the database.
Update:
I did further testing based on the information I gained from this answer and I did something like this:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
db.query(`SELECT * FROM table1 WHERE ReqID= ?; SELECT * FROM table2 WHERE ReqID= ?;` , [ReqID, ReqID], function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
Then I re-test with multiple variation of http://localhost:port/edit/reqid;";DROP TABLE users; (double quote in between)
http://localhost:port/edit/reqid;';DROP TABLE users; (single quote in between) etc. and it doesn't seem to be dropping the table anymore. However, I still see the statement being sent to the server so I'm still wary of the DROP syntax being effective somehow.
Update 2:
Note: Fortunately, the deployment has been delayed and I have more time to sort out the issue.
After researching for a while, taking the comments into consideration and testing multiple method, I came up with this structure:
function(req, res) {
let dcode = [req.body.dcode];
let query1 =`SELECT col1, col2 FROM table1 WHERE DCode=?`;
db.query(query1, dcode, function(err, result_1) {
if (err) {
return res.status(500).send(err);
}
let query2 =`SELECT col1, col2 FROM table2 WHERE DCode=?`;
db.query(query2, dcode, function(err, result_2) {
if (err) {
return res.status(500).send(err);
}
res.render("login.ejs", {
result1: result_1,
result2: result_2
});
});
});
}
Which is simple enough and no major change to my current codes. Would this be sufficient to prevent SQL injection in node.js?
Allowing multi-statement strings, itself, invites SQL injection. So, avoid it.
Plan A:
Consider ending an array (perhaps in JSON) to the server; let it then execute each statement, and return an array of resultsets.
But it would be simpler to simply issue the statements one at a time.
(If the client and server are far apart, the one-statement-at-a-time method may cause a noticeable latency.)
Plan B:
Build suitable Stored procedures for any multi-statement needs. This, where practical, avoids multi-statement calls. And avoids latency issues (usually).
Here are a few suggestions that might help:
Never use template strings like this: Select * from table where id = ${value}. SQL injections will happen - 100%!. Instead you should use build in driver defense mechanism. Like this: query('Select * from table where id = ?', [value]). This should prevent SQL injection.
Use single statements per query. If you need to do multiple operations in one request to database - consider creating stored procedure. Stored procedures also have build in security mechanism.
Consider using query builder or ORM. They also have additional layer of security on top of build in driver one.
You could also explicitly escape SQL string with help of 3rd party library.

Node.js doesn't execute code after MySQL Query

I have no idea what I did wrong, it's supposed to output onto console "Query Initiated" after it grabs the result but nothing is logged and I have no idea what I did wrong. Yes, I know the syntax is ugly, I ran a prettifier over it and now it is incredibly ugly and I am too lazy to manually go through 200+ lines of code to fix it.
connection.query(`SELECT * FROM pedodb WHERE ID='${msg.author.id}'`),
function (err, result) {
query.on('result', function (err2, result2) {
callback(null, rows, fields);
console.log("Query Initiated")
The callback should be associated with the query function.
And the practice of using parameters is that they should be in separate parameters otherwise there are chances that it can lead to SQL injection.
connection.query(`SELECT * FROM pedodb WHERE ID = ?`, [msg.author.id],
function (err, result, fields) {
callback(null, rows, fields);
console.log("Query Initiated")
})

SQL result lengths and showing nothing when searched

I want my Discord bot to check if a user that joined exists in a MySQL table. However, when it sends the query, it's basically telling me that it doesn't exist even though it should.
This is my current code:
bot.on('guildMemberAdd', async (member) => {
console.log(member.id)
let query = `SELECT userId FROM QR5PVGPh1D.users WHERE userId = '${member.id}'`
let result = connection.query(query)
if(result.length > 0){
console.log("It works!")
}
})
Node is asynchronous, so here you try to console.log result before it has been populated...
You'll find more info in this here :
Node JS MySQL query function not returning result
How do I return callback of MySQL query and push to an array in Node.js?
Here is the code with a callback function. Try it, it should work:
let query = `SELECT userId FROM QR5PVGPh1D.users WHERE userId = '${member.id}'`
connection.query(query, function (err, result) {
if (!err) {
if (result.length > 0) {
console.log("It works!")
}
}
});
Explanation:
As BadSpencer has stated, the mysql driver is asynchronous, and based around callbacks.
Say you're planning on picking your friend up to go to a sporting event. You're not sure when they want you to come, so you call them on the phone and ask them. They think about it for a while, and then tell you a time. You got the information you requested, so you hang up. In programming terms, this would be an example of synchronous code (sometimes thought of as "normal" code in Node.js).
Put yourself back in the same situation. However, when you call your friend this time, they're very busy. You don't want to bother them so you ask them to call you later. You hang up, but now you wait. An hour later, they call you back and tell you the time. This is the thought process of asynchronous code.
There's a lot more that goes on behind the screen, but for simplicity's sake, I'm not going to bombard you with all that information in this answer.
Solutions:
You should pass a function to act as a callback which will use the returned data. Consider this example:
let query = `SELECT userId FROM QR5PVGPh1D.users WHERE userId = '${member.id}'`;
// Passing the callback function as the second parameter.
connection.query(query, (err, result) => {
if (err) return console.error(err);
if (result.length > 0) console.log('It works (it actually does).');
});
However, this callback-based nature can become a nightmare due to the scope of the result and subsequent flow of the code. After a few queries, your code can become messy. To prevent this, you can wrap the query in your own Promise (or use a Promise-based version of the mysql package, like promise-mysql) and await the calls.
Here's an example setup:
// Defining 'connection' as a parameter so
// you can export this function and use it
// anywhere.
function query(connection, sql) {
return new Promise((resolve, reject) => {
connection.query(sql, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
}
// Asynchronous context needed for 'await' (this needs to be within an async function).
let query = `SELECT userId FROM QR5PVGPh1D.users WHERE userId = '${member.id}'`;
let result = await query(connection, query)
.catch(console.error);

NodeJs and MySQL ER_BAD_FIELD_ERROR

I have a problem I need help with a question. So the problem is I have a route to display all rows of my students table and it works, but know when I try the code below to search for a specific student_id I get:
"Failed to query for users: Error: ER_BAD_FIELD_ERROR: Unknown column 'undefined' in 'where clause'"
I have tried the queryString as:
My table has student_id, firstName, lastName, checkIn, and checkOut as columns.
They are all VARCHARS.
queryString = SELECT * FROM students WHERE student_id = ${id}
queryString = SELECT * FROM students WHERE student_id = ? and then getConnection().query(queryString, [id], (err, rows, fields) => {
But I get the same error, I would really appreciate the help.
The question that I have would be implementing after all my different search routes work as intended to display the data nicely in html, but I'm new to this and I can't find anything to help me with this problem. I would like it to display as a list almost like MySQL displays it. Is that possible? I'm using the ejs view engine as my html pages.
// Route to search by id
router.get("/searchById", (req, res) => {
const id = req.body.id
queryString = `SELECT * FROM students WHERE student_id = ${id}`
getConnection().query(queryString, (err, rows, fields) => {
// If error occures
if(err) {
console.log("Failed to query for users: " + err)
res.sendStatus(500)
return
}
if(rows) {
console.log(rows)
console.log("We fetched Students successfully")
res.json(rows)
}
})
})
For GET request you should use req.query.id
For post request you should use req.body.id
In your code you have used get request and tried to fetch req.body.id

findOne doesn't get any record

login: function (req, res) {
var username = req.param('username');
var password = req.param('password');
User.findOne({username: username}, function (err, user) {
if (err || !user) {
return res.status('401').send("User with username \"" + username + "\" not found. Are you registered?");
}
...
Gives me undefined for the user object and I always end up in the error statement. The username variable has the correct value. The method, where I create records, works (so my connection with the DB is alright), and when I look into the DB, the record is there, with all the correct data.
I also tried User.find().where({username: username}).exec(function (err, user) { with no success...
Any suggestions about where can I look into (debugging or smth) or what might be the cause of the problem?
Jonathan's comment to inspect err pointed me in the right direction. My User model was as follows:
attributes: {
// Relations
passports: {
model: 'Passport'
},
//... other attributes
and I also have the relation belongsTo declared in the Passport model. The err was giving Details: Error: ER_BAD_FIELD_ERROR: Unknown column 'users.passports' in 'field list', and of course in my user table in the database I did not have the passports column.
Conclusion: I had unnecessary entity relation declaration in the User model. This was working fine with MongoDB, but apparently with MySQL things quite different.