Kai Ole Hartwig
12 min read

Grundschutz++ as code, part 5: The ISMS as a build product

A security plan is usually a document someone revises once a year. In between, it quietly goes stale. Mine is now rebuilt every six hours, and on the way there I made a mistake that taught me more than most of what worked: I wrote the measurement into the plan.

The plan, though, is the operator's statement. Whether it is true is a second question, and OSCAL answers it in a document of its own. This part shows the whole chain as it runs for me now.

Part 5 of the series “Grundschutz++ as code”. The overview of all parts is further down.

01 — Four documents, four questions

OSCAL keeps apart what runs together in many security plans. Each question has a model of its own:

In Grundschutz terms, the first is the security concept with the IT-Grundschutz check, the third the audit or Revision report. There is a simple reason to keep them apart. Whoever checks whether a statement is true must not be able to rewrite it on the side.

02 — The security plan is a claim

For every applicable requirement my plan says whether it is implemented, partially implemented or planned, and next to it what the statement rests on. Usually that is my dated verdict from the applicability review, which a small tool reads from the status column of the package pages in the handbook. Without a verdict, a component's claim with evidence counts, such as the images' from part 3. And where nothing speaks for it, the requirement is planned. The plan says so explicitly.

Component definitions bring their statement and their evidence to every requirement they claim. They never lift a verdict, though. An image knows what its build had; it knows nothing about the whole system.

New is the inventory. What the infrastructure code declares appears in the plan as an entry, for me first the DNS records from the OpenTofu state. So the plan lists what exists according to the code, not anyone's memory of it.

 

node ssp.mjs system.json > ssp.json
node validate.mjs ssp.json

03 — The existing tool first

Before writing a generator of my own I tried compliance-trestle, the best-known open-source tool for OSCAL. Version 5.1.0 reads profile, catalogue, component definition and results without trouble and resolves the profile correctly.

For assembling the plan it did not fit my case:

So trestle now runs only as an advisory check in CI, next to NIST's oscal-cli. Both say whether they can read what I write. Neither stops the build.

04 — The first assembly found gaps in the scope

A plan that is assembled reports something a written one never does: a requirement a source names that the profile does not contain.

On the first run there were three. The images had claimed two of them, one was measured. All three were missing from the profile, because it was derived from the mapping to ISO 27001 and those requirements did not appear there.

Every such report has two possible answers. Either the profile lacks the requirement, or the source claims something outside the scope. Here it was the first, and the three requirements are now part of the applicability review. Nobody would have noticed while writing.

05 — The mistake: claim and verdict mixed up

My first version had an order of precedence. What is measured beats what is claimed, and that beats the verdict by hand. It sounded exactly like the aim of the series: facts instead of assertions.

The first plan out of the pipeline taught me otherwise. Two requirements stood on the package page as “partial”, each with a reason next to it. By then both were measured, both measurements were green, and suddenly the plan said “implemented”. But the checks had looked at something quite different from what my verdict rested on. A backup check sees whether the copies are fresh. Whether they sit far enough away from the original, it does not see.

I patched the rule, first for measurements and then for components' claims. Both patches helped only halfway, because the slant remained: an assessment result could rewrite my statement. What solved it was the separation OSCAL intends anyway.

Today my verdict alone decides what the plan says. For every requirement it names the latest assessment result with its date. If the result contradicts the claim, the requirement gets a remark and the metadata counts it. The statement stays until I change it.

06 — A spot check instead of trust

To close this phase I checked ten requirements against the plan by hand. Five were measured, three claimed, two rested only on a review.

Seven matched. One differed, and rightly so: the measurement had found a gap the package page did not show yet. Two did not match, and it was the same mistake as in section 05, from the other side. An image had claimed two requirements, and the claim had lifted the review to “implemented”.

A component definition says what one build had. About the whole system it says nothing. This spot check was the second reason for the separation in section 05.

Without it this would have gone unnoticed. The plan was valid, deterministic and signed, and it was still wrong in two places.

07 — The assessment plan

Between statement and result OSCAL puts a document of its own, the assessment plan. Mine is built from the same list the checks already read their mapping to Grundschutz++ from. Each check becomes an activity with its method, its subjects and the requirements it gives evidence for. A recurring task sets the cadence.

 

node assessment-plan.mjs checks.json > assessment-plan.json
node validate.mjs assessment-plan.json

 

Every assessment result points to this plan. Anyone holding a result can look up how it was meant to come about. The two are joined by the name of the check, which both carry.

08 — The bridge back: the status column

If results no longer rewrite the plan, a finding needs another way to reach the operator. Mine is a merge request.

After every run a tool compares the plan with the results. If a requirement stands in the status column as “met” and the latest check saw it differently, the tool proposes “partial”. The old word stays in brackets, so the diff shows what moved. I read the proposal like any other merge request.

The way leads only downward. A green check lifts no row, because I first have to weigh whether it touches the part my verdict rests on. Evidence and notes the tool does not touch at all.

The first proposal came the day after the build. A check had found a project with a deviating rule, and the tool wanted to move a requirement from “met” to “partial”. The page did not say so yet.

09 — The plan of action shows what is missing

The last document of the chain says what will be fixed, and by when. It draws on two directions. One is my own plan, wherever it lists a requirement as partially implemented or planned. The other is the results, wherever the latest check did not find a requirement satisfied. Then the finding counts, even if the plan says otherwise.

Every item gets an open risk. Only an action from the improvement register can close it, with a due date and a status. The link is the register number in the row of the package page. Where the register gives a quarter as the due date, its last day applies. “Ongoing” has no date.

 

node poam.mjs poam.json > poam-out.json
node validate.mjs poam-out.json

 

The tool invents no date. An item without an action says it has none, and the metadata counts such items.

The first run was more revealing than I would have liked. The applicability review knew many gaps, the register many actions, but only a few were linked. Both had been kept up, just separately. Without the plan of action I would hardly have noticed.

10 — Built, signed, locked

All four documents come out of the same run. They are stored like the assessment results in part 4, as a signed OCI artefact and as a copy in the locked bucket.

They are signed with a key that exists for this evidence only. At first I used the images' key, and that was a mistake. Many pipelines may assume its role, and any of them could have signed a forged result that looks genuine. Evidence is only worth as much as the list of those allowed to sign it.

So every run adds to a series of dated documents. Anyone who wants to know how things stood on a given day fetches exactly that version and checks the signature. There is no prettified version after the fact.

11 — What it costs to run

Little. That was a condition, because a security plan that is expensive to run will sooner or later be maintained by hand again.

What remains are the judgements. Someone still has to keep reviews and register up to date, and the chain does not take that off anyone's hands. It only shows very clearly where it is missing.

12 — What the chain is not

13 — The series at a glance

  1. Grundschutz++ against your own documentation: the method, the catalogue as a data model, the comparison with IaC and GitOps.
  2. Applicability as data: the statement of applicability as an OSCAL profile.
  3. Containers that say which requirements they meet: a signed component definition on every image the pipeline builds.
  4. Measurement as evidence: checks write their results as OSCAL, dated, signed, impossible to delete.
  5. The ISMS as a build product: security plan, assessment plan, results and plan of action as an OSCAL chain, rebuilt with every measurement. This post.
  6. What no repository knows: the second level, the running system against its code.
  7. One measurement, many standards: NIS2, DORA, C5 and SOC 2 as mappings onto Grundschutz++.
  8. The chain to take away: general checks, the report over a period and a template that wires it all together.

Parts 1 to 6 describe what runs in my ISMS. Each appears only once its step runs in operation and has been measured. Parts 7 and 8 show tools I built after feedback at the InfoSec-Stammtisch and do not use myself. The code is in grundschutz-worksheets. A map of the whole series is OSCAL in practice: eight models, one chain.

Frequently asked questions

Does the plan replace a security concept according to the BSI standard?+

It replaces the document you would otherwise keep by hand, not the work behind it. Protection needs, scope and the verdict on each requirement remain decisions made by people. The plan connects them with what is measured and evidenced.

Why doesn't the assessment result write straight into the plan?+

Because the plan is the operator's statement and the result is a check's verdict. A check sees only the part of a requirement it measures. If it rewrote the statement, a green measurement might cover up a gap it does not even know about. So the plan points to the result and marks contradictions, but it changes only when I change it.

Why a merge request instead of an automatic change?+

Because a proposal can be right and still incomplete. Whether a measurement touches the reason for the old verdict is a judgement. The merge request makes it visible, and I make it before the page changes.

Does this work with other catalogues?+

Yes, as long as they are in OSCAL. ssp.mjs knows nothing specific to Grundschutz++. It needs a profile, component definitions, results and, optionally, a file with reviews.

Conclusion

A security plan that builds itself is quickly explained. The hard part was keeping it from saying more than it knows. As long as assessment results could rewrite my statement, the plan claimed things I would never have claimed. Only the separation OSCAL intends anyway solved that. I say how things are. The checks say what they see. And whatever lies in between reaches me as a merge request.

Continue with part 6: What no repository knows.

I build your security plan as an OSCAL chain, from what your pipeline already knows.

Plan, assessment plan, results and plan of action, kept apart the way OSCAL intends, rebuilt with every measurement, signed and stored locked. And the status column as a merge request instead of a chore.

Platform operations instead of consulting on paper: if you like, I build the next steps right away, up to the second level.

Book a call →

About the author

Photo of Kai Ole Hartwig.

Kai Ole Hartwig

Freelance DevSecOps consultant · OnlyOle Consulting

Programming since 2002 – self-taught, set up my own business with KO-Web in 2012. Over 100 projects, with a focus on security, performance, automation and quality. Today freelance: DevSecOps consulting, training and software development.