Merge pull request #4 from Newtbot/XSS-FOR-ALL-DONE
XSS PREVENTION FOR ALL DONE
This commit is contained in:
commit
db1b513ad5
589
Sean/server.js
589
Sean/server.js
@ -7,6 +7,7 @@ const crypto = require("crypto");
|
|||||||
const nodemailer = require("nodemailer");
|
const nodemailer = require("nodemailer");
|
||||||
const otpGenerator = require('otp-generator');
|
const otpGenerator = require('otp-generator');
|
||||||
const { body, validationResult } = require('express-validator');
|
const { body, validationResult } = require('express-validator');
|
||||||
|
const validator = require('validator');
|
||||||
|
|
||||||
const { transporter } = require("./modules/nodeMailer");
|
const { transporter } = require("./modules/nodeMailer");
|
||||||
const { connection } = require("./modules/mysql");
|
const { connection } = require("./modules/mysql");
|
||||||
@ -296,173 +297,198 @@ const logUserCreationActivity = async (creatorUsername, success, message) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
app.post("/createUser", async (req, res) => {
|
app.post(
|
||||||
try {
|
'/createUser',
|
||||||
const { name, username, email, password, jobTitle } = req.body;
|
[
|
||||||
|
body('name').trim().isLength({ min: 1 }).withMessage('Name must not be empty').escape(),
|
||||||
|
body('username').trim().isLength({ min: 1 }).withMessage('Username must not be empty').escape(),
|
||||||
|
body('email').isEmail().withMessage('Invalid email address').normalizeEmail(),
|
||||||
|
body('password').custom((value) => {
|
||||||
|
if (!isStrongPassword(value)) {
|
||||||
|
throw new Error('Password does not meet complexity requirements');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}),
|
||||||
|
body('jobTitle').trim().isLength({ min: 1 }).withMessage('Job title must not be empty').escape(),
|
||||||
|
],
|
||||||
|
async (req, res) => {
|
||||||
|
try {
|
||||||
|
const errors = validationResult(req);
|
||||||
|
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
return res.status(400).json({ errors: errors.array() });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name, username, email, password, jobTitle } = req.body;
|
||||||
|
console.log("Sanitized Input:", {
|
||||||
|
name,
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
password: "*****", // Avoid logging passwords
|
||||||
|
jobTitle,
|
||||||
|
});
|
||||||
// Extract the username of the user creating a new user
|
// Extract the username of the user creating a new user
|
||||||
const creatorUsername = req.session.username; // Adjust this based on how you store the creator's username in your session
|
const creatorUsername = req.session.username; // Adjust this based on how you store the creator's username in your session
|
||||||
|
|
||||||
// Validate password complexity
|
// Validate password complexity (additional check)
|
||||||
if (!isStrongPassword(password)) {
|
if (!isStrongPassword(password)) {
|
||||||
return res
|
return res
|
||||||
.status(400)
|
.status(400)
|
||||||
.json({ error: "Password does not meet complexity requirements" });
|
.json({ error: "Password does not meet complexity requirements" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the username is already taken
|
// Check if the username is already taken
|
||||||
const checkUsernameQuery = "SELECT * FROM users WHERE username = ?";
|
const checkUsernameQuery = "SELECT * FROM users WHERE username = ?";
|
||||||
connection.query(
|
connection.query(
|
||||||
checkUsernameQuery,
|
checkUsernameQuery,
|
||||||
[username],
|
[username],
|
||||||
(usernameQueryErr, usernameResults) => {
|
(usernameQueryErr, usernameResults) => {
|
||||||
if (usernameQueryErr) {
|
if (usernameQueryErr) {
|
||||||
console.error("Error checking username:", usernameQueryErr);
|
console.error("Error checking username:", usernameQueryErr);
|
||||||
return res.status(500).json({ error: "Internal Server Error" });
|
return res.status(500).json({ error: "Internal Server Error" });
|
||||||
}
|
|
||||||
|
|
||||||
if (usernameResults.length > 0) {
|
|
||||||
// Log unsuccessful user creation due to username taken
|
|
||||||
logUserCreationActivity(creatorUsername, false, "username taken");
|
|
||||||
return res
|
|
||||||
.status(400)
|
|
||||||
.json({
|
|
||||||
error: "Username is already taken",
|
|
||||||
message:
|
|
||||||
"Username is already taken. Please choose a different username.",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the email is already taken
|
|
||||||
const checkEmailQuery = "SELECT * FROM users WHERE email = ?";
|
|
||||||
connection.query(
|
|
||||||
checkEmailQuery,
|
|
||||||
[email],
|
|
||||||
(emailQueryErr, emailResults) => {
|
|
||||||
if (emailQueryErr) {
|
|
||||||
console.error("Error checking email:", emailQueryErr);
|
|
||||||
return res.status(500).json({ error: "Internal Server Error" });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (emailResults.length > 0) {
|
|
||||||
// Log unsuccessful user creation due to email taken
|
|
||||||
logUserCreationActivity(creatorUsername, false, "email taken");
|
|
||||||
return res
|
|
||||||
.status(400)
|
|
||||||
.json({
|
|
||||||
error: "Email is already in use",
|
|
||||||
message:
|
|
||||||
"Email is already in use. Please choose another email.",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash the password before storing it in the database
|
|
||||||
bcrypt.hash(password, 10, (hashError, hashedPassword) => {
|
|
||||||
if (hashError) {
|
|
||||||
console.error("Error hashing password:", hashError);
|
|
||||||
return res.status(500).json({ error: "Internal Server Error" });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a transaction
|
|
||||||
connection.beginTransaction((transactionErr) => {
|
|
||||||
if (transactionErr) {
|
|
||||||
console.error("Error starting transaction:", transactionErr);
|
|
||||||
return res
|
|
||||||
.status(500)
|
|
||||||
.json({ error: "Internal Server Error" });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define the insert query
|
|
||||||
const insertUserQuery =
|
|
||||||
"INSERT INTO users (name, username, email, password, lastLogin, jobTitle) VALUES (?, ?, ?, ?, NULL, ?)";
|
|
||||||
|
|
||||||
// Log the query and its parameters
|
|
||||||
console.log("Insert Query:", insertUserQuery);
|
|
||||||
console.log("Query Parameters:", [
|
|
||||||
name,
|
|
||||||
username,
|
|
||||||
email,
|
|
||||||
hashedPassword,
|
|
||||||
jobTitle,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Execute the query with user data
|
|
||||||
connection.query(
|
|
||||||
insertUserQuery,
|
|
||||||
[name, username, email, hashedPassword, jobTitle],
|
|
||||||
(queryErr, results) => {
|
|
||||||
if (queryErr) {
|
|
||||||
console.error("Error executing query:", queryErr);
|
|
||||||
|
|
||||||
// Rollback the transaction in case of an error
|
|
||||||
connection.rollback((rollbackErr) => {
|
|
||||||
if (rollbackErr) {
|
|
||||||
console.error(
|
|
||||||
"Error rolling back transaction:",
|
|
||||||
rollbackErr
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Log unsuccessful user creation due to an error
|
|
||||||
logUserCreationActivity(
|
|
||||||
creatorUsername,
|
|
||||||
false,
|
|
||||||
"internal error"
|
|
||||||
);
|
|
||||||
return res
|
|
||||||
.status(500)
|
|
||||||
.json({ error: "Internal Server Error" });
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit the transaction
|
|
||||||
connection.commit((commitErr) => {
|
|
||||||
if (commitErr) {
|
|
||||||
console.error(
|
|
||||||
"Error committing transaction:",
|
|
||||||
commitErr
|
|
||||||
);
|
|
||||||
// Log unsuccessful user creation due to an error
|
|
||||||
logUserCreationActivity(
|
|
||||||
creatorUsername,
|
|
||||||
false,
|
|
||||||
"internal error"
|
|
||||||
);
|
|
||||||
return res
|
|
||||||
.status(500)
|
|
||||||
.json({ error: "Internal Server Error" });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log successful user creation
|
|
||||||
logUserCreationActivity(
|
|
||||||
creatorUsername,
|
|
||||||
true,
|
|
||||||
"user created successfully"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Log the results of the query
|
|
||||||
console.log("Query Results:", results);
|
|
||||||
|
|
||||||
// Respond with a success message
|
|
||||||
res
|
|
||||||
.status(201)
|
|
||||||
.json({ message: "User created successfully" });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (usernameResults.length > 0) {
|
||||||
|
// Log unsuccessful user creation due to username taken
|
||||||
|
logUserCreationActivity(creatorUsername, false, "username taken");
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({
|
||||||
|
error: "Username is already taken",
|
||||||
|
message: "Username is already taken. Please choose a different username.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the email is already taken
|
||||||
|
const checkEmailQuery = "SELECT * FROM users WHERE email = ?";
|
||||||
|
connection.query(
|
||||||
|
checkEmailQuery,
|
||||||
|
[email],
|
||||||
|
(emailQueryErr, emailResults) => {
|
||||||
|
if (emailQueryErr) {
|
||||||
|
console.error("Error checking email:", emailQueryErr);
|
||||||
|
return res.status(500).json({ error: "Internal Server Error" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emailResults.length > 0) {
|
||||||
|
// Log unsuccessful user creation due to email taken
|
||||||
|
logUserCreationActivity(creatorUsername, false, "email taken");
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({
|
||||||
|
error: "Email is already in use",
|
||||||
|
message: "Email is already in use. Please choose another email.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash the password before storing it in the database
|
||||||
|
bcrypt.hash(password, 10, (hashError, hashedPassword) => {
|
||||||
|
if (hashError) {
|
||||||
|
console.error("Error hashing password:", hashError);
|
||||||
|
return res.status(500).json({ error: "Internal Server Error" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a transaction
|
||||||
|
connection.beginTransaction((transactionErr) => {
|
||||||
|
if (transactionErr) {
|
||||||
|
console.error("Error starting transaction:", transactionErr);
|
||||||
|
return res
|
||||||
|
.status(500)
|
||||||
|
.json({ error: "Internal Server Error" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define the insert query
|
||||||
|
const insertUserQuery =
|
||||||
|
"INSERT INTO users (name, username, email, password, lastLogin, jobTitle) VALUES (?, ?, ?, ?, NULL, ?)";
|
||||||
|
|
||||||
|
// Log the query and its parameters
|
||||||
|
console.log("Insert Query:", insertUserQuery);
|
||||||
|
console.log("Query Parameters:", [
|
||||||
|
name,
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
hashedPassword,
|
||||||
|
jobTitle,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Execute the query with user data
|
||||||
|
connection.query(
|
||||||
|
insertUserQuery,
|
||||||
|
[name, username, email, hashedPassword, jobTitle],
|
||||||
|
(queryErr, results) => {
|
||||||
|
if (queryErr) {
|
||||||
|
console.error("Error executing query:", queryErr);
|
||||||
|
|
||||||
|
// Rollback the transaction in case of an error
|
||||||
|
connection.rollback((rollbackErr) => {
|
||||||
|
if (rollbackErr) {
|
||||||
|
console.error(
|
||||||
|
"Error rolling back transaction:",
|
||||||
|
rollbackErr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Log unsuccessful user creation due to an error
|
||||||
|
logUserCreationActivity(
|
||||||
|
creatorUsername,
|
||||||
|
false,
|
||||||
|
"internal error"
|
||||||
|
);
|
||||||
|
return res
|
||||||
|
.status(500)
|
||||||
|
.json({ error: "Internal Server Error" });
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit the transaction
|
||||||
|
connection.commit((commitErr) => {
|
||||||
|
if (commitErr) {
|
||||||
|
console.error(
|
||||||
|
"Error committing transaction:",
|
||||||
|
commitErr
|
||||||
|
);
|
||||||
|
// Log unsuccessful user creation due to an error
|
||||||
|
logUserCreationActivity(
|
||||||
|
creatorUsername,
|
||||||
|
false,
|
||||||
|
"internal error"
|
||||||
|
);
|
||||||
|
return res
|
||||||
|
.status(500)
|
||||||
|
.json({ error: "Internal Server Error" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log successful user creation
|
||||||
|
logUserCreationActivity(
|
||||||
|
creatorUsername,
|
||||||
|
true,
|
||||||
|
"user created successfully"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Log the results of the query
|
||||||
|
console.log("Query Results:", results);
|
||||||
|
|
||||||
|
// Respond with a success message
|
||||||
|
res
|
||||||
|
.status(201)
|
||||||
|
.json({ message: "User created successfully" });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error creating user:", error);
|
console.error("Error creating user:", error);
|
||||||
// Log unsuccessful user creation due to an error
|
// Log unsuccessful user creation due to an error
|
||||||
logUserCreationActivity(req.session.username, false, "internal error"); // Adjust this based on how you store the creator's username in your session
|
logUserCreationActivity(req.session.username, false, "internal error"); // Adjust this based on how you store the creator's username in your session
|
||||||
res.status(500).json({ error: "Internal Server Error" });
|
res.status(500).json({ error: "Internal Server Error" });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
app.get("/forgot-password", (req, res) => {
|
app.get("/forgot-password", (req, res) => {
|
||||||
res.render("forgot-password"); // Assuming you have an EJS template for this
|
res.render("forgot-password"); // Assuming you have an EJS template for this
|
||||||
@ -476,70 +502,73 @@ app.get("/forgot-password", (req, res) => {
|
|||||||
app.post("/forgot-password", (req, res) => {
|
app.post("/forgot-password", (req, res) => {
|
||||||
const { usernameOrEmail } = req.body;
|
const { usernameOrEmail } = req.body;
|
||||||
|
|
||||||
|
// Sanitize the input
|
||||||
|
const sanitizedUsernameOrEmail = validator.escape(usernameOrEmail);
|
||||||
|
|
||||||
// Perform the logic for sending the reset password email
|
// Perform the logic for sending the reset password email
|
||||||
|
|
||||||
// Check if the username or email exists in the database
|
// Check if the username or email exists in the database
|
||||||
const checkUserQuery = "SELECT * FROM users WHERE username = ? OR email = ?";
|
const checkUserQuery = "SELECT * FROM users WHERE username = ? OR email = ?";
|
||||||
connection.query(
|
connection.query(
|
||||||
checkUserQuery,
|
checkUserQuery,
|
||||||
[usernameOrEmail, usernameOrEmail],
|
[sanitizedUsernameOrEmail, sanitizedUsernameOrEmail],
|
||||||
(checkError, checkResults) => {
|
(checkError, checkResults) => {
|
||||||
if (checkError) {
|
if (checkError) {
|
||||||
console.error("Error checking user:", checkError);
|
console.error("Error checking user:", checkError);
|
||||||
const error = "An error occurred during the password reset process.";
|
const error = "An error occurred during the password reset process.";
|
||||||
res.render("forgot-password", { error, success: null });
|
res.render("forgot-password", { error, success: null });
|
||||||
} else if (checkResults.length === 0) {
|
} else if (checkResults.length === 0) {
|
||||||
const error = "Username or email not found.";
|
const error = "Username or email not found.";
|
||||||
res.render("forgot-password", { error, success: null });
|
res.render("forgot-password", { error, success: null });
|
||||||
} else {
|
} else {
|
||||||
// Assuming the user exists, generate a reset token and send an email
|
// Assuming the user exists, generate a reset token and send an email
|
||||||
const user = checkResults[0];
|
const user = checkResults[0];
|
||||||
const resetToken = crypto.randomBytes(20).toString("hex");
|
const resetToken = crypto.randomBytes(20).toString("hex");
|
||||||
const resetTokenExpiry = new Date(Date.now() + 3600000); // Token expires in 1 hour
|
const resetTokenExpiry = new Date(Date.now() + 3600000); // Token expires in 1 hour
|
||||||
|
|
||||||
// Update user with reset token and expiry
|
// Update user with reset token and expiry
|
||||||
const updateQuery =
|
const updateQuery =
|
||||||
"UPDATE users SET reset_token = ?, reset_token_expiry = ? WHERE id = ?";
|
"UPDATE users SET reset_token = ?, reset_token_expiry = ? WHERE id = ?";
|
||||||
connection.query(
|
connection.query(
|
||||||
updateQuery,
|
updateQuery,
|
||||||
[resetToken, resetTokenExpiry, user.id],
|
[resetToken, resetTokenExpiry, user.id],
|
||||||
(updateError) => {
|
(updateError) => {
|
||||||
if (updateError) {
|
if (updateError) {
|
||||||
console.error("Error updating reset token:", updateError);
|
console.error("Error updating reset token:", updateError);
|
||||||
const error =
|
const error =
|
||||||
"An error occurred during the password reset process.";
|
"An error occurred during the password reset process.";
|
||||||
res.render("forgot-password", { error, success: null });
|
res.render("forgot-password", { error, success: null });
|
||||||
} else {
|
} else {
|
||||||
// Send email with reset link
|
// Send email with reset link
|
||||||
const resetLink = `http://localhost:3000/reset-password/${resetToken}`;
|
const resetLink = `http://localhost:3000/reset-password/${resetToken}`;
|
||||||
const mailOptions = {
|
const mailOptions = {
|
||||||
to: user.email,
|
to: user.email,
|
||||||
subject: "Password Reset",
|
subject: "Password Reset",
|
||||||
text: `Click on the following link to reset your password: ${resetLink}`,
|
text: `Click on the following link to reset your password: ${resetLink}`,
|
||||||
};
|
};
|
||||||
transporter.sendMail(mailOptions, (emailError, info) => {
|
transporter.sendMail(mailOptions, (emailError, info) => {
|
||||||
if (emailError) {
|
if (emailError) {
|
||||||
console.error("Error sending email:", emailError);
|
console.error("Error sending email:", emailError);
|
||||||
const error =
|
const error =
|
||||||
"An error occurred during the password reset process.";
|
"An error occurred during the password reset process.";
|
||||||
res.render("forgot-password", { error, success: null });
|
res.render("forgot-password", { error, success: null });
|
||||||
} else {
|
} else {
|
||||||
console.log("Email sent: " + info.response);
|
console.log("Email sent: " + info.response);
|
||||||
const success =
|
const success =
|
||||||
"Password reset email sent successfully. Check your inbox.";
|
"Password reset email sent successfully. Check your inbox.";
|
||||||
res.render("forgot-password", { error: null, success });
|
res.render("forgot-password", { error: null, success });
|
||||||
|
|
||||||
// Log the successful sending of the reset link in the database
|
// Log the successful sending of the reset link in the database
|
||||||
logPasswordResetActivity(user.username,"link sent successfully");
|
logPasswordResetActivity(user.username,"link sent successfully");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Function to log the password reset activity in the database
|
// Function to log the password reset activity in the database
|
||||||
function logPasswordResetActivity(username, activity) {
|
function logPasswordResetActivity(username, activity) {
|
||||||
@ -559,72 +588,77 @@ app.post("/reset-password/:token", async (req, res) => {
|
|||||||
const { token } = req.params;
|
const { token } = req.params;
|
||||||
const { password, confirmPassword } = req.body;
|
const { password, confirmPassword } = req.body;
|
||||||
|
|
||||||
|
// Sanitize the inputs
|
||||||
|
const sanitizedToken = validator.escape(token);
|
||||||
|
const sanitizedPassword = validator.escape(password);
|
||||||
|
const sanitizedConfirmPassword = validator.escape(confirmPassword);
|
||||||
|
|
||||||
// Find user with matching reset token and not expired
|
// Find user with matching reset token and not expired
|
||||||
const selectQuery =
|
const selectQuery =
|
||||||
"SELECT * FROM users WHERE reset_token = ? AND reset_token_expiry > NOW()";
|
"SELECT * FROM users WHERE reset_token = ? AND reset_token_expiry > NOW()";
|
||||||
connection.query(selectQuery, [token], async (selectErr, selectResults) => {
|
connection.query(selectQuery, [sanitizedToken], async (selectErr, selectResults) => {
|
||||||
if (selectErr) {
|
if (selectErr) {
|
||||||
console.error("Error querying reset token:", selectErr);
|
console.error("Error querying reset token:", selectErr);
|
||||||
return res.status(500).json({ error: "Error querying reset token" });
|
return res.status(500).json({ error: "Error querying reset token" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectResults.length === 0) {
|
if (selectResults.length === 0) {
|
||||||
// Pass the error to the template when rendering the reset-password page
|
// Pass the error to the template when rendering the reset-password page
|
||||||
return res.render("reset-password", {
|
return res.render("reset-password", {
|
||||||
token,
|
token,
|
||||||
resetError: "Invalid or expired reset token",
|
resetError: "Invalid or expired reset token",
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if passwords match
|
|
||||||
if (password !== confirmPassword) {
|
|
||||||
// Pass the error to the template when rendering the reset-password page
|
|
||||||
return res.render("reset-password", {
|
|
||||||
token,
|
|
||||||
resetError: "Passwords do not match",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the new password meets complexity requirements
|
|
||||||
if (!isStrongPassword(password)) {
|
|
||||||
// Pass the error to the template when rendering the reset-password page
|
|
||||||
return res.render("reset-password", {
|
|
||||||
token,
|
|
||||||
resetError:
|
|
||||||
"Password does not meet complexity requirements. It must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash the new password
|
|
||||||
const hashedPassword = await bcrypt.hash(password, 10);
|
|
||||||
|
|
||||||
// Update user's password and clear reset token
|
|
||||||
const updateQuery =
|
|
||||||
"UPDATE users SET password = ?, reset_token = NULL, reset_token_expiry = NULL WHERE reset_token = ?";
|
|
||||||
connection.query(updateQuery, [hashedPassword, token], async (updateErr, updateResults) => {
|
|
||||||
if (updateErr) {
|
|
||||||
console.error("Error updating password:", updateErr);
|
|
||||||
// Pass the error to the template when rendering the reset-password page
|
|
||||||
res.render("reset-password", {
|
|
||||||
token,
|
|
||||||
resetError: "Error updating password",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Log password reset activity
|
|
||||||
const username = selectResults[0].username; // Assuming 'username' is the column name
|
|
||||||
const logQuery = "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, 'Password Reseted successfully', NOW())";
|
|
||||||
connection.query(logQuery, [username], (logErr) => {
|
|
||||||
if (logErr) {
|
|
||||||
console.error("Error logging password reset:", logErr);
|
|
||||||
// You might want to handle the logging error here
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Redirect to the success page upon successful password reset
|
|
||||||
res.redirect("/success");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if passwords match
|
||||||
|
if (sanitizedPassword !== sanitizedConfirmPassword) {
|
||||||
|
// Pass the error to the template when rendering the reset-password page
|
||||||
|
return res.render("reset-password", {
|
||||||
|
token,
|
||||||
|
resetError: "Passwords do not match",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the new password meets complexity requirements
|
||||||
|
if (!isStrongPassword(sanitizedPassword)) {
|
||||||
|
// Pass the error to the template when rendering the reset-password page
|
||||||
|
return res.render("reset-password", {
|
||||||
|
token,
|
||||||
|
resetError:
|
||||||
|
"Password does not meet complexity requirements. It must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash the new password
|
||||||
|
const hashedPassword = await bcrypt.hash(sanitizedPassword, 10);
|
||||||
|
|
||||||
|
// Update user's password and clear reset token
|
||||||
|
const updateQuery =
|
||||||
|
"UPDATE users SET password = ?, reset_token = NULL, reset_token_expiry = NULL WHERE reset_token = ?";
|
||||||
|
connection.query(updateQuery, [hashedPassword, sanitizedToken], async (updateErr, updateResults) => {
|
||||||
|
if (updateErr) {
|
||||||
|
console.error("Error updating password:", updateErr);
|
||||||
|
// Pass the error to the template when rendering the reset-password page
|
||||||
|
res.render("reset-password", {
|
||||||
|
token,
|
||||||
|
resetError: "Error updating password",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Log password reset activity
|
||||||
|
const username = selectResults[0].username; // Assuming 'username' is the column name
|
||||||
|
const logQuery = "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, 'Password Reseted successfully', NOW())";
|
||||||
|
connection.query(logQuery, [username], (logErr) => {
|
||||||
|
if (logErr) {
|
||||||
|
console.error("Error logging password reset:", logErr);
|
||||||
|
// You might want to handle the logging error here
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Redirect to the success page upon successful password reset
|
||||||
|
res.redirect("/success");
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/success", (req, res) => {
|
app.get("/success", (req, res) => {
|
||||||
res.render("success");
|
res.render("success");
|
||||||
@ -643,15 +677,20 @@ app.get("/reset-password/:token", (req, res) => {
|
|||||||
});
|
});
|
||||||
app.post("/reset-password", async (req, res) => {
|
app.post("/reset-password", async (req, res) => {
|
||||||
const { username, password, confirmPassword } = req.body;
|
const { username, password, confirmPassword } = req.body;
|
||||||
const creatorUsername = req.session.username;
|
const creatorUsername = req.session.username;
|
||||||
|
|
||||||
|
// Sanitize the inputs
|
||||||
|
const sanitizedUsername = validator.escape(username);
|
||||||
|
const sanitizedPassword = validator.escape(password);
|
||||||
|
const sanitizedConfirmPassword = validator.escape(confirmPassword);
|
||||||
|
|
||||||
// Check if passwords match
|
// Check if passwords match
|
||||||
if (password !== confirmPassword) {
|
if (sanitizedPassword !== sanitizedConfirmPassword) {
|
||||||
return res.status(400).json({ error: "Passwords do not match" });
|
return res.status(400).json({ error: "Passwords do not match" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the new password meets complexity requirements
|
// Check if the new password meets complexity requirements
|
||||||
if (!isStrongPassword(password)) {
|
if (!isStrongPassword(sanitizedPassword)) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
error:
|
error:
|
||||||
"Password does not meet complexity requirements. It must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.",
|
"Password does not meet complexity requirements. It must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.",
|
||||||
@ -659,10 +698,10 @@ app.post("/reset-password", async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Hash the new password
|
// Hash the new password
|
||||||
const hashedPassword = await bcrypt.hash(password, 10);
|
const hashedPassword = await bcrypt.hash(sanitizedPassword, 10);
|
||||||
|
|
||||||
// Check if the user exists in the database before updating the password
|
// Check if the user exists in the database before updating the password
|
||||||
const userExists = await checkIfUserExists(username);
|
const userExists = await checkIfUserExists(sanitizedUsername);
|
||||||
|
|
||||||
if (!userExists) {
|
if (!userExists) {
|
||||||
return res.status(404).json({ error: "User does not exist" });
|
return res.status(404).json({ error: "User does not exist" });
|
||||||
@ -670,7 +709,7 @@ app.post("/reset-password", async (req, res) => {
|
|||||||
|
|
||||||
// Update user's password based on the username
|
// Update user's password based on the username
|
||||||
const updateQuery = "UPDATE users SET password = ? WHERE username = ?";
|
const updateQuery = "UPDATE users SET password = ? WHERE username = ?";
|
||||||
connection.query(updateQuery, [hashedPassword, username], async (updateErr, updateResults) => {
|
connection.query(updateQuery, [hashedPassword, sanitizedUsername], async (updateErr, updateResults) => {
|
||||||
if (updateErr) {
|
if (updateErr) {
|
||||||
console.error("Error updating password:", updateErr);
|
console.error("Error updating password:", updateErr);
|
||||||
return res.status(500).json({ error: "Error updating password" });
|
return res.status(500).json({ error: "Error updating password" });
|
||||||
@ -680,7 +719,7 @@ app.post("/reset-password", async (req, res) => {
|
|||||||
if (updateResults.affectedRows > 0) {
|
if (updateResults.affectedRows > 0) {
|
||||||
// Log password reset activity
|
// Log password reset activity
|
||||||
const logQuery = "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, ?, NOW())";
|
const logQuery = "INSERT INTO user_logs (username, activity, timestamp) VALUES (?, ?, NOW())";
|
||||||
const logActivity = `Password has been reset for ${username}`;
|
const logActivity = `Password has been reset for ${sanitizedUsername}`;
|
||||||
connection.query(logQuery, [creatorUsername, logActivity], (logErr) => {
|
connection.query(logQuery, [creatorUsername, logActivity], (logErr) => {
|
||||||
if (logErr) {
|
if (logErr) {
|
||||||
console.error("Error logging password reset:", logErr);
|
console.error("Error logging password reset:", logErr);
|
||||||
@ -712,9 +751,13 @@ async function checkIfUserExists(username) {
|
|||||||
}
|
}
|
||||||
app.get('/searchUser', (req, res) => {
|
app.get('/searchUser', (req, res) => {
|
||||||
const { username } = req.query;
|
const { username } = req.query;
|
||||||
|
|
||||||
|
// Sanitize the input
|
||||||
|
const sanitizedUsername = validator.escape(username);
|
||||||
|
|
||||||
const query = 'SELECT * FROM users WHERE username = ?';
|
const query = 'SELECT * FROM users WHERE username = ?';
|
||||||
|
|
||||||
connection.query(query, [username], (err, results) => {
|
connection.query(query, [sanitizedUsername], (err, results) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error('MySQL query error:', err);
|
console.error('MySQL query error:', err);
|
||||||
res.status(500).json({ success: false, error: 'Internal Server Error' });
|
res.status(500).json({ success: false, error: 'Internal Server Error' });
|
||||||
|
107
package-lock.json
generated
107
package-lock.json
generated
@ -323,6 +323,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||||
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="
|
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="
|
||||||
},
|
},
|
||||||
|
"deepmerge": {
|
||||||
|
"version": "4.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
||||||
|
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
|
||||||
|
},
|
||||||
"define-data-property": {
|
"define-data-property": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
|
||||||
@ -363,6 +368,39 @@
|
|||||||
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
|
||||||
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="
|
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="
|
||||||
},
|
},
|
||||||
|
"dom-serializer": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||||
|
"requires": {
|
||||||
|
"domelementtype": "^2.3.0",
|
||||||
|
"domhandler": "^5.0.2",
|
||||||
|
"entities": "^4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domelementtype": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
|
||||||
|
},
|
||||||
|
"domhandler": {
|
||||||
|
"version": "5.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||||
|
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||||
|
"requires": {
|
||||||
|
"domelementtype": "^2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domutils": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
||||||
|
"requires": {
|
||||||
|
"dom-serializer": "^2.0.0",
|
||||||
|
"domelementtype": "^2.3.0",
|
||||||
|
"domhandler": "^5.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"dotenv": {
|
"dotenv": {
|
||||||
"version": "16.3.1",
|
"version": "16.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
||||||
@ -401,11 +439,21 @@
|
|||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
|
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
|
||||||
},
|
},
|
||||||
|
"entities": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
|
||||||
|
},
|
||||||
"escape-html": {
|
"escape-html": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||||
},
|
},
|
||||||
|
"escape-string-regexp": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
|
||||||
|
},
|
||||||
"etag": {
|
"etag": {
|
||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||||
@ -734,6 +782,17 @@
|
|||||||
"function-bind": "^1.1.2"
|
"function-bind": "^1.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"htmlparser2": {
|
||||||
|
"version": "8.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
||||||
|
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
||||||
|
"requires": {
|
||||||
|
"domelementtype": "^2.3.0",
|
||||||
|
"domhandler": "^5.0.3",
|
||||||
|
"domutils": "^3.0.1",
|
||||||
|
"entities": "^4.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"http-errors": {
|
"http-errors": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||||
@ -792,6 +851,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
|
||||||
},
|
},
|
||||||
|
"is-plain-object": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
|
||||||
|
},
|
||||||
"is-property": {
|
"is-property": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
|
||||||
@ -993,6 +1057,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nanoid": {
|
||||||
|
"version": "3.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||||
|
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g=="
|
||||||
|
},
|
||||||
"negotiator": {
|
"negotiator": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||||
@ -1102,6 +1171,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
|
||||||
},
|
},
|
||||||
|
"parse-srcset": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
|
||||||
|
},
|
||||||
"parseurl": {
|
"parseurl": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
@ -1127,11 +1201,26 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
|
||||||
"integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA=="
|
"integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA=="
|
||||||
},
|
},
|
||||||
|
"picocolors": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
|
||||||
|
},
|
||||||
"pngjs": {
|
"pngjs": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
||||||
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw=="
|
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw=="
|
||||||
},
|
},
|
||||||
|
"postcss": {
|
||||||
|
"version": "8.4.32",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
|
||||||
|
"integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
|
||||||
|
"requires": {
|
||||||
|
"nanoid": "^3.3.7",
|
||||||
|
"picocolors": "^1.0.0",
|
||||||
|
"source-map-js": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"proxy-addr": {
|
"proxy-addr": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
@ -1214,6 +1303,19 @@
|
|||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
},
|
},
|
||||||
|
"sanitize-html": {
|
||||||
|
"version": "2.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.11.0.tgz",
|
||||||
|
"integrity": "sha512-BG68EDHRaGKqlsNjJ2xUB7gpInPA8gVx/mvjO743hZaeMCZ2DwzW7xvsqZ+KNU4QKwj86HJ3uu2liISf2qBBUA==",
|
||||||
|
"requires": {
|
||||||
|
"deepmerge": "^4.2.2",
|
||||||
|
"escape-string-regexp": "^4.0.0",
|
||||||
|
"htmlparser2": "^8.0.0",
|
||||||
|
"is-plain-object": "^5.0.0",
|
||||||
|
"parse-srcset": "^1.0.2",
|
||||||
|
"postcss": "^8.3.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "7.5.4",
|
"version": "7.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||||
@ -1344,6 +1446,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
|
||||||
},
|
},
|
||||||
|
"source-map-js": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
|
||||||
|
},
|
||||||
"sqlstring": {
|
"sqlstring": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
"otp-generator": "^4.0.1",
|
"otp-generator": "^4.0.1",
|
||||||
"otplib": "^12.0.1",
|
"otplib": "^12.0.1",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
|
"sanitize-html": "^2.11.0",
|
||||||
"sequelize": "^6.35.2",
|
"sequelize": "^6.35.2",
|
||||||
"validator": "^13.11.0"
|
"validator": "^13.11.0"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user