Skip to content

Commit 056588f

Browse files
committed
fix: render proxy crash and secure data controller
1 parent 7c3cc5b commit 056588f

3 files changed

Lines changed: 47 additions & 30 deletions

File tree

backend/controllers/data.controller.js

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ const Project = require("../models/Project");
44
const { getConnection } = require("../utils/connectionManager");
55
const { getCompiledModel } = require("../utils/injectModel");
66

7+
// Helper: CodeQL ko satisfy karne ke liye ID validate karna zaroori hai
8+
const isValidId = (id) => mongoose.Types.ObjectId.isValid(id);
9+
710
// 1. INSERT DATA
811
module.exports.insertData = async (req, res) => {
912
try {
1013
const { collectionName } = req.params;
11-
const project = req.project; // Middleware se mila project metadata
14+
const project = req.project;
1215

1316
const collectionConfig = project.collections.find(c => c.name === collectionName);
1417
if (!collectionConfig) return res.status(404).json({ error: "Collection not found" });
@@ -17,46 +20,47 @@ module.exports.insertData = async (req, res) => {
1720
const incomingData = req.body;
1821
const cleanData = {};
1922

20-
// Validation & Sanitization
2123
for (const field of schemaRules) {
22-
if (field.required && incomingData[field.key] === undefined) {
24+
const value = incomingData[field.key];
25+
26+
if (field.required && (value === undefined || value === null)) {
2327
return res.status(400).json({ error: `Field '${field.key}' is required.` });
2428
}
25-
if (incomingData[field.key] !== undefined) {
26-
// Type Checks
27-
if (field.type === 'Number' && typeof incomingData[field.key] !== 'number') return res.status(400).send(`Field '${field.key}' must be a Number.`);
28-
if (field.type === 'Boolean' && typeof incomingData[field.key] !== 'boolean') return res.status(400).send(`Field '${field.key}' must be a Boolean.`);
29-
cleanData[field.key] = incomingData[field.key];
29+
30+
if (value !== undefined) {
31+
// Strong Type Checks (Added String and Date)
32+
if (field.type === 'Number' && typeof value !== 'number') return res.status(400).json({ error: `Field '${field.key}' must be a Number.` });
33+
if (field.type === 'Boolean' && typeof value !== 'boolean') return res.status(400).json({ error: `Field '${field.key}' must be a Boolean.` });
34+
if (field.type === 'String' && typeof value !== 'string') return res.status(400).json({ error: `Field '${field.key}' must be a String.` });
35+
if (field.type === 'Date' && isNaN(Date.parse(value))) return res.status(400).json({ error: `Field '${field.key}' must be a valid Date.` });
36+
37+
cleanData[field.key] = value;
3038
}
3139
}
3240

41+
// Sanitize removes MongoDB operators like $ne, $gt etc.
3342
const safeData = sanitize(cleanData);
34-
Object.assign(cleanData, safeData);
3543

36-
// Usage Limit Check (Internal Only)
3744
let docSize = 0;
3845
if (!project.isExternal) {
39-
docSize = Buffer.byteLength(JSON.stringify(cleanData));
46+
docSize = Buffer.byteLength(JSON.stringify(safeData));
4047
if ((project.databaseUsed || 0) + docSize > project.databaseLimit) {
41-
return res.status(403).send("Database limit exceeded.");
48+
return res.status(403).json({ error: "Database limit exceeded." });
4249
}
4350
}
4451

45-
// Get Connection & Model
4652
const connection = await getConnection(project._id);
4753
const Model = getCompiledModel(connection, collectionConfig, project._id, project.isExternal);
4854

49-
const result = await Model.create(cleanData);
55+
const result = await Model.create(safeData);
5056

51-
// Update Project Metadata (Internal Only)
5257
if (!project.isExternal) {
5358
project.databaseUsed = (project.databaseUsed || 0) + docSize;
5459
await project.save();
5560
}
5661

5762
res.status(201).json(result);
5863
} catch (err) {
59-
console.error("Insert Error:", err);
6064
res.status(500).json({ error: err.message });
6165
}
6266
};
@@ -73,10 +77,9 @@ module.exports.getAllData = async (req, res) => {
7377
const connection = await getConnection(project._id);
7478
const Model = getCompiledModel(connection, collectionConfig, project._id, project.isExternal);
7579

76-
const data = await Model.find({}).limit(100);
80+
const data = await Model.find({}).limit(100).lean();
7781
res.json(data);
7882
} catch (err) {
79-
console.log("error----------------")
8083
res.status(500).json({ error: err.message });
8184
}
8285
};
@@ -87,14 +90,17 @@ module.exports.getSingleDoc = async (req, res) => {
8790
const { collectionName, id } = req.params;
8891
const project = req.project;
8992

93+
// CodeQL Fix: User input ID ko pehle validate karo
94+
if (!isValidId(id)) return res.status(400).json({ error: "Invalid ID format." });
95+
9096
const collectionConfig = project.collections.find(c => c.name === collectionName);
9197
if (!collectionConfig) return res.status(404).json({ error: "Collection not found" });
9298

9399
const connection = await getConnection(project._id);
94100
const Model = getCompiledModel(connection, collectionConfig, project._id, project.isExternal);
95101

96-
const doc = await Model.findById(id);
97-
if (!doc) return res.status(404).send("Document not found.");
102+
const doc = await Model.findById(id).lean();
103+
if (!doc) return res.status(404).json({ error: "Document not found." });
98104

99105
res.json(doc);
100106
} catch (err) {
@@ -109,22 +115,33 @@ module.exports.updateSingleData = async (req, res) => {
109115
const project = req.project;
110116
const incomingData = req.body;
111117

118+
if (!isValidId(id)) return res.status(400).json({ error: "Invalid ID format." });
119+
112120
const collectionConfig = project.collections.find(c => c.name === collectionName);
113121
if (!collectionConfig) return res.status(404).json({ error: "Collection not found" });
114122

115123
const connection = await getConnection(project._id);
116124
const Model = getCompiledModel(connection, collectionConfig, project._id, project.isExternal);
117125

118-
// Basic Schema Validation
126+
// Strict Schema Validation
119127
const schemaRules = collectionConfig.model;
128+
const updateData = {};
120129
for (const key in incomingData) {
121130
const fieldRule = schemaRules.find(f => f.key === key);
122-
if (!fieldRule) return res.status(400).send(`Field '${key}' not in schema.`);
123-
// Type checks... (Number, Boolean etc.)
131+
if (!fieldRule) continue; // Unknown fields ko ignore karo
132+
133+
const value = incomingData[key];
134+
if (fieldRule.type === 'Number' && typeof value !== 'number') return res.status(400).json({ error: `Field '${key}' must be a Number.` });
135+
if (fieldRule.type === 'Boolean' && typeof value !== 'boolean') return res.status(400).json({ error: `Field '${key}' must be a Boolean.` });
136+
if (fieldRule.type === 'String' && typeof value !== 'string') return res.status(400).json({ error: `Field '${key}' must be a String.` });
137+
138+
updateData[key] = value;
124139
}
125140

126-
const result = await Model.findByIdAndUpdate(id, { $set: incomingData }, { new: true });
127-
if (!result) return res.status(404).send("Document not found.");
141+
const sanitizedData = sanitize(updateData);
142+
143+
const result = await Model.findByIdAndUpdate(id, { $set: sanitizedData }, { new: true }).lean();
144+
if (!result) return res.status(404).json({ error: "Document not found." });
128145

129146
res.json({ message: "Updated", data: result });
130147
} catch (err) {
@@ -138,14 +155,16 @@ module.exports.deleteSingleDoc = async (req, res) => {
138155
const { collectionName, id } = req.params;
139156
const project = req.project;
140157

158+
if (!isValidId(id)) return res.status(400).json({ error: "Invalid ID format." });
159+
141160
const collectionConfig = project.collections.find(c => c.name === collectionName);
142161
if (!collectionConfig) return res.status(404).json({ error: "Collection not found" });
143162

144163
const connection = await getConnection(project._id);
145164
const Model = getCompiledModel(connection, collectionConfig, project._id, project.isExternal);
146165

147166
const docToDelete = await Model.findById(id);
148-
if (!docToDelete) return res.status(404).send("Document not found.");
167+
if (!docToDelete) return res.status(404).json({ error: "Document not found." });
149168

150169
let docSize = 0;
151170
if (!project.isExternal) {

backend/middleware/api_usage.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const limiter = rateLimit({
99
standardHeaders: true,
1010
legacyHeaders: false,
1111
validate: { xForwardedForHeader: false },
12+
validate: { trustProxy: false }
1213
});
1314

1415
// Logger

backend/utils/connectionManager.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,7 @@ async function getConnection(projectId) {
2929
throw new Error("Invalid or corrupted external config");
3030
}
3131

32-
const connection = mongoose.createConnection(config.dbUri, {
33-
useNewUrlParser: true,
34-
useUnifiedTopology: true,
35-
});
32+
const connection = mongoose.createConnection(config.dbUri);
3633

3734
connection.on("connected", () => {
3835
console.log(`DB connected for project ${projectId}`);

0 commit comments

Comments
 (0)