Skip to content

Feature/workspaces api services#31

Open
philvarner-snyk wants to merge 7 commits intomainfrom
feature/workspaces-api-services
Open

Feature/workspaces api services#31
philvarner-snyk wants to merge 7 commits intomainfrom
feature/workspaces-api-services

Conversation

@philvarner-snyk
Copy link
Copy Markdown
Collaborator

No description provided.

Introduce multi-workspace support with role-based access control,
audit logging, webhook delivery, rule engine with scheduled execution,
CSV todo import, and notes CRUD. Adds new Mongoose models (Workspace,
WorkspaceMember, AuditEvent, Webhook, WebhookDelivery, Rule, Note),
API middleware, and supporting documentation.

Made-with: Cursor
@philvarner-snyk
Copy link
Copy Markdown
Collaborator Author

philvarner-snyk commented Feb 27, 2026

Snyk checks have failed. 12 issues have been found so far.

Status Scan Engine Critical High Medium Low Total (12)
Open Source Security 0 3 0 0 3 issues
Licenses 0 0 0 0 0 issues
Code Security 0 9 0 0 9 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

if (req.query.from) q.createdAt.$gte = new Date(req.query.from);
if (req.query.to) q.createdAt.$lte = new Date(req.query.to);
}
AuditEvent.countDocuments(q).exec(function (err, total) {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  NoSQL Injection

Unsanitized input from an HTTP parameter flows into countDocuments, where it is used in an NoSQL query. This may result in an NoSQL Injection vulnerability.

Line 70 | CWE-943 | Priority score 821 | Learn more about this vulnerability
Data flow: 11 steps

Step 1 - 9

if (typeof req.query.action === 'string' && req.query.action.trim()) q.action = req.query.action.trim();

Step 10 - 11

AuditEvent.countDocuments(q).exec(function (err, total) {

}
var workspaceObjId = new mongoose.Types.ObjectId(workspaceId);
var qSafe = Object.assign({}, q, { workspace: workspaceObjId });
Todo.countDocuments(qSafe).exec(function (err, total) {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  NoSQL Injection

Unsanitized input from an HTTP parameter flows into countDocuments, where it is used in an NoSQL query. This may result in an NoSQL Injection vulnerability.

Line 105 | CWE-943 | Priority score 821 | Learn more about this vulnerability
Data flow: 12 steps

Step 1 - 7

if (typeof req.query.priority === 'string' && VALID_PRIORITIES.indexOf(req.query.priority) !== -1) q.priority = req.query.priority;

Step 8 - 10 routes/workspace-todos.js#L104

Step 11 - 12

Todo.countDocuments(qSafe).exec(function (err, total) {

}
AuditEvent.countDocuments(q).exec(function (err, total) {
if (err) return next(err);
AuditEvent.find(q)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  NoSQL Injection

Unsanitized input from an HTTP parameter flows into find, where it is used in an NoSQL query. This may result in an NoSQL Injection vulnerability.

Line 72 | CWE-943 | Priority score 821 | Learn more about this vulnerability
Data flow: 11 steps

Step 1 - 9

if (typeof req.query.action === 'string' && req.query.action.trim()) q.action = req.query.action.trim();

Step 10 - 11

AuditEvent.find(q)

var qSafe = Object.assign({}, q, { workspace: workspaceObjId });
Todo.countDocuments(qSafe).exec(function (err, total) {
if (err) return next(err);
Todo.find(qSafe)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  NoSQL Injection

Unsanitized input from an HTTP parameter flows into find, where it is used in an NoSQL query. This may result in an NoSQL Injection vulnerability.

Line 107 | CWE-943 | Priority score 821 | Learn more about this vulnerability
Data flow: 12 steps

Step 1 - 7

if (typeof req.query.priority === 'string' && VALID_PRIORITIES.indexOf(req.query.priority) !== -1) q.priority = req.query.priority;

Step 8 - 10 routes/workspace-todos.js#L104

Step 11 - 12

Todo.find(qSafe)

details: { user: newUser, role: role },
ip: req.ip,
});
webhookDelivery.notifyWebhooks(workspaceId, 'member.added', { resourceId: String(member._id), data: { user: newUser, role } });
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  NoSQL Injection

Unsanitized input from an HTTP parameter flows into find, where it is used in an NoSQL query. This may result in an NoSQL Injection vulnerability.

Line 219 | CWE-943 | Priority score 821 | Learn more about this vulnerability
Data flow: 10 steps

Step 1 - 5

var workspaceId = req.params.id;

Step 6 routes/workspaces.js#L219

Step 7 services/webhook-delivery.js#L182

Step 8 - 10

Webhook.find({ workspace: workspaceId, active: true, events: event }).lean().exec(function (err, webhooks) {

if (actErr) return res.status(400).json({ error: actErr });
var workspaceId = req.workspaceId;
var id = req.params.id;
Rule.findOne({ _id: id, workspace: workspaceId }).exec(function (err, rule) {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  NoSQL Injection

Unsanitized input from an HTTP parameter flows into findOne, where it is used in an NoSQL query. This may result in an NoSQL Injection vulnerability.

Line 179 | CWE-943 | Priority score 821 | Learn more about this vulnerability
Data flow: 9 steps

Step 1 - 5

var id = req.params.id;

Step 6 - 9

Rule.findOne({ _id: id, workspace: workspaceId }).exec(function (err, rule) {

if (!workspaceAuth.ROLES_WITH_ADMIN.includes(membership.role)) {
return res.status(403).json({ error: 'Only owner or admin can add members' });
}
return WorkspaceMember.findOne({ workspace: workspaceId, user: newUser }).exec();
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  NoSQL Injection

Unsanitized input from an HTTP parameter flows into findOne, where it is used in an NoSQL query. This may result in an NoSQL Injection vulnerability.

Line 204 | CWE-943 | Priority score 821 | Learn more about this vulnerability
Data flow: 8 steps

Step 1 - 5

var workspaceId = req.params.id;

Step 6 - 8

return WorkspaceMember.findOne({ workspace: workspaceId, user: newUser }).exec();

if (targetUserId === actorId && membership.role === 'owner') {
return res.status(400).json({ error: 'Owner cannot remove themselves; transfer ownership first' });
}
return WorkspaceMember.findOneAndDelete({ workspace: workspaceId, user: targetUserId }).exec();
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  NoSQL Injection

Unsanitized input from an HTTP parameter flows into findOneAndDelete, where it is used in an NoSQL query. This may result in an NoSQL Injection vulnerability.

Line 242 | CWE-943 | Priority score 821 | Learn more about this vulnerability
Data flow: 8 steps

Step 1 - 5

var workspaceId = req.params.id;

Step 6 - 8

return WorkspaceMember.findOneAndDelete({ workspace: workspaceId, user: targetUserId }).exec();

function validateActions(actions) {
if (!Array.isArray(actions)) return 'actions must be an array';
if (actions.length > MAX_ACTIONS) return 'At most ' + MAX_ACTIONS + ' actions per rule';
for (var i = 0; i < actions.length; i++) {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  Unchecked Input for Loop Condition

The 0 object may not be an array, and its length property may be directly set to huge values by an attacker, which would make this loop iterate too many times. This may lead to a Denial-of-service vulnerability.

Line 44 | CWE-400 | CWE-606 | Priority score 803 | Learn more about this vulnerability
Data flow: 3 steps

Step 1 - 3

for (var i = 0; i < actions.length; i++) {

Comment out the pull_request trigger so the pipeline only runs
on pushes to main/master and manual dispatch.

Made-with: Cursor
Only the npm:adm-zip:20180415 (Zip Slip) vulnerability is ignored,
to test visibility in the Snyk Dashboard UI.

Made-with: Cursor
- Serve profile images from public/images/profiles with extension allowlist,
  numeric user ID validation, and path traversal checks
- Add express-rate-limit dependency
- Enable Snyk Code global exclude for tests/** in .snyk

Pre-commit: Snyk Code scan and Snyk Open Source (SCA) scan were run on the repo.
Made-with: Cursor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant