索引に戻る

セキュリティ統制例外台帳

表
生成可能
セキュリティ統制例外台帳

作品として見る

持ち帰る

コピーして貼るか、ファイルに保存して渡す。ネットにつながらない環境には「全部入り」を 1 つ渡せば足りる。

全部入り

security-exception-register-everything.md

このパネルの中身を、URL の先にあるファイルの中身まで含めて 1 つにまとめた Markdown

依頼文

1.7 KB
「セキュリティ統制例外台帳」でHTML資料を作ってください。この型は実行できるので、版面を組み直す必要はありません — 必要なのは中身です。

## この型がすること
一時的な統制逸脱に、範囲・代替策・リスク受容者・承認記録・終了期限を結びつける。

## 渡す必要があるもの
- 対象統制、適用範囲、理由、残留リスク、代替策、責任者、決裁者、再審査日、終了日を定義する。

## 入力の形
- `title` — string — 例外統制の要点
- `subtitle` — string? — 適用組織・ポリシー版・基準日
- `program` — string — 対象セキュリティプログラム
- `exceptions` — array (3–12) — 統制例外(理由、リスク、代替策、責任者、決裁者、承認参照、再審査日、終了日、状態)

入力の機械可読な検証は schema.ts が持ちます。足りない入力は推測せず質問してください。

## 向かない場合
- 侵害や障害の発生後に原因と再発防止を記録する場合は、インシデントレビューを使う。

## 作り方
入力を JSON にまとめ、次を実行します。

```bash
node scripts/render.mjs security-exception-register <入力.json> --theme ink -o out.html
```

出力は 1 ファイルで完結し、外部から何も読み込みません。

## 原本
- https://design-vault.pages.dev/designs/security-exception-register/source/template.tsx
- https://design-vault.pages.dev/designs/security-exception-register/source/schema.ts
- https://design-vault.pages.dev/designs/security-exception-register/source/sample-data.json
- https://design-vault.pages.dev/designs/security-exception-register/sample.html

ファイル

テンプレート・入力スキーマ・サンプルデータ・メタ情報。この文書はこの 4 つから生成されています。 生成された文書

template.tsx

7.1 KB

/designs/security-exception-register/source/template.tsx

中身を見る
import type { Props } from "./schema";

const CSS = String.raw`
.asset-security-exception-register{font-family:var(--font-sans);color:var(--color-primary);background:var(--color-surface);max-width:var(--container-slide);margin:auto;padding:3rem 2.5rem;line-height:1.55;font-size:var(--text-body-sm)}
.asset-security-exception-register *{box-sizing:border-box}.asset-security-exception-register header{margin-bottom:1.25rem}.asset-security-exception-register .eyebrow{font-family:var(--font-mono);font-size:var(--text-label);color:var(--color-tertiary);margin:0 0 .6rem}.asset-security-exception-register h1{font-size:var(--text-h1);line-height:1.25;max-width:28em;margin:0}.asset-security-exception-register .subtitle{color:var(--color-secondary);margin:.65rem 0 0}.asset-security-exception-register .summary{display:flex;gap:1.5rem;flex-wrap:wrap;padding:.7rem 0;border-block:1px solid var(--color-outline);margin-bottom:1rem}.asset-security-exception-register .summary p{margin:0;font-size:var(--text-label);color:var(--color-secondary)}.asset-security-exception-register .summary strong{font-family:var(--font-mono);font-size:var(--text-h3);color:var(--color-primary);margin-right:.3rem}.asset-security-exception-register .exception{padding:.85rem 0;border-top:1px solid var(--color-outline)}.asset-security-exception-register .exception:last-child{border-bottom:1px solid var(--color-outline)}.asset-security-exception-register .head{display:flex;gap:.55rem;align-items:center;flex-wrap:wrap}.asset-security-exception-register code{font-family:var(--font-mono);font-size:var(--text-label);overflow-wrap:anywhere}.asset-security-exception-register .head h2{font-size:var(--text-body-md);margin:0;flex:1}.asset-security-exception-register .badge{font-size:var(--text-label);border:1px solid var(--color-outline);padding:.04rem .4rem;color:var(--color-secondary)}.asset-security-exception-register .detail{display:grid;grid-template-columns:1fr 1fr;gap:.55rem 1.4rem;margin-top:.55rem}.asset-security-exception-register .field{min-width:0}.asset-security-exception-register .label{display:block;color:var(--color-secondary);font-size:var(--text-label)}.asset-security-exception-register .field p{margin:.08rem 0 0;overflow-wrap:anywhere}.asset-security-exception-register .span{grid-column:1/3}.asset-security-exception-register .decision{display:flex;gap:.6rem;align-items:baseline;flex-wrap:wrap}.asset-security-exception-register .approval{font-family:var(--font-mono);font-size:var(--text-label);color:var(--color-secondary)}.asset-security-exception-register .still-svg{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
@media(max-width:680px){.asset-security-exception-register{padding:1.5rem 1rem}.asset-security-exception-register h1{font-size:var(--text-h2)}.asset-security-exception-register .detail{grid-template-columns:minmax(0,1fr);gap:.45rem}.asset-security-exception-register .span{grid-column:auto}.asset-security-exception-register .summary{gap:.8rem}}
@media print{.asset-security-exception-register{max-width:none;padding:1rem}.asset-security-exception-register .exception{break-inside:avoid}}
`;

const riskLabel = { low: "低", medium: "中", high: "高", critical: "重大" };
const statusLabel = { proposed: "審査中", approved: "承認済", denied: "却下", closed: "終了" };
const compact = (value: string, max: number) => [...value].length > max ? `${[...value].slice(0, max - 1).join("")}…` : value;

function Still({ data }: { data: Props }) {
  return <svg className="still-svg" data-layout-still="true" width="1280" height="720" viewBox="0 0 1280 720" aria-hidden="true">
    <rect width="1280" height="720" fill="var(--color-surface)" />
    <text x="48" y="44" fontSize="14" fontFamily="sans-serif" fill="var(--color-secondary)">{data.program}</text>
    <text x="48" y="85" fontSize="26" fontWeight="700" fontFamily="sans-serif" fill="var(--color-on-surface)">{compact(data.title, 64)}</text>
    <text x="48" y="120" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">対象統制 · 代替策 · リスク · 決裁者 · 再審査・終了日</text>
    {data.exceptions.slice(0, 8).map((exception, index) => {
      const y = 166 + index * 62;
      return <g key={exception.id}>
        <line x1="48" x2="1232" y1={y - 19} y2={y - 19} stroke="var(--color-outline)" />
        <text x="52" y={y + 3} fontSize="11" fontFamily="monospace" fill="var(--color-tertiary)">{exception.id}</text>
        <text x="170" y={y + 3} fontSize="12" fontWeight="700" fontFamily="sans-serif" fill="var(--color-on-surface)">{compact(exception.control, 28)}</text>
        <text x="440" y={y + 3} fontSize="11" fontFamily="sans-serif" fill="var(--color-secondary)">{riskLabel[exception.risk]} · {compact(exception.owner, 29)}</text>
        <text x="790" y={y + 3} fontSize="11" fontFamily="sans-serif" fill="var(--color-on-surface)">{compact(exception.compensatingControl, 44)}</text>
        <text x="1110" y={y + 3} fontSize="11" fontFamily="sans-serif" fill="var(--color-secondary)">{exception.expiry}</text>
      </g>;
    })}
    <text x="48" y="704" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">{data.exceptions.length}例外 · 期限前に再審査または解消</text>
  </svg>;
}

export default function Template(data: Props) {
  const approved = data.exceptions.filter((item) => item.status === "approved").length;
  const critical = data.exceptions.filter((item) => item.risk === "critical").length;
  return <section className="asset asset-security-exception-register">
    <style>{CSS}</style>
    <header>
      <p className="eyebrow">{data.program} · 統制例外</p>
      <h1>{data.title}</h1>
      {data.subtitle && <p className="subtitle">{data.subtitle}</p>}
    </header>
    <div className="summary"><p><strong>{data.exceptions.length}</strong>例外</p><p><strong>{approved}</strong>承認済</p><p><strong>{critical}</strong>重大リスク</p></div>
    {data.exceptions.map((exception) => <article className="exception" key={exception.id}>
      <div className="head"><code>{exception.id}</code><h2>{exception.control}</h2><span className="badge">リスク {riskLabel[exception.risk]}</span><span className="badge" data-status={exception.status}>{statusLabel[exception.status]}</span></div>
      <div className="detail">
        <div className="field span"><span className="label">適用範囲</span><p>{exception.scope}</p></div>
        <div className="field"><span className="label">標準統制を満たせない理由</span><p>{exception.justification}</p></div>
        <div className="field"><span className="label">代替統制</span><p>{exception.compensatingControl}</p></div>
        <div className="field"><span className="label">責任者</span><p>{exception.owner}</p></div>
        <div className="field"><span className="label">決裁者・承認参照</span><p className="decision">{exception.approver}{exception.approvalRef && <code className="approval">{exception.approvalRef}</code>}</p></div>
        <div className="field span"><span className="label">再審査日 → 例外終了日</span><p>{exception.reviewDate} → {exception.expiry}</p></div>
      </div>
    </article>)}
    <Still data={data} />
  </section>;
}

schema.ts

2.2 KB

/designs/security-exception-register/source/schema.ts

中身を見る
import { z } from "zod";

const text = z.string().trim().min(1).max(160);
const short = z.string().trim().min(1).max(48);
const date = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "日付はYYYY-MM-DD形式にすること");
const exceptionSchema = z.object({
  id: short.describe("一時例外の一意な識別子"),
  control: short.describe("対象となるセキュリティ統制"),
  scope: text.describe("例外が適用されるシステム・データ・利用者の範囲"),
  justification: text.describe("標準統制を満たせない理由"),
  risk: z.enum(["low", "medium", "high", "critical"]).describe("例外が残すリスク"),
  compensatingControl: text.describe("リスクを抑える代替策"),
  owner: short.describe("例外と代替策の実行責任者"),
  approver: short.describe("リスク受容の決裁者"),
  approvalRef: short.optional().describe("承認記録への参照"),
  reviewDate: date.describe("次の再審査日"),
  expiry: date.describe("例外の終了日"),
  status: z.enum(["proposed", "approved", "denied", "closed"]).describe("例外の審査・運用状態"),
}).strict();

export const schema = z.object({
  title: text.describe("例外統制の要点"),
  subtitle: text.optional().describe("適用組織・ポリシー版・基準日"),
  program: short.describe("対象セキュリティプログラム"),
  exceptions: z.array(exceptionSchema).min(3).max(12).describe("期限と代替策が明確な統制例外"),
}).strict().superRefine((data, ctx) => {
  const ids = data.exceptions.map(({ id }) => id);
  if (new Set(ids).size !== ids.length) {
    ctx.addIssue({ code: "custom", path: ["exceptions"], message: "例外IDは一意にすること" });
  }
  data.exceptions.forEach((exception, index) => {
    if (exception.status === "approved" && !exception.approvalRef) {
      ctx.addIssue({ code: "custom", path: ["exceptions", index, "approvalRef"], message: "承認済み例外には承認記録の参照が必要" });
    }
    if (exception.reviewDate > exception.expiry) {
      ctx.addIssue({ code: "custom", path: ["exceptions", index, "reviewDate"], message: "再審査日は例外終了日以前にすること" });
    }
  });
});

export type Props = z.infer<typeof schema>;

meta.yaml

1.9 KB

/designs/security-exception-register/source/meta.yaml

中身を見る
id: security-exception-register
name:
  ja: セキュリティ統制例外台帳
  en: Security Control Exception Register
purpose:
  ja: 一時的な統制逸脱に、範囲・代替策・リスク受容者・承認記録・終了期限を結びつける。
  en: Tie temporary control deviations to scope, compensating controls, risk approvers, approval records, and expiry.
status: published
still: ./still.png
category: governance
catalogOrder: 850
jobs:
  - prioritize-diagnose
  - assign-governance
useCases:
  - requirements
  - status
layoutKinds:
  - table
  - stack
tags:
  - security-exception
  - risk-acceptance
  - compensating-control
  - expiry-review
  - responsive
whenToUse:
  - ja: 標準セキュリティ統制を一時的に満たせず、明示的なリスク受容と期限つき代替策が必要なとき。
    en: Use when a security control cannot temporarily be met and the deviation needs explicit risk acceptance and expiring compensating measures.
avoidWhen:
  - ja: 侵害や障害の発生後に原因と再発防止を記録する場合は、インシデントレビューを使う。
    en: Use an incident review to record causes and prevention after an incident.
requiredInputs:
  - ja: 対象統制、適用範囲、理由、残留リスク、代替策、責任者、決裁者、再審査日、終了日を定義する。
    en: Define the control, scope, rationale, residual risk, compensating measure, owner, approver, review date, and expiry.
relatedLayouts:
  - risk-matrix-register
  - permission-matrix
dataSchema:
  title: string — 例外統制の要点
  subtitle: string? — 適用組織・ポリシー版・基準日
  program: string — 対象セキュリティプログラム
  exceptions: array (3–12) — 統制例外(理由、リスク、代替策、責任者、決裁者、承認参照、再審査日、終了日、状態)
sources: []

sample-data.json

1.9 KB

/designs/security-exception-register/source/sample-data.json

中身を見る
{
  "title": "旧式監視ノードのTLS例外を短期承認し、隔離と更新期限を設定する",
  "subtitle": "Security Standard 5.2 · 2026-09-20 review",
  "program": "Northstar Security Assurance",
  "exceptions": [
    { "id": "SEC-EX-041", "control": "TLS 1.2 minimum", "scope": "監視センサー ingest-east-2 と専用ゲートウェイ間", "justification": "ベンダー更新は次回保守窓まで適用できず、即時更新は計測停止を招く。", "risk": "medium", "compensatingControl": "送信元を専用VLANに限定し、受信側ACLと証明書期限アラートを有効化する。", "owner": "Observability Platform", "approver": "Security Duty Officer", "approvalRef": "GRC-APR-2841", "reviewDate": "2026-10-01", "expiry": "2026-10-30", "status": "approved" },
    { "id": "SEC-EX-044", "control": "MFA for service account", "scope": "夜間バッチ専用の移行アカウント svc-ledger-import", "justification": "対話ログインをせず、作業停止を避けるため段階的にワークロードIDへ切り替える。", "risk": "high", "compensatingControl": "鍵をVaultで短期ローテーションし、送信元IPと実行時刻を監視する。", "owner": "Finance Data Ops", "approver": "Identity Control Lead", "reviewDate": "2026-09-28", "expiry": "2026-10-15", "status": "proposed" },
    { "id": "SEC-EX-047", "control": "Encrypted backup retention", "scope": "地域規制データの隔離アーカイブ保管庫", "justification": "移管先の鍵管理検証が完了するまで旧保管形式を読み取り専用で保持する。", "risk": "low", "compensatingControl": "ネットワークを切断し、復号鍵を二者承認のHSMに保管する。", "owner": "Records Platform", "approver": "Data Protection Officer", "approvalRef": "GRC-APR-2858", "reviewDate": "2026-10-05", "expiry": "2026-11-01", "status": "approved" }
  ]
}