bmad-plus 0.20.0 → 0.22.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 (50) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +14 -14
  3. package/SECURITY.md +62 -0
  4. package/osint-agent-package/skills/bmad-osint-investigate/osint/scripts/_http.py +68 -24
  5. package/package.json +1 -1
  6. package/readme-international/README.de.md +14 -14
  7. package/readme-international/README.es.md +14 -14
  8. package/readme-international/README.fr.md +14 -14
  9. package/src/bmad-plus/agents/agent-quality/SKILL.md +1 -1
  10. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review.md +26 -5
  11. package/src/bmad-plus/packs/pack-seo/SKILL.md +3 -1
  12. package/src/bmad-plus/packs/pack-seo/ref/cwv-thresholds.md +2 -2
  13. package/src/bmad-plus/packs/pack-seo/requirements.txt +1 -1
  14. package/src/bmad-plus/packs/pack-seo/scripts/seo_apis.py +72 -30
  15. package/src/bmad-plus/packs/pack-seo/scripts/seo_crawl.py +36 -24
  16. package/src/bmad-plus/packs/pack-seo/scripts/seo_fetch.py +179 -59
  17. package/src/bmad-plus/packs/pack-seo/scripts/seo_report.py +5 -6
  18. package/src/bmad-plus/packs/pack-seo/scripts/seo_screenshot.py +176 -14
  19. package/src/bmad-plus/packs/pack-shield/README.md +12 -0
  20. package/src/bmad-plus/packs/pack-shield/SKILL.md +7 -1
  21. package/src/bmad-plus/packs/pack-shield/review-rules/access-control.md +10 -0
  22. package/src/bmad-plus/packs/pack-shield/review-rules/ai-integrations.md +10 -0
  23. package/src/bmad-plus/packs/pack-shield/review-rules/change-and-supply-chain.md +10 -0
  24. package/src/bmad-plus/packs/pack-shield/review-rules/cryptography.md +10 -0
  25. package/src/bmad-plus/packs/pack-shield/review-rules/index.yaml +134 -0
  26. package/src/bmad-plus/packs/pack-shield/review-rules/logging.md +10 -0
  27. package/src/bmad-plus/packs/pack-shield/review-rules/personal-data.md +10 -0
  28. package/src/bmad-plus/packs/pack-shield/shared/ai-processing-register-template.yaml +53 -0
  29. package/src/bmad-plus/packs/pack-shield/shared/ai-processing-register.md +32 -0
  30. package/src/bmad-plus/packs/pack-shield/shared/assurance-case-template.yaml +87 -0
  31. package/src/bmad-plus/packs/pack-shield/shared/assurance-case.md +50 -0
  32. package/src/bmad-plus/packs/pack-shield/shield-orchestrator.md +24 -1
  33. package/src/bmad-plus/skills/bmad-plus-uat/SKILL.md +1 -0
  34. package/src/bmad-plus/skills/bmad-plus-uat/template/page.html +5 -4
  35. package/tools/build/generate-adapters.js +7 -0
  36. package/tools/build/generate.js +14 -0
  37. package/tools/cli/bmad-plus-cli.js +2 -0
  38. package/tools/cli/commands/ai-register.js +63 -0
  39. package/tools/cli/commands/assurance.js +162 -0
  40. package/tools/cli/commands/review.js +141 -7
  41. package/tools/cli/lib/ai-register.js +393 -0
  42. package/tools/cli/lib/assurance.js +822 -0
  43. package/tools/cli/lib/control-refs.js +132 -0
  44. package/tools/cli/lib/installation-health.js +17 -0
  45. package/tools/cli/lib/packs.js +60 -2
  46. package/tools/cli/lib/page-origins.js +582 -0
  47. package/tools/cli/lib/review-rules.js +124 -26
  48. package/tools/cli/lib/review.js +493 -10
  49. package/tools/cli/lib/uat.js +22 -5
  50. package/tools/cli/review-rules/index.yaml +9 -0
@@ -0,0 +1,10 @@
1
+ Read the change as the person who should not get in.
2
+
3
+ - **Enforcement point.** Every new route, handler, job or command checks who is calling and what they may do, on the server, before it reads or writes. A check only in the UI, in a client-side guard or in the caller is not enforcement.
4
+ - **Object ownership.** A lookup by an id taken from the request is scoped to the caller (tenant, owner, organisation). Listing, export, search and bulk endpoints apply the same filter as the single-object read.
5
+ - **Least privilege.** A new role, scope, permission or service account grants only what the feature needs; a wildcard, an admin fallback or a default of "allow" when the policy is missing is a defect.
6
+ - **Authentication.** Passwords hashed with a slow, salted algorithm; comparison of secrets in constant time; MFA not bypassable through a secondary path (API token, password reset, legacy login, support impersonation).
7
+ - **Sessions and tokens.** Expiry and revocation exist and are checked; logout and password change invalidate what they should; tokens are bound to their audience and never accepted from a query string where they would be logged.
8
+ - **Access changes.** Granting, changing or removing a right is recorded with who did it; removal takes effect on the next request, not at the next login.
9
+
10
+ Name the control a finding breaks (for example `ISO27001:A.8.5` for authentication) in the finding's description.
@@ -0,0 +1,10 @@
1
+ Every model, agent or MCP server the change reaches is a recipient of data and a source of untrusted input.
2
+
3
+ - **Register.** A new provider, model endpoint, agent tool or MCP server appears in the AI processing register (`_bmad/ai-processing-register.yaml`) with its purpose, the data it sees, legal basis, retention and transfers; `bmad-plus ai-register check` reports the ones missing.
4
+ - **Data sent.** Prompts, context windows, retrieved documents and tool results carry only what the task needs; personal data, secrets and customer content are filtered or pseudonymised before they leave. A new field added to a prompt template is a new disclosure.
5
+ - **Transfers.** A provider outside the EEA, or a region setting that changed, needs a transfer mechanism; a processor needs an agreement covering training use and retention of prompts.
6
+ - **Untrusted output.** Model output and retrieved text reach no query, shell command, file path, HTML or permission decision without the same validation as user input; instructions found in data are data (prompt injection).
7
+ - **Agent tooling.** Tools, MCP servers and agent adapters get the narrowest permissions and paths; a new tool that can write, send or spend has an explicit confirmation or an allow-list.
8
+ - **Transparency.** A person who interacts with the system, or receives generated content presented as fact, is told it comes from an AI where the law requires it.
9
+
10
+ Name the control a finding breaks (for example `GDPR:Art.28` for a processor without an agreement) in the finding's description.
@@ -0,0 +1,10 @@
1
+ Look at what reaches production and who could change it.
2
+
3
+ - **Change path.** A pipeline change keeps review and approval before deployment: no new branch that deploys unreviewed, no `continue-on-error` or skipped job on a required check, no manual override without a record.
4
+ - **Separation.** The identity that writes code cannot approve and deploy it alone; a workflow that widens `permissions`, adds `pull_request_target` with a checkout of untrusted code, or exposes deployment secrets to forks breaks that.
5
+ - **Dependencies.** A new dependency comes from the expected publisher, is pinned by lockfile or hash, and is needed; actions and images are pinned to a digest or a full commit, not a moving tag. Install scripts of new packages are read.
6
+ - **Build provenance.** Artifacts are built by the pipeline from the reviewed commit; a step that downloads and runs a script from a URL, or publishes from a developer machine, removes the link between review and release.
7
+ - **Configuration baseline.** Hardening settings in images and manifests (non-root user, read-only filesystem, dropped capabilities, resource limits) are not weakened; a changed base image is a new supplier.
8
+ - **Vulnerabilities.** A dependency with a known exploitable vulnerability, or a scanner disabled for convenience, is reported with its advisory id.
9
+
10
+ Name the control a finding breaks (for example `ISO27001:A.8.32` for change management) in the finding's description.
@@ -0,0 +1,10 @@
1
+ Check that the protection claimed is the protection delivered.
2
+
3
+ - **Primitives.** Vetted library calls only; no home-made encryption, padding or random numbers. Reject MD5, SHA-1 or an unsalted fast hash for anything security-relevant, ECB mode, static or reused IVs and nonces, and `Math.random`-class generators for tokens or keys.
4
+ - **Keys.** Keys and secrets come from a secret store or the environment, never from source, fixtures or container images. Each key has one purpose, a rotation path, and an owner; a key used both to sign and to encrypt is a defect.
5
+ - **In transit.** TLS verification stays on (no `verify=False`, `rejectUnauthorized: false`, `InsecureSkipVerify`); internal calls that carry personal data or credentials are encrypted too.
6
+ - **At rest.** Data the change stores that needs confidentiality is encrypted at the field or volume level the design promised; exports and backups receive the same protection as the primary store.
7
+ - **Tokens and signatures.** Signed tokens pin the algorithm (no `none`, no algorithm taken from the token header), verify issuer, audience and expiry, and compare in constant time.
8
+ - **Failure.** A decryption or verification failure is an error, never a fallback to the plaintext or unsigned path.
9
+
10
+ Name the control a finding breaks (for example `ISO27001:A.8.24` for the use of cryptography) in the finding's description.
@@ -0,0 +1,134 @@
1
+ # Shield compliance review rules, applied by path once the Shield pack is installed.
2
+ # They add to the built-in rules (a pack never replaces or disables one); a project replaces
3
+ # or disables them by id in _bmad/review-rules.yaml. Every rule names the controls it
4
+ # examines as FRAMEWORK:ID (bmad-plus review rules lists them), so a checklist can say which
5
+ # controls a change touches. All rules share the `compliance` group: one parallel reviewer
6
+ # can take them together.
7
+ schema: bmad-plus/review-rules/1
8
+ rules:
9
+ - id: shield-access-control
10
+ title: Identity, authentication and authorisation
11
+ group: compliance
12
+ globs:
13
+ - '**/{auth,authn,authz,login,session,sessions,permissions,rbac,acl,oauth,oidc,sso,iam,guards,policies}/**'
14
+ - '**/*{auth,Auth,session,Session,permission,Permission,policy,Policy}*.*'
15
+ controls:
16
+ [
17
+ ISO27001:A.5.15,
18
+ ISO27001:A.5.18,
19
+ ISO27001:A.8.2,
20
+ ISO27001:A.8.5,
21
+ SOC2:CC6.1,
22
+ SOC2:CC6.2,
23
+ SOC2:CC6.3,
24
+ NIST-800-53:AC-3,
25
+ NIST-800-53:AC-6,
26
+ NIST-800-53:IA-2,
27
+ NIS2:Art.21(2)(i),
28
+ NIS2:Art.21(2)(j),
29
+ ]
30
+ doc: access-control.md
31
+ - id: shield-personal-data
32
+ title: Personal data in models, schemas and migrations
33
+ group: compliance
34
+ globs:
35
+ - '**/{models,entities,schemas,schema,dto,dtos,migrations,migrate}/**'
36
+ - '**/*{user,User,customer,Customer,profile,Profile,account,Account,contact,Contact,consent,Consent,privacy,Privacy}*.*'
37
+ controls:
38
+ [
39
+ GDPR:Art.5(1)(c),
40
+ GDPR:Art.5(1)(e),
41
+ GDPR:Art.25,
42
+ GDPR:Art.30,
43
+ GDPR:Art.32,
44
+ ISO27001:A.5.34,
45
+ ISO27001:A.8.10,
46
+ ISO27001:A.8.11,
47
+ SOC2:P4.2,
48
+ SOC2:P4.3,
49
+ ]
50
+ doc: personal-data.md
51
+ - id: shield-cryptography
52
+ title: Cryptography, keys and secrets handling
53
+ group: compliance
54
+ globs:
55
+ - '**/{crypto,cryptography,security,secrets,certs,certificates,tls,vault,kms}/**'
56
+ - '**/*{crypt,Crypt,cipher,Cipher,hash,Hash,signature,Signature,token,Token,secret,Secret}*.*'
57
+ controls:
58
+ [
59
+ ISO27001:A.8.24,
60
+ ISO27001:A.5.17,
61
+ SOC2:CC6.1,
62
+ SOC2:CC6.7,
63
+ NIST-800-53:SC-8,
64
+ NIST-800-53:SC-12,
65
+ NIST-800-53:SC-13,
66
+ NIST-800-53:SC-28,
67
+ GDPR:Art.32(1)(a),
68
+ NIS2:Art.21(2)(h),
69
+ ]
70
+ doc: cryptography.md
71
+ - id: shield-logging
72
+ title: Logging, audit trail and monitoring
73
+ group: compliance
74
+ globs:
75
+ - '**/{logs,logger,logging,audit,telemetry,observability,monitoring,metrics,tracing}/**'
76
+ - '**/*{logger,Logger,logging,Logging,audit,Audit,telemetry,Telemetry}*.*'
77
+ controls:
78
+ [
79
+ ISO27001:A.8.15,
80
+ ISO27001:A.8.16,
81
+ ISO27001:A.8.17,
82
+ SOC2:CC7.2,
83
+ SOC2:CC7.3,
84
+ NIST-800-53:AU-2,
85
+ NIST-800-53:AU-3,
86
+ NIST-800-53:AU-9,
87
+ NIST-800-53:AU-11,
88
+ GDPR:Art.5(1)(c),
89
+ NIS2:Art.23,
90
+ ]
91
+ doc: logging.md
92
+ - id: shield-ai-integrations
93
+ title: AI models, prompts and agent tooling
94
+ group: compliance
95
+ globs:
96
+ - '**/{ai,llm,llms,prompts,agents,mcp,rag,embeddings}/**'
97
+ - '**/*{openai,OpenAI,anthropic,Anthropic,gemini,Gemini,llm,LLM,prompt,Prompt}*.*'
98
+ - '**/{.mcp.json,mcp.json}'
99
+ - '{CLAUDE,GEMINI,AGENTS,CONVENTIONS}.md'
100
+ - '{.claude,.cursor,.codex,.opencode,.gemini}/**'
101
+ controls:
102
+ [
103
+ GDPR:Art.28,
104
+ GDPR:Art.35,
105
+ GDPR:Art.44,
106
+ EU-AI-Act:Art.50,
107
+ ISO27001:A.5.19,
108
+ ISO27001:A.5.23,
109
+ ISO27001:A.8.12,
110
+ ]
111
+ doc: ai-integrations.md
112
+ - id: shield-change-and-supply-chain
113
+ title: Change control and supply chain
114
+ group: compliance
115
+ globs:
116
+ - '.github/workflows/**/*.{yml,yaml}'
117
+ - '**/{.gitlab-ci.yml,azure-pipelines.yml,bitbucket-pipelines.yml,CODEOWNERS}'
118
+ - '**/{package.json,pyproject.toml,requirements*.txt,go.mod,Cargo.toml,composer.json,Gemfile}'
119
+ - '**/{Dockerfile,Containerfile}'
120
+ controls:
121
+ [
122
+ ISO27001:A.8.25,
123
+ ISO27001:A.8.32,
124
+ ISO27001:A.5.21,
125
+ ISO27001:A.8.9,
126
+ SOC2:CC8.1,
127
+ SOC2:CC7.1,
128
+ NIST-800-53:CM-3,
129
+ NIST-800-53:SA-10,
130
+ NIST-800-53:SR-3,
131
+ NIS2:Art.21(2)(d),
132
+ NIS2:Art.21(2)(e),
133
+ ]
134
+ doc: change-and-supply-chain.md
@@ -0,0 +1,10 @@
1
+ A log is evidence for an investigation and a store of personal data at the same time.
2
+
3
+ - **Security events.** Sign-in success and failure, access denied, privilege and role changes, exports, deletions and configuration changes are logged with who, what, when and from where. A security-relevant path the change adds without an event is a gap.
4
+ - **Content.** No passwords, tokens, keys, full card numbers, session ids or request bodies with personal data in log lines, traces, metrics labels or error reports. Identifiers are pseudonymised where the investigation does not need them in clear.
5
+ - **Integrity.** Audit records cannot be edited or deleted by the application account that writes them; a change that routes audit events through a mutable table or a best-effort queue that drops on failure weakens the trail.
6
+ - **Time.** Timestamps are in UTC from a synchronised clock and carry their zone; ordering never depends on a client-supplied time.
7
+ - **Retention.** The retention of new logs is set, matches the policy for their content, and is shorter for logs holding personal data than for pure security events where the policy says so.
8
+ - **Detection.** An alert or dashboard that the change removes, renames or silences is called out: incident detection and notification deadlines depend on it.
9
+
10
+ Name the control a finding breaks (for example `ISO27001:A.8.15` for logging) in the finding's description.
@@ -0,0 +1,10 @@
1
+ Follow each personal field from where it enters to where it is deleted.
2
+
3
+ - **Minimisation.** A new field, column or payload property that identifies a person has a purpose the feature needs now. Collecting "in case" is the defect; so is copying a whole user object into another table, a cache, an event or a third-party call.
4
+ - **Special categories.** Health, biometric, political, religious, sexual orientation, criminal or children's data needs its own legal basis and safeguards; flag it even when the rest of the change is clean.
5
+ - **Retention and deletion.** Data added here has a retention period, and account deletion or an erasure request reaches it: backups, search indexes, analytics exports, soft-deleted rows and denormalised copies included.
6
+ - **Masking.** Identifiers and contact details are masked or pseudonymised where the full value is not needed: admin lists, logs, support views, test fixtures and seed data.
7
+ - **Migrations.** A migration that copies, backfills or widens personal data states why; a rollback does not resurrect erased data; a new nullable column that will hold personal data has a default that collects nothing.
8
+ - **Records of processing.** A new purpose, recipient or category of data subject is a change to the record of processing activities; say so in the finding so the register is updated.
9
+
10
+ Name the control a finding breaks (for example `GDPR:Art.5(1)(c)` for minimisation) in the finding's description.
@@ -0,0 +1,53 @@
1
+ # AI processing register (bmad-plus/ai-processing-register/1).
2
+ #
3
+ # Start with `bmad-plus ai-register init` (it writes _bmad/ai-processing-register.yaml)
4
+ # and describe every AI tool the project uses:
5
+ # coding assistants, agent CLIs, model APIs called by the product, MCP servers. Then:
6
+ # bmad-plus ai-register check
7
+ # warns about any AI integration found in the project (adapter files such as CLAUDE.md,
8
+ # tool folders such as .cursor/, servers declared in .mcp.json) that no entry covers.
9
+ # `bmad-plus doctor` reports the same when the Shield pack is installed. A missing entry
10
+ # never fails a build: the register is a record of processing, kept honest by review.
11
+ #
12
+ # integrations: the ids this entry covers. Tool ids are those of the BMAD+ adapters
13
+ # (claude-code, gemini-cli, antigravity, cursor, codex-cli, opencode, aider) or of other
14
+ # tools (github-copilot, windsurf, continue); an MCP server is mcp:<name as declared>.
15
+ # legalBasis (GDPR Art. 6(1)), only when personalData is true: consent, contract,
16
+ # legal-obligation, vital-interests, public-task, legitimate-interests.
17
+ # transfers: every destination outside the EEA with its mechanism: adequacy-decision,
18
+ # standard-contractual-clauses, binding-corporate-rules, derogation. [] when none.
19
+ # Every value below is an example: replace it with what the provider's terms actually say.
20
+ schema: bmad-plus/ai-processing-register/1
21
+ controller: Example Ltd, 1 Example Street, Example City
22
+ reviewed: 2026-09-29
23
+
24
+ tools:
25
+ - id: coding-assistant
26
+ name: Claude Code
27
+ provider: Anthropic PBC
28
+ integrations: [claude-code]
29
+ purpose: Assist developers in reading, writing and reviewing the code of this repository.
30
+ data:
31
+ - Source code and commit history of this repository
32
+ - File contents and command output the developer shares in a session
33
+ personalData: true
34
+ legalBasis: legitimate-interests
35
+ retention: The period set by the provider's terms for the plan in use, written here in days.
36
+ transfers:
37
+ - to: United States
38
+ mechanism: standard-contractual-clauses
39
+ agreement: Data processing addendum attached to the commercial terms.
40
+
41
+ - id: issue-tracker-connector
42
+ name: GitHub MCP server
43
+ provider: GitHub, Inc.
44
+ integrations: ['mcp:github']
45
+ purpose: Let the coding assistant read and comment on issues and pull requests.
46
+ data:
47
+ - Issue and pull-request text, including contributor names and handles
48
+ personalData: true
49
+ legalBasis: legitimate-interests
50
+ retention: Not stored by the connector; the assistant's retention applies to what it reads.
51
+ transfers:
52
+ - to: United States
53
+ mechanism: standard-contractual-clauses
@@ -0,0 +1,32 @@
1
+ # AI Processing Register
2
+
3
+ > Procedure used by Shield when a user asks which AI tools touch the project's data, prepares a record of processing (GDPR Art. 30), a DPIA involving AI tooling, or an ISO 27001 / ISO 42001 supplier review. Template: `shared/ai-processing-register-template.yaml`. Tooling: `bmad-plus ai-register init|check`, and `bmad-plus doctor` when Shield is installed.
4
+
5
+ ## What it produces
6
+
7
+ `_bmad/ai-processing-register.yaml`: one entry per AI tool the project uses — coding assistants, agent CLIs, model APIs called by the product, MCP servers — with its provider, purpose, the categories of data it sees, whether that includes personal data and on which legal basis, retention, transfers outside the EEA with their mechanism, and the processing agreement in place.
8
+
9
+ ## Steps
10
+
11
+ 1. **Inventory.** Run `bmad-plus ai-register check`. It lists the AI integrations present in the project: the adapter files BMAD+ installs (`CLAUDE.md`, `GEMINI.md`, `.cursor/rules/`, `.codex/AGENTS.md`...), the tools' own folders (`.claude/`, `.cursor/`, `.gemini/`...), and the MCP servers declared in `.mcp.json`, `.cursor/mcp.json`, `.gemini/settings.json` or `.vscode/mcp.json` (comments and trailing commas allowed). Ask the user for tools used without a trace in the repository (a model API called by the product, a browser assistant).
12
+ 2. **One entry per tool.** `bmad-plus ai-register init` starts the file from the template; replace its example entries. An MCP server is registered as `mcp:` followed by its name exactly as declared. Fill each field from facts: the provider's terms and data processing addendum, the tool's settings (training opt-out, retention, region), what the tool can actually read. When a fact is unknown, say so to the user; do not guess a retention period or a transfer mechanism.
13
+ 3. **Personal data.** Code rarely is, but commit authors, issue text, customer data in fixtures, logs and support tickets are. When `personalData` is true, choose the legal basis with the user (the `gdpr-agent` and `legitimate-interest` workflow help) and flag the entry for a DPIA (`dpia-sentinel`) when the processing is large-scale or novel.
14
+ 4. **Transfers.** Every destination outside the EEA needs a mechanism; an adequacy decision covers a US provider only while it is certified under the Data Privacy Framework.
15
+ 5. **Check again.** `bmad-plus ai-register check` must report every integration registered. Set `reviewed` to the date of the review; the check warns after twelve months.
16
+
17
+ ## How the gate behaves
18
+
19
+ | Situation | Result |
20
+ | --------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
21
+ | An AI integration found in the project that no entry covers | Warning (exit code 0) |
22
+ | No register while AI integrations are present | Warning |
23
+ | Register last reviewed more than 365 days ago | Warning |
24
+ | An entry that matches nothing found here | Listed as not found; it may be used outside the repository |
25
+ | A register that does not follow the schema (unknown key, legal basis missing for personal data, unknown transfer mechanism) | Error (exit code 1) |
26
+
27
+ The gate is deliberately soft: it keeps the register in step with the tooling, and a missing entry never blocks a build; only a register that cannot be read exits non-zero. `bmad-plus doctor` shows every case, that one included, as a warning.
28
+
29
+ ## Boundaries
30
+
31
+ - The register records processing; it does not make it lawful. The legal basis, transfer mechanism and agreements are decisions for the controller and its DPO.
32
+ - Detection sees configuration in the repository only. A tool installed on a developer's machine without project configuration is found by asking, not by the check.
@@ -0,0 +1,87 @@
1
+ # Security assurance case (bmad-plus/assurance-case/1).
2
+ #
3
+ # Start with `bmad-plus assurance init _bmad/assurance/<id>.yaml`, then replace every
4
+ # example with the project's own claims and checks. At the commit the case is about:
5
+ # bmad-plus assurance run _bmad/assurance/<id>.yaml
6
+ # bmad-plus assurance verify _bmad/assurance/<id>.yaml --ledger-head <head reported by run>
7
+ # --emit-check _bmad-output/assurance/<id>/check.json
8
+ # Keep the head outside the ledger, and set BMAD_PLUS_ASSURANCE_KEY from a secret in CI:
9
+ # without them the ledger shows accidental edits, not deliberate ones.
10
+ #
11
+ # Evidence is only ever a check below that ran: its command, exit code, output digest and
12
+ # artifact digests are recorded in a hash-chained ledger (_bmad-output/assurance/<id>/runs.jsonl).
13
+ # A statement, a document or a link is not evidence, and verify refuses a case that relies
14
+ # on one. Evidence goes stale when the commit, the command or an artifact changes, or when
15
+ # it is older than freshness.maxAgeDays.
16
+ #
17
+ # Commands run without a shell: write each argument separately. On Windows, programs such
18
+ # as npm are .cmd files and need [cmd, /c, npm, ...]. A check inherits only PATH, HOME and
19
+ # the temporary folders; name any other variable it needs under env.
20
+ schema: bmad-plus/assurance-case/1
21
+ id: release-security
22
+ title: Release security case
23
+ scope: >-
24
+ The service at the commit being released, built by the pipeline from this repository.
25
+ freshness:
26
+ maxAgeDays: 30
27
+ sameCommit: true
28
+
29
+ checks:
30
+ - id: unit-tests
31
+ run: [npm, test]
32
+ timeoutSeconds: 900
33
+ - id: dependency-audit
34
+ run: [npm, audit, --audit-level=high, --omit=dev]
35
+ - id: secret-scan
36
+ run: [gitleaks, detect, --no-banner, --redact, --report-path, reports/gitleaks.json]
37
+ artifacts: [reports/gitleaks.json]
38
+ - id: review-gate
39
+ run: [npx, bmad-plus, review, gate, release, --emit-check, reports/review-check.json]
40
+ artifacts: [reports/review-check.json]
41
+
42
+ claims:
43
+ - id: C1
44
+ claim: The release ships no known exploitable weakness, no committed credential, and no unreviewed change.
45
+ argument: >-
46
+ The top claim holds when each of the four sub-claims below holds; together they cover
47
+ third-party code, secrets, the change process and the behaviour the controls rely on.
48
+ - id: C1.1
49
+ parent: C1
50
+ claim: Production dependencies carry no known vulnerability rated high or critical.
51
+ argument: >-
52
+ npm audit compares the resolved dependency tree with the advisory database and exits
53
+ non-zero at the chosen level; a zero exit at this commit shows none is known today.
54
+ controls: [ISO27001:A.8.8, NIST-800-53:RA-5]
55
+ evidence:
56
+ - check: dependency-audit
57
+ shows: npm audit reports no advisory at high or above for production dependencies.
58
+ - id: C1.2
59
+ parent: C1
60
+ claim: No credential is committed in the repository history.
61
+ argument: >-
62
+ The scanner reads every commit reachable from this one and exits non-zero on a finding;
63
+ its report is kept as an artifact so the result can be inspected.
64
+ controls: [ISO27001:A.5.17, SOC2:CC6.1]
65
+ evidence:
66
+ - check: secret-scan
67
+ shows: gitleaks finds no secret in the history and writes its report.
68
+ - id: C1.3
69
+ parent: C1
70
+ claim: Every changed file of the release was reviewed and no finding is left open.
71
+ argument: >-
72
+ The review gate derives its verdict from coverage of the sealed scope: it exits zero
73
+ only when every selected file is completed or waived and no finding is open.
74
+ controls: [ISO27001:A.8.32, SOC2:CC8.1]
75
+ evidence:
76
+ - check: review-gate
77
+ shows: bmad-plus review gate reports the release review clean.
78
+ - id: C1.4
79
+ parent: C1
80
+ claim: The behaviour the security controls rely on is covered by passing tests.
81
+ argument: >-
82
+ Access checks, input validation and redaction have tests in the suite; the suite
83
+ passing at this commit shows that behaviour is intact.
84
+ controls: [ISO27001:A.8.29]
85
+ evidence:
86
+ - check: unit-tests
87
+ shows: The test suite passes at this commit.
@@ -0,0 +1,50 @@
1
+ # Security Assurance Case
2
+
3
+ > Procedure used by Shield when a user asks for an assurance case, evidence for an audit, or proof that security controls hold at a given commit. Template: `shared/assurance-case-template.yaml`. Tooling: `bmad-plus assurance init|run|verify`.
4
+
5
+ ## What it produces
6
+
7
+ A case in `_bmad/assurance/<id>.yaml` that states **claims**, gives for each an **argument**, and supports it with **evidence**. Evidence is only ever a check that ran: `bmad-plus assurance run` executes each declared command without a shell and appends to a hash-chained ledger what happened (exit code, output digest, digests of the artifacts the run wrote, commit, whether the tree was clean of changes and untracked files). `bmad-plus assurance verify` then refuses any claim whose evidence is missing, failed or stale, and the case file itself cannot name evidence that is only asserted.
8
+
9
+ ## Steps
10
+
11
+ 0. **Start.** `bmad-plus assurance init _bmad/assurance/<id>.yaml` copies this template under that id; it never overwrites a case.
12
+ 1. **Scope.** Agree with the user on what the case covers (system, commit or release, deployment) and which frameworks matter. Write it in `scope`.
13
+ 2. **Claims from controls.** Start from the controls in scope (the relevant framework agent and `shared/cross-framework-mapper.md` help). Write one top claim, then sub-claims narrow enough that a single command can show each one. Tag a claim with the controls it supports (`ISO27001:A.8.8`, `SOC2:CC8.1`, `GDPR:Art.32`); unknown or mistyped controls are refused.
14
+ 3. **Checks.** For every leaf claim, choose a command that exits zero only when the claim holds: a test suite, a dependency audit, a secret scanner, a configuration linter, `bmad-plus review gate`, a policy-as-code evaluation. Declare artifacts the command writes (reports, SBOMs) so they are hashed with the run.
15
+ 4. **Argument.** Say why a passing check shows the claim, and what it does not show. A check that cannot fail for the reason the claim is about is not evidence of it.
16
+ 5. **Run and verify** at the commit the case is about, with a clean working tree (untracked files count unless git ignores them; the case's own ledger folder and artifacts do not):
17
+ ```
18
+ bmad-plus assurance run _bmad/assurance/<id>.yaml
19
+ bmad-plus assurance verify _bmad/assurance/<id>.yaml --ledger-head <head> --emit-check _bmad-output/assurance/<id>/check.json
20
+ ```
21
+ `run` reports the ledger head; keep it where the ledger's writers cannot change it (the CI job log, the audit file) and pass it to `verify`. In CI, run both in the same job with `BMAD_PLUS_ASSURANCE_KEY` set from a secret.
22
+ 6. **Report.** Present the verdict: supported and unsupported claims with their reasons, and the controls that now have executed evidence (`controls.supported`). Never describe an unsupported claim as met.
23
+
24
+ ## What is refused
25
+
26
+ | Situation | Result |
27
+ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
28
+ | Evidence that is a statement, a document or a link | The case does not load: evidence must name a check |
29
+ | A claim with neither evidence nor sub-claims | The case does not load: the claim is only asserted |
30
+ | A check that never ran | `missing` — the claim is unsupported |
31
+ | A non-zero or unexpected exit code, a timeout, an artifact the run did not write (absent, or left unchanged from before) | `failed` |
32
+ | A run at another commit or on uncommitted changes or untracked files, a command changed since, an artifact changed since, a run older than `maxAgeDays` | `stale` |
33
+ | A ledger record edited or reordered, or removed before the last one | The whole case is unsupported until the ledger is rebuilt |
34
+ | The last records removed | Unsupported with `--ledger-head` given; unseen without it |
35
+ | A record written by hand, with its digest recomputed | Unsupported with `BMAD_PLUS_ASSURANCE_KEY` set; unseen without it |
36
+
37
+ ## How far the ledger can be trusted
38
+
39
+ Without a key, the chain of SHA-256 digests shows accidental and careless edits only: anyone who can write the ledger can append a record that verifies, or cut off the records after a run they want forgotten. Two things close that, both kept outside the ledger:
40
+
41
+ - **A key.** With `BMAD_PLUS_ASSURANCE_KEY` (at least 32 characters) set, `run` signs every record with an HMAC and `verify` refuses any record the key does not authenticate. The key is never passed to a check, and a case cannot ask for it.
42
+ - **The head.** `verify --ledger-head <sha256>` refuses a ledger that no longer holds the record `run` reported last, so removed runs are seen.
43
+
44
+ Report which of the two applied (`ledger.authenticated`, `ledger.anchor` in the verdict); without either, say the evidence is recorded, not tamper-proof.
45
+
46
+ ## Boundaries
47
+
48
+ - The case shows that named checks passed at a commit. It does not certify the system, and it is not an audit opinion: a qualified auditor decides what the evidence is worth.
49
+ - `assurance run` executes the commands written in the case, with the same trust as a project's own scripts. Read a case from someone else before running it.
50
+ - Evidence is bound to the commit by default (`freshness.sameCommit: true`). Set it to false only for a project outside git, and say so in the report.
@@ -128,6 +128,25 @@ Detect the relevant framework(s) from user input using these trigger patterns:
128
128
  - FRIA, fundamental rights, Art. 27, impact assessment AI → route to `ai-act-fria`
129
129
  - AI incident, serious incident, Art. 73, incident reporting → route to `ai-act-incidents`
130
130
 
131
+ **Evidence & tooling triggers:**
132
+ - assurance case, security case, audit evidence, prove the controls hold, claims and evidence → follow `shared/assurance-case.md`
133
+ - AI tools register, which AI tools see our data, record of processing for AI tooling, MCP servers inventory → follow `shared/ai-processing-register.md`
134
+ - compliance review of a change, which controls does this change touch → use the Shield review rules (see Evidence & Tooling below)
135
+
136
+ ---
137
+
138
+ ## Evidence & Tooling
139
+
140
+ Three Shield capabilities rest on BMAD+ CLI commands, so their results are checked by code, not asserted:
141
+
142
+ | Need | Procedure | Command |
143
+ |------|-----------|---------|
144
+ | A security assurance case whose every piece of evidence is a check that ran, at the commit concerned | `shared/assurance-case.md`, template `shared/assurance-case-template.yaml` | `bmad-plus assurance init <case>`, `bmad-plus assurance run <case>`, `bmad-plus assurance verify <case> --ledger-head <head>`; `BMAD_PLUS_ASSURANCE_KEY` authenticates the ledger |
145
+ | A register of the AI tools the project uses, what data they see and on which legal basis | `shared/ai-processing-register.md`, template `shared/ai-processing-register-template.yaml` | `bmad-plus ai-register init`, `bmad-plus ai-register check` (a warning, never a failure, for an unregistered tool) |
146
+ | A code review that says which compliance controls a change touches | `review-rules/`: access control, personal data, cryptography, logging, AI integrations, change and supply chain, all in the `compliance` group | `bmad-plus review scope` writes the checklist; `bmad-plus review rules <path>` lists the rules and controls for one file |
147
+
148
+ Controls are written `FRAMEWORK:ID` (`ISO27001:A.8.28`, `SOC2:CC8.1`, `GDPR:Art.32(1)(b)`, `NIS2:Art.21(2)(d)`, `NIST-800-53:AC-6(1)`, `EU-AI-Act:Art.50`); an unknown or mistyped control is refused. Report a supported claim or a touched control as what it is: evidence that a named check passed, or a prompt to review, never a certification. An assurance ledger without a key and a kept head shows accidental edits, not deliberate ones: say which protections applied.
149
+
131
150
  ---
132
151
 
133
152
  ## Multi-Framework Analysis
@@ -200,7 +219,11 @@ What type of compliance question do you have?
200
219
  8. 🔄 Cross-Framework Analysis
201
220
  "I need to comply with multiple frameworks — help me map controls."
202
221
 
203
- Which area? (1-8, or describe your situation)
222
+ 9. 🧾 Evidence & AI Tooling
223
+ → Assurance case, AI processing register, compliance review rules
224
+ "Prove our controls hold at this release" / "Which AI tools see our data?"
225
+
226
+ Which area? (1-9, or describe your situation)
204
227
  ```
205
228
 
206
229
  ---
@@ -116,6 +116,7 @@ template lacks is a change to the template, made in BMAD+ with its tests.
116
116
  | `storage-explained` | where the answers live and what makes them disappear (site data cleared, private window closed), and that nobody receives them until the JSON is handed over |
117
117
  | `finished-is-not-accepted` · `unload-guard` | unanswered lines are said on the page before finishing (a second click finishes anyway); finishing keeps the date and says it is not an acceptance; leaving with an unsaved run is questioned |
118
118
  | `utf8-and-escaped-diacritics` · `export-is-the-run` | UTF-8 declared, diacritics handled as escapes, and the exported JSON is the run as answered |
119
+ | `no-third-party-requests` | opening the page asks nothing of any origin but its own: no remote font, stylesheet, script or image, so no third party learns who opened it |
119
120
 
120
121
  **Before a person opens the page**, in this order, and say in the delivery which ones ran:
121
122
 
@@ -4,16 +4,17 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>__TITLE__</title>
7
- <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Serif:wght@500;600&family=IBM+Plex+Mono:wght@400;500&display=swap">
8
7
  <style>
9
8
  :root {
10
9
  --bg: #f6f5f2; --card: #ffffff; --ink: #1b1d1f; --muted: #6b6f73; --line: #d9d7d1; --line-strong: #b9b6ae;
11
10
  --accent: #0f6f74; --accent-ink: #ffffff; --accent-soft: #e2f0f0;
12
11
  --ok: #1f7a3a; --ok-soft: #e4f2e8; --ko: #b3261e; --ko-soft: #f8e3e1; --warn: #b26a00; --warn-soft: #fbeeda;
13
12
  --mono-bg: #edece7; --focus: #0f6f74;
14
- --sans: "IBM Plex Sans", "Segoe UI", system-ui, sans-serif;
15
- --serif: "IBM Plex Serif", Georgia, "Times New Roman", serif;
16
- --mono: "IBM Plex Mono", Consolas, "Courier New", monospace;
13
+ /* Installed faces only: the page asks nothing of any origin but its own, and every
14
+ platform's own fonts cover the scripts of every language the page ships. */
15
+ --sans: system-ui, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif;
16
+ --serif: Charter, "Bitstream Charter", "Sitka Text", Cambria, "Noto Serif", "DejaVu Serif", Georgia, serif;
17
+ --mono: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "DejaVu Sans Mono", monospace;
17
18
  }
18
19
  @media (prefers-color-scheme: dark) {
19
20
  :root:not([data-theme="light"]) {
@@ -155,6 +155,9 @@ function factsFromDerived(derived, packs = derived.packOrder) {
155
155
  ? derived.installerAgents
156
156
  : selected.reduce((total, pack) => total + pack.agentCount, 0),
157
157
  packs: selected,
158
+ dataFlows: (derived.dataHandling || []).filter(
159
+ (flow) => flow.component === 'cli' || packs.includes(flow.component)
160
+ ),
158
161
  };
159
162
  }
160
163
 
@@ -217,6 +220,10 @@ function renderFactsSection(facts) {
217
220
  ` - ${p.name}${p.required ? ' (required)' : ''}: ${p.agentCount} installer agent${p.agentCount === 1 ? '' : 's'} — ${p.desc}${suffix}`
218
221
  );
219
222
  }
223
+ if (facts.dataFlows.length)
224
+ lines.push(
225
+ `- Data handling: no telemetry; ${facts.dataFlows.length} flow${facts.dataFlows.length === 1 ? '' : 's'} can send data off the machine, each under its consent basis: ${facts.dataFlows.map((flow) => `${flow.id} (${flow.consent})`).join(', ')}. What, to whom and when: "Data handling" in the bmad-plus SECURITY.md.`
226
+ );
220
227
  return lines;
221
228
  }
222
229
 
@@ -302,6 +302,19 @@ function renderSummary(pack, derived, field = 'summary') {
302
302
  });
303
303
  }
304
304
 
305
+ /**
306
+ * The package flows of the data-handling doctrine that can leave the machine, with their
307
+ * consent basis; maintainer services are not installed and stay out. verify-egress.js owns
308
+ * the doctrine's schema and its agreement with the code.
309
+ */
310
+ function buildDataHandling(registry) {
311
+ const doctrine = registry.data_handling || {};
312
+ const services = new Set((doctrine.services || []).map((service) => service.id));
313
+ return (doctrine.flows || [])
314
+ .filter((flow) => flow.egress !== 'none' && !services.has(flow.component))
315
+ .map((flow) => ({ id: flow.id, component: flow.component, consent: flow.consent?.basis }));
316
+ }
317
+
305
318
  /** The one derivation engine; its JSON result is shipped for runtime consumers. */
306
319
  function buildDerived(registry, { sourceRoot = path.join(REPO_ROOT, 'src', 'bmad-plus') } = {}) {
307
320
  const packs = {};
@@ -381,6 +394,7 @@ function buildDerived(registry, { sourceRoot = path.join(REPO_ROOT, 'src', 'bmad
381
394
  languages: Object.keys(LANGUAGES),
382
395
  packs,
383
396
  pythonPacks,
397
+ dataHandling: buildDataHandling(registry),
384
398
  diagnostics: {
385
399
  schemaVersion: 1,
386
400
  processExecution: registry.targets.optional_process_backend || null,
@@ -120,6 +120,8 @@ for (const modulePath of [
120
120
  './commands/nexus',
121
121
  './commands/uat',
122
122
  './commands/review',
123
+ './commands/assurance',
124
+ './commands/ai-register',
123
125
  ]) {
124
126
  const command = require(modulePath);
125
127
  const configured = program.command(command.command).description(command.description);
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The AI processing register: start it from the Shield template, warn about AI integrations
3
+ * it does not cover.
4
+ */
5
+ 'use strict';
6
+
7
+ const path = require('node:path');
8
+ const register = require('../lib/ai-register');
9
+
10
+ module.exports = {
11
+ command: 'ai-register <action>',
12
+ description:
13
+ 'AI processing register: start it from the Shield template, check which AI integrations it does not cover',
14
+ options: [
15
+ ['-d, --directory <path>', 'Project directory'],
16
+ ['--json', 'Machine-readable output'],
17
+ ],
18
+ action: (action, options = {}) => {
19
+ const projectDir = path.resolve(options.directory || process.cwd());
20
+ if (action === 'init') {
21
+ try {
22
+ const file = register.initRegister(projectDir);
23
+ if (options.json) console.log(JSON.stringify({ schemaVersion: 1, action, file }, null, 2));
24
+ else
25
+ console.log(
26
+ `${file}: started from the Shield template. Replace the example entries, then run bmad-plus ai-register check.`
27
+ );
28
+ } catch (error) {
29
+ console.error(`ai-register: ${error.message}`);
30
+ process.exitCode = 3;
31
+ }
32
+ return;
33
+ }
34
+ if (action !== 'check') {
35
+ console.error(`ai-register: unknown action "${action}" (init, check)`);
36
+ process.exitCode = 3;
37
+ return;
38
+ }
39
+ const result = register.checkRegister(projectDir);
40
+ // A soft gate: warnings never fail the command; only an unreadable register does.
41
+ process.exitCode = result.errors.length ? 1 : 0;
42
+ if (options.json) {
43
+ console.log(JSON.stringify({ schemaVersion: 1, ...result }, null, 2));
44
+ return;
45
+ }
46
+ const covered = result.integrations.filter((item) => item.coveredBy);
47
+ console.log(
48
+ `${result.registerFile}: ${result.register ? `reviewed ${result.register.reviewed}` : 'absent'} — ${covered.length}/${result.integrations.length} AI integration(s) registered`
49
+ );
50
+ for (const item of covered)
51
+ console.log(` registered ${item.ids.join(' or ')} → ${item.coveredBy}`);
52
+ for (const warning of result.warnings) console.log(` warning ${warning}`);
53
+ for (const error of result.errors) console.error(` error ${error}`);
54
+ if (result.unmatched.length)
55
+ console.log(
56
+ ` not found here (may be used outside the repository): ${result.unmatched.join(', ')}`
57
+ );
58
+ if (result.warnings.length && !result.errors.length)
59
+ console.log(
60
+ ' Add each tool with its purpose, data, legal basis, retention and transfers; see the Shield template shared/ai-processing-register-template.yaml.'
61
+ );
62
+ },
63
+ };