Catalyst / admin/Strike 14.8 GB / 57.8 GB 40.0 GB free
Help Sign in

admin / Strike

public

Web-Based UK Cyber Compliance Tool with Reporting

Code Issues Pull requests Pipelines Packages Security Insights Wiki Settings
Strike / strikexi-v2 / backend / app / itsm.py 2603 B · main
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
"""
Future-proofing: ITSM integration layer.

The roadmap items (RemediationAction rows) are deliberately modelled so they
can be pushed to external systems. This module provides a pluggable adapter
interface; the default is a no-op/log adapter. Swap in Jira/ServiceNow later
without touching the assessment or scoring logic.

To enable a real integration:
  1. Implement an adapter (e.g. JiraAdapter.push(action) -> external_ref).
  2. Register it in ADAPTERS.
  3. Call push_actions(db, assessment_id, system="jira").
"""
from abc import ABC, abstractmethod

from sqlalchemy.orm import Session

from . import models


class ITSMAdapter(ABC):
    name = "base"

    @abstractmethod
    def push(self, action: models.RemediationAction, suggestion: models.RemediationSuggestion) -> str:
        """Create an external ticket and return its reference id."""
        raise NotImplementedError


class LogAdapter(ITSMAdapter):
    """Default safe adapter — records intent without external calls."""
    name = "log"

    def push(self, action, suggestion) -> str:
        return f"LOG-{action.id.hex[:8]}"


# Stubs showing the intended shape for future webhook/API integrations.
class JiraAdapter(ITSMAdapter):
    name = "jira"

    def push(self, action, suggestion) -> str:
        # TODO: POST to {JIRA_URL}/rest/api/3/issue with auth + payload:
        #   {"fields": {"project": {...}, "summary": suggestion.title,
        #    "description": suggestion.detail, "issuetype": {"name": "Task"}}}
        return f"JIRA-STUB-{action.id.hex[:6]}"


class ServiceNowAdapter(ITSMAdapter):
    name = "servicenow"

    def push(self, action, suggestion) -> str:
        # TODO: POST to {SN_URL}/api/now/table/incident with basic auth.
        return f"SN-STUB-{action.id.hex[:6]}"


ADAPTERS = {a.name: a for a in [LogAdapter(), JiraAdapter(), ServiceNowAdapter()]}


def push_actions(db: Session, assessment_id, system: str = "log") -> list[dict]:
    adapter = ADAPTERS.get(system, ADAPTERS["log"])
    sugg = {s.id: s for s in db.query(models.RemediationSuggestion).all()}
    actions = (
        db.query(models.RemediationAction)
        .filter(models.RemediationAction.assessment_id == assessment_id,
                models.RemediationAction.status == "queued").all()
    )
    results = []
    for a in actions:
        ref = adapter.push(a, sugg.get(a.remediation_id))
        a.external_ref = ref
        a.external_system = adapter.name
        a.status = "exported"
        results.append({"action_id": str(a.id), "external_ref": ref, "system": adapter.name})
    db.commit()
    return results