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 / docs / build_user_guide.py 8294 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
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""Generate the StrikeXi User Guide PDF (reportlab)."""
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm
from reportlab.lib import colors
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, ListFlowable, ListItem, HRFlowable, Table, TableStyle
)
import os

NAVY = colors.HexColor("#16324f")
BLUE = colors.HexColor("#2e86de")

styles = getSampleStyleSheet()
styles.add(ParagraphStyle("Brand", fontName="Helvetica-Bold", fontSize=28, textColor=NAVY, spaceAfter=2))
styles.add(ParagraphStyle("Sub", fontName="Helvetica", fontSize=13, textColor=colors.HexColor("#5b6b7b"), spaceAfter=18))
styles.add(ParagraphStyle("H1", fontName="Helvetica-Bold", fontSize=15, textColor=NAVY, spaceBefore=16, spaceAfter=6))
styles.add(ParagraphStyle("Body2", fontName="Helvetica", fontSize=10.5, leading=15, spaceAfter=6))
styles.add(ParagraphStyle("BulletX", fontName="Helvetica", fontSize=10.5, leading=15))
styles.add(ParagraphStyle("CodeX", fontName="Courier", fontSize=9.5, leading=13, backColor=colors.HexColor("#f1f4f8"),
                          borderPadding=6, textColor=colors.HexColor("#1f2d3d")))


def h1(t): return Paragraph(t, styles["H1"])
def p(t): return Paragraph(t, styles["Body2"])
def code(t): return Paragraph(t.replace(" ", "&nbsp;").replace("\n", "<br/>"), styles["CodeX"])
def bullets(items):
    return ListFlowable([ListItem(Paragraph(i, styles["BulletX"]), leftIndent=10) for i in items],
                        bulletType="bullet", start="•", leftIndent=14)


def build(path):
    doc = SimpleDocTemplate(path, pagesize=A4, topMargin=1.6*cm, bottomMargin=1.6*cm,
                            leftMargin=1.8*cm, rightMargin=1.8*cm, title="StrikeXi User Guide")
    e = []
    e.append(Paragraph('Strike<font color="#2e86de">Xi</font>', styles["Brand"]))
    e.append(Paragraph("User Guide - NCSC CAF Cyber Maturity Assessment Platform", styles["Sub"]))
    e.append(HRFlowable(width="100%", color=BLUE, thickness=2))
    e.append(Spacer(1, 10))

    e.append(p("StrikeXi is a containerised web application for running Cyber Maturity Assessments "
               "against the NCSC Cyber Assessment Framework (CAF). It scores responses, generates an "
               "actionable remediation roadmap, and exports a professional PDF report."))

    e.append(h1("1. Getting Started (Administrator)"))
    e.append(p("Prerequisites: Docker and Docker Compose installed on the host."))
    e.append(code("cd strikexi\ncp .env.example .env      # edit secrets before first run\ndocker compose up -d --build"))
    e.append(p("The first launch initialises the PostgreSQL database, applies the schema and seed data, "
               "and creates the default administrator account."))
    e.append(bullets([
        "Frontend (web UI): <b>http://localhost:8080</b>",
        "Backend API docs (Swagger): <b>http://localhost:8000/docs</b>",
        "Default login: username <b>admin</b> / password as set in <b>.env</b> (ADMIN_PASSWORD).",
    ]))
    e.append(p('<b>Important:</b> change <font face="Courier">SECRET_KEY</font>, '
               '<font face="Courier">POSTGRES_PASSWORD</font> and '
               '<font face="Courier">ADMIN_PASSWORD</font> in <font face="Courier">.env</font> before production use.'))

    e.append(h1("2. Logging In"))
    e.append(bullets([
        "Open the web UI and enter your username and password.",
        "Sessions are secured with a signed JWT token that expires automatically.",
        "Every login - successful or failed - is recorded in the Audit Log.",
    ]))

    e.append(h1("3. The Dashboard"))
    e.append(p("After login you land on the Dashboard, which lists all your assessments with their "
               "organisation, date, score and status. From here you can:"))
    e.append(bullets([
        "<b>Resume</b> an in-progress assessment.",
        "<b>Review</b> a completed assessment and its results/roadmap.",
        "Start a new assessment via the sidebar.",
    ]))

    e.append(h1("4. Running an Assessment"))
    e.append(bullets([
        "Click <b>New Assessment</b>, enter the organisation name and assessment date, then <b>Create &amp; begin</b>.",
        "Questions cover all four CAF Objectives (A-D) and all fourteen Principles "
        "(A1-A4, B1-B6, C1-C2, D1-D2), grouped by objective and principle.",
        "For each question, select an answer (Achieved / Partially achieved / Not achieved). "
        "Each option carries a weighted score.",
        "Use <b>Save &amp; pause</b> at any time - your answers are stored and you can resume later.",
        "Click <b>Complete &amp; score</b> to calculate maturity and generate the roadmap.",
    ]))

    e.append(h1("5. Understanding Your Scores"))
    e.append(p("Scoring is weighted: each question's contribution is its option score multiplied by its "
               "weight. Principle scores roll up into Objective scores, and the four objectives average "
               "into an overall maturity score out of 100."))
    band = Table([
        ["Score", "Maturity band"],
        ["80-100", "Strong"],
        ["60-79", "Established"],
        ["40-59", "Developing"],
        ["0-39", "Initial"],
    ], colWidths=[4*cm, 8*cm])
    band.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,0), NAVY),
        ("TEXTCOLOR", (0,0), (-1,0), colors.white),
        ("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
        ("FONTSIZE", (0,0), (-1,-1), 10),
        ("GRID", (0,0), (-1,-1), 0.5, colors.HexColor("#d4dde7")),
        ("ROWBACKGROUNDS", (0,1), (-1,-1), [colors.white, colors.HexColor("#f6f9fc")]),
        ("TOPPADDING", (0,0), (-1,-1), 5), ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ]))
    e.append(band)
    e.append(Spacer(1, 8))

    e.append(h1("6. The Maturity Roadmap"))
    e.append(p("When a principle scores below its maturity threshold, StrikeXi automatically queues the "
               "mapped remediation action. Each roadmap step references the specific CAF Objective and "
               "Principle it addresses and provides detailed mitigation guidance. Steps are ordered by "
               "priority so you know exactly what to tackle first to raise maturity."))

    e.append(h1("7. Exporting the PDF Report"))
    e.append(bullets([
        "From the results screen, click <b>Export PDF report</b>.",
        "Confirm or edit the <b>Organisation Assessed</b> and <b>Date of Assessment</b>.",
        "Click <b>Generate PDF</b> - the report downloads automatically.",
        "The report includes the overall score, per-objective scores, a breakdown, and the full roadmap.",
    ]))

    e.append(h1("8. Audit Log"))
    e.append(p("The Audit Log (sidebar) records key actions with timestamps and source IP: logins, "
               "failed logins, assessments started/completed, and PDF exports - supporting accountability "
               "and compliance evidence."))

    e.append(h1("9. Future Integrations (ITSM)"))
    e.append(p("Roadmap items are structured so they can be pushed to ITSM tools such as Jira or "
               "ServiceNow via the backend's pluggable adapter layer. A stub endpoint "
               "(<font face=\"Courier\">POST /api/assessments/{id}/push-itsm</font>) is included for future use."))

    e.append(h1("10. Maintenance"))
    e.append(bullets([
        "Data persists in Docker named volumes (<font face=\"Courier\">strikexi_pgdata</font>, "
        "<font face=\"Courier\">strikexi_reports</font>) and survives reboots.",
        "Stop: <font face=\"Courier\">docker compose down</font> (add <font face=\"Courier\">-v</font> only if you intend to wipe data).",
        "View logs: <font face=\"Courier\">docker compose logs -f backend</font>.",
    ]))

    e.append(Spacer(1, 14))
    e.append(HRFlowable(width="100%", color=colors.HexColor("#d4dde7")))
    e.append(Paragraph("StrikeXi - Confidential. For authorised assessor use only.",
                       ParagraphStyle("foot", fontName="Helvetica", fontSize=8.5,
                                      textColor=colors.HexColor("#8a97a6"))))
    doc.build(e)
    print("Wrote", path)


if __name__ == "__main__":
    out = os.path.join(os.path.dirname(__file__), "StrikeXi_User_Guide.pdf")
    build(out)