bmad-plus 0.21.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 (46) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +13 -13
  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 +13 -13
  7. package/readme-international/README.es.md +13 -13
  8. package/readme-international/README.fr.md +13 -13
  9. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review.md +3 -1
  10. package/src/bmad-plus/packs/pack-seo/SKILL.md +3 -1
  11. package/src/bmad-plus/packs/pack-seo/ref/cwv-thresholds.md +2 -2
  12. package/src/bmad-plus/packs/pack-seo/requirements.txt +1 -1
  13. package/src/bmad-plus/packs/pack-seo/scripts/seo_apis.py +72 -30
  14. package/src/bmad-plus/packs/pack-seo/scripts/seo_report.py +5 -6
  15. package/src/bmad-plus/packs/pack-seo/scripts/seo_screenshot.py +176 -14
  16. package/src/bmad-plus/packs/pack-shield/README.md +12 -0
  17. package/src/bmad-plus/packs/pack-shield/SKILL.md +7 -1
  18. package/src/bmad-plus/packs/pack-shield/review-rules/access-control.md +10 -0
  19. package/src/bmad-plus/packs/pack-shield/review-rules/ai-integrations.md +10 -0
  20. package/src/bmad-plus/packs/pack-shield/review-rules/change-and-supply-chain.md +10 -0
  21. package/src/bmad-plus/packs/pack-shield/review-rules/cryptography.md +10 -0
  22. package/src/bmad-plus/packs/pack-shield/review-rules/index.yaml +134 -0
  23. package/src/bmad-plus/packs/pack-shield/review-rules/logging.md +10 -0
  24. package/src/bmad-plus/packs/pack-shield/review-rules/personal-data.md +10 -0
  25. package/src/bmad-plus/packs/pack-shield/shared/ai-processing-register-template.yaml +53 -0
  26. package/src/bmad-plus/packs/pack-shield/shared/ai-processing-register.md +32 -0
  27. package/src/bmad-plus/packs/pack-shield/shared/assurance-case-template.yaml +87 -0
  28. package/src/bmad-plus/packs/pack-shield/shared/assurance-case.md +50 -0
  29. package/src/bmad-plus/packs/pack-shield/shield-orchestrator.md +24 -1
  30. package/src/bmad-plus/skills/bmad-plus-uat/SKILL.md +1 -0
  31. package/src/bmad-plus/skills/bmad-plus-uat/template/page.html +5 -4
  32. package/tools/build/generate-adapters.js +7 -0
  33. package/tools/build/generate.js +14 -0
  34. package/tools/cli/bmad-plus-cli.js +2 -0
  35. package/tools/cli/commands/ai-register.js +63 -0
  36. package/tools/cli/commands/assurance.js +162 -0
  37. package/tools/cli/commands/review.js +10 -3
  38. package/tools/cli/lib/ai-register.js +393 -0
  39. package/tools/cli/lib/assurance.js +822 -0
  40. package/tools/cli/lib/control-refs.js +132 -0
  41. package/tools/cli/lib/installation-health.js +17 -0
  42. package/tools/cli/lib/packs.js +60 -2
  43. package/tools/cli/lib/page-origins.js +582 -0
  44. package/tools/cli/lib/review-rules.js +92 -24
  45. package/tools/cli/lib/review.js +28 -1
  46. package/tools/cli/lib/uat.js +22 -5
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Compliance control references: `FRAMEWORK:ID`, e.g. `ISO27001:A.8.28`, `SOC2:CC8.1`,
3
+ * `GDPR:Art.32(1)(b)`. Framework names are the Shield pack's compliance tags. Each framework
4
+ * has a closed grammar built from its published numbering, so a mistyped or invented control
5
+ * is refused instead of travelling into a checklist or an assurance case as if it were real.
6
+ */
7
+ 'use strict';
8
+
9
+ const range = (prefix, last) => Array.from({ length: last }, (_, i) => `${prefix}${i + 1}`);
10
+
11
+ /** ISO/IEC 27001:2022 Annex A: themes 5 to 8 and their control counts. */
12
+ const ISO27001 = new Set([
13
+ ...range('A.5.', 37),
14
+ ...range('A.6.', 8),
15
+ ...range('A.7.', 14),
16
+ ...range('A.8.', 34),
17
+ ]);
18
+
19
+ /** SOC 2 Trust Services Criteria (2017, revised points of focus 2022). */
20
+ const SOC2 = new Set(
21
+ Object.entries({
22
+ CC1: 5,
23
+ CC2: 3,
24
+ CC3: 4,
25
+ CC4: 2,
26
+ CC5: 3,
27
+ CC6: 8,
28
+ CC7: 5,
29
+ CC8: 1,
30
+ CC9: 2,
31
+ A1: 3,
32
+ C1: 2,
33
+ PI1: 5,
34
+ P1: 1,
35
+ P2: 1,
36
+ P3: 2,
37
+ P4: 3,
38
+ P5: 2,
39
+ P6: 7,
40
+ P7: 1,
41
+ P8: 1,
42
+ }).flatMap(([series, last]) => range(`${series}.`, last))
43
+ );
44
+
45
+ /** NIST SP 800-53 Rev. 5: control count per family; enhancements are written `AC-2(1)`. */
46
+ const NIST_800_53 = {
47
+ AC: 25,
48
+ AT: 6,
49
+ AU: 16,
50
+ CA: 9,
51
+ CM: 14,
52
+ CP: 13,
53
+ IA: 13,
54
+ IR: 10,
55
+ MA: 7,
56
+ MP: 8,
57
+ PE: 23,
58
+ PL: 11,
59
+ PM: 32,
60
+ PS: 9,
61
+ PT: 8,
62
+ RA: 10,
63
+ SA: 23,
64
+ SC: 51,
65
+ SI: 23,
66
+ SR: 12,
67
+ };
68
+
69
+ /** `Art.N`, optionally a paragraph `(n)` and a point `(x)`, within the act's article count. */
70
+ function article(last) {
71
+ const form = /^Art\.([1-9]\d{0,2})(?:\(([1-9]\d?)\)(?:\(([a-z])\))?)?$/;
72
+ return (id) => {
73
+ const match = form.exec(id);
74
+ return Boolean(match) && Number(match[1]) <= last;
75
+ };
76
+ }
77
+
78
+ const FRAMEWORKS = {
79
+ ISO27001: { example: 'A.8.28', valid: (id) => ISO27001.has(id) },
80
+ SOC2: { example: 'CC8.1', valid: (id) => SOC2.has(id) },
81
+ GDPR: { example: 'Art.32(1)(b)', valid: article(99) },
82
+ 'EU-AI-Act': { example: 'Art.50', valid: article(113) },
83
+ NIS2: { example: 'Art.21(2)(d)', valid: article(46) },
84
+ 'NIST-800-53': {
85
+ example: 'AC-6(1)',
86
+ valid: (id) => {
87
+ const match = /^([A-Z]{2})-([1-9]\d?)(?:\(([1-9]\d?)\))?$/.exec(id);
88
+ return Boolean(match) && Number(match[2]) <= (NIST_800_53[match[1]] || 0);
89
+ },
90
+ },
91
+ };
92
+
93
+ const REF = /^([A-Za-z0-9-]{2,20}):(\S{1,40})$/;
94
+
95
+ /**
96
+ * Check one reference. Returns the reference split into framework and id, or throws with
97
+ * the reason and the accepted form.
98
+ */
99
+ function parseControl(ref) {
100
+ const match = typeof ref === 'string' ? REF.exec(ref) : null;
101
+ if (!match) throw new Error(`"${ref}" is not a control reference (FRAMEWORK:ID)`);
102
+ const [, framework, id] = match;
103
+ const grammar = FRAMEWORKS[framework];
104
+ if (!grammar)
105
+ throw new Error(
106
+ `"${ref}": no control catalog for ${framework} (${Object.keys(FRAMEWORKS).join(', ')})`
107
+ );
108
+ if (!grammar.valid(id))
109
+ throw new Error(
110
+ `"${ref}" names no ${framework} control (e.g. ${framework}:${grammar.example})`
111
+ );
112
+ return { framework, id };
113
+ }
114
+
115
+ /** A list of references: each valid, none repeated. Returns the list unchanged. */
116
+ function parseControls(list, where) {
117
+ if (!Array.isArray(list) || !list.length)
118
+ throw new Error(`${where}: controls must be a non-empty list of FRAMEWORK:ID references`);
119
+ const seen = new Set();
120
+ for (const ref of list) {
121
+ try {
122
+ parseControl(ref);
123
+ } catch (error) {
124
+ throw new Error(`${where}: ${error.message}`, { cause: error });
125
+ }
126
+ if (seen.has(ref)) throw new Error(`${where}: control ${ref} is listed twice`);
127
+ seen.add(ref);
128
+ }
129
+ return [...list];
130
+ }
131
+
132
+ module.exports = { FRAMEWORKS, parseControl, parseControls };
@@ -155,6 +155,23 @@ function collectInstallationHealth({
155
155
  }
156
156
  }
157
157
 
158
+ if (selectedPacks.includes('shield')) {
159
+ // Soft by design: an AI tool missing from the register is reported, never an error.
160
+ const { checkRegister } = require('./ai-register');
161
+ const result = checkRegister(projectDir);
162
+ const problems = [...result.errors, ...result.warnings];
163
+ check(
164
+ 'shield.ai-register',
165
+ problems.length ? 'warning' : 'pass',
166
+ problems.length
167
+ ? 'AI processing register: ' +
168
+ problems.join('; ') +
169
+ '. Run `bmad-plus ai-register check` for details.'
170
+ : `AI processing register covers the ${result.integrations.length} AI integration(s) found.`,
171
+ { path: result.registerFile }
172
+ );
173
+ }
174
+
158
175
  const inventory = inventoryOf(manifest);
159
176
  const validInventory =
160
177
  inventory &&
@@ -191,7 +191,7 @@ const DERIVED = {
191
191
  "product": {
192
192
  "code": "bmad-plus",
193
193
  "displayName": "BMAD+",
194
- "version": "0.21.0",
194
+ "version": "0.22.0",
195
195
  "derivedFrom": "BMAD-METHOD v6.6.0"
196
196
  },
197
197
  "packOrder": [
@@ -381,7 +381,8 @@ const DERIVED = {
381
381
  ],
382
382
  "requiredResources": [
383
383
  ".agents/skills/pack-shield/shield-orchestrator.md",
384
- ".agents/skills/pack-shield/SKILL.md"
384
+ ".agents/skills/pack-shield/SKILL.md",
385
+ ".agents/skills/pack-shield/review-rules/index.yaml"
385
386
  ],
386
387
  "personas": [
387
388
  {
@@ -655,6 +656,63 @@ const DERIVED = {
655
656
  ]
656
657
  }
657
658
  },
659
+ "dataHandling": [
660
+ {
661
+ "id": "update-check",
662
+ "component": "cli",
663
+ "consent": "opt-out"
664
+ },
665
+ {
666
+ "id": "update-apply",
667
+ "component": "cli",
668
+ "consent": "opt-in"
669
+ },
670
+ {
671
+ "id": "python-provision",
672
+ "component": "cli",
673
+ "consent": "opt-in"
674
+ },
675
+ {
676
+ "id": "nexus-worker",
677
+ "component": "cli",
678
+ "consent": "invocation"
679
+ },
680
+ {
681
+ "id": "nexus-verifier",
682
+ "component": "cli",
683
+ "consent": "invocation"
684
+ },
685
+ {
686
+ "id": "uat-page-results",
687
+ "component": "core",
688
+ "consent": "invocation"
689
+ },
690
+ {
691
+ "id": "osint-providers",
692
+ "component": "osint",
693
+ "consent": "credential"
694
+ },
695
+ {
696
+ "id": "seo-site-fetch",
697
+ "component": "seo",
698
+ "consent": "invocation"
699
+ },
700
+ {
701
+ "id": "seo-google-apis",
702
+ "component": "seo",
703
+ "consent": "credential"
704
+ },
705
+ {
706
+ "id": "seo-screenshot",
707
+ "component": "seo",
708
+ "consent": "invocation"
709
+ },
710
+ {
711
+ "id": "assurance-checks",
712
+ "component": "cli",
713
+ "consent": "invocation"
714
+ }
715
+ ],
658
716
  "diagnostics": {
659
717
  "schemaVersion": 1,
660
718
  "processExecution": {