Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Davide Arcuri committed Sep 2, 2024
1 parent 4a06c6e commit bb6b8e0
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 160 deletions.
4 changes: 2 additions & 2 deletions compose/local/dask/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM daskdev/dask:2024.8.1-py3.12
FROM daskdev/dask:2024.8.2-py3.12
ENV DEBIAN_FRONTEND noninteractive

ARG local_folder=/uploads
Expand Down Expand Up @@ -27,7 +27,7 @@ RUN freshclam
# Workers should have similar reqs as django
WORKDIR /
COPY ./requirements /requirements
RUN pip install uv==0.3.5 -e git+https://github.com/dadokkio/volatility3.git@517f46e833648d1232b410436e53e07da429d6f5#egg=volatility3 \
RUN pip install uv==0.4.2 -e git+https://github.com/dadokkio/volatility3.git@517f46e833648d1232b410436e53e07da429d6f5#egg=volatility3 \
&& uv pip install --no-cache --system -r /requirements/base.txt

COPY ./compose/local/dask/prepare.sh /usr/bin/prepare.sh
Expand Down
2 changes: 1 addition & 1 deletion compose/local/django/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ RUN /usr/local/go/bin/go build
FROM common-base
WORKDIR /
COPY ./requirements /requirements
RUN pip install uv==0.3.5 -e git+https://github.com/dadokkio/volatility3.git@517f46e833648d1232b410436e53e07da429d6f5#egg=volatility3 \
RUN pip install uv==0.4.2 -e git+https://github.com/dadokkio/volatility3.git@517f46e833648d1232b410436e53e07da429d6f5#egg=volatility3 \
&& uv pip install --no-cache --system -r /requirements/base.txt

COPY ./compose/local/__init__.py /src/volatility3/volatility3/framework/constants/__init__.py
Expand Down
6 changes: 6 additions & 0 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import environ
import ldap
from django_auth_ldap.config import LDAPSearch
from import_export.formats.base_formats import JSON

ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent.parent
# orochi/
Expand Down Expand Up @@ -70,6 +71,7 @@
"django_admin_listfilter_dropdown",
"django_admin_multiple_choice_list_filter",
"extra_settings",
"import_export",
]

LOCAL_APPS = [
Expand Down Expand Up @@ -240,6 +242,7 @@
"loggers": {
"distributed": {"level": DEBUG_LEVEL, "handlers": ["console"]},
"django_auth_ldap": {"level": DEBUG_LEVEL, "handlers": ["console"]},
"import_export": {"level": DEBUG_LEVEL, "handlers": ["console"]},
},
}

Expand All @@ -266,6 +269,9 @@
# AUTOFIELD
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"

# IMPORT/EXPORT
IMPORT_EXPORT_FORMATS = [JSON]

# Channels
# -------------------------------------------------------------------------------
ASGI_APPLICATION = "config.routing.application"
Expand Down
71 changes: 67 additions & 4 deletions orochi/website/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.conf import settings
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
Expand All @@ -9,13 +10,17 @@
)
from django_file_form.model_admin import FileFormAdmin
from django_file_form.models import TemporaryUploadedFile
from guardian.admin import GuardedModelAdmin
from guardian.shortcuts import assign_perm, get_objects_for_user, get_perms, remove_perm
from guardian.admin import GuardedModelAdminMixin
from guardian.shortcuts import assign_perm, get_perms, remove_perm
from import_export import fields, resources
from import_export.admin import ExportActionMixin, ImportExportModelAdmin
from import_export.widgets import ForeignKeyWidget

from orochi.website.defaults import RESULT
from orochi.website.forms import (
PluginCreateAdminForm,
PluginEditAdminForm,
ResultDumpExportForm,
UserListForm,
)
from orochi.website.models import (
Expand Down Expand Up @@ -58,25 +63,83 @@ def get_indexes_names(self, obj):
return ", ".join([p.name for p in obj.indexes.all()])


class ResultResource(resources.ModelResource):
dump = fields.Field(
column_name="dump",
attribute="dump",
widget=ForeignKeyWidget(Dump, field="name"),
)

plugin = fields.Field(
column_name="plugin",
attribute="plugin",
widget=ForeignKeyWidget(Plugin, field="name"),
)

def __init__(self, **kwargs):
super().__init__()
self.dump_ids = kwargs.get("dump_ids")

def filter_export(self, queryset, **kwargs):
if self.dump_ids:
return queryset.filter(dump__pk__in=self.dump_ids)
return queryset.all()

class Meta:
model = Result
import_id_fields = ("dump", "plugin")
exclude = ("id",)


@admin.register(Result)
class ResultAdmin(admin.ModelAdmin):
class ResultAdmin(ImportExportModelAdmin):
list_display = ("dump", "plugin", "result")
search_fields = ("dump__name", "plugin__name")
resource_classes = [ResultResource]
export_form_class = ResultDumpExportForm
list_filter = (
"dump",
ResultListFilter,
"updated_at",
("plugin", RelatedDropdownFilter),
)

def get_export_resource_kwargs(self, request, **kwargs):
if export_form := kwargs.get("export_form"):
kwargs.update(dump_ids=export_form.cleaned_data["dump"])
return kwargs


class AuthorWidget(ForeignKeyWidget):
def clean(self, value, row=None, *args, **kwargs):
return (
get_user_model().objects.get_or_create(name=value)
if value and value != ""
else get_user_model().objects.first()
)


class DumpResource(resources.ModelResource):
author = fields.Field(
column_name="author",
attribute="author",
widget=AuthorWidget(settings.AUTH_USER_MODEL, field="name"),
)

class Meta:
model = Dump
import_id_fields = ("name",)
exclude = ("id", "plugins", "folder")


@admin.register(Dump)
class DumpAdmin(GuardedModelAdmin):
class DumpAdmin(ImportExportModelAdmin, GuardedModelAdminMixin, ExportActionMixin):
actions = ["assign_to_users", "remove_from_users"]
list_display = ("name", "author", "index", "status", "get_auth_users")
search_fields = ["author__name", "name", "index"]
list_filter = ("author", "status", "created_at")
exclude = ("suggested_symbols_path", "regipy_plugins", "banner")
resource_classes = [DumpResource]

def get_auth_users(self, obj):
auth_users = [
Expand Down
3 changes: 3 additions & 0 deletions orochi/website/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@

class WebsiteConfig(AppConfig):
name = "orochi.website"

def ready(self):
import orochi.website.signals
11 changes: 11 additions & 0 deletions orochi/website/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
MultipleUploadedFileField,
UploadedFileField,
)
from import_export.forms import ExportForm

from orochi.utils.plugin_install import plugin_install
from orochi.website.defaults import (
Expand All @@ -20,6 +21,16 @@
from orochi.website.models import Bookmark, Dump, Folder, Plugin, Result, UserPlugin


######################################
# EXPORT
######################################
class ResultDumpExportForm(ExportForm):
dump = forms.ModelMultipleChoiceField(
widget=forms.CheckboxSelectMultiple,
queryset=Dump.objects.all(),
)


######################################
# FOLDERS
######################################
Expand Down
149 changes: 2 additions & 147 deletions orochi/website/models.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,9 @@
import os
from datetime import datetime

from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from colorfield.fields import ColorField
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from guardian.shortcuts import assign_perm, get_users_with_perms

from orochi.website.defaults import (
DEFAULT_YARA_PATH,
RESULT,
RESULT_STATUS_DISABLED,
RESULT_STATUS_NOT_STARTED,
SERVICES,
STATUS,
TOAST_DUMP_COLORS,
TOAST_RESULT_COLORS,
IconEnum,
OSEnum,
)
from orochi.ya.models import Ruleset

from orochi.website.defaults import RESULT, SERVICES, STATUS, IconEnum, OSEnum


class Service(models.Model):
Expand Down Expand Up @@ -177,127 +156,3 @@ class CustomRule(models.Model):
public = models.BooleanField(default=False)
path = models.CharField(max_length=255)
default = models.BooleanField(default=False)


@receiver(post_save, sender=Dump)
def set_permission(sender, instance, created, **kwargs):
"""Add object specific permission to the author"""
if created:
assign_perm(
"website.can_see",
instance.author,
instance,
)


@receiver(post_save, sender=get_user_model())
def get_plugins(sender, instance, created, **kwargs):
if created:
UserPlugin.objects.bulk_create(
[
UserPlugin(user=instance, plugin=plugin)
for plugin in Plugin.objects.all()
]
)
Ruleset.objects.create(
name=f"{instance.username}-Ruleset",
user=instance,
description="Your crafted ruleset",
)
if os.path.exists(DEFAULT_YARA_PATH):
CustomRule.objects.create(
user=instance,
path=DEFAULT_YARA_PATH,
default=True,
name="DEFAULT",
)


@receiver(post_save, sender=Plugin)
def new_plugin(sender, instance, created, **kwargs):
if created:
# Add new plugin in old dump
for dump in Dump.objects.all():
if instance.operating_system in [dump.operating_system, "Other"]:
up, created = Result.objects.get_or_create(dump=dump, plugin=instance)
up.result = RESULT_STATUS_NOT_STARTED
up.save()

# Add new plugin to user
for user in get_user_model().objects.all():
up, created = UserPlugin.objects.get_or_create(user=user, plugin=instance)


@staticmethod
@receiver(pre_save, sender=Dump)
def cache_previous_status(sender, instance, *args, **kwargs):
original_status = None
if instance.id:
original_status = Dump.objects.get(pk=instance.id).status
instance.__original_status = original_status


@staticmethod
@receiver(post_save, sender=Dump)
def dump_saved(sender, instance, created, **kwargs):
users = get_users_with_perms(instance, only_with_perms_in=["can_see"])
if created:
message = f"Dump <b>{instance.name}</b> has been created"
elif instance.__original_status != instance.status:
message = f"Dump <b>{instance.name}</b> has been updated."
else:
return

message = f"{datetime.now()} || {message}<br>Status: <b style='color:{TOAST_DUMP_COLORS[instance.status]}'>{instance.get_status_display()}</b>"

for user in users:
# Send message to room group
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
f"chat_{user.pk}",
{
"type": "chat_message",
"message": message,
},
)


@staticmethod
@receiver(pre_save, sender=Result)
def cache_previous_result(sender, instance, *args, **kwargs):
original_result = None
if instance.id:
original_result = Result.objects.get(pk=instance.id).result
instance.__original_result = original_result


@staticmethod
@receiver(post_save, sender=Result)
def result_saved(sender, instance, created, **kwargs):
dump = instance.dump
users = get_users_with_perms(dump, only_with_perms_in=["can_see"])
if instance.result in [RESULT_STATUS_DISABLED, RESULT_STATUS_NOT_STARTED]:
return
if created:
message = (
f"Plugin {instance.plugin.name} on {instance.dump.name} has been created"
)
elif instance.__original_result != instance.result:
message = (
f"Plugin {instance.plugin.name} on {instance.dump.name} has been updated"
)
else:
return

message = f"{datetime.now()} || {message}<br>Status: <b style='color:{TOAST_RESULT_COLORS[instance.result]}'>{instance.get_result_display()}</b>"

for user in users:
# Send message to room group
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
f"chat_{user.pk}",
{
"type": "chat_message",
"message": message,
},
)
Loading

0 comments on commit bb6b8e0

Please sign in to comment.