# Control Template

Every control on every domain page (AWS, Azure, GCP, OCI, General) **MUST**
include all of the mandatory fields listed below, rendered using the documented
CSS class names. This template is the contract between Phase 1 (design system)
and Phases 4–9 (content). Renaming a class or omitting a field after Phase 2
ships breaks 37 pages.

Severity tag: see [`docs/severity-rubric.md`](./severity-rubric.md) — the rubric
defines Critical / High / Medium / Low criteria; this template defines the
markup that renders them.

---

## Mandatory fields

Each control entry MUST include the following fields, in order:

1. **Control ID** — Stable, lowercase, kebab-case identifier (e.g.,
   `aws-iam-01-root-mfa`). Used as the `id` attribute on the wrapping
   `<article class="control-box">` so it can be deep-linked from the compliance
   matrix (Phase 10) and the search index (Phase 11). Once published, the ID is
   immutable; renaming breaks every inbound anchor.

2. **Title** — Short imperative noun phrase (e.g., "Enable hardware MFA on the
   root account"). Rendered inside `<h3 class="control-title">`. Avoid verbs
   like "should" or "consider"; the title states what the configured outcome is.

3. **Severity tag** — One of CRITICAL / HIGH / MEDIUM / LOW, assigned **per
   `docs/severity-rubric.md`** (link explicitly; never re-derive criteria
   inline). Rendered as a single `<span class="sev sev-*">` element that
   contains **both** a `<span class="sev-icon">` AND a `<span class="sev-label">`
   child. Color alone is forbidden (DS-07).

4. **Control type** — One of PREVENTIVE / DETECTIVE / RESPONSIVE. Rendered as
   `<span class="control-type">`. Always emitted adjacent to the severity tag
   so readers can distinguish CRITICAL PREVENTIVE from CRITICAL DETECTIVE
   (PITFALLS.md B-14).

5. **Threat-model box** — `<aside class="threat-model">` containing three
   sub-fields, each rendered as a labeled paragraph:
   - **Mitigates** — One-sentence threat statement (what attack this control
     prevents, detects, or responds to).
   - **Attack vector** — How the attack occurs absent this control (the
     concrete kill-chain step that becomes possible).
   - **Blast radius** — Scope of compromise if exploited (data classes,
     accounts, blast surface).

6. **CLI remediation** — Provider CLI snippet (`aws`, `az`, `gcloud`, `oci`) in
   `<pre class="code-block"><code class="language-bash">`. Snippet must be
   executable as-is against a configured CLI; placeholders (e.g.,
   `<ACCOUNT_ID>`) must be uppercase angle-bracketed and explained in the
   surrounding prose.

7. **IaC remediation** — Terraform HCL snippet in
   `<pre class="code-block"><code class="language-hcl">`. **MUST** include a
   provider version comment as the first line of the snippet (STD-07):

   ```hcl
   # Terraform AWS provider ~> 5.0
   ```

   Use `~> 5.0` style pessimistic version constraints; do not pin to bare
   floating `latest` and do not omit the comment. Per-provider examples:

   - `# Terraform AWS provider ~> 5.0`
   - `# Terraform AzureRM provider ~> 3.0`
   - `# Terraform Google provider ~> 5.0`
   - `# Terraform OCI provider ~> 5.0`

8. **Compliance mapping table** — `<table class="compliance-table">` whose
   header row **pins specific framework versions** (STD-02). The version
   strings below MUST appear verbatim in the header (no abbreviations, no
   "latest", no missing minor digits):

   | Framework                                | Pinned version |
   |------------------------------------------|----------------|
   | CIS AWS Foundations                      | v3.0.0         |
   | CIS Microsoft Azure Foundations          | v3.0.0         |
   | CIS GCP Foundation                       | v4.0.0         |
   | CIS OCI Foundation                       | v2.0.0         |
   | NIST SP 800-53                           | rev5           |
   | ISO/IEC 27001                            | :2022          |
   | ISO/IEC 27017                            | :2015          |

   Each control row maps the control to the specific section / control ID
   within each framework (e.g., `CIS AWS Foundations v3.0.0 — 1.5`,
   `NIST 800-53 rev5 — IA-2(1)`).

9. **Source citations** — Citation-quality anchor text per STD-06. Every link
   in a control's source list **MUST** use full descriptive anchor text that
   includes the publisher and the accessed date. Format:

   ```html
   <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html">
     AWS IAM User Guide: Root user best practices (accessed 2026-05)
   </a>
   ```

   **Banned anchor text** (STD-06 anti-pattern): `click`+`here`, `see this`,
   `more info`, `this link`, bare `here`, `read more`, and bare URLs without
   surrounding citation text. The validation script `build/check-citations.sh`
   greps for these tokens and fails the build.

---

## Pinned framework versions

The following version strings appear in every compliance table header row.
**Copy them verbatim** — the validation grep is exact:

- CIS AWS Foundations v3.0.0
- CIS Microsoft Azure Foundations v3.0.0
- CIS GCP Foundation v4.0.0
- CIS OCI Foundation v2.0.0
- NIST SP 800-53 rev5 (upd1, Jan 2022)
- ISO/IEC 27001:2022
- ISO/IEC 27017:2015

If a framework releases a new version mid-project, the **entire content corpus**
is bumped in one operation (Phase 12 polish), never piecemeal. Mixed-version
references across pages destroy the compliance matrix (Phase 10).

---

## Reference HTML markup

The following is the canonical markup for a single control entry. Every control
on every domain page mirrors this structure. Class names match
`css/components.css` Pattern 5 inventory.

```html
<article class="control-box" id="aws-iam-01-root-mfa">

  <header class="control-header">
    <span class="control-id">aws-iam-01-root-mfa</span>
    <h3 class="control-title">Enable hardware MFA on the root account</h3>
    <span class="sev sev-critical">
      <span class="sev-icon" aria-hidden="true">&#x26d4;</span>
      <span class="sev-label">CRITICAL</span>
    </span>
    <span class="control-type">PREVENTIVE</span>
  </header>

  <aside class="threat-model">
    <p><span class="label">MITIGATES</span>
       Full AWS account takeover via compromised root credentials.</p>
    <p><span class="label">ATTACK VECTOR</span>
       Phished root password or leaked root access key used to sign in
       without a second factor, then assume any role and exfiltrate or
       destroy resources.</p>
    <p><span class="label">BLAST RADIUS</span>
       Entire AWS account: every region, every service, every linked
       organization member account reachable via OrganizationAccountAccessRole.</p>
  </aside>

  <h4>CLI remediation</h4>
  <pre class="code-block"><code class="language-bash">aws iam enable-mfa-device \
  --user-name root \
  --serial-number arn:aws:iam::&lt;ACCOUNT_ID&gt;:mfa/root-account-mfa-device \
  --authentication-code-1 &lt;CODE1&gt; \
  --authentication-code-2 &lt;CODE2&gt;</code></pre>

  <h4>IaC remediation</h4>
  <pre class="code-block"><code class="language-hcl"># Terraform AWS provider ~> 5.0
# Note: root MFA is configured via the AWS console (hardware token registration).
# This Terraform resource enforces the organizational policy that requires it.

resource "aws_organizations_policy" "require_root_mfa" {
  name = "require-root-mfa"
  type = "SERVICE_CONTROL_POLICY"
  content = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Sid       = "DenyRootWithoutMFA"
      Effect    = "Deny"
      Action    = "*"
      Resource  = "*"
      Condition = {
        Bool        = { "aws:MultiFactorAuthPresent" = "false" }
        StringLike  = { "aws:PrincipalArn" = "arn:aws:iam::*:root" }
      }
    }]
  })
}</code></pre>

  <h4>Compliance mapping</h4>
  <table class="compliance-table">
    <thead>
      <tr>
        <th>CIS AWS Foundations v3.0.0</th>
        <th>CIS Microsoft Azure Foundations v3.0.0</th>
        <th>CIS GCP Foundation v4.0.0</th>
        <th>CIS OCI Foundation v2.0.0</th>
        <th>NIST SP 800-53 rev5</th>
        <th>ISO/IEC 27001:2022</th>
        <th>ISO/IEC 27017:2015</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1.5 — Ensure MFA is enabled for the "root" user account</td>
        <td>n/a (cross-provider control mapping shown for context)</td>
        <td>n/a</td>
        <td>n/a</td>
        <td>IA-2(1) — Multifactor authentication to privileged accounts</td>
        <td>A.5.17 — Authentication information</td>
        <td>CLD.9.5.2 — Tenant access to shared services</td>
      </tr>
    </tbody>
  </table>

  <h4>Sources</h4>
  <ul class="source-list">
    <li><a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html">AWS IAM User Guide: Root user best practices (accessed 2026-05)</a></li>
    <li><a href="https://www.cisecurity.org/benchmark/amazon_web_services">CIS Amazon Web Services Foundations Benchmark v3.0.0 (accessed 2026-05)</a></li>
    <li><a href="https://csrc.nist.gov/pubs/sp/800/53/r5/upd1/final">NIST SP 800-53 rev5 (upd1, Jan 2022) — IA-2 Identification and Authentication (accessed 2026-05)</a></li>
  </ul>

</article>
```

---

## Image placeholder convention

Per PITFALLS.md A-13 and STD-05, **no `<img>` tag may reference a non-existent
file**. Diagrams that have not yet been authored use the placeholder figure
pattern below. The placeholder renders as a dashed-border box with descriptive
caption and ships immediately; real diagrams replace the placeholder in Phase 12
without changing surrounding markup.

```html
<figure class="img-placeholder">
  <div class="img-placeholder-box" aria-hidden="true">[Diagram placeholder]</div>
  <figcaption>
    Figure 3 — Root account MFA flow: console sign-in challenges hardware
    token before AssumeRole succeeds. Diagram pending; described above in
    threat-model "Attack vector".
  </figcaption>
</figure>
```

Rules:

- The `<figure class="img-placeholder">` wrapper is mandatory; the
  `<figcaption>` is mandatory and must describe the diagram in prose so
  screen-reader users and print readers lose nothing.
- The inner `<div class="img-placeholder-box">` carries `aria-hidden="true"`
  because the placeholder text is decorative; the caption carries the meaning.
- When a real image replaces the placeholder, the surrounding `<figure>` /
  `<figcaption>` stay; the `<div class="img-placeholder-box">` becomes
  `<img src="…" alt="…">`.

---

## Anti-patterns

The following are **forbidden** in every control entry. The validation scripts
(`build/check-citations.sh`, `build/check-severity-markup.sh`, etc.) fail the
build when any are present.

- **No `<img>` to non-existent files.** Use the
  `<figure class="img-placeholder">` pattern above. PITFALLS.md A-13.
- **No `click`+`here` / `see this` / `more info` / `read more` / bare-URL
  anchor text.** Every `<a>` uses citation-quality anchor text per STD-06. The
  grep guard explicitly bans these tokens.
- **No `<span class="sev sev-*">` without nested `<span class="sev-icon">` AND
  `<span class="sev-label">`.** Severity is icon + label + color, never color
  alone (DS-07).
- **No IaC code block without a provider version comment** on the first line
  (STD-07). Bare HCL with `provider "aws" {}` and no `# Terraform AWS provider
  ~> 5.0` comment fails the version-annotation grep.
- **No compliance-table row without a pinned-version header.** Every row maps
  to the framework versions listed in "Pinned framework versions" above; bare
  "CIS AWS" or "NIST 800-53" without the version suffix fails STD-02 grep.
- **No mixing of CSS class names.** Use `control-box`, `threat-model`,
  `sev sev-*`, `control-type`, `compliance-table`, `img-placeholder` — never
  invented variants like `control-card`, `threat-box`, or `severity-tag`.
- **No severity assigned by intuition.** Severity is applied from
  `docs/severity-rubric.md`; if the criterion match is ambiguous, the author
  rounds up and records the ambiguity in the threat-model "Attack vector"
  sub-field.
- **No "Last updated: today" stamps.** Use the `<time datetime="YYYY-MM-DD">`
  pattern documented in the site shell (Phase 2) so the last-reviewed-stamp
  CSS class can render and the build can verify freshness.
