diff --git a/colander/core/admin.py b/colander/core/admin.py index 1714795..e6a8aed 100644 --- a/colander/core/admin.py +++ b/colander/core/admin.py @@ -1,8 +1,26 @@ from django.contrib import admin -from colander.core.models import ObservableType, Observable, Case, Threat, ObservableRelation, \ - Artifact, ArtifactType, Event, Actor, EventType, Comment, PiRogueExperiment, EntityRelation, BackendCredentials, \ - DetectionRule, UploadRequest, ColanderTeam, DetectionRuleOutgoingFeed, EntityOutgoingFeed +from colander.core.models import ( + Actor, + Artifact, + ArtifactType, + BackendCredentials, + Case, + ColanderTeam, + Comment, + DetectionRule, + DetectionRuleOutgoingFeed, + EntityOutgoingFeed, + EntityRelation, + Event, + EventType, + Observable, + ObservableRelation, + ObservableType, + PiRogueExperiment, + Threat, + UploadRequest, +) class ColanderTeamAdmin(admin.ModelAdmin): diff --git a/colander/core/api/serializers.py b/colander/core/api/serializers.py index ed9cf31..2278353 100644 --- a/colander/core/api/serializers.py +++ b/colander/core/api/serializers.py @@ -1,11 +1,20 @@ -import magic import pathlib -from rest_framework import serializers +import magic from django.db import transaction +from rest_framework import serializers -from colander.core.models import Artifact, ArtifactType, Case, Device, DeviceType, UploadRequest, PiRogueExperiment, \ - Observable, ObservableType +from colander.core.models import ( + Artifact, + ArtifactType, + Case, + Device, + DeviceType, + Observable, + ObservableType, + PiRogueExperiment, + UploadRequest, +) from colander.core.signals import process_hash_and_signing diff --git a/colander/core/api/views.py b/colander/core/api/views.py index fc57a45..4e6277e 100644 --- a/colander/core/api/views.py +++ b/colander/core/api/views.py @@ -5,11 +5,26 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.viewsets import GenericViewSet -from colander.core.api.serializers import ArtifactSerializer, \ - ArtifactTypeSerializer, CaseSerializer, DeviceSerializer, \ - DeviceTypeSerializer, PiRogueExperimentSerializer, ObservableSerializer, ObservableTypeSerializer -from colander.core.models import Artifact, ArtifactType, Device, DeviceType, UploadRequest, PiRogueExperiment, \ - Observable, ObservableType +from colander.core.api.serializers import ( + ArtifactSerializer, + ArtifactTypeSerializer, + CaseSerializer, + DeviceSerializer, + DeviceTypeSerializer, + ObservableSerializer, + ObservableTypeSerializer, + PiRogueExperimentSerializer, +) +from colander.core.models import ( + Artifact, + ArtifactType, + Device, + DeviceType, + Observable, + ObservableType, + PiRogueExperiment, + UploadRequest, +) from colander.core.serializers.upload_request_serializers import UploadRequestSerializer diff --git a/colander/core/crons.py b/colander/core/crons.py index 28af92b..b8f34cb 100644 --- a/colander/core/crons.py +++ b/colander/core/crons.py @@ -1,6 +1,7 @@ -import pytz import logging from datetime import datetime, timedelta + +import pytz from django.core.files import File from colander.core.models import Artifact, UploadRequest diff --git a/colander/core/es_utils.py b/colander/core/es_utils.py index 062d536..d3879a0 100644 --- a/colander/core/es_utils.py +++ b/colander/core/es_utils.py @@ -2,7 +2,6 @@ from elasticsearch.client import Elasticsearch - geoip_pipeline_id = 'geoip_pipeline' diff --git a/colander/core/experiment_tasks.py b/colander/core/experiment_tasks.py index ec3d3e3..0f30446 100644 --- a/colander/core/experiment_tasks.py +++ b/colander/core/experiment_tasks.py @@ -14,7 +14,7 @@ from yara import StringMatchInstance from colander.core.es_utils import geoip_pipeline_id -from colander.core.models import PiRogueExperiment, PiRogueExperimentAnalysis, DetectionRule +from colander.core.models import DetectionRule, PiRogueExperiment, PiRogueExperimentAnalysis external_packages = [ 'com.android.org.conscrypt.', @@ -82,7 +82,7 @@ def build_stack_traces(socket_trace_file): trace['data']['dest_ip'] = flow_data.get('dst_ip') trace['data']['community_id'] = flow_data.get('community_id') traces.append(trace) - if not flow_data.get('community_id') in community_id_stack_trace: + if flow_data.get('community_id') not in community_id_stack_trace: community_id_stack_trace[flow_data.get('community_id')] = trace return community_id_stack_trace, traces @@ -121,7 +121,7 @@ def parse_ip_layer(ip_layer: dict): 'ip': ip_layer.get('ip_ip_dst'), 'host': ip_layer.get('ip_ip_dst_host'), } - except Exception as e: + except Exception: return None @@ -467,7 +467,7 @@ def save_decrypted_traffic(pirogue_dump_id): aes_traces = [] if pirogue_dump.aes_trace: aes_traces_file = f'{tmp_dir}/{aes_trace}' - with open(aes_traces_file, 'r') as aes: + with open(aes_traces_file) as aes: aes_traces = json.load(aes) tracker_definitions = {} diff --git a/colander/core/forms.py b/colander/core/forms.py index 6783f97..3d96ecf 100644 --- a/colander/core/forms.py +++ b/colander/core/forms.py @@ -1,10 +1,10 @@ import yara from django import forms from django.core.exceptions import ValidationError - -from colander.core.models import Case, Comment, ObservableType, DetectionRule from django.utils.translation import gettext_lazy as _ +from colander.core.models import Case, Comment, DetectionRule, ObservableType + class CaseForm(forms.ModelForm): class Meta: diff --git a/colander/core/graph/serializers.py b/colander/core/graph/serializers.py index 2a09275..9d9de8e 100644 --- a/colander/core/graph/serializers.py +++ b/colander/core/graph/serializers.py @@ -1,4 +1,3 @@ -from typing import Any from rest_framework import serializers diff --git a/colander/core/management/commands/fix_entity_relations.py b/colander/core/management/commands/fix_entity_relations.py index 05d6051..346c654 100644 --- a/colander/core/management/commands/fix_entity_relations.py +++ b/colander/core/management/commands/fix_entity_relations.py @@ -2,7 +2,7 @@ from django.core.management.base import BaseCommand from django.db.models import Q -from colander.core.models import EntityRelation, Entity +from colander.core.models import Entity, EntityRelation class Command(BaseCommand): diff --git a/colander/core/management/commands/insert_default_data.py b/colander/core/management/commands/insert_default_data.py index 42a5fa2..f677290 100644 --- a/colander/core/management/commands/insert_default_data.py +++ b/colander/core/management/commands/insert_default_data.py @@ -3,8 +3,16 @@ import pkg_resources from django.core.management.base import BaseCommand -from colander.core.models import ArtifactType, ObservableType, ThreatType, ActorType, EventType, DeviceType, \ - DetectionRuleType, DataFragmentType +from colander.core.models import ( + ActorType, + ArtifactType, + DataFragmentType, + DetectionRuleType, + DeviceType, + EventType, + ObservableType, + ThreatType, +) class Command(BaseCommand): diff --git a/colander/core/middlewares.py b/colander/core/middlewares.py index b42a1e8..2d44374 100644 --- a/colander/core/middlewares.py +++ b/colander/core/middlewares.py @@ -4,7 +4,6 @@ from colander.core.forms import DocumentationForm from colander.core.models import Case - # class ActiveCaseMiddleware: # def __init__(self, get_response): # self.get_response = get_response diff --git a/colander/core/models.py b/colander/core/models.py index 1816ccc..6b77fc2 100644 --- a/colander/core/models.py +++ b/colander/core/models.py @@ -8,25 +8,20 @@ import django from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import padding -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.hazmat.primitives.asymmetric import utils +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding, rsa, utils from django.conf import settings from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.fields import HStoreField from django.db import models -from django.db.models import F -from django.db.models import Q +from django.db.models import F, Q from django.db.models.signals import pre_delete from django.dispatch import receiver from django.utils import timezone from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ -from django_q.models import Schedule -from elasticsearch_dsl import Document, Keyword, Date, Object, Text, Index +from elasticsearch_dsl import Date, Document, Index, Keyword, Object, Text logger = logging.getLogger(__name__) @@ -1797,7 +1792,7 @@ def analysis(self): total = search.count() search = search[0:total] return search.sort('-timestamp').execute() - except Exception as e: + except Exception: return None def get_es_index(self): diff --git a/colander/core/observable_tasks.py b/colander/core/observable_tasks.py index 6a31fc9..68c213d 100644 --- a/colander/core/observable_tasks.py +++ b/colander/core/observable_tasks.py @@ -4,7 +4,7 @@ import requests from colander.core.artifact_utils import import_file_as_artifact -from colander.core.models import Observable, EntityRelation, ArtifactType +from colander.core.models import ArtifactType, EntityRelation, Observable def capture_url(observable_id): diff --git a/colander/core/rest/serializers.py b/colander/core/rest/serializers.py index f644c1c..20be47b 100644 --- a/colander/core/rest/serializers.py +++ b/colander/core/rest/serializers.py @@ -1,7 +1,6 @@ from rest_framework import serializers -import colander.core.templatetags.colander_tags -from colander.core.models import colander_models, Entity, EntityRelation +from colander.core.models import Entity, EntityRelation, colander_models from colander.core.rest.commons import CommonTypeSerializer, KeyedListSerializer diff --git a/colander/core/rest/views.py b/colander/core/rest/views.py index 1e395ec..ce53398 100644 --- a/colander/core/rest/views.py +++ b/colander/core/rest/views.py @@ -1,22 +1,29 @@ import json from django.contrib.auth.decorators import login_required -from django.contrib.contenttypes.models import ContentType from django.http import JsonResponse from rest_framework import mixins from rest_framework.authentication import SessionAuthentication +from rest_framework.decorators import action from rest_framework.mixins import RetrieveModelMixin from rest_framework.permissions import IsAuthenticated -from rest_framework.viewsets import GenericViewSet, ViewSet -from rest_framework.decorators import action from rest_framework.response import Response +from rest_framework.viewsets import GenericViewSet, ViewSet from colander.core import datasets from colander.core.graph.serializers import GraphRelationSerializer -from colander.core.models import Case, EntityRelation, Entity, ObservableType, Observable, Threat, ThreatType, Event, \ - EventType +from colander.core.models import ( + Case, + Entity, + EntityRelation, + Event, + EventType, + Observable, + ObservableType, + Threat, + ThreatType, +) from colander.core.rest.serializers import DetailedEntitySerializer -from colander.core.views.views import get_active_case class DatasetViewSet(ViewSet): diff --git a/colander/core/serializers/generic.py b/colander/core/serializers/generic.py index a341d4d..c810102 100644 --- a/colander/core/serializers/generic.py +++ b/colander/core/serializers/generic.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from colander.core.models import Artifact, Device, Actor, Threat, Observable, EntityRelation, Event +from colander.core.models import Actor, Artifact, Device, EntityRelation, Event, Observable, Threat class EntitySerializer(serializers.Serializer): diff --git a/colander/core/signals.py b/colander/core/signals.py index 4b99fb0..ac16b42 100644 --- a/colander/core/signals.py +++ b/colander/core/signals.py @@ -1,7 +1,8 @@ +from threading import Thread + import django.dispatch from django.core.files import File from django.dispatch import receiver -from threading import Thread from colander.core.models import Artifact, UploadRequest from colander.core.utils import hash_file diff --git a/colander/core/templatetags/colander_tags.py b/colander/core/templatetags/colander_tags.py index 6777061..abd69c5 100644 --- a/colander/core/templatetags/colander_tags.py +++ b/colander/core/templatetags/colander_tags.py @@ -1,6 +1,7 @@ from base64 import urlsafe_b64encode from django import template + register = template.Library() @register.filter(name="model_name") diff --git a/colander/core/views/actor_views.py b/colander/core/views/actor_views.py index c0baf42..7d9a952 100644 --- a/colander/core/views/actor_views.py +++ b/colander/core/views/actor_views.py @@ -1,9 +1,9 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin -from django.forms.widgets import Textarea, RadioSelect +from django.forms.widgets import RadioSelect, Textarea from django.shortcuts import redirect from django.utils.safestring import mark_safe -from django.views.generic import CreateView, UpdateView, DetailView +from django.views.generic import CreateView, DetailView, UpdateView from colander.core.forms import CommentForm from colander.core.models import Actor, ActorType diff --git a/colander/core/views/artifact_views.py b/colander/core/views/artifact_views.py index 0a9e66d..9a9ce89 100644 --- a/colander/core/views/artifact_views.py +++ b/colander/core/views/artifact_views.py @@ -5,17 +5,18 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.db import transaction -from django.forms.widgets import Textarea, RadioSelect +from django.forms.widgets import RadioSelect, Textarea from django.http import HttpResponse, StreamingHttpResponse from django.shortcuts import redirect from django.utils.safestring import mark_safe -from django.views.generic import CreateView, UpdateView, DetailView +from django.views.generic import CreateView, DetailView, UpdateView from nacl.encoding import Base64Encoder from colander.core.forms import CommentForm from colander.core.models import Artifact, ArtifactType, Device, UploadRequest -from colander.core.views.views import CaseContextMixin from colander.core.signals import process_hash_and_signing +from colander.core.views.views import CaseContextMixin + class ArtifactCreateView(LoginRequiredMixin, CaseContextMixin, CreateView): model = Artifact diff --git a/colander/core/views/collaborate_views.py b/colander/core/views/collaborate_views.py index 08490a9..16926b2 100644 --- a/colander/core/views/collaborate_views.py +++ b/colander/core/views/collaborate_views.py @@ -1,11 +1,10 @@ +from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin -from django.core.exceptions import PermissionDenied from django.http import HttpResponseForbidden from django.shortcuts import redirect from django.urls import reverse_lazy -from django.contrib import messages -from django.views.generic import CreateView, UpdateView, DetailView +from django.views.generic import CreateView, DetailView, UpdateView from colander.core.forms import AddRemoveTeamContributorForm from colander.core.models import ColanderTeam @@ -73,7 +72,7 @@ def add_remove_team_contributor(request, pk): if form.is_valid(): contributor_id = form.cleaned_data['contributor_id'] if contributor_id == request.user.contributor_id and 'add_contributor' in request.POST: - messages.warning(request, f'You can not add the owner of this team as a contributor too.') + messages.warning(request, 'You can not add the owner of this team as a contributor too.') else: try: contributor = User.objects.get(contributor_id=contributor_id) diff --git a/colander/core/views/comment_views.py b/colander/core/views/comment_views.py index e69334a..95cb7a4 100644 --- a/colander/core/views/comment_views.py +++ b/colander/core/views/comment_views.py @@ -1,7 +1,7 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.shortcuts import redirect -from django.views.generic import DeleteView, UpdateView +from django.views.generic import UpdateView from colander.core.forms import CommentForm from colander.core.models import Comment diff --git a/colander/core/views/data_fragment_views.py b/colander/core/views/data_fragment_views.py index e151fed..46729e0 100644 --- a/colander/core/views/data_fragment_views.py +++ b/colander/core/views/data_fragment_views.py @@ -1,12 +1,12 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin -from django.forms.widgets import Textarea, RadioSelect +from django.forms.widgets import RadioSelect, Textarea from django.shortcuts import redirect from django.utils.safestring import mark_safe -from django.views.generic import CreateView, UpdateView, DetailView +from django.views.generic import CreateView, DetailView, UpdateView from colander.core.forms import CommentForm -from colander.core.models import DataFragment, DataFragmentType, Artifact +from colander.core.models import Artifact, DataFragment, DataFragmentType from colander.core.views.views import CaseContextMixin diff --git a/colander/core/views/detection_rule_views.py b/colander/core/views/detection_rule_views.py index b6dec9a..66dffb1 100644 --- a/colander/core/views/detection_rule_views.py +++ b/colander/core/views/detection_rule_views.py @@ -1,10 +1,10 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin -from django.forms.widgets import Textarea, RadioSelect +from django.forms.widgets import RadioSelect from django.shortcuts import redirect from django.urls import reverse_lazy from django.utils.safestring import mark_safe -from django.views.generic import CreateView, UpdateView, DetailView +from django.views.generic import CreateView, DetailView, UpdateView from colander.core.forms import CommentForm, DetectionRuleForm from colander.core.models import DetectionRule, DetectionRuleType diff --git a/colander/core/views/device_views.py b/colander/core/views/device_views.py index ce1c2f2..b65f4e6 100644 --- a/colander/core/views/device_views.py +++ b/colander/core/views/device_views.py @@ -1,12 +1,12 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin -from django.forms.widgets import Textarea, RadioSelect +from django.forms.widgets import RadioSelect, Textarea from django.shortcuts import redirect from django.utils.safestring import mark_safe -from django.views.generic import CreateView, UpdateView, DetailView +from django.views.generic import CreateView, DetailView, UpdateView from colander.core.forms import CommentForm -from colander.core.models import Device, DeviceType, Actor +from colander.core.models import Actor, Device, DeviceType from colander.core.views.views import CaseContextMixin diff --git a/colander/core/views/documentation_views.py b/colander/core/views/documentation_views.py index 5344fb0..6d958f0 100644 --- a/colander/core/views/documentation_views.py +++ b/colander/core/views/documentation_views.py @@ -1,8 +1,6 @@ +from django.contrib import messages from django.contrib.auth.decorators import login_required from django.shortcuts import redirect, render -from django.contrib import messages - -from colander.core.views.views import get_active_case @login_required diff --git a/colander/core/views/enrich_view.py b/colander/core/views/enrich_view.py index 5e6b9b9..d2217ce 100644 --- a/colander/core/views/enrich_view.py +++ b/colander/core/views/enrich_view.py @@ -6,7 +6,7 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.shortcuts import get_object_or_404, render -from elasticsearch_dsl import Document, Keyword, Date, Index, Object +from elasticsearch_dsl import Date, Document, Index, Keyword, Object from colander.core.models import Observable from colander.core.scarlet_utils import clean_results diff --git a/colander/core/views/event_views.py b/colander/core/views/event_views.py index 5fb642f..2c99fef 100644 --- a/colander/core/views/event_views.py +++ b/colander/core/views/event_views.py @@ -1,14 +1,13 @@ +from bootstrap_datepicker_plus.widgets import DateTimePickerInput from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin -from django.forms.widgets import Textarea, RadioSelect +from django.forms.widgets import RadioSelect, Textarea from django.shortcuts import redirect from django.utils.safestring import mark_safe -from django.views.generic import CreateView, UpdateView, DetailView -from bootstrap_datepicker_plus.widgets import DateTimePickerInput +from django.views.generic import CreateView, DetailView, UpdateView from colander.core.forms import CommentForm -from colander.core.models import Event, EventType, Observable, Artifact, Device, DetectionRule -from colander.core.serializers.generic import EventSerializer +from colander.core.models import Artifact, DetectionRule, Device, Event, EventType, Observable from colander.core.views.views import CaseContextMixin diff --git a/colander/core/views/experiment_views.py b/colander/core/views/experiment_views.py index bd476e3..cdb43a4 100644 --- a/colander/core/views/experiment_views.py +++ b/colander/core/views/experiment_views.py @@ -1,15 +1,16 @@ +from datetime import datetime + +from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.shortcuts import redirect -from django.urls import reverse_lazy -from django.views.generic import CreateView, UpdateView, DetailView -from django.contrib import messages +from django.views.generic import CreateView, DetailView, UpdateView from django_q.tasks import async_task -from datetime import datetime + +from colander.core.experiment_tasks import apply_detection_rules, save_decrypted_traffic from colander.core.forms import CommentForm -from colander.core.models import PiRogueExperiment, Artifact, PiRogueExperimentAnalysis, DetectionRule -from colander.core.experiment_tasks import save_decrypted_traffic, apply_detection_rules -from colander.core.views.views import get_active_case, CaseContextMixin +from colander.core.models import Artifact, DetectionRule, PiRogueExperiment, PiRogueExperimentAnalysis +from colander.core.views.views import CaseContextMixin class PiRogueExperimentCreateView(LoginRequiredMixin, CaseContextMixin, CreateView): diff --git a/colander/core/views/graph_views.py b/colander/core/views/graph_views.py index 3a1d23b..a5a225e 100644 --- a/colander/core/views/graph_views.py +++ b/colander/core/views/graph_views.py @@ -1,8 +1,5 @@ -from django.contrib import messages from django.contrib.auth.decorators import login_required -from django.shortcuts import render, redirect, get_object_or_404 - -from colander.core.views.views import get_active_case +from django.shortcuts import render @login_required diff --git a/colander/core/views/investigate_views.py b/colander/core/views/investigate_views.py index e97febd..ee96cc1 100644 --- a/colander/core/views/investigate_views.py +++ b/colander/core/views/investigate_views.py @@ -2,13 +2,13 @@ import requests from django.conf import settings +from django.contrib import messages from django.contrib.auth.decorators import login_required from django.shortcuts import render -from django.contrib import messages from django.views.decorators.csrf import csrf_exempt from colander.core.forms import InvestigateSearchForm -from colander.core.models import colander_models, color_scheme, icons, BackendCredentials, ObservableType +from colander.core.models import BackendCredentials, ObservableType, colander_models, color_scheme THREAT_BACKEND_IDENTIFIER = 'threatr' logger = logging.getLogger(__name__) @@ -41,7 +41,7 @@ def investigate_search_view(request): api_key = credentials.credentials.get('api_key') threatr_types = get_threatr_types(api_key) if not threatr_types: - logger.error(f'Unable to retrieve threatr types') + logger.error('Unable to retrieve threatr types') entities = {} form = InvestigateSearchForm(request.GET) diff --git a/colander/core/views/obversable_views.py b/colander/core/views/obversable_views.py index 4282910..a9221cf 100644 --- a/colander/core/views/obversable_views.py +++ b/colander/core/views/obversable_views.py @@ -1,14 +1,14 @@ from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin -from django.forms.widgets import Textarea, RadioSelect +from django.forms.widgets import RadioSelect, Textarea from django.shortcuts import redirect from django.utils.safestring import mark_safe -from django.views.generic import CreateView, UpdateView, DetailView +from django.views.generic import CreateView, DetailView, UpdateView from django_q.tasks import async_task from colander.core.forms import CommentForm -from colander.core.models import Observable, ObservableRelation, ObservableType, Artifact, Threat, Actor +from colander.core.models import Actor, Artifact, Observable, ObservableRelation, ObservableType, Threat from colander.core.observable_tasks import capture_url from colander.core.views.views import CaseContextMixin diff --git a/colander/core/views/outgoing_feeds_views.py b/colander/core/views/outgoing_feeds_views.py index 20d799f..f7ac7e2 100644 --- a/colander/core/views/outgoing_feeds_views.py +++ b/colander/core/views/outgoing_feeds_views.py @@ -1,21 +1,19 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin +from django.core.cache import cache from django.forms.widgets import RadioSelect, Textarea -from django.http import JsonResponse, HttpResponse +from django.http import HttpResponse, JsonResponse from django.shortcuts import redirect -from django.urls import reverse_lazy from django.utils.safestring import mark_safe from django.utils.text import slugify from django.views.generic import CreateView, UpdateView -from django.core.cache import cache from colander.core.exporters.csv import CsvCaseExporter from colander.core.exporters.json import JsonCaseExporter from colander.core.exporters.stix2 import Stix2CaseExporter -from colander.core.models import DetectionRuleOutgoingFeed, \ - DetectionRuleType, EntityOutgoingFeed +from colander.core.models import DetectionRuleOutgoingFeed, DetectionRuleType, EntityOutgoingFeed from colander.core.serializers.generic import OutgoingFeedSerializer -from colander.core.views.views import get_active_case, CaseContextMixin +from colander.core.views.views import CaseContextMixin class DetectionRuleOutgoingFeedCreateView(LoginRequiredMixin, CaseContextMixin, CreateView): @@ -165,9 +163,7 @@ def outgoing_entities_feed_view(request, pk): cache_key = f'feed_{feed.id}_{format}_{feed.secret}' cached = cache.get(cache_key) if cached: - if format == 'json': - return JsonResponse(cached, json_dumps_params={}, headers={'X-Colander-Feed-Cache': 'hit'}) - elif format == 'stix2': + if format == 'json' or format == 'stix2': return JsonResponse(cached, json_dumps_params={}, headers={'X-Colander-Feed-Cache': 'hit'}) elif format == 'csv': return HttpResponse(cached, status=200, content_type='text/plain', headers={'X-Colander-Feed-Cache': 'hit'}) diff --git a/colander/core/views/relation_views.py b/colander/core/views/relation_views.py index 9151c50..af654a8 100644 --- a/colander/core/views/relation_views.py +++ b/colander/core/views/relation_views.py @@ -1,14 +1,12 @@ +from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.contenttypes.models import ContentType -from django.contrib import messages -from django.core.exceptions import NON_FIELD_ERRORS, ValidationError +from django.core.exceptions import ValidationError from django.shortcuts import redirect, render -from django.urls import reverse from django.utils.safestring import mark_safe from colander.core.forms import EntityRelationForm from colander.core.models import EntityRelation -from colander.core.views.views import get_active_case @login_required @@ -52,7 +50,7 @@ def create_or_edit_entity_relation_view(request): try: relation.full_clean() relation.save() - except ValidationError as e: + except ValidationError: messages.info(request, 'This relation already exists.', extra_tags='danger') else: messages.info(request, 'The two entities have to belong to the active case.', extra_tags='danger') diff --git a/colander/core/views/threat_views.py b/colander/core/views/threat_views.py index d78c94b..cb9a809 100644 --- a/colander/core/views/threat_views.py +++ b/colander/core/views/threat_views.py @@ -1,9 +1,9 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin -from django.forms.widgets import Textarea, RadioSelect +from django.forms.widgets import RadioSelect, Textarea from django.shortcuts import redirect from django.utils.safestring import mark_safe -from django.views.generic import CreateView, UpdateView, DetailView +from django.views.generic import CreateView, DetailView, UpdateView from colander.core.forms import CommentForm from colander.core.models import Threat, ThreatType diff --git a/colander/core/views/threatr_proxy.py b/colander/core/views/threatr_proxy.py index a7f1741..f1f2a97 100644 --- a/colander/core/views/threatr_proxy.py +++ b/colander/core/views/threatr_proxy.py @@ -1,5 +1,7 @@ -from django.contrib.auth.decorators import login_required import logging + +from django.contrib.auth.decorators import login_required + from colander.core.models import BackendCredentials THREAT_BACKEND_IDENTIFIER='threatr' diff --git a/colander/core/views/upload_views.py b/colander/core/views/upload_views.py index ab3fc61..5073253 100644 --- a/colander/core/views/upload_views.py +++ b/colander/core/views/upload_views.py @@ -1,11 +1,13 @@ +import hashlib +import json +import logging + from django.contrib.auth.decorators import login_required from django.http.response import JsonResponse from django.shortcuts import get_object_or_404 -import json + from colander.core.models import UploadRequest from colander.core.serializers.upload_request_serializers import UploadRequestSerializer -import logging -import hashlib logger = logging.getLogger(__name__) @@ -27,8 +29,8 @@ def initialize_upload(request): with open(upload_request.path, 'wb') as f: f.seek(file_size - 1) f.write(b'\0') - except IOError as e: - error_message = "Unable to create file: {}".format(e) + except OSError as e: + error_message = f"Unable to create file: {e}" logger.error(error_message, exc_info=True) upload_request.cleanup() upload_request.status = UploadRequest.Status.FAILED diff --git a/colander/core/views/views.py b/colander/core/views/views.py index d0696e2..7df2286 100644 --- a/colander/core/views/views.py +++ b/colander/core/views/views.py @@ -1,28 +1,31 @@ import json from urllib.parse import urlparse -from django.contrib.auth.decorators import login_required -from django.contrib.auth.mixins import LoginRequiredMixin, AccessMixin from django.contrib import messages +from django.contrib.auth.decorators import login_required +from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin from django.core.files.base import ContentFile from django.forms.widgets import Textarea -from django.http import JsonResponse, HttpResponse, HttpResponseForbidden, HttpResponseNotFound -from django.shortcuts import render, redirect, get_object_or_404 -from django.urls import reverse, reverse_lazy, resolve +from django.http import HttpResponse, HttpResponseForbidden, HttpResponseNotFound, JsonResponse +from django.shortcuts import get_object_or_404, redirect, render +from django.urls import resolve, reverse, reverse_lazy from django.utils.translation import gettext_lazy as _ -from django.views.generic import CreateView, UpdateView, DetailView from django.views.decorators.cache import cache_page +from django.views.generic import CreateView, DetailView, UpdateView from django.views.generic.detail import SingleObjectMixin -from django.views.generic.edit import ModelFormMixin from django_serverless_cron.services import RunJobs -from django.views.static import serve -from os import path - from colander.core import datasets from colander.core.forms import DocumentationForm -from colander.core.models import Entity, Case, colander_models, color_scheme, icons, DetectionRuleOutgoingFeed, \ - EntityOutgoingFeed +from colander.core.models import ( + Case, + DetectionRuleOutgoingFeed, + Entity, + EntityOutgoingFeed, + colander_models, + color_scheme, + icons, +) from colander.core.templatetags.colander_tags import model_name diff --git a/colander/users/adapters.py b/colander/users/adapters.py index a399932..8acb9f9 100644 --- a/colander/users/adapters.py +++ b/colander/users/adapters.py @@ -1,6 +1,5 @@ from typing import Any -from allauth.account.adapter import DefaultAccountAdapter from allauth.socialaccount.adapter import DefaultSocialAccountAdapter from allauth_2fa.adapter import OTPAdapter from django.conf import settings diff --git a/colander/users/models.py b/colander/users/models.py index c91caa2..1b43e76 100644 --- a/colander/users/models.py +++ b/colander/users/models.py @@ -1,12 +1,12 @@ import uuid from django.contrib.auth.models import AbstractUser +from django.db import models from django.db.models import CharField, Q from django.urls import reverse from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ from rest_framework.authtoken.models import Token -from django.db import models from colander.core.models import Case, ColanderTeam diff --git a/colander/users/urls.py b/colander/users/urls.py index aa4621f..cfc88f7 100644 --- a/colander/users/urls.py +++ b/colander/users/urls.py @@ -1,5 +1,4 @@ from django.urls import path -from django.views.generic import TemplateView from colander.users.views import ( user_detail_view, diff --git a/colander/users/views.py b/colander/users/views.py index f0debe2..d6a35e0 100644 --- a/colander/users/views.py +++ b/colander/users/views.py @@ -1,4 +1,3 @@ -from base64 import b32encode from urllib.parse import quote, urlencode from allauth_2fa.views import TwoFactorSetup diff --git a/config/api_router.py b/config/api_router.py index 88cd7a5..dadb7d5 100644 --- a/config/api_router.py +++ b/config/api_router.py @@ -1,10 +1,17 @@ from django.conf import settings from rest_framework.routers import DefaultRouter, SimpleRouter -from colander.core.api.views import ApiCaseViewSet, ApiDeviceViewSet, ApiDeviceTypeViewSet, ApiArtifactViewSet, \ - ApiArtifactTypeViewSet, ApiUploadRequestViewSet, ApiPiRogueExperimentViewSet, ApiObservableViewSet, \ - ApiObservableTypeViewSet -from colander.users.api.views import UserViewSet +from colander.core.api.views import ( + ApiArtifactTypeViewSet, + ApiArtifactViewSet, + ApiCaseViewSet, + ApiDeviceTypeViewSet, + ApiDeviceViewSet, + ApiObservableTypeViewSet, + ApiObservableViewSet, + ApiPiRogueExperimentViewSet, + ApiUploadRequestViewSet, +) if settings.DEBUG: router = DefaultRouter() diff --git a/config/rest_router.py b/config/rest_router.py index eb1ce0d..77cf25e 100644 --- a/config/rest_router.py +++ b/config/rest_router.py @@ -1,10 +1,8 @@ from django.conf import settings -from rest_framework.routers import DefaultRouter, SimpleRouter from django.urls import path +from rest_framework.routers import DefaultRouter, SimpleRouter -from colander.core.api.views import ApiCaseViewSet, ApiDeviceViewSet, ApiDeviceTypeViewSet, ApiArtifactViewSet, \ - ApiArtifactTypeViewSet, ApiUploadRequestViewSet, ApiPiRogueExperimentViewSet -from colander.core.rest.views import EntityRelationViewSet, import_entity_from_threatr, EntityViewSet, DatasetViewSet +from colander.core.rest.views import DatasetViewSet, EntityRelationViewSet, EntityViewSet, import_entity_from_threatr from colander.core.views.views import entity_exists, overall_search if settings.DEBUG: diff --git a/config/urls.py b/config/urls.py index 7f13580..8b3d923 100644 --- a/config/urls.py +++ b/config/urls.py @@ -6,46 +6,93 @@ from django.views import defaults as default_views from django.views.generic import TemplateView from django.views.i18n import JavaScriptCatalog -from drf_spectacular.views import SpectacularAPIView, SpectacularJSONAPIView, SpectacularSwaggerView -from django_serverless_cron.views import RunJobsView from rest_framework.schemas import get_schema_view -from colander.core.views.actor_views import ActorDetailsView, ActorUpdateView, ActorCreateView, delete_actor_view -from colander.core.views.artifact_views import ArtifactDetailsView, ArtifactCreateView, ArtifactUpdateView, \ - download_artifact, \ - download_artifact_signature, delete_artifact_view, view_artifact -from colander.core.views.collaborate_views import ColanderTeamCreateView, ColanderTeamUpdateView, delete_team_view, \ - ColanderTeamDetailsView, add_remove_team_contributor -from colander.core.views.comment_views import create_comment_view, delete_comment_view, CommentUpdateView -from colander.core.views.data_fragment_views import DataFragmentCreateView, DataFragmentUpdateView, \ - DataFragmentDetailsView, delete_data_fragment_view -from colander.core.views.detection_rule_views import delete_detection_rule_view, DetectionRuleCreateView, \ - DetectionRuleUpdateView, DetectionRuleDetailsView -from colander.core.views.device_views import DeviceDetailsView, DeviceCreateView, DeviceUpdateView, delete_device_view +from colander.core.graph.views import case_graph +from colander.core.views.actor_views import ActorCreateView, ActorDetailsView, ActorUpdateView, delete_actor_view +from colander.core.views.artifact_views import ( + ArtifactCreateView, + ArtifactDetailsView, + ArtifactUpdateView, + delete_artifact_view, + download_artifact, + download_artifact_signature, + view_artifact, +) +from colander.core.views.collaborate_views import ( + ColanderTeamCreateView, + ColanderTeamDetailsView, + ColanderTeamUpdateView, + add_remove_team_contributor, + delete_team_view, +) +from colander.core.views.comment_views import CommentUpdateView, create_comment_view, delete_comment_view +from colander.core.views.data_fragment_views import ( + DataFragmentCreateView, + DataFragmentDetailsView, + DataFragmentUpdateView, + delete_data_fragment_view, +) +from colander.core.views.detection_rule_views import ( + DetectionRuleCreateView, + DetectionRuleDetailsView, + DetectionRuleUpdateView, + delete_detection_rule_view, +) +from colander.core.views.device_views import DeviceCreateView, DeviceDetailsView, DeviceUpdateView, delete_device_view from colander.core.views.documentation_views import write_documentation_view -from colander.core.views.enrich_view import enrich_observable -from colander.core.views.experiment_views import PiRogueExperimentCreateView, PiRogueExperimentUpdateView, \ - PiRogueExperimentDetailsView, start_decryption, delete_experiment_view, save_decoded_content_view, start_detection, \ - PiRogueExperimentAnalysisReportView +from colander.core.views.event_views import EventCreateView, EventDetailsView, EventUpdateView, delete_event_view +from colander.core.views.experiment_views import ( + PiRogueExperimentAnalysisReportView, + PiRogueExperimentCreateView, + PiRogueExperimentDetailsView, + PiRogueExperimentUpdateView, + delete_experiment_view, + save_decoded_content_view, + start_decryption, + start_detection, +) from colander.core.views.graph_views import graph_base_view from colander.core.views.investigate_views import investigate_search_view -from colander.core.views.obversable_views import ObservableCreateView, ObservableUpdateView, \ - ObservableDetailsView, delete_observable_view, capture_observable_view -from colander.core.views.outgoing_feeds_views import DetectionRuleOutgoingFeedCreateView, \ - DetectionRuleOutgoingFeedUpdateView, delete_detection_rule_out_feed_view, outgoing_detection_rules_feed_view, \ - delete_entity_out_feed_view, EntityOutgoingFeedCreateView, EntityOutgoingFeedUpdateView, outgoing_entities_feed_view +from colander.core.views.obversable_views import ( + ObservableCreateView, + ObservableDetailsView, + ObservableUpdateView, + capture_observable_view, + delete_observable_view, +) +from colander.core.views.outgoing_feeds_views import ( + DetectionRuleOutgoingFeedCreateView, + DetectionRuleOutgoingFeedUpdateView, + EntityOutgoingFeedCreateView, + EntityOutgoingFeedUpdateView, + delete_detection_rule_out_feed_view, + delete_entity_out_feed_view, + outgoing_detection_rules_feed_view, + outgoing_entities_feed_view, +) from colander.core.views.relation_views import create_or_edit_entity_relation_view, delete_relation_view -from colander.core.views.views import case_close, landing_view, collect_base_view, \ - report_base_view, cases_select_view, CaseCreateView, \ - CaseUpdateView, entity_exists, quick_search, CaseDetailsView, download_case_public_key, \ - save_case_documentation_view, enable_documentation_editor, disable_documentation_editor, quick_creation_view, \ - forward_auth, cron_ish_view, collaborate_base_view, vues_view, export_case_documentation_as_markdown_view, \ - case_workspace_view, feeds_view -from colander.core.graph.views import case_graph -from colander.core.views.event_views import EventCreateView, EventUpdateView, EventDetailsView, delete_event_view -from colander.core.views.threat_views import ThreatCreateView, ThreatUpdateView, ThreatDetailsView, delete_threat_view +from colander.core.views.threat_views import ThreatCreateView, ThreatDetailsView, ThreatUpdateView, delete_threat_view +from colander.core.views.upload_views import append_to_upload, initialize_upload +from colander.core.views.views import ( + CaseCreateView, + CaseDetailsView, + CaseUpdateView, + case_close, + case_workspace_view, + cases_select_view, + collaborate_base_view, + cron_ish_view, + download_case_public_key, + export_case_documentation_as_markdown_view, + feeds_view, + landing_view, + quick_creation_view, + quick_search, + save_case_documentation_view, + vues_view, +) from colander.users.views import UserTwoFactorSetup -from colander.core.views.upload_views import initialize_upload, append_to_upload case_contextualized_url_patterns = [ path("", case_workspace_view, name="case_workspace_view"), diff --git a/docs/conf.py b/docs/conf.py index b6e61f9..1a4e811 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,6 +12,7 @@ import os import sys + import django if os.getenv("READTHEDOCS", default=False) == "True": diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..13184c8 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,76 @@ +[tool.black] +line-length = 120 +target-version = ['py39'] +extend-exclude = ''' +# A regex preceded with ^/ will apply only to files and directories +# in the root of the project. +( + migrations +) +''' + +[tool.ruff] +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "migrations", + "build", + "dist", + "node_modules", + "site-packages", + "venv", +] + +# Same as Black. +line-length = 120 +indent-width = 4 + +# Assume Python 3.9 +target-version = "py39" + +[tool.ruff.lint] +select = [ + # pycodestyle + "E", + # Pyflakes + "F", + # pyupgrade + "UP", + # flake8-bugbear + "B", + # flake8-simplify + "SIM", + # isort + "I", +] +ignore = [] +fixable = ["ALL"] +unfixable = [] + +[tool.ruff.lint.per-file-ignores] +"*/urls.py" = ["E501"] + +[tool.ruff.format] +quote-style = "single" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto"