@kaademos/secure-sdlc 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.claude/agents/ai-security-engineer.md +209 -0
  2. package/.claude/agents/appsec-engineer.md +131 -0
  3. package/.claude/agents/cloud-platform-engineer.md +119 -0
  4. package/.claude/agents/dev-lead.md +138 -0
  5. package/.claude/agents/grc-analyst.md +143 -0
  6. package/.claude/agents/product-manager.md +100 -0
  7. package/.claude/agents/release-manager.md +126 -0
  8. package/.claude/agents/security-champion.md +148 -0
  9. package/.cursor/rules/secure-sdlc.mdc +98 -0
  10. package/.github/workflows/secure-sdlc-gate.yml +325 -0
  11. package/CHANGELOG.md +49 -0
  12. package/CLAUDE.md +195 -0
  13. package/LICENSE +21 -0
  14. package/README.md +394 -0
  15. package/cli/bin/secure-sdlc.js +95 -0
  16. package/cli/src/commands/gate.js +129 -0
  17. package/cli/src/commands/init.js +219 -0
  18. package/cli/src/commands/install-mcp.js +121 -0
  19. package/cli/src/commands/kickoff.js +261 -0
  20. package/cli/src/commands/paths.js +33 -0
  21. package/cli/src/commands/review.js +53 -0
  22. package/cli/src/commands/status.js +122 -0
  23. package/cli/src/utils/banner.js +43 -0
  24. package/cli/src/utils/package-root.js +23 -0
  25. package/cli/src/utils/phase-detect.js +107 -0
  26. package/cli/src/utils/stack-detect.js +138 -0
  27. package/docs/templates/compliance-attestation.md +159 -0
  28. package/docs/templates/infra-security-review.md +133 -0
  29. package/docs/templates/release-sign-off.md +119 -0
  30. package/docs/templates/risk-register.md +72 -0
  31. package/docs/templates/sast-findings.md +110 -0
  32. package/docs/templates/security-requirements.md +98 -0
  33. package/docs/templates/test-security-report.md +143 -0
  34. package/docs/templates/threat-model.md +129 -0
  35. package/hooks/install.sh +37 -0
  36. package/hooks/pre-commit +208 -0
  37. package/hooks/pre-push +127 -0
  38. package/mcp/README.md +116 -0
  39. package/mcp/package.json +23 -0
  40. package/mcp/src/server.js +638 -0
  41. package/package.json +67 -0
  42. package/stacks/django.md +216 -0
  43. package/stacks/express.md +229 -0
  44. package/stacks/fastapi.md +247 -0
  45. package/stacks/nextjs.md +198 -0
  46. package/stacks/nodejs.md +28 -0
  47. package/stacks/rails.md +247 -0
  48. package/warp-workflows/README.md +25 -0
  49. package/warp-workflows/feature-kickoff.yaml +49 -0
  50. package/warp-workflows/pr-security-review.yaml +47 -0
  51. package/warp-workflows/release-gate.yaml +44 -0
  52. package/warp-workflows/sdlc-status.yaml +48 -0
  53. package/warp-workflows/threat-model.yaml +56 -0
@@ -0,0 +1,122 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { join, resolve } from "path";
3
+ import chalk from "chalk";
4
+ import { printBanner } from "../utils/banner.js";
5
+ import { detectStack } from "../utils/stack-detect.js";
6
+
7
+ const PHASES = ["PLAN", "DESIGN", "BUILD", "TEST", "RELEASE"];
8
+
9
+ const PHASE_ARTEFACTS = {
10
+ PLAN: ["docs/security-requirements.md", "docs/risk-register.md"],
11
+ DESIGN: ["docs/threat-model.md", "docs/infra-security-review.md"],
12
+ BUILD: ["docs/sast-findings.md"],
13
+ TEST: ["docs/test-security-report.md"],
14
+ RELEASE: ["docs/release-security-sign-off.md"],
15
+ };
16
+
17
+ const PHASE_AGENTS = {
18
+ PLAN: ["product-manager", "grc-analyst"],
19
+ DESIGN: ["appsec-engineer", "cloud-platform-engineer"],
20
+ BUILD: ["dev-lead", "appsec-engineer"],
21
+ TEST: ["appsec-engineer", "dev-lead", "grc-analyst"],
22
+ RELEASE: ["release-manager", "grc-analyst", "cloud-platform-engineer"],
23
+ };
24
+
25
+ const PHASE_COLORS = {
26
+ PLAN: chalk.blue,
27
+ DESIGN: chalk.magenta,
28
+ BUILD: chalk.yellow,
29
+ TEST: chalk.cyan,
30
+ RELEASE: chalk.green,
31
+ };
32
+
33
+ function isTemplate(filePath) {
34
+ try {
35
+ const content = readFileSync(filePath, "utf-8");
36
+ return (
37
+ content.includes("[Feature Name]") ||
38
+ content.includes("[YYYY-MM-DD]") ||
39
+ content.includes("[Brief description") ||
40
+ content.includes("## Feature: [")
41
+ );
42
+ } catch {
43
+ return false;
44
+ }
45
+ }
46
+
47
+ export default async function status(options) {
48
+ const projectRoot = resolve(options.path || process.cwd());
49
+
50
+ printBanner();
51
+
52
+ const stack = detectStack(projectRoot);
53
+ console.log(chalk.bold(`Project: ${projectRoot}`));
54
+ if (stack.name !== "unknown") {
55
+ console.log(chalk.dim(`Stack: ${stack.display}\n`));
56
+ }
57
+
58
+ let currentPhase = null;
59
+ let phaseComplete = {};
60
+
61
+ console.log(chalk.bold("SDLC Phase Status\n"));
62
+
63
+ for (const phase of PHASES) {
64
+ const artefacts = PHASE_ARTEFACTS[phase];
65
+ const colorFn = PHASE_COLORS[phase];
66
+ let allPresent = true;
67
+ let results = [];
68
+
69
+ for (const rel of artefacts) {
70
+ const abs = join(projectRoot, rel);
71
+ const exists = existsSync(abs);
72
+ const template = exists && isTemplate(abs);
73
+
74
+ results.push({ rel, exists, template });
75
+ if (!exists || template) allPresent = false;
76
+ }
77
+
78
+ phaseComplete[phase] = allPresent;
79
+ if (!currentPhase && !allPresent) currentPhase = phase;
80
+
81
+ const phaseLabel = colorFn(` ${phase} `);
82
+ const status = allPresent
83
+ ? chalk.green("✓ COMPLETE")
84
+ : results.some((r) => r.exists && !r.template)
85
+ ? chalk.yellow("◑ IN PROGRESS")
86
+ : chalk.dim("○ NOT STARTED");
87
+
88
+ console.log(` ${phaseLabel} ${status}`);
89
+
90
+ for (const r of results) {
91
+ const icon = r.exists && !r.template ? chalk.green("✓") : r.exists && r.template ? chalk.yellow("~") : chalk.dim("○");
92
+ const label = r.exists && r.template ? chalk.dim(`${r.rel} (template — not filled)`) : chalk.dim(r.rel);
93
+ console.log(` ${icon} ${label}`);
94
+ }
95
+ console.log();
96
+ }
97
+
98
+ // Current phase recommendation
99
+ if (currentPhase) {
100
+ console.log(chalk.bold(`Current phase: ${PHASE_COLORS[currentPhase](currentPhase)}\n`));
101
+ console.log(chalk.bold("Agents to invoke:"));
102
+ PHASE_AGENTS[currentPhase].forEach((a) => {
103
+ console.log(chalk.dim(` • ${a}`));
104
+ });
105
+ console.log();
106
+ console.log(chalk.bold("Quick start:"));
107
+ console.log(chalk.dim(` secure-sdlc kickoff # Interactive wizard`));
108
+ console.log(chalk.dim(` secure-sdlc review # Security review current changes`));
109
+
110
+ if (currentPhase === "RELEASE") {
111
+ console.log(chalk.dim(` secure-sdlc gate v1.0.0 # Run pre-release security gate`));
112
+ }
113
+ } else {
114
+ console.log(chalk.bold.green("All phases complete. Project is release-gated.\n"));
115
+ }
116
+
117
+ // Check for config
118
+ const configPath = join(projectRoot, "secure-sdlc.yaml");
119
+ if (!existsSync(configPath)) {
120
+ console.log(chalk.yellow("\n⚠ No secure-sdlc.yaml found. Run: secure-sdlc init\n"));
121
+ }
122
+ }
@@ -0,0 +1,43 @@
1
+ import chalk from "chalk";
2
+
3
+ export function printBanner() {
4
+ console.log(
5
+ chalk.bold.cyan(`
6
+ ███████╗███████╗ ██████╗██╗ ██╗██████╗ ███████╗
7
+ ██╔════╝██╔════╝██╔════╝██║ ██║██╔══██╗██╔════╝
8
+ ███████╗█████╗ ██║ ██║ ██║██████╔╝█████╗
9
+ ╚════██║██╔══╝ ██║ ██║ ██║██╔══██╗██╔══╝
10
+ ███████║███████╗╚██████╗╚██████╔╝██║ ██║███████╗
11
+ ╚══════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
12
+ `)
13
+ );
14
+ console.log(chalk.dim(" SDLC Agents — Security at every step of the build\n"));
15
+ }
16
+
17
+ export function printPhase(phase, description) {
18
+ const phases = {
19
+ PLAN: chalk.bgBlue.white,
20
+ DESIGN: chalk.bgMagenta.white,
21
+ BUILD: chalk.bgYellow.black,
22
+ TEST: chalk.bgCyan.black,
23
+ RELEASE: chalk.bgGreen.black,
24
+ };
25
+ const colorFn = phases[phase] || chalk.bgGray.white;
26
+ console.log(`\n${colorFn(` ${phase} `)} ${chalk.bold(description)}\n`);
27
+ }
28
+
29
+ export function printSuccess(msg) {
30
+ console.log(chalk.green(`✓ ${msg}`));
31
+ }
32
+
33
+ export function printWarn(msg) {
34
+ console.log(chalk.yellow(`⚠ ${msg}`));
35
+ }
36
+
37
+ export function printError(msg) {
38
+ console.log(chalk.red(`✗ ${msg}`));
39
+ }
40
+
41
+ export function printInfo(msg) {
42
+ console.log(chalk.dim(` ${msg}`));
43
+ }
@@ -0,0 +1,23 @@
1
+ import { fileURLToPath } from "url";
2
+ import { dirname, join } from "path";
3
+ import { existsSync } from "fs";
4
+
5
+ /**
6
+ * Directory where this npm package is installed (contains cli/, mcp/, docs/templates/, …).
7
+ * Works for: git clone, npm install -g @kaademos/secure-sdlc, and npx.
8
+ */
9
+ export function getPackageRoot() {
10
+ const here = dirname(fileURLToPath(import.meta.url));
11
+ // cli/src/utils/package-root.js → ../../../ = package root
12
+ const root = join(here, "..", "..", "..");
13
+ const marker = join(root, "cli", "bin", "secure-sdlc.js");
14
+ if (existsSync(marker)) {
15
+ return root;
16
+ }
17
+ // Fallback: walk up from cwd (e.g. unusual symlinks)
18
+ return root;
19
+ }
20
+
21
+ export function getMcpServerPath() {
22
+ return join(getPackageRoot(), "mcp", "src", "server.js");
23
+ }
@@ -0,0 +1,107 @@
1
+ import { existsSync, statSync } from "fs";
2
+ import { join } from "path";
3
+ import { execSync } from "child_process";
4
+
5
+ const ARTEFACTS = {
6
+ PLAN: ["docs/security-requirements.md", "docs/risk-register.md"],
7
+ DESIGN: ["docs/threat-model.md", "docs/infra-security-review.md"],
8
+ BUILD: ["docs/sast-findings.md"],
9
+ TEST: ["docs/test-security-report.md"],
10
+ RELEASE: ["docs/release-security-sign-off.md", "docs/audit-evidence"],
11
+ };
12
+
13
+ /**
14
+ * Check which SDLC artefacts are present in the project.
15
+ * Returns a detailed status object.
16
+ */
17
+ export function getSDLCStatus(projectRoot) {
18
+ const status = {};
19
+
20
+ for (const [phase, files] of Object.entries(ARTEFACTS)) {
21
+ const results = files.map((rel) => {
22
+ const abs = join(projectRoot, rel);
23
+ const exists = existsSync(abs);
24
+ let isTemplate = false;
25
+
26
+ if (exists && !rel.endsWith("/") && !rel.includes("audit-evidence")) {
27
+ try {
28
+ const { readFileSync } = require("fs");
29
+ const content = readFileSync(abs, "utf-8");
30
+ // Detect unfilled templates — they still have [PLACEHOLDER] markers
31
+ isTemplate = content.includes("[YYYY-MM-DD]") || content.includes("[Feature Name]");
32
+ } catch {
33
+ // ignore
34
+ }
35
+ }
36
+
37
+ return { path: rel, exists, isTemplate };
38
+ });
39
+
40
+ const allPresent = results.every((r) => r.exists);
41
+ const somePresent = results.some((r) => r.exists);
42
+ const allFilled = results.every((r) => r.exists && !r.isTemplate);
43
+
44
+ status[phase] = {
45
+ files: results,
46
+ allPresent,
47
+ somePresent,
48
+ allFilled,
49
+ complete: allPresent && allFilled,
50
+ };
51
+ }
52
+
53
+ return status;
54
+ }
55
+
56
+ /**
57
+ * Detect current SDLC phase based on git state and artefact presence.
58
+ */
59
+ export function detectCurrentPhase(projectRoot) {
60
+ const status = getSDLCStatus(projectRoot);
61
+
62
+ // Walk phases in order — the current phase is the first incomplete one
63
+ const order = ["PLAN", "DESIGN", "BUILD", "TEST", "RELEASE"];
64
+
65
+ for (const phase of order) {
66
+ if (!status[phase].complete) {
67
+ return { current: phase, status };
68
+ }
69
+ }
70
+
71
+ return { current: "COMPLETE", status };
72
+ }
73
+
74
+ /**
75
+ * Get the next recommended action based on phase state.
76
+ */
77
+ export function getNextAction(phase, status) {
78
+ const actions = {
79
+ PLAN: {
80
+ command: 'claude --agent product-manager "Define security requirements for [your feature]"',
81
+ mcp: "sdlc_plan_feature",
82
+ description: "Generate ASVS-mapped security requirements and risk register",
83
+ },
84
+ DESIGN: {
85
+ command: 'claude --agent appsec-engineer "Threat model [your architecture] using STRIDE"',
86
+ mcp: "sdlc_threat_model",
87
+ description: "Run STRIDE threat model and infrastructure security review",
88
+ },
89
+ BUILD: {
90
+ command: 'claude --agent dev-lead "Review PR #[N] for security issues"',
91
+ mcp: "sdlc_review_pr",
92
+ description: "Security review pull requests and triage SAST findings",
93
+ },
94
+ TEST: {
95
+ command: 'claude --agent appsec-engineer "Interpret DAST findings: [findings]"',
96
+ mcp: "sdlc_triage_sast",
97
+ description: "Interpret DAST/pentest results and collect audit evidence",
98
+ },
99
+ RELEASE: {
100
+ command: 'claude --agent release-manager "Run pre-release security checklist for v[X.Y.Z]"',
101
+ mcp: "sdlc_release_gate",
102
+ description: "Run go/no-go security gate and produce compliance attestation",
103
+ },
104
+ };
105
+
106
+ return actions[phase] || null;
107
+ }
@@ -0,0 +1,138 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { join } from "path";
3
+
4
+ /**
5
+ * Detect the technology stack from a project directory.
6
+ * Returns a stack profile name used to load appropriate security guidance.
7
+ */
8
+ export function detectStack(projectRoot) {
9
+ const has = (file) => existsSync(join(projectRoot, file));
10
+
11
+ // Node / JavaScript / TypeScript
12
+ if (has("package.json")) {
13
+ const pkg = JSON.parse(readFileSync(join(projectRoot, "package.json"), "utf-8"));
14
+ const deps = {
15
+ ...pkg.dependencies,
16
+ ...pkg.devDependencies,
17
+ };
18
+
19
+ if (deps["next"]) return { name: "nextjs", display: "Next.js", language: "TypeScript/JavaScript" };
20
+ if (deps["nuxt"]) return { name: "nuxt", display: "Nuxt.js", language: "TypeScript/JavaScript" };
21
+ if (deps["@remix-run/node"]) return { name: "remix", display: "Remix", language: "TypeScript/JavaScript" };
22
+ if (deps["astro"]) return { name: "astro", display: "Astro", language: "TypeScript/JavaScript" };
23
+ if (deps["express"]) return { name: "express", display: "Express.js", language: "JavaScript" };
24
+ if (deps["fastify"]) return { name: "fastify", display: "Fastify", language: "TypeScript/JavaScript" };
25
+ if (deps["@nestjs/core"]) return { name: "nestjs", display: "NestJS", language: "TypeScript" };
26
+ if (deps["hono"]) return { name: "hono", display: "Hono", language: "TypeScript" };
27
+
28
+ return { name: "nodejs", display: "Node.js", language: "JavaScript/TypeScript" };
29
+ }
30
+
31
+ // Python
32
+ if (has("pyproject.toml") || has("requirements.txt") || has("setup.py") || has("Pipfile")) {
33
+ const pyfiles = ["pyproject.toml", "requirements.txt", "Pipfile"].map(f =>
34
+ has(f) ? readFileSync(join(projectRoot, f), "utf-8") : ""
35
+ ).join("\n");
36
+
37
+ if (pyfiles.includes("fastapi")) return { name: "fastapi", display: "FastAPI", language: "Python" };
38
+ if (pyfiles.includes("django")) return { name: "django", display: "Django", language: "Python" };
39
+ if (pyfiles.includes("flask")) return { name: "flask", display: "Flask", language: "Python" };
40
+ if (pyfiles.includes("litestar")) return { name: "litestar",display: "Litestar", language: "Python" };
41
+
42
+ return { name: "python", display: "Python", language: "Python" };
43
+ }
44
+
45
+ // Ruby
46
+ if (has("Gemfile")) {
47
+ const gemfile = readFileSync(join(projectRoot, "Gemfile"), "utf-8");
48
+ if (gemfile.includes("rails")) return { name: "rails", display: "Ruby on Rails", language: "Ruby" };
49
+ if (gemfile.includes("sinatra")) return { name: "sinatra", display: "Sinatra", language: "Ruby" };
50
+ return { name: "ruby", display: "Ruby", language: "Ruby" };
51
+ }
52
+
53
+ // Go
54
+ if (has("go.mod")) {
55
+ const gomod = readFileSync(join(projectRoot, "go.mod"), "utf-8");
56
+ if (gomod.includes("gin-gonic/gin")) return { name: "gin", display: "Gin (Go)", language: "Go" };
57
+ if (gomod.includes("labstack/echo")) return { name: "echo", display: "Echo (Go)", language: "Go" };
58
+ if (gomod.includes("gofiber/fiber")) return { name: "fiber", display: "Fiber (Go)",language: "Go" };
59
+ return { name: "golang", display: "Go", language: "Go" };
60
+ }
61
+
62
+ // Java / Kotlin / JVM
63
+ if (has("pom.xml")) {
64
+ const pom = readFileSync(join(projectRoot, "pom.xml"), "utf-8");
65
+ if (pom.includes("spring-boot")) return { name: "spring-boot", display: "Spring Boot", language: "Java/Kotlin" };
66
+ return { name: "java", display: "Java", language: "Java" };
67
+ }
68
+ if (has("build.gradle") || has("build.gradle.kts")) {
69
+ return { name: "java", display: "Gradle/JVM", language: "Java/Kotlin" };
70
+ }
71
+
72
+ // Rust
73
+ if (has("Cargo.toml")) return { name: "rust", display: "Rust", language: "Rust" };
74
+
75
+ // Infrastructure-only
76
+ if (has("main.tf") || has("terraform.tf")) return { name: "terraform", display: "Terraform", language: "HCL" };
77
+ if (has("Chart.yaml")) return { name: "helm", display: "Helm Chart", language: "YAML" };
78
+
79
+ return { name: "unknown", display: "Unknown", language: "Unknown" };
80
+ }
81
+
82
+ /**
83
+ * Returns the top security considerations for a given stack.
84
+ */
85
+ export function getStackSecurityNotes(stackName) {
86
+ const notes = {
87
+ nextjs: [
88
+ "Review Server Actions for CSRF and authorisation — they're POST endpoints by default",
89
+ "Ensure API routes in app/api/ validate auth on every request — no route-level middleware by default",
90
+ "Use next/headers to read cookies server-side — avoid exposing auth tokens to client components",
91
+ "Review CORS config in next.config.js — wildcard origins are dangerous on API routes",
92
+ "Server Components can access secrets but ensure no secrets leak into Client Component props",
93
+ "Validate Zod schemas server-side on all Server Action inputs, even if validated client-side",
94
+ ],
95
+ express: [
96
+ "Use helmet middleware for security headers (CSP, HSTS, X-Frame-Options)",
97
+ "Never trust req.body without validation — use express-validator or zod",
98
+ "Avoid req.body[key] concatenation in queries — always use parameterised queries",
99
+ "Set trust proxy correctly if behind a load balancer (affects rate limiting by IP)",
100
+ "Disable X-Powered-By: Express header (helmet does this)",
101
+ ],
102
+ django: [
103
+ "Use Django's built-in CSRF middleware — never disable it for API endpoints without CORS-based protection",
104
+ "Use Django ORM querysets — avoid .raw() without parameterisation",
105
+ "DEBUG must be False in production; ALLOWED_HOSTS must be explicitly set",
106
+ "Use django-environ or similar for secrets — never commit SECRET_KEY to source",
107
+ "SECURE_SSL_REDIRECT, SECURE_HSTS_SECONDS, SESSION_COOKIE_SECURE must be True in production",
108
+ ],
109
+ fastapi: [
110
+ "Use Depends() for auth on every endpoint — there is no global auth middleware by default",
111
+ "Validate all path and query parameters with Pydantic — FastAPI does this if types are annotated",
112
+ "Never use 'response_model=None' to bypass output filtering on sensitive endpoints",
113
+ "Use OAuth2PasswordBearer with proper scope checking — not just token presence",
114
+ "CORS: set allow_origins explicitly, never use '*' for authenticated APIs",
115
+ ],
116
+ rails: [
117
+ "Rails has CSRF protection by default — never use protect_from_forgery :null_session on non-API controllers",
118
+ "Use strong parameters everywhere — never pass params directly to model methods",
119
+ "Rails 7+ uses encrypted credentials — use rails credentials:edit, never ENV vars for secrets in production",
120
+ "Audit before_action filters for auth — ensure every controller action is covered",
121
+ "Brakeman is the standard Rails SAST tool — run on every PR",
122
+ ],
123
+ terraform: [
124
+ "Pin provider versions with ~> constraints, not latest",
125
+ "Use terraform-aws-modules/terraform-google-modules — don't write IAM from scratch",
126
+ "Never use wildcard permissions (actions = ['*'])",
127
+ "Use tfsec or Checkov in CI for IaC scanning",
128
+ "Store state in encrypted remote backends — never commit .tfstate to git",
129
+ ],
130
+ };
131
+
132
+ return notes[stackName] || [
133
+ "Apply OWASP ASVS L2 as the baseline security requirements",
134
+ "Validate all inputs server-side — never trust client-supplied data",
135
+ "Use parameterised queries — never string-concatenate user input into queries",
136
+ "Store secrets in a secrets manager, not in code or environment files committed to git",
137
+ ];
138
+ }
@@ -0,0 +1,159 @@
1
+ # Compliance Attestation — v[X.Y.Z]
2
+
3
+ **Release version:** v[X.Y.Z]
4
+ **Date:** [YYYY-MM-DD]
5
+ **Author:** GRC Analyst Agent + [Human GRC lead]
6
+ **Frameworks in scope:** [SOC 2 / ISO 27001 / NIST CSF / PCI DSS / GDPR — delete inapplicable]
7
+ **Status:** Draft / Review / Approved
8
+
9
+ ---
10
+
11
+ ## Scope
12
+
13
+ **Systems and services covered by this attestation:**
14
+ [List the systems, APIs, data stores, and infrastructure components in scope for this release]
15
+
16
+ **Data in scope:**
17
+ [Describe what categories of data are processed — PII, payment data, health data, etc.]
18
+
19
+ **Frameworks assessed:**
20
+ [List frameworks and the specific version or edition — e.g. SOC 2 Trust Services Criteria 2017,
21
+ ISO/IEC 27001:2022 Annex A, NIST CSF 2.0]
22
+
23
+ ---
24
+
25
+ ## Control Status by Framework
26
+
27
+ ### SOC 2 — Trust Services Criteria
28
+
29
+ | Criteria | Control Description | Status | Evidence Reference | Notes |
30
+ |----------|--------------------|---------|--------------------|-------|
31
+ | CC6.1 | Logical access controls restrict access to information assets | ✅ Met / ⚠️ Gap / 🚫 Fail | `docs/audit-evidence/` | |
32
+ | CC6.2 | New internal and external users provisioned with appropriate access | | | |
33
+ | CC6.3 | Access removed when no longer required | | | |
34
+ | CC6.6 | Logical access restrictions meet security requirements | | | |
35
+ | CC6.7 | Transmission of data restricted to authorised parties | | | |
36
+ | CC7.1 | Vulnerabilities in system components detected | | | |
37
+ | CC7.2 | Security incidents identified and managed | | | |
38
+ | CC8.1 | Changes authorised, designed, and implemented to meet objectives | | | |
39
+ | CC9.1 | Risk assessment process identifies and manages risks | | | |
40
+
41
+ *Add or remove rows based on applicable Trust Services Criteria for your engagement.*
42
+
43
+ ---
44
+
45
+ ### ISO/IEC 27001:2022 — Annex A
46
+
47
+ | Control | Name | Status | Evidence Reference | Notes |
48
+ |---------|------|--------|--------------------|-------|
49
+ | A.5.15 | Access control | ✅ Met / ⚠️ Gap / 🚫 Fail | | |
50
+ | A.5.17 | Authentication information | | | |
51
+ | A.8.5 | Secure authentication | | | |
52
+ | A.8.7 | Protection against malware | | | |
53
+ | A.8.9 | Configuration management | | | |
54
+ | A.8.15 | Logging | | | |
55
+ | A.8.20 | Networks security | | | |
56
+ | A.8.24 | Use of cryptography | | | |
57
+ | A.8.25 | Secure development lifecycle | | | |
58
+ | A.8.26 | Application security requirements | | | |
59
+ | A.8.27 | Secure system architecture and engineering principles | | | |
60
+ | A.8.28 | Secure coding | | | |
61
+ | A.8.29 | Security testing in development and acceptance | | | |
62
+
63
+ *Extend with additional Annex A controls relevant to the systems in scope.*
64
+
65
+ ---
66
+
67
+ ### NIST CSF 2.0
68
+
69
+ | Function | Category | Status | Evidence Reference | Notes |
70
+ |----------|----------|--------|--------------------|-------|
71
+ | Govern | GV.OC — Organisational context | ✅ Met / ⚠️ Gap / 🚫 Fail | | |
72
+ | Identify | ID.AM — Asset management | | | |
73
+ | Protect | PR.AC — Identity management and access control | | | |
74
+ | Protect | PR.DS — Data security | | | |
75
+ | Protect | PR.IP — Information protection processes | | | |
76
+ | Detect | DE.CM — Security continuous monitoring | | | |
77
+ | Respond | RS.RP — Response planning | | | |
78
+ | Recover | RC.RP — Recovery planning | | | |
79
+
80
+ ---
81
+
82
+ ### PCI DSS v4.0 *(complete only if payment card data is in scope)*
83
+
84
+ | Requirement | Description | Status | Evidence Reference | Notes |
85
+ |-------------|-------------|--------|--------------------|-------|
86
+ | 2.2 | System components configured securely | ✅ Met / ⚠️ Gap / 🚫 Fail | | |
87
+ | 3.5 | Primary account number secured | | | |
88
+ | 4.2 | PAN protected during transmission | | | |
89
+ | 6.2 | Bespoke software developed securely | | | |
90
+ | 6.3 | Security vulnerabilities identified and addressed | | | |
91
+ | 7.2 | Access controlled based on need to know | | | |
92
+ | 8.2 | User identification and authentication | | | |
93
+ | 10.2 | Audit logs implemented | | | |
94
+ | 11.3 | External and internal penetration testing | | | |
95
+
96
+ ---
97
+
98
+ ### GDPR / UK GDPR *(complete only if personal data of EU/UK residents is processed)*
99
+
100
+ | Article / Principle | Description | Status | Evidence Reference | Notes |
101
+ |--------------------|-------------|--------|--------------------|-------|
102
+ | Art. 5(1)(a) | Lawfulness, fairness, transparency | ✅ Met / ⚠️ Gap / 🚫 Fail | | |
103
+ | Art. 5(1)(b) | Purpose limitation | | | |
104
+ | Art. 5(1)(c) | Data minimisation | | | |
105
+ | Art. 5(1)(e) | Storage limitation | | | |
106
+ | Art. 5(1)(f) | Integrity and confidentiality | | | |
107
+ | Art. 25 | Data protection by design and by default | | | |
108
+ | Art. 32 | Security of processing | | | |
109
+ | Art. 33/34 | Breach notification capability | | | |
110
+
111
+ ---
112
+
113
+ ## Gaps
114
+
115
+ Controls that are not fully met at the time of this attestation:
116
+
117
+ | Framework | Control | Gap Description | Risk Rating | Remediation Plan | Owner | Target Date |
118
+ |-----------|---------|-----------------|-------------|-----------------|-------|-------------|
119
+ | | | | | | | |
120
+
121
+ ---
122
+
123
+ ## Accepted Risks
124
+
125
+ Controls with residual gaps that have been formally accepted for this release:
126
+
127
+ | Framework | Control | Gap | Business Justification | Risk Register Ref | Approver | Acceptance Date | Review Date |
128
+ |-----------|---------|-----|------------------------|-------------------|----------|----------------|-------------|
129
+ | | | | | | | | |
130
+
131
+ ---
132
+
133
+ ## Audit Evidence Index
134
+
135
+ Evidence collected in support of this attestation, located in `docs/audit-evidence/`:
136
+
137
+ | Evidence ID | Description | Framework / Control | Collection Date | Location |
138
+ |-------------|-------------|--------------------|-----------------|---------|
139
+ | AE-001 | [e.g. SAST scan results showing no CRITICAL findings] | [SOC 2 CC7.1] | [YYYY-MM-DD] | `docs/audit-evidence/sast-results-vX.Y.Z.pdf` |
140
+ | AE-002 | | | | |
141
+
142
+ ---
143
+
144
+ ## Attestation Statement
145
+
146
+ All in-scope controls have been reviewed against the evidence collected for release
147
+ v[X.Y.Z]. The gaps and accepted risks listed above have been formally acknowledged by the
148
+ named approvers. This attestation is valid for this release only and does not constitute
149
+ a certification under any framework.
150
+
151
+ ---
152
+
153
+ ## Sign-off
154
+
155
+ | Role | Name | Date | Status |
156
+ |------|------|------|--------|
157
+ | GRC Analyst Agent | (automated) | [YYYY-MM-DD] | Approved |
158
+ | GRC Lead / CISO | | | Approved / Pending |
159
+ | Release Manager | | | Approved / Pending |