feat(classification): org data classification scheme, tactic-based initial default, operator edit rights

This commit is contained in:
kitos
2026-07-07 11:16:35 +02:00
parent 949e5d42c4
commit a66fefa30b
12 changed files with 205 additions and 29 deletions
+26 -1
View File
@@ -18,7 +18,7 @@ from app.domain.errors import (
EntityNotFoundError,
PermissionViolation,
)
from app.models.enums import TestState
from app.models.enums import DataClassification, TestState
from app.models.technique import Technique
from app.models.test import Test
from app.models.test_template import TestTemplate
@@ -26,6 +26,27 @@ from app.models.campaign import CampaignTest
from app.models.audit import AuditLog
from app.utils import escape_like
# Tactics whose test data is more likely to touch customer content, PII, or
# high-impact operational detail — these get an initial classification of
# 'restricted' rather than the 'confidential' baseline. This is a starting
# point only; the assigned operator or lead can always correct it.
_RESTRICTED_TACTICS = frozenset({
"exfiltration", "collection", "credential-access", "impact",
})
def determine_initial_classification(technique: Technique | None) -> str:
"""Best-effort initial data classification for a new test.
Every security test reveals internal attack/defense posture, so the
baseline is 'confidential' (internal use only) — never public or
general use by default. Escalated to 'restricted' when the technique's
tactic implies customer data, PII, or destructive impact.
"""
if technique and technique.tactic and technique.tactic.lower() in _RESTRICTED_TACTICS:
return DataClassification.restricted.value
return DataClassification.confidential.value
# Define function list_tests
def list_tests(
@@ -121,6 +142,8 @@ def create_test(
# Raise EntityNotFoundError
raise EntityNotFoundError("Technique", str(technique_id))
data_classification = fields.pop("data_classification", None) or determine_initial_classification(technique)
# Assign test = Test(
test = Test(
# Keyword argument: technique_id
@@ -130,6 +153,7 @@ def create_test(
# Keyword argument: state
state=TestState.draft,
created_at=datetime.utcnow(), # explicit — DB column has no server default
data_classification=data_classification,
**fields,
)
# Stage new record(s) for database insertion
@@ -223,6 +247,7 @@ def create_test_from_template(
# Keyword argument: state
state=TestState.draft,
created_at=datetime.utcnow(), # explicit — DB column has no server default
data_classification=determine_initial_classification(technique),
)
# Stage new record(s) for database insertion
db.add(test)