Skip to content

Commit ca080da

Browse files
author
Davide Arcuri
committed
start working on new feature
1 parent de6d812 commit ca080da

60 files changed

Lines changed: 3493 additions & 954 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,24 @@
11
## Changelog
2+
<details>
3+
<summary><b>OROCHI 2.5.0</b></summary>
4+
5+
* Added Investigation Workspace Epic [[#1534](https://github.com/LDO-CERT/orochi/issues/1534)]
6+
* Case management with Findings and Evidence. [[#1536](https://github.com/LDO-CERT/orochi/issues/1536)]
7+
* Investigation Timeline tracking. [[#1538](https://github.com/LDO-CERT/orochi/issues/1538)]
8+
* Case Export to `.tar.gz` bundle. [[#1539](https://github.com/LDO-CERT/orochi/issues/1539)]
9+
* Capture The Flag (CTF) Mode. [[#1542](https://github.com/LDO-CERT/orochi/issues/1542)]
10+
* Automated Report Generation with customizable templates. [[#1540](https://github.com/LDO-CERT/orochi/issues/1540)]
11+
* Optional AI integration with Ollama for report summarization. [[#1540](https://github.com/LDO-CERT/orochi/issues/1540)]
12+
* Added robust Background Task Logging mechanism with admin visibility.
13+
* Added real-time websocket notifications to admins for background task completion/failure. [[#1566](https://github.com/LDO-CERT/orochi/issues/1566)]
14+
* Integrated `django-easy-audit` for comprehensive tracking of manual and automated database activities, including login events. [[#1563](https://github.com/LDO-CERT/orochi/issues/1563)]
15+
* Added `Host` concept for optionally linking related dumps/assets and performing diff comparisons.
16+
* Added external notifications system (Email, Webhook, Slack) configurable per-user via Account Settings.
17+
* Added DataTable export functionalities (CSV, Excel, JSON, XML).
18+
* Added "Restart all failed plugins" capability to the auto-restart plugin flow.
19+
* Fixed cache-building background task being triggered redundantly on all app instances and disabled `TaskLog` from auditing to reduce noise.
20+
</details>
21+
222
<details>
323
<summary><b>OROCHI 2.4.2</b></summary>
424

compose/local/dask/Dockerfile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
ARG TARGETARCH
22

3-
FROM python:3.13.6-slim-bookworm as common-base
3+
FROM python:3.13.14-slim-bookworm as common-base
44

55
# DISABLE TZDATA INTERACTION
66
ENV DEBIAN_FRONTEND noninteractive
@@ -50,8 +50,8 @@ RUN apk add --no-cache git
5050
WORKDIR /
5151
RUN git clone https://github.com/volatilityfoundation/dwarf2json.git
5252
WORKDIR /dwarf2json
53-
RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \
54-
/usr/local/go/bin/go build -o dwarf2json .
53+
RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH:-amd64} \
54+
/usr/local/go/bin/go build -o dwarf2json .
5555

5656
FROM common-base
5757

@@ -88,13 +88,13 @@ COPY ./compose/local/vol_fix/arm.py /app/.venv/lib/python3.13/site-packages/vola
8888
RUN sed -i 's/\["Intel32", "Intel64"\]/\["Intel32", "Intel64", "AArch64"\]/g' /app/.venv/lib/python3.13/site-packages/volatility3/framework/plugins/linux/*.py && pip install --force-reinstall --no-cache-dir python-ldap==3.4.4 && ldconfig
8989

9090
ENV TINI_VERSION v0.19.0
91-
ARG TARGETARCH
91+
ARG TARGETARCH
9292

9393
RUN set -eux; \
9494
TINI_PLATFORM="tini"; \
95-
if [ "${TARGETARCH}" = "arm64" ]; then \
95+
if [ "${TARGETARCH:-amd64}" = "arm64" ]; then \
9696
TINI_PLATFORM="tini-arm64"; \
97-
elif [ "${TARGETARCH}" = "amd64" ]; then \
97+
elif [ "${TARGETARCH:-amd64}" = "amd64" ]; then \
9898
TINI_PLATFORM="tini"; \
9999
fi; \
100100
curl -fsSLO "https://github.com/krallin/tini/releases/download/${TINI_VERSION}/${TINI_PLATFORM}"; \

compose/local/django/Dockerfile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
ARG TARGETARCH
1+
ARG TARGETARCH
22

3-
FROM python:3.13.6-slim-bookworm as common-base
3+
FROM python:3.13.14-slim-bookworm as common-base
44

55

66
ENV DJANGO_SETTINGS_MODULE config.settings.local
@@ -57,8 +57,8 @@ RUN apk add --no-cache git
5757
WORKDIR /
5858
RUN git clone https://github.com/volatilityfoundation/dwarf2json.git
5959
WORKDIR /dwarf2json
60-
RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \
61-
/usr/local/go/bin/go build -o dwarf2json .
60+
RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH:-amd64} \
61+
/usr/local/go/bin/go build -o dwarf2json .
6262

6363
FROM common-base
6464

@@ -99,4 +99,4 @@ RUN sed -i 's/\r$//g' /start && chmod +x /start
9999

100100
COPY --from=go-builder /dwarf2json/dwarf2json /dwarf2json/dwarf2json
101101

102-
ENTRYPOINT ["/entrypoint"]
102+
ENTRYPOINT ["/entrypoint"]

config/settings/base.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
"import_export",
7777
"django_htmx",
7878
"django_tailwind_cli",
79+
"easyaudit",
7980
]
8081

8182
LOCAL_APPS = [
@@ -143,6 +144,7 @@
143144
"orochi.website.middleware.UpdatesMiddleware",
144145
"allauth.account.middleware.AccountMiddleware",
145146
"django_htmx.middleware.HtmxMiddleware",
147+
"easyaudit.middleware.easyaudit.EasyAuditMiddleware",
146148
]
147149

148150
# STATIC
@@ -288,7 +290,13 @@
288290
"default": {
289291
"BACKEND": "channels_redis.core.RedisChannelLayer",
290292
"CONFIG": {
291-
"hosts": [(env("REDIS_SERVER"), env("REDIS_PORT"))],
293+
"hosts": [
294+
{
295+
"host": env("REDIS_SERVER"),
296+
"port": env("REDIS_PORT"),
297+
"socket_timeout": None,
298+
}
299+
],
292300
},
293301
},
294302
}
@@ -381,3 +389,7 @@
381389
if env.bool("HTTPS", False):
382390
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
383391
SECURE_SSL_REDIRECT = True
392+
393+
# EASY AUDIT
394+
DJANGO_EASY_AUDIT_UNREGISTERED_CLASSES_EXTRA = ["website.TaskLog"]
395+
DJANGO_EASY_AUDIT_UNREGISTERED_URLS_EXTRA = [r"^.*/dask_status/?$"]

docker-compose.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,14 @@ services:
195195
- ./.envs/.local/.django
196196
- ./.envs/.local/.postgres
197197
- ./.envs/.local/.dask
198+
199+
ollama:
200+
image: ollama/ollama:latest
201+
profiles: [ "ollama" ]
202+
container_name: orochi_ollama
203+
hostname: ollama
204+
restart: always
205+
ports:
206+
- "11434:11434"
207+
volumes:
208+
- ./ollama_data:/root/.ollama

orochi/api/models.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ class Meta:
169169
# Dump
170170
###################################################
171171
class DumpIn(ModelSchema):
172-
folder: Optional[FolderSchema] = None
172+
folder: Optional[str] = None
173+
host: Optional[str] = None
173174
local_folder: Optional[str] = None
174175
password: Optional[str] = None
175176
original_name: Optional[str] = None
@@ -186,7 +187,8 @@ class Meta:
186187

187188

188189
class DumpEditIn(ModelSchema):
189-
folder: Optional[FolderSchema] = None
190+
folder: Optional[str] = None
191+
host: Optional[str] = None
190192
authorized_users: Optional[List[int]] = None
191193

192194
class Meta:

orochi/api/routers/dumps.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,19 @@ def create_dump(request, payload: DumpIn, upload: Optional[UploadedFile] = File(
213213
"""
214214

215215
try:
216-
if payload.folder:
216+
if getattr(payload, "folder", None):
217217
folder, _ = Folder.objects.get_or_create(
218-
name=payload.folder.name, user=request.user
218+
name=payload.folder, user=request.user
219219
)
220220
else:
221221
folder = None
222+
223+
if getattr(payload, "host", None):
224+
from orochi.website.models import Host
225+
226+
host_obj, _ = Host.objects.get_or_create(name=payload.host)
227+
else:
228+
host_obj = None
222229
dump_index = str(uuid1())
223230
Path(f"{settings.MEDIA_ROOT}/{dump_index}").mkdir()
224231
dump = Dump.objects.create(
@@ -227,6 +234,7 @@ def create_dump(request, payload: DumpIn, upload: Optional[UploadedFile] = File(
227234
comment=payload.comment,
228235
operating_system=payload.operating_system,
229236
folder=folder,
237+
host=host_obj,
230238
author=request.user,
231239
index=dump_index,
232240
)
@@ -315,14 +323,20 @@ def edit_dump(request, pk: UUID, payload: PatchDict[DumpEditIn]):
315323
if "can_see" in get_perms(user, dump) and user != request.user
316324
]
317325

318-
if payload.get("folder"):
326+
if getattr(payload, "folder", None):
319327
folder, _ = Folder.objects.get_or_create(
320-
name=payload["folder"]["name"], user=request.user
328+
name=payload.get("folder"), user=request.user
321329
)
322330
dump.folder = folder
323331

332+
if getattr(payload, "host", None):
333+
from orochi.website.models import Host
334+
335+
host_obj, _ = Host.objects.get_or_create(name=payload.get("host"))
336+
dump.host = host_obj
337+
324338
for attr, value in payload.items():
325-
if attr not in ["authorized_users", "folder"]:
339+
if attr not in ["authorized_users", "folder", "host"]:
326340
setattr(dump, attr, value)
327341
else:
328342
for user_pk in payload.get("authorized_users", []):

orochi/backends/dask.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import uuid
23
from copy import deepcopy
34

45
from dask.distributed import Client, fire_and_forget
@@ -9,6 +10,43 @@
910
logger = logging.getLogger(__name__)
1011

1112

13+
def _dask_task_wrapper(task_func, task_id, *args, **kwargs):
14+
import django
15+
16+
if not django.apps.apps.ready:
17+
django.setup()
18+
19+
from orochi.website.models import TaskLog
20+
21+
try:
22+
log = TaskLog.objects.get(task_id=task_id)
23+
log.status = "Running"
24+
log.save()
25+
except Exception as e:
26+
logger.error(f"Failed to update TaskLog {task_id} to Running: {e}")
27+
28+
try:
29+
result = task_func(*args, **kwargs)
30+
try:
31+
log = TaskLog.objects.get(task_id=task_id)
32+
log.status = "Completed"
33+
if result:
34+
log.result = str(result)
35+
log.save()
36+
except Exception as e:
37+
logger.error(f"Failed to update TaskLog {task_id} to Completed: {e}")
38+
return result
39+
except Exception as e:
40+
try:
41+
log = TaskLog.objects.get(task_id=task_id)
42+
log.status = "Failed"
43+
log.error = str(e)
44+
log.save()
45+
except Exception as err:
46+
logger.error(f"Failed to update TaskLog {task_id} to Failed: {err}")
47+
raise
48+
49+
1250
class DaskTaskBackend(BaseTaskBackend):
1351

1452
def __init__(self, alias, **kwargs):
@@ -23,7 +61,30 @@ def client(self):
2361

2462
def enqueue(self, task, args, kwargs):
2563
logger.info(f"Enqueuing task {task.name}")
26-
future = self.client.submit(task.func, *args, pure=False, **kwargs)
64+
65+
task_id = str(uuid.uuid4())
66+
try:
67+
from django.db.utils import OperationalError, ProgrammingError
68+
69+
from orochi.website.models import TaskLog
70+
71+
TaskLog.objects.create(task_id=task_id, name=task.name, status="Submitted")
72+
except (ProgrammingError, OperationalError):
73+
logger.warning(
74+
f"Failed to create TaskLog for {task.name}. Has the database been migrated?"
75+
)
76+
except ImportError:
77+
pass
78+
79+
future = self.client.submit(
80+
_dask_task_wrapper,
81+
task.func,
82+
task_id,
83+
*args,
84+
pure=False,
85+
key=task_id,
86+
**kwargs,
87+
)
2788
fire_and_forget(future)
2889
result = TaskResult(
2990
task=task,

0 commit comments

Comments
 (0)