Skip to content

Commit 0ee5c46

Browse files
authored
Merge pull request #928 from dfir-iris/tidy_imports
Tidy imports
2 parents a2e845e + ec9a98b commit 0ee5c46

File tree

20 files changed

+429
-232
lines changed

20 files changed

+429
-232
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ jobs:
3131
uses: astral-sh/ruff-action@v3
3232
with:
3333
args: check --output-format=github
34+
- name: Check dependencies with import-linter
35+
run: |
36+
python -m venv venv
37+
source venv/bin/activate
38+
pip install import-linter
39+
PYTHONPATH=source lint-imports
3440
3541
build-docker-db:
3642
name: Build docker db

pyproject.toml

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,30 @@
11
[tool.ruff.lint]
22
preview = true
3-
select = ["E101", "E225", "E23", "E24", "E3", "E4", "E7", "E9", "F", "RET506", "UP032", "W29"]
3+
select = ["E101", "E225", "E23", "E24", "E3", "E4", "E7", "E9", "F", "PLR0402", "RET506", "UP032", "W29"]
44
ignore = ["E402", "E711", "E712", "E721", "E722"]
55

6+
[tool.importlinter]
7+
root_package = "app"
8+
include_external_packages = true
9+
10+
[[tool.importlinter.contracts]]
11+
name = "Do not import the persistence layer from the API layer"
12+
type = "forbidden"
13+
source_modules = "app.blueprints.rest.v2.alerts"
14+
forbidden_modules = "app.datamgmt.alerts"
15+
allow_indirect_imports = true
16+
17+
[[tool.importlinter.contracts]]
18+
name = "Do not import sqlalchemy from the API layer"
19+
type = "forbidden"
20+
source_modules = ["app.blueprints.rest.search_routes", "app.blueprints.rest.dim_tasks_routes", "app.blueprints.rest.case.case_notes_routes", "app.blueprints.rest.case.case_routes"]
21+
forbidden_modules = "sqlalchemy"
22+
allow_indirect_imports = true
23+
24+
[[tool.importlinter.contracts]]
25+
name = "Do not import sqlalchemy from the business layer"
26+
type = "forbidden"
27+
source_modules = "app.business"
28+
forbidden_modules = "sqlalchemy"
29+
allow_indirect_imports = true
30+

source/app/blueprints/pages/dim_tasks/dim_tasks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from app.models.authorization import Permissions
2727
from app.blueprints.access_controls import ac_case_requires, ac_requires
2828
from app.blueprints.responses import response_error
29-
from app.business.dim_tasks import dim_tasks_get
29+
from app.business.asynchronous_tasks import dim_tasks_get
3030

3131
dim_tasks_blueprint = Blueprint(
3232
'dim_tasks',

source/app/blueprints/rest/case/case_notes_routes.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
from datetime import datetime
2121
from flask import Blueprint
2222
from flask import request
23-
from sqlalchemy import or_
24-
from sqlalchemy import and_
2523

2624
from app import db
2725
from app import app
@@ -48,7 +46,7 @@
4846
from app.datamgmt.states import get_notes_state
4947
from app.iris_engine.module_handler.module_handler import call_modules_hook
5048
from app.iris_engine.utils.tracker import track_activity
51-
from app.models.models import Notes
49+
from app.business.notes import notes_search
5250
from app.models.authorization import CaseAccessLevel
5351
from app.schema.marshables import CaseNoteDirectorySchema
5452
from app.schema.marshables import CaseNoteRevisionSchema
@@ -335,11 +333,7 @@ def case_notes_state(caseid):
335333
def case_search_notes(caseid):
336334
search_input = request.args.get('search_input')
337335

338-
notes = Notes.query.filter(
339-
and_(Notes.note_case_id == caseid,
340-
or_(Notes.note_title.ilike(f'%{search_input}%'),
341-
Notes.note_content.ilike(f'%{search_input}%')))
342-
).all()
336+
notes = notes_search(caseid, search_input)
343337

344338
note_schema = CaseNoteSchema(many=True)
345339
serialized_notes = note_schema.dump(notes)

source/app/blueprints/rest/case/case_routes.py

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222
import traceback
2323
from flask import Blueprint
2424
from flask import request
25-
from sqlalchemy import and_
26-
from sqlalchemy import desc
2725

2826
from app import app
2927
from app import db
@@ -39,14 +37,13 @@
3937
from app.datamgmt.manage.manage_users_db import get_user
4038
from app.datamgmt.manage.manage_users_db import get_users_list_restricted_from_case
4139
from app.business.access_controls import set_user_case_access, ac_fast_check_user_has_case_access
40+
from app.business.activity import activity_search_in_case
4241
from app.business.cases import cases_export_to_json
4342
from app.iris_engine.access_control.utils import ac_set_case_access_for_users
4443
from app.iris_engine.utils.tracker import track_activity
4544
from app.models.models import CaseStatus
4645
from app.models.models import ReviewStatusList
47-
from app.models.models import UserActivity
4846
from app.models.authorization import CaseAccessLevel
49-
from app.models.authorization import User
5047
from app.schema.marshables import TaskLogSchema
5148
from app.schema.marshables import CaseSchema
5249
from app.schema.marshables import CaseDetailsSchema
@@ -111,21 +108,7 @@ def summary_fetch(caseid):
111108
@ac_requires_case_identifier(CaseAccessLevel.read_only, CaseAccessLevel.full_access)
112109
@ac_api_requires()
113110
def activity_fetch(caseid):
114-
ua = UserActivity.query.with_entities(
115-
UserActivity.activity_date,
116-
User.name,
117-
UserActivity.activity_desc,
118-
UserActivity.is_from_api
119-
).filter(and_(
120-
UserActivity.case_id == caseid,
121-
UserActivity.display_in_ui == True
122-
)).join(
123-
UserActivity.user
124-
).order_by(
125-
desc(UserActivity.activity_date)
126-
).limit(40).all()
127-
128-
output = [a._asdict() for a in ua]
111+
output = activity_search_in_case(caseid)
129112

130113
return response_success('', data=output)
131114

source/app/blueprints/rest/dim_tasks_routes.py

Lines changed: 3 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,8 @@
1818

1919
from flask import Blueprint
2020
from flask import request
21-
import json
22-
import pickle
23-
from sqlalchemy import desc
2421

2522
from app.iris_engine.module_handler.module_handler import call_modules_hook
26-
from app.models.models import CeleryTaskMeta
2723
from app.models.models import IrisHook
2824
from app.models.models import IrisModule
2925
from app.models.models import IrisModuleHook
@@ -41,7 +37,7 @@
4137
from app.blueprints.access_controls import ac_api_requires
4238
from app.blueprints.responses import response_error
4339
from app.blueprints.responses import response_success
44-
from iris_interface.IrisInterfaceStatus import IIStatus
40+
from app.business.asynchronous_tasks import asynchronous_tasks_search
4541

4642
dim_tasks_rest_blueprint = Blueprint('dim_tasks_rest', __name__)
4743

@@ -176,55 +172,6 @@ def list_dim_hook_options_ioc(hook_type):
176172
@dim_tasks_rest_blueprint.route('/dim/tasks/list/<int:count>', methods=['GET'])
177173
@ac_api_requires()
178174
def list_dim_tasks(count):
179-
tasks = CeleryTaskMeta.query.filter(
180-
~ CeleryTaskMeta.name.like('app.iris_engine.updater.updater.%')
181-
).order_by(desc(CeleryTaskMeta.date_done)).limit(count).all()
175+
data = asynchronous_tasks_search(count)
182176

183-
data = []
184-
185-
for row in tasks:
186-
187-
tkp = {'state': row.status, 'case': "Unknown", 'module': row.name, 'task_id': row.task_id, 'date_done': row.date_done, 'user': "Unknown"}
188-
189-
try:
190-
_ = row.result
191-
except AttributeError:
192-
# Legacy task
193-
data.append(tkp)
194-
continue
195-
196-
if row.name is not None and 'task_hook_wrapper' in row.name:
197-
task_name = f"{row.kwargs}::{row.kwargs}"
198-
else:
199-
task_name = row.name
200-
201-
user = None
202-
case_name = None
203-
if row.kwargs and row.kwargs != b'{}':
204-
kwargs = json.loads(row.kwargs.decode('utf-8'))
205-
if kwargs:
206-
user = kwargs.get('init_user')
207-
case_name = f"Case #{kwargs.get('caseid')}"
208-
task_name = f"{kwargs.get('module_name')}::{kwargs.get('hook_name')}"
209-
210-
try:
211-
result = pickle.loads(row.result)
212-
except:
213-
result = None
214-
215-
if isinstance(result, IIStatus):
216-
try:
217-
success = result.is_success()
218-
except:
219-
success = None
220-
else:
221-
success = None
222-
223-
tkp['state'] = "success" if success else str(row.result)
224-
tkp['user'] = user if user else "Shadow Iris"
225-
tkp['module'] = task_name
226-
tkp['case'] = case_name if case_name else ""
227-
228-
data.append(tkp)
229-
230-
return response_success("", data=data)
177+
return response_success('', data=data)

source/app/blueprints/rest/search_routes.py

Lines changed: 3 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,11 @@
1818

1919
from flask import Blueprint
2020
from flask import request
21-
from sqlalchemy import and_
2221

23-
from app.iris_engine.utils.tracker import track_activity
24-
from app.models.comments import Comments
2522
from app.models.authorization import Permissions
26-
from app.models.cases import Cases
27-
from app.models.models import Client
28-
from app.models.iocs import Ioc
29-
from app.models.models import IocType
30-
from app.models.models import Notes
31-
from app.models.iocs import Tlp
3223
from app.blueprints.access_controls import ac_api_requires
3324
from app.blueprints.responses import response_success
25+
from app.business.search import search
3426

3527
search_rest_blueprint = Blueprint('search_rest', __name__)
3628

@@ -42,79 +34,7 @@ def search_file_post():
4234
jsdata = request.get_json()
4335
search_value = jsdata.get('search_value')
4436
search_type = jsdata.get('search_type')
45-
files = []
46-
search_condition = and_()
4737

48-
track_activity(f'started a global search for {search_value} on {search_type}')
38+
files = search(search_type, search_value)
4939

50-
if search_type == "ioc":
51-
res = Ioc.query.with_entities(
52-
Ioc.ioc_value.label('ioc_name'),
53-
Ioc.ioc_description.label('ioc_description'),
54-
Ioc.ioc_misp,
55-
IocType.type_name,
56-
Tlp.tlp_name,
57-
Tlp.tlp_bscolor,
58-
Cases.name.label('case_name'),
59-
Cases.case_id,
60-
Client.name.label('customer_name')
61-
).filter(
62-
and_(
63-
Ioc.ioc_value.like(search_value),
64-
Ioc.case_id == Cases.case_id,
65-
Client.client_id == Cases.client_id,
66-
Ioc.ioc_tlp_id == Tlp.tlp_id,
67-
search_condition
68-
)
69-
).join(Ioc.ioc_type).all()
70-
71-
files = [row._asdict() for row in res]
72-
73-
if search_type == "notes":
74-
75-
ns = []
76-
if search_value:
77-
search_value = f'%{search_value}%'
78-
ns = Notes.query.filter(
79-
Notes.note_content.like(search_value),
80-
Cases.client_id == Client.client_id,
81-
search_condition
82-
).with_entities(
83-
Notes.note_id,
84-
Notes.note_title,
85-
Cases.name.label('case_name'),
86-
Client.name.label('client_name'),
87-
Cases.case_id
88-
).join(
89-
Notes.case
90-
).order_by(
91-
Client.name
92-
).all()
93-
94-
ns = [row._asdict() for row in ns]
95-
96-
files = ns
97-
98-
if search_type == "comments":
99-
search_value = f'%{search_value}%'
100-
comments = Comments.query.filter(
101-
Comments.comment_text.like(search_value),
102-
Cases.client_id == Client.client_id,
103-
search_condition
104-
).with_entities(
105-
Comments.comment_id,
106-
Comments.comment_text,
107-
Cases.name.label('case_name'),
108-
Client.name.label('customer_name'),
109-
Cases.case_id
110-
).join(
111-
Comments.case
112-
).join(
113-
Cases.client
114-
).order_by(
115-
Client.name
116-
).all()
117-
118-
files = [row._asdict() for row in comments]
119-
120-
return response_success("Results fetched", files)
40+
return response_success('Results fetched', files)

source/app/blueprints/rest/v2/alerts.py

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from app.blueprints.rest.parsing import parse_comma_separated_identifiers
3232
from app.blueprints.rest.v2.alerts_routes.comments import alerts_comments_blueprint
3333
from app.iris_engine.access_control.iris_user import iris_current_user
34-
from app.datamgmt.alerts.alerts_db import get_filtered_alerts
34+
from app.business.alerts import alerts_search
3535
from app.models.authorization import Permissions
3636
from app.schema.marshables import AlertSchema
3737
from app.schema.marshables import IocSchema
@@ -50,7 +50,7 @@ class AlertsOperations:
5050
def __init__(self):
5151
self._schema = AlertSchema()
5252

53-
def list(self):
53+
def search(self):
5454
page = request.args.get('page', 1, type=int)
5555
per_page = request.args.get('per_page', 10, type=int)
5656

@@ -91,31 +91,31 @@ def list(self):
9191
else:
9292
fields = None
9393

94-
filtered_alerts = get_filtered_alerts(
95-
start_date=request.args.get('creation_start_date'),
96-
end_date=request.args.get('creation_end_date'),
97-
source_start_date=request.args.get('source_start_date'),
98-
source_end_date=request.args.get('source_end_date'),
99-
source_reference=request.args.get('source_reference'),
100-
title=request.args.get('alert_title'),
101-
description=request.args.get('alert_description'),
102-
status=request.args.get('alert_status_id', type=int),
103-
severity=request.args.get('alert_severity_id', type=int),
104-
owner=request.args.get('alert_owner_id', type=int),
105-
source=request.args.get('alert_source'),
106-
tags=request.args.get('alert_tags'),
107-
classification=request.args.get('alert_classification_id', type=int),
108-
client=request.args.get('alert_customer_id'),
109-
case_id=request.args.get('case_id', type=int),
110-
alert_ids=alert_ids,
111-
page=page,
112-
per_page=per_page,
113-
sort=request.args.get('sort'),
114-
custom_conditions=request.args.get('custom_conditions'),
115-
assets=alert_assets,
116-
iocs=alert_iocs,
117-
resolution_status=request.args.get('alert_resolution_id', type=int),
118-
current_user_id=iris_current_user.id
94+
filtered_alerts = alerts_search(
95+
iris_current_user.id,
96+
request.args.get('creation_start_date'),
97+
request.args.get('creation_end_date'),
98+
request.args.get('source_start_date'),
99+
request.args.get('source_end_date'),
100+
request.args.get('alert_title'),
101+
request.args.get('alert_description'),
102+
request.args.get('alert_status_id', type=int),
103+
request.args.get('alert_severity_id', type=int),
104+
request.args.get('alert_owner_id', type=int),
105+
request.args.get('alert_source'),
106+
request.args.get('alert_tags'),
107+
request.args.get('case_id', type=int),
108+
request.args.get('alert_customer_id'),
109+
request.args.get('alert_classification_id', type=int),
110+
alert_ids,
111+
alert_assets,
112+
alert_iocs,
113+
request.args.get('alert_resolution_id', type=int),
114+
request.args.get('source_reference'),
115+
request.args.get('custom_conditions'),
116+
page,
117+
per_page,
118+
request.args.get('sort')
119119
)
120120

121121
if filtered_alerts is None:
@@ -218,7 +218,7 @@ def delete(self, identifier):
218218
@alerts_blueprint.get('')
219219
@ac_api_requires(Permissions.alerts_read)
220220
def alerts_list_route() -> Response:
221-
return alerts_operations.list()
221+
return alerts_operations.search()
222222

223223

224224
@alerts_blueprint.post('')

0 commit comments

Comments
 (0)