Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions source/app/blueprints/rest/dashboard_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ def add_gtask(caseid):
@ac_requires_case_identifier()
def edit_gtask(cur_id, caseid):
form = CaseGlobalTaskForm()
task = GlobalTasks.query.filter(GlobalTasks.id == cur_id).first()
task = GlobalTasks.query.filter(GlobalTasks.id == cur_id).filter(or_(GlobalTasks.task_userid_open == current_user.id, GlobalTasks.task_assignee_id == current_user.id)).first()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm `or_` is not imported in the file and that the new code references it.
rg -nP '\bor_\b' source/app/blueprints/rest/dashboard_routes.py
rg -nP '^\s*(from|import)\b.*\bor_\b' source/app/blueprints/rest/dashboard_routes.py

Repository: dfir-iris/iris-web

Length of output: 607


🏁 Script executed:

# Get the view_gtask function and surrounding context (lines 157-164)
sed -n '155,170p' source/app/blueprints/rest/dashboard_routes.py

# Get the gtask_delete function and surrounding context (lines 280-295)
sed -n '278,296p' source/app/blueprints/rest/dashboard_routes.py

# Also check imports at the top of the file to confirm no or_ import
sed -n '1,60p' source/app/blueprints/rest/dashboard_routes.py

Repository: dfir-iris/iris-web

Length of output: 3926


🏁 Script executed:

# Find and inspect the get_global_task function to see if it includes ownership filters
fd -n 'dashboard_db.py' source/

Repository: dfir-iris/iris-web

Length of output: 292


🏁 Script executed:

# Find dashboard_db.py
fd dashboard_db.py source/

# Then inspect the get_global_task function

Repository: dfir-iris/iris-web

Length of output: 107


🏁 Script executed:

# Search for get_global_task function definition and its implementation
rg -A 10 'def get_global_task' source/app/datamgmt/dashboard/dashboard_db.py

Repository: dfir-iris/iris-web

Length of output: 456


🏁 Script executed:

# Get more context of get_global_task function
rg -A 25 'def get_global_task' source/app/datamgmt/dashboard/dashboard_db.py

Repository: dfir-iris/iris-web

Length of output: 739


Critical: or_ is not imported — endpoints will raise NameError at runtime.

The new ownership filters at lines 245, 288, and 292 all reference or_, but sqlalchemy.or_ is not imported anywhere in this module. As soon as a user calls POST /global/tasks/update/<id> or POST /global/tasks/delete/<id>, Flask will raise NameError: name 'or_' is not defined and return a 500 — so the IDOR fix not only doesn't land, it also breaks legitimate edit/delete flows.

Additionally, the view_gtask endpoint (lines 157-164) is still vulnerable to IDOR. It calls get_global_task(task_id=cur_id), which only filters by task ID with no ownership check. Any user can retrieve details of any global task regardless of whether they created it or are assigned to it.

🛠️ Proposed fixes

Add missing import:

 import marshmallow
 from datetime import datetime
 from datetime import timedelta
 from oic.oauth2.exception import GrantError

+from sqlalchemy import or_
+
 from flask import Blueprint

Apply ownership filter in view_gtask:

 def view_gtask(cur_id):
-    task = get_global_task(task_id=cur_id)
+    task = GlobalTasks.query.filter(GlobalTasks.id == cur_id).filter(or_(GlobalTasks.task_userid_open == current_user.id, GlobalTasks.task_assignee_id == current_user.id)).first()
     if not task:
         return response_error(f'Global task ID {cur_id} not found')

Minor refactor — avoid redundant query in delete:

     data = GlobalTasks.query.filter(GlobalTasks.id == cur_id).filter(or_(GlobalTasks.task_userid_open == current_user.id, GlobalTasks.task_assignee_id == current_user.id)).first()
     if not data:
         return response_error("Invalid global task ID")

-    GlobalTasks.query.filter(GlobalTasks.id == cur_id).filter(or_(GlobalTasks.task_userid_open == current_user.id, GlobalTasks.task_assignee_id == current_user.id)).delete()
+    db.session.delete(data)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/app/blueprints/rest/dashboard_routes.py` at line 245, Import
sqlalchemy.or_ into the module and apply ownership filtering wherever tasks are
fetched: update the imports to include or_ and change queries that fetch
GlobalTasks (e.g., the one in GlobalTasks.query.filter(...) used in the update
and delete flows) to use or_(GlobalTasks.task_userid_open == current_user.id,
GlobalTasks.task_assignee_id == current_user.id). Also fix the
view_gtask/get_global_task flow by either adding the same ownership filters
inside get_global_task or by applying the ownership filter in view_gtask before
returning the task (ensure get_global_task(task_id=cur_id) cannot return tasks
the current_user does not own), and simplify delete to use that filtered query
instead of re-querying redundantly.

form.task_assignee_id.choices = [(user.id, user.name) for user in User.query.filter(
User.active == True).order_by(User.name).all()]
form.task_status_id.choices = [(a.id, a.status_name)
Expand Down Expand Up @@ -285,11 +285,11 @@ def gtask_delete(cur_id, caseid):
if not cur_id:
return response_error("Missing parameter")

data = GlobalTasks.query.filter(GlobalTasks.id == cur_id).first()
data = GlobalTasks.query.filter(GlobalTasks.id == cur_id).filter(or_(GlobalTasks.task_userid_open == current_user.id, GlobalTasks.task_assignee_id == current_user.id)).first()
if not data:
return response_error("Invalid global task ID")

GlobalTasks.query.filter(GlobalTasks.id == cur_id).delete()
GlobalTasks.query.filter(GlobalTasks.id == cur_id).filter(or_(GlobalTasks.task_userid_open == current_user.id, GlobalTasks.task_assignee_id == current_user.id)).delete()
db.session.commit()

call_modules_hook('on_postload_global_task_delete',
Expand Down