case_handler models¶
case_handler.models
¶
Status
¶
Bases: TextChoices
Enumeration of possible case statuses.
Result
¶
Bases: TextChoices
Enumeration of possible case result statuses.
SAFE = ('Safe', _('Safe'))
class-attribute
instance-attribute
¶
INCONCLUSIVE = ('Inconclusive', _('Inconclusive'))
class-attribute
instance-attribute
¶
UNCHALLENGED = ('Unchallenged', _('Unchallenged'))
class-attribute
instance-attribute
¶
ALLOW_LISTED = ('AllowListed', _('AllowListed'))
class-attribute
instance-attribute
¶
FAILURE = ('Failure', _('Failure'))
class-attribute
instance-attribute
¶
SUSPICIOUS = ('Suspicious', _('Suspicious'))
class-attribute
instance-attribute
¶
DANGEROUS = ('Dangerous', _('Dangerous'))
class-attribute
instance-attribute
¶
ProposedVerdict
¶
Case
¶
Bases: Model
Main incident investigation case, storing scores, analyst decisions and AI predictions.
description = models.TextField()
class-attribute
instance-attribute
¶
reporter = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=(models.CASCADE), related_name='cases', db_index=True)
class-attribute
instance-attribute
¶
analysis_done = models.PositiveIntegerField(default=0)
class-attribute
instance-attribute
¶
status = models.CharField(max_length=20, choices=(Status.choices), default=(Status.TODO), verbose_name='Status', db_index=True)
class-attribute
instance-attribute
¶
results = models.CharField(max_length=20, choices=(Result.choices), default=(Result.INCONCLUSIVE), verbose_name='Results', db_index=True)
class-attribute
instance-attribute
¶
final_score = models.FloatField(default=0)
class-attribute
instance-attribute
¶
final_confidence = models.FloatField(default=0)
class-attribute
instance-attribute
¶
score = models.FloatField(default=0)
class-attribute
instance-attribute
¶
confidence = models.FloatField(default=0)
class-attribute
instance-attribute
¶
results_ai = models.CharField(max_length=20, default='Suspicious', verbose_name='ResultsAI')
class-attribute
instance-attribute
¶
score_ai = models.FloatField(default=0)
class-attribute
instance-attribute
¶
confidence_ai = models.FloatField(default=0)
class-attribute
instance-attribute
¶
category_ai = models.CharField(max_length=20, default='Uncategorized', verbose_name='Category AI', db_index=True)
class-attribute
instance-attribute
¶
fileOrMail = models.ForeignKey('CaseHasFileOrMail', on_delete=(models.CASCADE), related_name='cases', null=True, blank=True, db_index=True)
class-attribute
instance-attribute
¶
nonFileIocs = models.ForeignKey('CaseHasNonFileIocs', on_delete=(models.CASCADE), related_name='cases', null=True, blank=True, db_index=True)
class-attribute
instance-attribute
¶
is_challenged = models.BooleanField(default=False)
class-attribute
instance-attribute
¶
is_challengeable = models.BooleanField(default=True)
class-attribute
instance-attribute
¶
challenged_result = models.CharField(max_length=20, choices=(Result.choices), default=(Result.UNCHALLENGED), verbose_name='Challenged Result')
class-attribute
instance-attribute
¶
creation_date = models.DateTimeField(auto_now_add=True, db_index=True)
class-attribute
instance-attribute
¶
last_update = models.DateTimeField(auto_now=True)
class-attribute
instance-attribute
¶
last_update_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=(models.CASCADE), related_name='cases_last_update_by', null=True, blank=True, db_index=True)
class-attribute
instance-attribute
¶
lifecycle_state = models.CharField(max_length=20, choices=(LifecycleState.choices), default=(LifecycleState.CREATED), db_index=True, verbose_name='Lifecycle State')
class-attribute
instance-attribute
¶
finalized_at = models.DateTimeField(null=True, blank=True, verbose_name='Finalized At')
class-attribute
instance-attribute
¶
dispatched_at = models.DateTimeField(null=True, blank=True, verbose_name='Dispatched At')
class-attribute
instance-attribute
¶
kpi_counted = models.BooleanField(default=False, verbose_name='KPI Counted')
class-attribute
instance-attribute
¶
challenge_proposed_result = models.CharField(max_length=20, choices=(ProposedVerdict.choices), blank=True, default='', verbose_name='Challenge Proposed Result')
class-attribute
instance-attribute
¶
challenge_reason = models.TextField(blank=True, default='', verbose_name='Challenge Reason')
class-attribute
instance-attribute
¶
reporter_context = models.TextField(blank=True, default='', verbose_name='Reporter Context')
class-attribute
instance-attribute
¶
thehive_alert_id = models.CharField(max_length=64, blank=True, default='', verbose_name='TheHive Alert ID')
class-attribute
instance-attribute
¶
is_allowlisted = models.BooleanField(default=False, verbose_name='Is Allowlisted')
class-attribute
instance-attribute
¶
is_denylisted = models.BooleanField(default=False, verbose_name='Is Denylisted')
class-attribute
instance-attribute
¶
list_reason = models.TextField(blank=True, default='', verbose_name='Allow/Deny List Reason')
class-attribute
instance-attribute
¶
was_published_recently()
¶
Check if the case was created in the last 24 hours.
CaseChallengeToken
¶
Bases: Model
case = models.ForeignKey(Case, on_delete=(models.CASCADE), related_name='challenge_tokens', db_index=True)
class-attribute
instance-attribute
¶
token_hash = models.CharField(max_length=64, unique=True, db_index=True)
class-attribute
instance-attribute
¶
expires_at = models.DateTimeField(db_index=True)
class-attribute
instance-attribute
¶
used_at = models.DateTimeField(null=True, blank=True, db_index=True)
class-attribute
instance-attribute
¶
created_at = models.DateTimeField(auto_now_add=True)
class-attribute
instance-attribute
¶
Meta
¶
indexes = [models.Index(fields=['case', 'expires_at'], name='cct_case_expires_idx'), models.Index(fields=['case', 'used_at'], name='cct_case_used_idx')]
class-attribute
instance-attribute
¶
hash_token(token)
staticmethod
¶
issue_token(case, *, lifetime=None)
classmethod
¶
build_challenge_url(case_id, token, api_base)
staticmethod
¶
normalize_api_base(submissions_url)
staticmethod
¶
mark_used()
¶
CaseComment
¶
Bases: Model
case = models.ForeignKey(Case, on_delete=(models.CASCADE), related_name='comments', db_index=True)
class-attribute
instance-attribute
¶
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=(models.CASCADE), related_name='case_comments')
class-attribute
instance-attribute
¶
body = models.TextField()
class-attribute
instance-attribute
¶
is_internal = models.BooleanField(default=False, verbose_name='Internal (analyst-only)')
class-attribute
instance-attribute
¶
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
class-attribute
instance-attribute
¶
CaseHasFileOrMail
¶
Bases: Model
Association model for cases linked to either a file or an email (one-to-one per instance).
case = models.ForeignKey('Case', on_delete=(models.CASCADE), related_name='case_has_file_or_mail', db_index=True)
class-attribute
instance-attribute
¶
file = models.ForeignKey(File, on_delete=(models.CASCADE), related_name='case_has_file_or_mail', null=True, blank=True, db_index=True)
class-attribute
instance-attribute
¶
mail = models.ForeignKey(Mail, on_delete=(models.CASCADE), related_name='case_has_file_or_mail', null=True, blank=True, db_index=True)
class-attribute
instance-attribute
¶
creation_date = models.DateTimeField(auto_now_add=True, db_index=True)
class-attribute
instance-attribute
¶
last_update = models.DateTimeField(auto_now=True)
class-attribute
instance-attribute
¶
get_iocs()
¶
Return the associated file or mail object in a dict.
CaseHasNonFileIocs
¶
Bases: Model
Association model for cases linked to non-file IOCs: URLs, IPs, and hashes.
case = models.ForeignKey('Case', on_delete=(models.CASCADE), related_name='case_has_non_file_iocs', db_index=True)
class-attribute
instance-attribute
¶
url = models.ForeignKey(URL, on_delete=(models.CASCADE), related_name='case_has_non_file_iocs', null=True, blank=True, db_index=True)
class-attribute
instance-attribute
¶
ip = models.ForeignKey(IP, on_delete=(models.CASCADE), related_name='case_has_non_file_iocs', null=True, blank=True, db_index=True)
class-attribute
instance-attribute
¶
hash = models.ForeignKey(Hash, on_delete=(models.CASCADE), related_name='case_has_non_file_iocs', null=True, blank=True, db_index=True)
class-attribute
instance-attribute
¶
creation_date = models.DateTimeField(auto_now_add=True, db_index=True)
class-attribute
instance-attribute
¶
last_update = models.DateTimeField(auto_now=True)
class-attribute
instance-attribute
¶
get_iocs()
¶
Return the associated URL, IP, or hash as a dict.
CaseArtifact
¶
Bases: Model
Per-(case, artifact) link replacing the old FileInCases / HashInCases / UrlInCases / IpInCases / MailInCases models. Exactly one of file, hash, url, ip, mail is populated per row; artifact_type matches the populated FK.
case = models.ForeignKey('Case', on_delete=(models.CASCADE), related_name='case_artifacts', db_index=True)
class-attribute
instance-attribute
¶
artifact_type = models.CharField(max_length=10, choices=(ArtifactType.choices), db_index=True)
class-attribute
instance-attribute
¶
file = models.ForeignKey(File, on_delete=(models.CASCADE), related_name='case_artifacts', null=True, blank=True, db_index=True)
class-attribute
instance-attribute
¶
hash = models.ForeignKey(Hash, on_delete=(models.CASCADE), related_name='case_artifacts', null=True, blank=True, db_index=True)
class-attribute
instance-attribute
¶
url = models.ForeignKey(URL, on_delete=(models.CASCADE), related_name='case_artifacts', null=True, blank=True, db_index=True)
class-attribute
instance-attribute
¶
ip = models.ForeignKey(IP, on_delete=(models.CASCADE), related_name='case_artifacts', null=True, blank=True, db_index=True)
class-attribute
instance-attribute
¶
mail = models.ForeignKey(Mail, on_delete=(models.CASCADE), related_name='case_artifacts', null=True, blank=True, db_index=True)
class-attribute
instance-attribute
¶
creation_date = models.DateTimeField(auto_now_add=True, db_index=True)
class-attribute
instance-attribute
¶
last_update = models.DateTimeField(auto_now=True)
class-attribute
instance-attribute
¶
ArtifactType
¶
Bases: TextChoices