Skip to content
Draft
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
npm-debug.log
5 changes: 3 additions & 2 deletions .env
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

PORT=2000
PORT=3000
JWT_SECRET=askfeed1234

[email protected]
PASS=*Saheb13*
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
.env
Empty file added .scannerwork/.sonar_lock
Empty file.
6 changes: 6 additions & 0 deletions .scannerwork/report-task.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
projectKey=token
serverUrl=http://dev-sonarqube.springrole.com:9000
serverVersion=8.9.1.44547
dashboardUrl=http://dev-sonarqube.springrole.com:9000/dashboard?id=token
ceTaskId=AXoaL8nL8zulQfXeM_9Q
ceTaskUrl=http://dev-sonarqube.springrole.com:9000/api/ce/task?id=AXoaL8nL8zulQfXeM_9Q
15 changes: 15 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"configurations": [
{
"name": "Docker Node.js Launch",
"type": "docker",
"request": "launch",
"preLaunchTask": "docker-run: debug",
"platform": "node",
"node": {
"package": "${workspaceFolder}/node_modules/which-module/package.json",
"localRoot": "${workspaceFolder}/node_modules/which-module"
}
}
]
}
46 changes: 46 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "docker-build",
"label": "docker-build",
"platform": "node",
"dockerBuild": {
"dockerfile": "${workspaceFolder}/node_modules/which-module/Dockerfile",
"context": "${workspaceFolder}/node_modules/which-module",
"pull": true
},
"node": {
"package": "${workspaceFolder}/node_modules/which-module/package.json"
}
},
{
"type": "docker-run",
"label": "docker-run: release",
"dependsOn": [
"docker-build"
],
"platform": "node",
"node": {
"package": "${workspaceFolder}/node_modules/which-module/package.json"
}
},
{
"type": "docker-run",
"label": "docker-run: debug",
"dependsOn": [
"docker-build"
],
"dockerRun": {
"env": {
"DEBUG": "*",
"NODE_ENV": "development"
}
},
"node": {
"package": "${workspaceFolder}/node_modules/which-module/package.json",
"enableDebugging": true
}
}
]
}
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM node:14.16

WORKDIR /ask-feed

COPY package.json /ask-feed/package.json

RUN npm install

COPY . /ask-feed

EXPOSE 3000

CMD ["npm","start"]
11 changes: 8 additions & 3 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const cors = require("cors");
const bcryptjs = require("bcryptjs");
require("dotenv").config();
require("./db/connectionDB");
require("./models/user");
require("./models/survey");
const authRoutes = require("./routes/auth");
const surveyRoutes = require("./routes/survey");

Expand All @@ -14,7 +16,10 @@ app.use(express.json());
app.use(cors());
app.use("/survey", surveyRoutes);
app.use("/api", authRoutes);

app.listen(process.env.PORT || 2000, () => {
console.log("server running on port 2000");
app.get("/health", function (req, res) {
const message = "Running on port " + process.env.PORT;
return res.send(message);
});
app.listen(process.env.PORT || 3000, () => {
console.log("server running on port 3000");
});
118 changes: 87 additions & 31 deletions controllers/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,10 @@
const bcryptjs = require("bcryptjs");
const jwt = require("jsonwebtoken");

const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: "[email protected]",
pass: "*Saheb13*",
},
});
const { transporter } = require("../utils/transporter");

const signup = async (req, res) => {
try{
const { username, password, email, phoneNo } = req.body;
const userexist = await User.findOne({ email });
if (!userexist) {
Expand All @@ -26,16 +21,32 @@
phoneNo,
token,
};

const user = await User.create(newUser);
if (user) {
const data = {
from: "[email protected]",
to: email,
subject: "Account activation link",
html: `<h2>PLease click on given link to activate your account</h2>
<p>http://localhost:2000/api/activate/${token}</p>
`,
subject: "This is your Email verification link",
html: ` <hr style="height:2px;border-width:0;color:gray;background-color:gray">
<div style="text-align:center;">
<img src="cid:askfeedlogo" width="150px" height="150px">
<h2 style="color:black;">Please click on the button to verify your mail!<br>We’re very excited to have you on board!</h2>
<button style="background-color:#3362a8" target="_blank"><p input type="button" onclick="window.location.href='link' "><a style="color:white; text-decoration:none;" href="http://localhost:3000/api/activate/${token}">VERIFY EMAIL</a></p> </button>

<hr style="height:2px;border-width:0;color:gray;background-color:gray">

</div>
</div>
</div>
</body>
</html>`,
attachments: [
{
filename: "askfeedlogo.jpeg",
path: `${__dirname}/images/askfeedlogo.jpeg`,
cid: "askfeedlogo",
},
],
};
try {
await transporter.sendMail(data);
Expand All @@ -48,6 +59,11 @@
} else {
res.send("User already exist!");
}
}
catch(e)
{
throw new Error("Failed to create the function")
}
};

const verifyAccount = async (req, res) => {
Expand Down Expand Up @@ -78,7 +94,6 @@
const resetlink = async (req, res) => {
const { email } = req.body;
const user = await User.findOne({ email });
// console.log("user", user, email);
if (user) {
const token = jwt.sign({ email }, process.env.JWT_SECRET, {
expiresIn: "30m",
Expand All @@ -95,10 +110,26 @@
const data = {
from: "[email protected]",
to: email,
subject: "Reset link",
html: `<h2>PLease click on given link to reset your account</h2>
<p>http://localhost:2000/api/changepassword/?token=${token}</p>
`,
subject: "This is your account reset link",
html: ` <hr style="height:2px;border-width:0;color:gray;background-color:gray">
<div style="text-align:center;">
<img src="cid:askfeedlogo" width="150px" height="150px">
<h2 style="color:black;">This is your account reset link!<br>Please click on the link to reset your password!</h2>
<button style="background-color:#3362a8" target="_blank"><p input type="button" onclick="window.location.href='link' "><a style="color:white; text-decoration:none;" href="http://localhost:3000/api/changepassword/${token}">RESET PASSWORD</a></p> </button>
<hr style="height:2px;border-width:0;color:gray;background-color:gray">

</div>
</div>
</div>
</body>
</html>`,
attachments: [
{
filename: "askfeedlogo.jpeg",
path: `${__dirname}/images/askfeedlogo.jpeg`,
cid: "askfeedlogo",
},
],
};
try {
await transporter.sendMail(data);
Expand All @@ -113,7 +144,7 @@

const changepassword = async (req, res) => {
const { password } = req.body;
const token = req.query.token;
const token = req.params.token;
const decodedtoken = jwt.verify(token, process.env.JWT_SECRET);
const hashedPass = await bcryptjs.hashSync(password, 10);
const user = await User.findOneAndUpdate(
Expand All @@ -132,20 +163,45 @@
}
};
const login = async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
const { _id } = user;
if (user) {
console.log("user", user);
const matchpassword = await bcryptjs.compare(password, user.password);
if (matchpassword && user.isVarified) {
const token = jwt.sign({ email, _id }, process.env.JWT_SECRET);
res.send({ token });
try {
const { email, password } = req.body;
const user = await User.findOne({ email });

Check failure

Code scanning / CodeQL

Database query built from user-controlled sources High

This query object depends on a
user-provided value
.

Copilot Autofix

AI 5 days ago

The best way to fix this problem is to ensure that the email value used in MongoDB queries is always interpreted as a literal value and not as a query object. There are two safe approaches:

  1. Use the $eq operator in the query: { email: { $eq: email } }, which ensures that the value is not interpreted as a query object.
  2. Explicitly check that email is a string before constructing the query object.

We will edit controllers/auth.js at line 168 in the login function. We'll change the call from:

const user = await User.findOne({ email });

to:

const user = await User.findOne({ email: { $eq: email } });

Optionally, for extra safety, you can add a type check for email. But using $eq is sufficient and idiomatic.

No additional imports or dependencies are needed.


Suggested changeset 1
controllers/auth.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/controllers/auth.js b/controllers/auth.js
--- a/controllers/auth.js
+++ b/controllers/auth.js
@@ -165,7 +165,7 @@
 const login = async (req, res) => {
   try {
     const { email, password } = req.body;
-    const user = await User.findOne({ email });
+    const user = await User.findOne({ email: { $eq: email } });
 
     if (user) {
       const { _id } = user;
EOF
@@ -165,7 +165,7 @@
const login = async (req, res) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ email });
const user = await User.findOne({ email: { $eq: email } });

if (user) {
const { _id } = user;
Copilot is powered by AI and may make mistakes. Always verify output.

if (user) {
const { _id } = user;

const matchpassword = await bcryptjs.compare(password, user.password);
if (matchpassword && user.isVarified) {
const token = jwt.sign({ email, _id }, process.env.JWT_SECRET);
res.send({ token, email: user.email, username: user.username });
}
} else {
res.send("Login unsuccessful!");
res.send("Incorrect Email or password!");
}
} else {
res.send("Incorrect Email or password!");
} catch (e) {
throw new Error("Fail to create operation");
}
};
module.exports = { signup, verifyAccount, resetlink, changepassword, login };
const updateProfile = async (req, res) => {
try {
const { _id } = req.body;
const userexist = await User.findOneAndUpdate({ _id }, { $set: req.body });

Check failure

Code scanning / CodeQL

Database query built from user-controlled sources High

This query object depends on a
user-provided value
.

Copilot Autofix

AI 5 days ago

To fix the vulnerability, we should validate that _id from req.body is a string (or a valid MongoDB ObjectId, if required by your schema) and not an object that could be used for query manipulation. This can be done by checking typeof _id === "string" or, for better security and compatibility, by using the ObjectId.isValid helper from the mongodb or mongoose library and converting to ObjectId. Alternatively, you could use the $eq operator in the query to ensure the user input is interpreted as a literal value.

Best fix:
In the block for updateProfile, add a check:

  • If _id is not a string (and optionally not a valid ObjectId), return a 400 error response.
  • Otherwise, proceed as normal using _id safely.

Required changes:
Add a type check on _id at the start of the updateProfile function, before passing it to .findOneAndUpdate. If possible, validate it as an ObjectId. (As shown code uses Mongoose, so you can use mongoose.Types.ObjectId.isValid if ObjectId is used, else keep a string check.)


Suggested changeset 1
controllers/auth.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/controllers/auth.js b/controllers/auth.js
--- a/controllers/auth.js
+++ b/controllers/auth.js
@@ -185,6 +185,11 @@
 const updateProfile = async (req, res) => {
   try {
     const { _id } = req.body;
+    // Validate _id is a string (or valid ObjectId)
+    if (typeof _id !== "string") {
+      res.status(400).send("Invalid user id");
+      return;
+    }
     const userexist = await User.findOneAndUpdate({ _id }, { $set: req.body });
     if (userexist) {
       res.send("user updated successfully");
EOF
@@ -185,6 +185,11 @@
const updateProfile = async (req, res) => {
try {
const { _id } = req.body;
// Validate _id is a string (or valid ObjectId)
if (typeof _id !== "string") {
res.status(400).send("Invalid user id");
return;
}
const userexist = await User.findOneAndUpdate({ _id }, { $set: req.body });
if (userexist) {
res.send("user updated successfully");
Copilot is powered by AI and may make mistakes. Always verify output.

Check failure

Code scanning / CodeQL

Database query built from user-controlled sources High

This query object depends on a
user-provided value
.

Copilot Autofix

AI 5 days ago

To fix the vulnerability, only a predefined list of fields should be updatable by the user. This can be accomplished by creating an explicit whitelist (array) of allowed fields for User updates, extracting just those fields from req.body, and then using that filtered object for the update operation.

  • Add an array of allowed profile fields (e.g., ['username', 'email', 'phoneNo']—add fields as fits your model).
  • Extract only these fields from req.body, e.g., via .reduce or a library like lodash.pick—but since we cannot add new dependencies unnecessarily, use only plain JavaScript here.
  • Use the filtered object in { $set: filteredBody } in the update operation.
  • Optionally, reject any requests where the body attempts to update fields not in the whitelist.

All changes are to be made within the updateProfile function in controllers/auth.js.

Suggested changeset 1
controllers/auth.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/controllers/auth.js b/controllers/auth.js
--- a/controllers/auth.js
+++ b/controllers/auth.js
@@ -185,7 +185,15 @@
 const updateProfile = async (req, res) => {
   try {
     const { _id } = req.body;
-    const userexist = await User.findOneAndUpdate({ _id }, { $set: req.body });
+    // Only allow the following fields to be updated:
+    const allowedFields = ['username', 'email', 'phoneNo']; // Add more as appropriate
+    const updateData = {};
+    for (const field of allowedFields) {
+      if (Object.prototype.hasOwnProperty.call(req.body, field)) {
+        updateData[field] = req.body[field];
+      }
+    }
+    const userexist = await User.findOneAndUpdate({ _id }, { $set: updateData });
     if (userexist) {
       res.send("user updated successfully");
     } else {
EOF
@@ -185,7 +185,15 @@
const updateProfile = async (req, res) => {
try {
const { _id } = req.body;
const userexist = await User.findOneAndUpdate({ _id }, { $set: req.body });
// Only allow the following fields to be updated:
const allowedFields = ['username', 'email', 'phoneNo']; // Add more as appropriate
const updateData = {};
for (const field of allowedFields) {
if (Object.prototype.hasOwnProperty.call(req.body, field)) {
updateData[field] = req.body[field];
}
}
const userexist = await User.findOneAndUpdate({ _id }, { $set: updateData });
if (userexist) {
res.send("user updated successfully");
} else {
Copilot is powered by AI and may make mistakes. Always verify output.
if (userexist) {
res.send("user updated successfully");
} else {
res.send("unable to update");
}
} catch (e) {
throw new Error("Unable to update");
}
};


module.exports = {
signup,
verifyAccount,
resetlink,
changepassword,
login,
updateProfile,
};
Binary file added controllers/images/askfeedlogo.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading