Skip to content

Commit fd074a7

Browse files
author
Davide Arcuri
committed
1 parent 44a2145 commit fd074a7

File tree

16 files changed

+300
-141
lines changed

16 files changed

+300
-141
lines changed

compose/local/dask/Dockerfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM daskdev/dask:2024.9.1-py3.12
1+
FROM daskdev/dask:2024.12.1-py3.12
22
ENV DEBIAN_FRONTEND noninteractive
33

44
ARG local_folder=/uploads
@@ -27,7 +27,7 @@ RUN freshclam
2727
# Workers should have similar reqs as django
2828
WORKDIR /
2929
COPY ./requirements /requirements
30-
RUN pip install uv==0.5.6 -e git+https://github.com/dadokkio/volatility3.git@e2cdbdc2bf30b8c17ae36b68559ca4ff5c78b461#egg=volatility3 \
30+
RUN pip install uv==0.5.15 -e git+https://github.com/dadokkio/volatility3.git@a98a23fa41395b9bbd961f6e50d0a0f19201fcc7#egg=volatility3 \
3131
&& uv pip install --no-cache --system -r /requirements/base.txt
3232

3333
COPY ./compose/local/dask/prepare.sh /usr/bin/prepare.sh

compose/local/django/Dockerfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM python:3.12.6-slim-bookworm as common-base
1+
FROM python:3.12.8-slim-bookworm as common-base
22

33
ENV DJANGO_SETTINGS_MODULE config.settings.local
44
ENV PYTHONUNBUFFERED 1
@@ -44,7 +44,7 @@ RUN /usr/local/go/bin/go build
4444
FROM common-base
4545
WORKDIR /
4646
COPY ./requirements /requirements
47-
RUN pip install uv==0.5.6 -e git+https://github.com/dadokkio/volatility3.git@e2cdbdc2bf30b8c17ae36b68559ca4ff5c78b461#egg=volatility3 \
47+
RUN pip install uv==0.5.15 -e git+https://github.com/dadokkio/volatility3.git@a98a23fa41395b9bbd961f6e50d0a0f19201fcc7#egg=volatility3 \
4848
&& uv pip install --no-cache --system -r /requirements/base.txt
4949

5050
COPY ./compose/local/__init__.py /src/volatility3/volatility3/framework/constants/__init__.py

orochi/api/api.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from ninja import NinjaAPI
22

3+
from orochi.api.routers.admin import router as admin_router
34
from orochi.api.routers.auth import router as auth_router
45
from orochi.api.routers.bookmarks import router as bookmarks_router
56
from orochi.api.routers.customrules import router as customrules_router
@@ -12,6 +13,7 @@
1213
from orochi.api.routers.utils import router as utils_router
1314

1415
api = NinjaAPI(csrf=True, title="Orochi API", urls_namespace="api")
16+
api.add_router("/admin/", admin_router, tags=["Admin"])
1517
api.add_router("/auth/", auth_router, tags=["Auth"])
1618
api.add_router("/users/", users_router, tags=["Users"])
1719
api.add_router("/folders/", folders_router, tags=["Folders"])

orochi/api/models.py

+40-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from enum import Enum
22
from pathlib import Path
3-
from typing import Dict, List, Optional
3+
from typing import Dict, List, Optional, Tuple
44

55
from django.contrib.auth import get_user_model
66
from django.contrib.auth.models import Group
@@ -367,13 +367,16 @@ class RuleOut(Schema):
367367
headline: Optional[str] = None
368368

369369

370-
class RuleFilter(Schema):
370+
###################################################
371+
# Datatables
372+
###################################################
373+
class TableFilter(Schema):
371374
search: str = None
372375
order_column: int = 1
373376
order_dir: str = Field("asc", pattern="^(asc|desc)$")
374377

375378

376-
class CustomPagination(PaginationBase):
379+
class RulePagination(PaginationBase):
377380
class Input(Schema):
378381
start: int
379382
length: int
@@ -412,8 +415,6 @@ def paginate_queryset(self, queryset, pagination: Input, **params):
412415
###################################################
413416
# Symbols
414417
###################################################
415-
416-
417418
class SymbolsBannerIn(Schema):
418419
path: List[str] = []
419420
index: str
@@ -432,3 +433,37 @@ class UploadFileIn(Schema):
432433

433434
class ISFIn(Schema):
434435
path: str
436+
437+
438+
class SymbolsOut(Schema):
439+
id: str
440+
path: str
441+
action: Tuple[str, str]
442+
443+
444+
class CustomSymbolsPagination(PaginationBase):
445+
class Input(Schema):
446+
start: int
447+
length: int
448+
449+
class Output(Schema):
450+
draw: int
451+
recordsTotal: int
452+
recordsFiltered: int
453+
data: List[SymbolsOut]
454+
455+
items_attribute: str = "data"
456+
457+
def paginate_queryset(self, queryset, pagination: Input, **params):
458+
request = params["request"]
459+
return {
460+
"draw": request.draw,
461+
"recordsTotal": request.total,
462+
"recordsFiltered": len(queryset),
463+
"data": [
464+
SymbolsOut(**{"id": x.id, "path": x.path, "action": x.action})
465+
for x in queryset[
466+
pagination.start : pagination.start + pagination.length
467+
]
468+
],
469+
}

orochi/api/routers/admin.py

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
from django.contrib import messages
2+
from django.core import management
3+
from ninja import Router
4+
from ninja.security import django_auth_superuser
5+
6+
from orochi.api.models import ErrorsOut, SuccessResponse
7+
8+
router = Router()
9+
10+
11+
@router.get(
12+
"/rules/update",
13+
auth=django_auth_superuser,
14+
response={200: SuccessResponse, 400: ErrorsOut},
15+
url_name="update_rules",
16+
)
17+
def update_rules(request):
18+
"""Update rules.
19+
20+
This endpoint triggers the synchronization of rules using a management command.
21+
It returns a success message if the synchronization is successful, or an error message if it fails.
22+
23+
Args:
24+
request: The request object.
25+
26+
Returns:
27+
Tuple[int, dict]: A tuple containing the status code and a dictionary with a message.
28+
Returns 200 and a success message if the synchronization is successful.
29+
Returns 404 and an error message if the synchronization fails.
30+
31+
Raises:
32+
Exception: If an error occurs during rule synchronization.
33+
"""
34+
try:
35+
management.call_command("rules_sync", verbosity=0)
36+
messages.add_message(request, messages.INFO, "Sync Rules done")
37+
return 200, {"message": "Sync Symbols done"}
38+
except Exception as e:
39+
messages.add_message(request, messages.ERROR, f"Sync Plugin failed: {e}")
40+
return 404, {"errors": "Forbidden"}
41+
42+
43+
@router.get(
44+
"/rules/generate",
45+
auth=django_auth_superuser,
46+
response={200: SuccessResponse, 400: ErrorsOut},
47+
url_name="generate_default_rule",
48+
)
49+
def generate_default_rule(request):
50+
"""Generate a default rule.
51+
52+
This endpoint triggers the generation of a default rule using a management command.
53+
It returns a success message if the rule creation is successful, or an error message if it fails.
54+
55+
Args:
56+
request: The request object.
57+
58+
Returns:
59+
Tuple[int, dict]: A tuple containing the status code and a dictionary with a message.
60+
Returns 200 and a success message if the rule creation is successful.
61+
Returns 404 and an error message if the rule creation fails.
62+
63+
Raises:
64+
Exception: If an error occurs during rule generation.
65+
"""
66+
try:
67+
management.call_command("generate_default_rule", verbosity=0)
68+
messages.add_message(request, messages.INFO, "Default Rule created")
69+
return 200, {"message": "Sync Symbols done"}
70+
except Exception as e:
71+
messages.add_message(request, messages.ERROR, f"Sync Plugin failed: {e}")
72+
return 404, {"errors": "Forbidden"}
73+
74+
75+
@router.get(
76+
"/plugins/update",
77+
auth=django_auth_superuser,
78+
response={200: SuccessResponse, 400: ErrorsOut},
79+
url_name="update_plugins",
80+
)
81+
def update_plugins(request):
82+
"""Update plugins for the application.
83+
84+
This endpoint triggers a plugin synchronization process. It then redirects to the admin page, displaying a success or error message.
85+
86+
Args:
87+
request: The incoming HTTP request.
88+
89+
Returns:
90+
A redirect to /admin with a success message if the synchronization is successful, or a 404 error with an error message if it fails.
91+
92+
Raises:
93+
Exception: If an error occurs during plugin synchronization.
94+
"""
95+
96+
try:
97+
management.call_command("plugins_sync", verbosity=0)
98+
messages.add_message(request, messages.INFO, "Sync Plugin done")
99+
return 200, {"message": "Sync Plugin done"}
100+
except Exception as e:
101+
messages.add_message(request, messages.ERROR, f"Sync Plugin failed: {e}")
102+
return 404, {"errors": "Forbidden"}
103+
104+
105+
@router.get(
106+
"/symbols/update",
107+
auth=django_auth_superuser,
108+
response={200: SuccessResponse, 400: ErrorsOut},
109+
url_name="update_symbols",
110+
)
111+
def update_symbols(request):
112+
"""Update symbols for the application.
113+
114+
This endpoint triggers a symbol synchronization process. It then redirects to the admin page, displaying a success or error message.
115+
116+
Args:
117+
request: The incoming HTTP request.
118+
119+
Returns:
120+
A redirect to /admin with a success message if the synchronization is successful, or a 404 error with an error message if it fails.
121+
122+
Raises:
123+
Exception: If an error occurs during symbol synchronization.
124+
"""
125+
126+
try:
127+
management.call_command("symbols_sync", verbosity=0)
128+
messages.add_message(request, messages.INFO, "Sync Symbols done")
129+
return 200, {"message": "Sync Symbols done"}
130+
except Exception as e:
131+
messages.add_message(request, messages.ERROR, f"Sync Symbols failed: {e}")
132+
return 404, {"errors": "Forbidden"}

orochi/api/routers/customrules.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
ListStr,
1717
ListStrAction,
1818
RuleData,
19-
RuleFilter,
2019
SuccessResponse,
20+
TableFilter,
2121
)
2222
from orochi.website.models import CustomRule
2323

@@ -32,7 +32,7 @@
3232
)
3333
@paginate(CustomRulePagination)
3434
def list_custom_rules(
35-
request: HttpRequest, draw: Optional[int], filters: RuleFilter = Query(...)
35+
request: HttpRequest, draw: Optional[int], filters: TableFilter = Query(...)
3636
):
3737
rules = CustomRule.objects.filter(Q(public=True) | Q(user=request.user))
3838
request.draw = draw

orochi/api/routers/rules.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@
1414
from ninja.security import django_auth
1515

1616
from orochi.api.models import (
17-
CustomPagination,
1817
ErrorsOut,
1918
ListStr,
2019
RuleBuildSchema,
2120
RuleEditInSchena,
22-
RuleFilter,
2321
RuleOut,
22+
RulePagination,
2423
RulesOutSchema,
2524
SuccessResponse,
25+
TableFilter,
2626
)
2727
from orochi.website.models import CustomRule
2828
from orochi.ya.models import Rule, Ruleset
@@ -31,9 +31,9 @@
3131

3232

3333
@router.get("/", auth=django_auth, url_name="list_rules", response=List[RuleOut])
34-
@paginate(CustomPagination)
34+
@paginate(RulePagination)
3535
def list_rules(
36-
request: HttpRequest, draw: Optional[int], filters: RuleFilter = Query(...)
36+
request: HttpRequest, draw: Optional[int], filters: TableFilter = Query(...)
3737
):
3838
"""Retrieve a list of rules based on the provided filters and pagination.
3939
@@ -43,7 +43,7 @@ def list_rules(
4343
Args:
4444
request (HttpRequest): The HTTP request object containing user and query information.
4545
draw (int, optional): A draw counter for the DataTables plugin to ensure proper response handling.
46-
filters (RuleFilter, optional): An object containing search and order criteria. Defaults to Query(...).
46+
filters (TableFilter, optional): An object containing search and order criteria. Defaults to Query(...).
4747
4848
Returns:
4949
List[RuleOut]: A list of rules that match the specified filters and pagination settings.

orochi/api/routers/symbols.py

+47-1
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,24 @@
99

1010
import magic
1111
import requests
12+
from django.http import HttpRequest
1213
from django.shortcuts import get_object_or_404
1314
from django.utils.text import slugify
1415
from extra_settings.models import Setting
15-
from ninja import File, Router
16+
from ninja import File, Query, Router
1617
from ninja.files import UploadedFile
18+
from ninja.pagination import paginate
1719
from ninja.security import django_auth
20+
from volatility3.framework import automagic, contexts
1821

1922
from orochi.api.models import (
23+
CustomSymbolsPagination,
2024
ErrorsOut,
2125
ISFIn,
2226
SuccessResponse,
2327
SymbolsBannerIn,
28+
SymbolsOut,
29+
TableFilter,
2430
UploadFileIn,
2531
)
2632
from orochi.utils.download_symbols import Downloader
@@ -31,6 +37,46 @@
3137
router = Router()
3238

3339

40+
@router.get("/", auth=django_auth, url_name="list_symbols", response=List[SymbolsOut])
41+
@paginate(CustomSymbolsPagination)
42+
def list_symbols(
43+
request: HttpRequest, draw: Optional[int], filters: TableFilter = Query(...)
44+
):
45+
symbols = []
46+
47+
ctx = contexts.Context()
48+
automagics = automagic.available(ctx)
49+
if banners := [x for x in automagics if x._config_path == "automagic.SymbolFinder"]:
50+
banner = banners[0].banners
51+
else:
52+
banner = []
53+
54+
request.draw = draw
55+
request.total = len(banner)
56+
request.search = filters.search or None
57+
58+
for k, v in banner.items():
59+
try:
60+
k = k.decode("utf-8")
61+
v = str(v)
62+
except AttributeError:
63+
k = str(k)
64+
65+
if filters.search and (filters.search not in k and filters.search not in v):
66+
continue
67+
68+
if "file://" in v:
69+
path = v.replace("file://", "").replace(
70+
Setting.get("VOLATILITY_SYMBOL_PATH"), ""
71+
)
72+
action = ("list", "-") if "/added/" not in v else ("delete", path)
73+
else:
74+
action = ("down", v)
75+
76+
symbols.append(SymbolsOut(id=k, path=path, action=action))
77+
return symbols
78+
79+
3480
@router.post(
3581
"/banner",
3682
auth=django_auth,

0 commit comments

Comments
 (0)