Assess the whole programme and report it to a board

capstone · 150 min · Objective 5.5

Task

Pull Domain 5 together: run a gap analysis of the security programme you have built across this course against a named framework, map your detection coverage against MITRE ATT&CK, bring the risk register up to date, and write the board report: one page, in business terms, ending in the decisions you are asking for.

Steps

  1. Choose and name the framework, with its version: ISO/IEC 27001 Annex A, the CIS Controls, or another you could defend. Run a gap analysis against at least twenty of its controls, reusing the baseline from the 5.1 lab and the evidence from the Domain 3 and 4 labs. Save it as /tmp/programme-gap.csv with the header control,status,evidence,remediation,owner,target_date (status exactly met, partial or not met, target_date as YYYY-MM-DD).
  2. Map detection coverage: pick at least ten ATT&CK techniques relevant to your estate and, for each, record whether your lab would prevent it, detect it or miss it, with the rule or log that proves the answer. Save it as /tmp/attack-coverage.csv with the header technique_id,technique,result,evidence, result one of prevent, detect or miss.
  3. Produce the risk position: copy the register from the 5.2 applied lab to /tmp/register-final.csv (same columns) and update every entry against what you now know, including the gaps and the missed techniques. Every accept row needs accepted_by and a review date.
  4. State the risk appetite you are measuring against, in one sentence a board would recognise, and identify the entries that sit outside it.
  5. List the risks that have been accepted, who accepted each, and when each is next reviewed. This is the item most often omitted from board reporting and the one with the sharpest governance consequence.
  6. Write /tmp/board-report.md, ONE page: the three things that could stop the business, the trend since the last report, what is outside appetite, what has been accepted on the board's behalf, and the decisions you are asking for: funding, acceptance, or a change of direction. Write the technical appendix separately; the board report must stand alone without it.
  7. Finally, review your own report: does it contain a vulnerability count, a technique ID, a severity distribution, or any number a board cannot act on? Move them to the appendix.

Verify

python3 - <<'PY'
import re
t=open('/tmp/board-report.md').read()
words=len(t.split())
print('board report length:',words,'words')
assert words<=700, 'the board report is longer than a page - it will not be read'
low=t.lower()
need={'top risks':'could stop|top risk|greatest',
      'trend':'trend|since the last|improving|worsening',
      'outside appetite':'appetite',
      'accepted on their behalf':'accepted',
      'decisions asked for':'decision|we are asking|recommend'}
missing=[k for k,p in need.items() if not re.search(p,low)]
assert not missing, 'missing from the board report: '+', '.join(missing)
tech=re.findall(r'\b(cvss|cve-\d{4}|t1\d{3}|port \d+|tcp/\d+|sha256)\b', low)
assert not tech, 'technical detail that belongs in the appendix: '+', '.join(set(tech))
print('board report contains all five required elements and no technical noise')
PY
python3 - <<'PY'
import csv,re
gap=list(csv.DictReader(open('/tmp/programme-gap.csv')))
assert len(gap)>=20, 'fewer than twenty controls in the gap analysis'
gaps=[r for r in gap if r['status'].strip().lower()!='met']
assert gaps, 'everything met - the control selection avoided the gaps'
assert all(r['owner'].strip() and re.match(r'\d{4}-\d{2}-\d{2}$', r['target_date'].strip()) for r in gaps), \
    'a gap with no owner or no target date'
cov=list(csv.DictReader(open('/tmp/attack-coverage.csv')))
assert len(cov)>=10, 'fewer than ten techniques mapped'
assert all(re.match(r'T\d{4}', r['technique_id'].strip()) for r in cov), 'technique IDs look wrong'
res=[r['result'].strip().lower() for r in cov]
assert set(res)<= {'prevent','detect','miss'} and 'miss' in res, 'no technique marked miss - the map shows no gaps'
reg=list(csv.DictReader(open('/tmp/register-final.csv')))
acc=[r for r in reg if r['strategy'].strip().lower()=='accept']
assert acc, 'no accepted risks in the final register'
for r in acc:
    assert r['accepted_by'].strip(), 'an accepted risk with nobody named: '+r['id']
    assert re.match(r'\d{4}-\d{2}-\d{2}', r['review_date'].strip()), 'accepted risk with no review date: '+r['id']
print('%d controls (%d gaps, all owned), %d techniques (%d missed), %d accepted risk(s) named and dated'
      % (len(gap),len(gaps),len(cov),res.count('miss'),len(acc)))
PY

The length assertion is not arbitrary: a board report that runs past a page gets skimmed, and the material that gets skipped is the part asking for a decision. The technical-noise assertion enforces the point of the exercise: a board cannot act on a CVSS distribution or a technique ID, and including them signals that the report was written for the author rather than the reader. The second block checks that the gap analysis found real gaps with owners, that the coverage map admits what you would miss, and that every acceptance has a name and a date.

Notes

The accepted-risk section is the one to get right. Risks accepted on the board's behalf, by people acting under delegated authority, are the board's exposure and they frequently never appear in what the board sees. Listing them, with who accepted each and when it is revisited, is the single most useful thing a security report can contain, and it is the natural end of this course: the technical work of Domains 1 to 4 becomes a decision somebody with the authority to make it has actually made.

This is an independent study companion for CompTIA Security+ SY0-801 and is not produced by or endorsed by CompTIA.