gaia-framework 1.66.0 → 1.83.2
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/.claude/commands/gaia-create-stakeholder.md +20 -0
- package/.claude/commands/gaia-test-gap-analysis.md +17 -0
- package/CLAUDE.md +87 -1
- package/README.md +2 -2
- package/_gaia/_config/global.yaml +5 -1
- package/_gaia/_config/lifecycle-sequence.yaml +20 -0
- package/_gaia/_config/skill-manifest.csv +2 -0
- package/_gaia/_config/workflow-manifest.csv +3 -1
- package/_gaia/core/engine/workflow.xml +5 -1
- package/_gaia/core/workflows/party-mode/steps/step-01-agent-loading.md +60 -9
- package/_gaia/creative/workflows/problem-solving/checklist.md +64 -14
- package/_gaia/creative/workflows/problem-solving/instructions.xml +367 -22
- package/_gaia/creative/workflows/problem-solving/workflow.yaml +31 -1
- package/_gaia/dev/agents/_base-dev.md +7 -1
- package/_gaia/dev/skills/_skill-index.yaml +9 -0
- package/_gaia/dev/skills/figma-integration.md +296 -0
- package/_gaia/lifecycle/templates/brownfield-scan-security-prompt.md +228 -0
- package/_gaia/lifecycle/templates/gap-entry-schema.md +39 -4
- package/_gaia/lifecycle/templates/story-template.md +22 -1
- package/_gaia/lifecycle/workflows/2-planning/create-ux-design/instructions.xml +52 -3
- package/_gaia/lifecycle/workflows/4-implementation/create-stakeholder/checklist.md +25 -0
- package/_gaia/lifecycle/workflows/4-implementation/create-stakeholder/instructions.xml +79 -0
- package/_gaia/lifecycle/workflows/4-implementation/create-stakeholder/workflow.yaml +22 -0
- package/_gaia/lifecycle/workflows/4-implementation/create-story/instructions.xml +10 -0
- package/_gaia/lifecycle/workflows/4-implementation/retrospective/instructions.xml +3 -3
- package/_gaia/lifecycle/workflows/4-implementation/validate-story/instructions.xml +11 -0
- package/_gaia/lifecycle/workflows/anytime/brownfield-onboarding/instructions.xml +11 -7
- package/_gaia/testing/workflows/test-gap-analysis/checklist.md +8 -0
- package/_gaia/testing/workflows/test-gap-analysis/instructions.xml +53 -0
- package/_gaia/testing/workflows/test-gap-analysis/workflow.yaml +38 -0
- package/bin/gaia-framework.js +36 -2
- package/bin/helpers/derive-bump-label.js +41 -0
- package/bin/helpers/validate-bump-labels.js +38 -0
- package/gaia-install.sh +71 -4
- package/package.json +1 -1
- package/_gaia/_memory/tier2-results/.gitkeep +0 -0
- package/_gaia/_memory/tier2-results/checkpoint-resume-2026-03-24.yaml +0 -6
- package/_gaia/_memory/tier2-results/engine-scenarios-2026-03-22.yaml +0 -14
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: figma-integration
|
|
3
|
+
version: '1.0'
|
|
4
|
+
requires_mcp: design-tool
|
|
5
|
+
applicable_agents: [typescript-dev, angular-dev, flutter-dev, java-dev, python-dev, mobile-dev]
|
|
6
|
+
test_scenarios:
|
|
7
|
+
- scenario: Figma MCP server available and healthy
|
|
8
|
+
expected: Mode selection (Generate/Import/Skip) presented to user
|
|
9
|
+
- scenario: Figma MCP server not installed
|
|
10
|
+
expected: Silent fallback to markdown-only, no error or warning
|
|
11
|
+
- scenario: Figma MCP server not running
|
|
12
|
+
expected: Silent fallback to markdown-only, no error or warning
|
|
13
|
+
- scenario: Figma API token expired
|
|
14
|
+
expected: Warning displayed, fallback to markdown-only
|
|
15
|
+
- scenario: Rate limited (429)
|
|
16
|
+
expected: Single retry after delay, fallback with warning if retry fails
|
|
17
|
+
- scenario: Timeout exceeding 5 seconds
|
|
18
|
+
expected: Fallback with warning, continue markdown-only
|
|
19
|
+
- scenario: Design tool detection via MCP probe
|
|
20
|
+
expected: Correct adapter selected based on available MCP tool prefix
|
|
21
|
+
- scenario: Token extraction produces W3C DTCG format
|
|
22
|
+
expected: design-tokens.json contains $type/$value structure with semantic aliases
|
|
23
|
+
- scenario: Component spec extraction
|
|
24
|
+
expected: component-specs.yaml contains typed props, abstract layout, and states
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
**DesignToolProvider Interface** — abstract interface for design tool integrations. Adapters implement these 5 operations:
|
|
28
|
+
|
|
29
|
+
| Operation | Description | Returns |
|
|
30
|
+
|-----------|-------------|---------|
|
|
31
|
+
| `detect()` | Probe MCP tools to identify available design tool | Adapter instance or null |
|
|
32
|
+
| `getTokens()` | Extract design tokens from the design file | W3C DTCG JSON (`design-tokens.json`) |
|
|
33
|
+
| `getComponents()` | Extract component specifications | YAML spec (`component-specs.yaml`) |
|
|
34
|
+
| `getFrames()` | Generate UI kit frames across viewports | Frame metadata for UI kit page |
|
|
35
|
+
| `exportAssets()` | Export images and icons at required densities | Asset files in `assets/` directory |
|
|
36
|
+
|
|
37
|
+
**Adapter Implementations:**
|
|
38
|
+
- **FigmaAdapter** (active) — wraps `figma_*` / `figma/` MCP tools (e.g., `figma/get_file`, `figma/get_styles`, `figma/get_components`). Detected when MCP tools matching prefix `figma` are available.
|
|
39
|
+
- **PenpotAdapter** (planned) — will wrap `penpot_*` MCP tools. Detected via `penpot_` prefix. Not yet implemented.
|
|
40
|
+
- **SketchAdapter** (planned) — will wrap `sketch_*` MCP tools. Detected via `sketch_` prefix. Not yet implemented.
|
|
41
|
+
|
|
42
|
+
**Selection logic:** probe MCP tool list for known prefixes in order: `figma_` / `figma/` → `penpot_` → `sketch_`. Use the first match. If none found, report "No design tool MCP server detected."
|
|
43
|
+
|
|
44
|
+
**MCP constraint (FR-140):** operations are read-heavy/write-light. Most interactions read design data (tokens, components, styles). Write operations are limited to frame generation and are clearly documented per section.
|
|
45
|
+
|
|
46
|
+
<!-- SECTION: detection -->
|
|
47
|
+
## Detection Probe
|
|
48
|
+
|
|
49
|
+
Detect Figma MCP server availability using a lightweight, read-only probe call.
|
|
50
|
+
This section is consumed by `/gaia-create-ux` at workflow start.
|
|
51
|
+
|
|
52
|
+
> **Security mandate:** NEVER persist Figma API tokens in any GAIA file — checkpoints, sidecars, logs, or artifacts. MCP auth is handled by the MCP server process; GAIA does not touch tokens.
|
|
53
|
+
|
|
54
|
+
> **Detection-only mandate:** GAIA MUST never install, configure, or modify the MCP server. Detection is read-only — probe for availability via `figma/get_user_info` or tool listing, nothing more.
|
|
55
|
+
|
|
56
|
+
### Probe Call
|
|
57
|
+
|
|
58
|
+
Use `figma/get_user_info` as the detection probe:
|
|
59
|
+
- Read-only, lightweight, validates both connectivity and token validity
|
|
60
|
+
- 5-second hard timeout (NFR-026 compliance)
|
|
61
|
+
- Zero added latency when MCP is not available (silent skip)
|
|
62
|
+
|
|
63
|
+
### Detection Flow
|
|
64
|
+
|
|
65
|
+
1. **Attempt probe:** call `figma/get_user_info` with a 5-second hard timeout
|
|
66
|
+
2. **On success:** set `figma_mcp_available = true`, proceed to mode selection
|
|
67
|
+
3. **On failure:** classify the failure and handle per the failure mode table below
|
|
68
|
+
|
|
69
|
+
### Failure Mode Handling
|
|
70
|
+
|
|
71
|
+
| Failure | Detection Signal | Behavior |
|
|
72
|
+
|---------|-----------------|----------|
|
|
73
|
+
| **Not installed** (AC5) | Tool not found / tool not available | Silent fallback to markdown-only mode — no error, no warning, no prompt |
|
|
74
|
+
| **Not running** (AC6) | Connection refused / connection error | Silent fallback to markdown-only mode — no error, no warning, no prompt |
|
|
75
|
+
| **Token expired** (AC7) | 401 or 403 response from `figma/get_user_info` | Warn: "Figma token expired — falling back to markdown" then continue markdown-only |
|
|
76
|
+
| **Rate limited** (AC8) | 429 response | Retry once after `Retry-After` header delay (default: 2 seconds). If retry also fails, warn and fallback to markdown-only |
|
|
77
|
+
| **Timeout** (AC9) | No response within 5-second hard timeout | Warn: "Figma MCP did not respond within 5 seconds — falling back to markdown" then continue markdown-only |
|
|
78
|
+
| **Malformed response** | Unexpected or partial data | Treat as unavailable — silent fallback to markdown-only |
|
|
79
|
+
|
|
80
|
+
### Mode Selection (on success)
|
|
81
|
+
|
|
82
|
+
When `figma_mcp_available == true`, present the user with:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
Figma MCP detected. Select UX design mode:
|
|
86
|
+
[g] Generate — AI-generated UX with Figma export
|
|
87
|
+
[i] Import — Import existing Figma designs into GAIA
|
|
88
|
+
[s] Skip — Proceed with markdown-only (ignore Figma)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Minimum API Scopes
|
|
92
|
+
|
|
93
|
+
The Figma API token used by the MCP server requires these minimum scopes:
|
|
94
|
+
|
|
95
|
+
| Scope | Required For | Mode |
|
|
96
|
+
|-------|-------------|------|
|
|
97
|
+
| `files:read` | Reading design files, styles, components | Default (all modes) |
|
|
98
|
+
| `file_content:read` | Reading file content, nodes, images | Default (all modes) |
|
|
99
|
+
| `files:write` | Creating frames, writing to design files | Generate mode only |
|
|
100
|
+
|
|
101
|
+
Scope enforcement is the MCP server's responsibility — GAIA documents scope expectations only and does not validate or request token scopes.
|
|
102
|
+
|
|
103
|
+
### Error Sanitization Rules
|
|
104
|
+
|
|
105
|
+
All error messages from MCP operations MUST follow this safe error format:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
Figma MCP error: {status_code} — {generic_description}. Falling back to markdown-only workflow.
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Disallowed content in error messages:** Figma file URLs, file keys, node IDs, design data, access tokens, or any dynamic content from the Figma API response.
|
|
112
|
+
|
|
113
|
+
| Status Code | Generic Description |
|
|
114
|
+
|-------------|-------------------|
|
|
115
|
+
| 401 | Authentication failed |
|
|
116
|
+
| 403 | Access denied |
|
|
117
|
+
| 404 | Resource not found |
|
|
118
|
+
| 429 | Rate limit exceeded — retry once, then fallback |
|
|
119
|
+
| 500 | Server error |
|
|
120
|
+
|
|
121
|
+
### Security Boundary
|
|
122
|
+
|
|
123
|
+
- The Figma API token lives exclusively in the MCP server configuration (ADR-024)
|
|
124
|
+
- GAIA files must NEVER contain or log Figma tokens, API keys, or credentials
|
|
125
|
+
- Detection probe interacts through MCP tool abstraction only — no direct HTTP calls
|
|
126
|
+
|
|
127
|
+
### Traceability
|
|
128
|
+
|
|
129
|
+
- FR-132: Figma MCP detection probe requirement
|
|
130
|
+
- FR-143: Graceful MCP failure handling
|
|
131
|
+
- NFR-026: MCP detection latency < 5 seconds
|
|
132
|
+
- ADR-024: Figma MCP integration via shared skill
|
|
133
|
+
|
|
134
|
+
<!-- SECTION: tokens -->
|
|
135
|
+
## Design Token Extraction
|
|
136
|
+
|
|
137
|
+
> **Security mandate:** MCP auth is handled by the MCP server — NEVER persist or reference Figma API tokens in extraction outputs, logs, or GAIA files.
|
|
138
|
+
|
|
139
|
+
Extract design tokens from the connected design tool and output in W3C DTCG format.
|
|
140
|
+
|
|
141
|
+
### Extraction Steps
|
|
142
|
+
|
|
143
|
+
1. **Fetch styles** — call `figma/get_styles` to retrieve all published styles (colors, typography, effects, grids)
|
|
144
|
+
2. **Map to W3C DTCG** — transform each style into the W3C Design Tokens Community Group draft format:
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"color": {
|
|
148
|
+
"primary": { "$type": "color", "$value": "#3B82F6", "$description": "Brand primary" }
|
|
149
|
+
},
|
|
150
|
+
"spacing": {
|
|
151
|
+
"sm": { "$type": "dimension", "$value": "8px" }
|
|
152
|
+
},
|
|
153
|
+
"typography": {
|
|
154
|
+
"heading-1": {
|
|
155
|
+
"$type": "typography",
|
|
156
|
+
"$value": { "fontFamily": "Inter", "fontSize": "32px", "fontWeight": 700, "lineHeight": 1.2 }
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
3. **Include semantic aliases** — map raw tokens to semantic names (e.g., `color.surface.primary` → `color.blue.500`)
|
|
162
|
+
4. **Add composite tokens** — typography composites, shadow composites, border-radius scales
|
|
163
|
+
5. **Write output** — save to `{planning_artifacts}/design-system/design-tokens.json` with `"schema_version": "1.0"`
|
|
164
|
+
|
|
165
|
+
<!-- SECTION: components -->
|
|
166
|
+
## Component Spec Extraction
|
|
167
|
+
|
|
168
|
+
> **Security mandate:** MCP auth is handled by the MCP server — NEVER include Figma API tokens in component specs, logs, or any GAIA output files.
|
|
169
|
+
|
|
170
|
+
Extract component specifications into a tech-agnostic intermediate format.
|
|
171
|
+
|
|
172
|
+
### Extraction Steps
|
|
173
|
+
|
|
174
|
+
1. **Fetch components** — call `figma/get_components` to list all published components and variants
|
|
175
|
+
2. **For each component**, extract:
|
|
176
|
+
- **name** — component name (PascalCase)
|
|
177
|
+
- **props** — typed properties: `{ name: string, type: "string"|"number"|"boolean"|"enum", values?: string[] }`
|
|
178
|
+
- **layout** — abstract layout type: `row | column | stack | grid` with spacing via token references (`{spacing.sm}`)
|
|
179
|
+
- **states** — `[default, hover, active, disabled, focus]` with visual diff per state
|
|
180
|
+
- **children** — nested component references with slot definitions
|
|
181
|
+
- **variants** — named variants with their property overrides
|
|
182
|
+
- **responsive** — breakpoint behavior at 375px, 768px, 1280px
|
|
183
|
+
- **a11y** — role, aria-label pattern, description, keyboard interaction
|
|
184
|
+
3. **Write output** — save to `{planning_artifacts}/design-system/component-specs.yaml` with `schema_version: "1.0"`
|
|
185
|
+
|
|
186
|
+
### Output Schema
|
|
187
|
+
|
|
188
|
+
```yaml
|
|
189
|
+
schema_version: "1.0"
|
|
190
|
+
components:
|
|
191
|
+
- name: Button
|
|
192
|
+
props:
|
|
193
|
+
- { name: label, type: string }
|
|
194
|
+
- { name: variant, type: enum, values: [primary, secondary, ghost] }
|
|
195
|
+
- { name: disabled, type: boolean }
|
|
196
|
+
layout: { type: row, gap: "{spacing.sm}" }
|
|
197
|
+
states: [default, hover, active, disabled, focus]
|
|
198
|
+
a11y: { role: button, label: "{props.label}" }
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
<!-- SECTION: frames -->
|
|
202
|
+
## Frame Generation
|
|
203
|
+
|
|
204
|
+
> **Security mandate:** MCP auth is handled by the MCP server — NEVER persist Figma API tokens in frame metadata, logs, or any GAIA output files.
|
|
205
|
+
|
|
206
|
+
Create UI kit frames in the design tool across standard viewports.
|
|
207
|
+
|
|
208
|
+
### Generation Steps
|
|
209
|
+
|
|
210
|
+
1. **Create UI Kit page** — create a dedicated page named "UI Kit — Generated" in the design file
|
|
211
|
+
2. **For each screen** defined in the UX design:
|
|
212
|
+
- Create 3 viewport frames: mobile (375px), tablet (768px), desktop (1280px)
|
|
213
|
+
- Apply auto-layout with responsive constraints from component specs
|
|
214
|
+
- Place components using the extracted component specs and token values
|
|
215
|
+
3. **Add prototype flows** — link frames with interaction flows matching the UX navigation spec
|
|
216
|
+
4. **Label frames** — use naming convention: `{ScreenName}/{Viewport}` (e.g., `Dashboard/Desktop`)
|
|
217
|
+
|
|
218
|
+
### Output
|
|
219
|
+
|
|
220
|
+
Frame metadata logged for verification. No file output — frames are created directly in the design tool via MCP calls (`figma/create_frame`, `figma/create_component_instance`).
|
|
221
|
+
|
|
222
|
+
<!-- SECTION: assets -->
|
|
223
|
+
## Asset Export
|
|
224
|
+
|
|
225
|
+
> **Security mandate:** MCP auth is handled by the MCP server — NEVER include Figma API tokens in asset manifests, export logs, or any GAIA output files.
|
|
226
|
+
|
|
227
|
+
Export raster and vector assets from the design tool at required densities.
|
|
228
|
+
|
|
229
|
+
### Export Steps
|
|
230
|
+
|
|
231
|
+
1. **Identify exportable nodes** — scan the design file for nodes marked as exportable (icons, images, illustrations)
|
|
232
|
+
2. **Export icons** as SVG — call `figma/get_images` with `format: svg` for all icon nodes
|
|
233
|
+
3. **Export images** as PNG at 3 densities — call `figma/get_images` with `format: png` and `scale: 1`, `scale: 2`, `scale: 3` for image nodes
|
|
234
|
+
4. **Organize output** into directory structure:
|
|
235
|
+
```
|
|
236
|
+
{planning_artifacts}/design-system/assets/
|
|
237
|
+
├── icons/ # SVG icons
|
|
238
|
+
│ ├── icon-name.svg
|
|
239
|
+
├── images/ # PNG images at 1x/2x/3x
|
|
240
|
+
│ ├── image-name@1x.png
|
|
241
|
+
│ ├── image-name@2x.png
|
|
242
|
+
│ └── image-name@3x.png
|
|
243
|
+
```
|
|
244
|
+
5. **Generate asset manifest** — list all exported assets with dimensions and file sizes
|
|
245
|
+
|
|
246
|
+
<!-- SECTION: export -->
|
|
247
|
+
## Per-Stack Token Resolution
|
|
248
|
+
|
|
249
|
+
> **Security mandate:** MCP auth is handled by the MCP server — NEVER embed Figma API tokens in generated token files, stack outputs, or any GAIA output files.
|
|
250
|
+
|
|
251
|
+
Maps abstract design tokens to framework-specific implementations. Each dev agent uses this table to generate native code from `design-tokens.json`.
|
|
252
|
+
|
|
253
|
+
| Agent | Stack | Token Format | Example |
|
|
254
|
+
|-------|-------|-------------|---------|
|
|
255
|
+
| Cleo | TypeScript/React | CSS custom properties | `--color-primary: #3B82F6;` in `:root {}` |
|
|
256
|
+
| Lena | Angular | SCSS variables + CSS custom properties | `$color-primary: #3B82F6;` in `_tokens.scss` |
|
|
257
|
+
| Freya | Flutter/Dart | ThemeData extensions | `ThemeData(primaryColor: Color(0xFF3B82F6))` |
|
|
258
|
+
| Hugo | Java/Spring | Spring properties + Java constants | `design.color.primary=#3B82F6` in `application.properties` |
|
|
259
|
+
| Ravi | Python | Python dict constants | `TOKENS = {"color": {"primary": "#3B82F6"}}` in `design_tokens.py` |
|
|
260
|
+
| Talia | Mobile (RN/Swift/Compose) | RN StyleSheet / Swift extensions / Compose theme | `StyleSheet.create({primary: '#3B82F6'})` or `extension UIColor { static let primary = UIColor(hex: "3B82F6") }` or `val Primary = Color(0xFF3B82F6)` |
|
|
261
|
+
|
|
262
|
+
### Resolution Process
|
|
263
|
+
|
|
264
|
+
1. Read `design-tokens.json` (W3C DTCG format) from `{planning_artifacts}/design-system/`
|
|
265
|
+
2. Read `component-specs.yaml` from the same directory for component definitions and widget hints
|
|
266
|
+
3. Identify the active dev agent's stack from the agent persona
|
|
267
|
+
4. For each token, generate the stack-native representation using the table above
|
|
268
|
+
5. For each component, use the `widget_hints` field to guide framework-specific widget/component tree generation
|
|
269
|
+
6. Output token files to the project's design system directory (stack-specific path)
|
|
270
|
+
|
|
271
|
+
### Token Path Resolution Rules
|
|
272
|
+
|
|
273
|
+
Token paths use `{group.token}` syntax. The resolution pattern per stack:
|
|
274
|
+
|
|
275
|
+
| Stack | Pattern | Example Path | Resolved Output |
|
|
276
|
+
|-------|---------|-------------|-----------------|
|
|
277
|
+
| TypeScript/React | `--{group}-{token}` | `{color.blue-500}` | `var(--color-blue-500)` |
|
|
278
|
+
| Angular | `${group}-{token}` | `{spacing.4}` | `$spacing-4` |
|
|
279
|
+
| Flutter/Dart | `AppTokens.{group}.{token}` | `{typography.body}` | `AppTokens.typography.body` |
|
|
280
|
+
| Java/Spring | `design.{group}.{token}` | `{color.interactive-primary}` | `design.color.interactive-primary` |
|
|
281
|
+
| Python | `TOKENS['{group}']['{token}']` | `{shadow.md}` | `TOKENS['shadow']['md']` |
|
|
282
|
+
| Mobile (RN) | `tokens.{group}.{token}` | `{borderRadius.md}` | `tokens.borderRadius.md` |
|
|
283
|
+
| Mobile (Swift) | `DesignTokens.{group}.{token}` | `{color.blue-500}` | `DesignTokens.color.blue500` |
|
|
284
|
+
| Mobile (Compose) | `AppTheme.{group}.{token}` | `{spacing.2}` | `AppTheme.spacing.s2` |
|
|
285
|
+
|
|
286
|
+
### Semantic Alias Resolution
|
|
287
|
+
|
|
288
|
+
Semantic tokens reference primitives via `{group.token}` syntax in their `$value` field. When generating stack-specific code, resolve the alias chain to produce the final value. Example:
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
"interactive-primary": { "$type": "color", "$value": "{color.blue-500}" }
|
|
292
|
+
→ resolves to → #3B82F6
|
|
293
|
+
→ CSS: --color-interactive-primary: #3B82F6;
|
|
294
|
+
→ SCSS: $color-interactive-primary: #3B82F6;
|
|
295
|
+
→ Dart: static const interactivePrimary = Color(0xFF3B82F6);
|
|
296
|
+
```
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
> Brownfield deep analysis scan subagent. Detects security gaps in API endpoints and infrastructure security configurations.
|
|
4
4
|
> Reference: Architecture ADR-021, Section 10.15.2, Section 10.15.5, ADR-022 §10.16.5
|
|
5
5
|
> Infra-awareness: E12-S6 — applies infra-specific patterns when project_type is infrastructure or platform.
|
|
6
|
+
> Non-REST protocols: E11-S17 — GraphQL and gRPC endpoint detection and security gap scanning.
|
|
6
7
|
|
|
7
8
|
## Objective
|
|
8
9
|
|
|
@@ -51,6 +52,89 @@ Apply framework-specific patterns based on {tech_stack}:
|
|
|
51
52
|
|
|
52
53
|
If no API endpoints are detected, output a summary note and zero gap entries for the application phase.
|
|
53
54
|
|
|
55
|
+
## Phase 1b: GraphQL Endpoint Discovery
|
|
56
|
+
|
|
57
|
+
Catalog all GraphQL endpoints. For each endpoint, record: operation type (query/mutation/subscription), resolver function, authentication directives, authorization checks.
|
|
58
|
+
|
|
59
|
+
### Schema-First Detection Patterns
|
|
60
|
+
|
|
61
|
+
- `.graphql` and `.gql` schema files
|
|
62
|
+
- `type Query { ... }` and `type Mutation { ... }` blocks in schema files
|
|
63
|
+
- `typeDefs` variable definitions in JS/TS files (template literals with SDL)
|
|
64
|
+
|
|
65
|
+
### Code-First Detection Patterns
|
|
66
|
+
|
|
67
|
+
- `@Resolver()` decorators (NestJS, TypeGraphQL)
|
|
68
|
+
- `@Query()` and `@Mutation()` decorators
|
|
69
|
+
- `resolvers` objects exported from modules
|
|
70
|
+
- Python class-based resolvers (Strawberry `@strawberry.type`, Ariadne `@query_type.field`)
|
|
71
|
+
- Go resolver structs (gqlgen `resolver.go` files)
|
|
72
|
+
|
|
73
|
+
### GraphQL Framework Variants
|
|
74
|
+
|
|
75
|
+
Apply framework-specific patterns based on detected GraphQL library:
|
|
76
|
+
|
|
77
|
+
| Framework | Language | Detection Pattern |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| Apollo Server | Node/TS | `ApolloServer`, `@apollo/server`, Apollo plugins |
|
|
80
|
+
| GraphQL Yoga | Node/TS | `createYoga`, `@graphql-yoga/node` |
|
|
81
|
+
| Mercurius | Node/TS (Fastify) | `mercurius`, `app.graphql` |
|
|
82
|
+
| Strawberry | Python | `strawberry.Schema`, `@strawberry.type` |
|
|
83
|
+
| gqlgen | Go | `gqlgen.yml`, `generated.go`, resolver structs |
|
|
84
|
+
| Ariadne | Python | `ariadne`, `make_executable_schema` |
|
|
85
|
+
| NestJS GraphQL | Node/TS | `@nestjs/graphql`, `@Resolver()`, `@Query()`, `@Mutation()` |
|
|
86
|
+
|
|
87
|
+
### GraphQL Middleware Chain Detection
|
|
88
|
+
|
|
89
|
+
- `graphql-shield` rule trees (`const permissions = shield({ ... })`)
|
|
90
|
+
- `@UseGuards(AuthGuard)` decorators (NestJS)
|
|
91
|
+
- Apollo Server plugins (`ApolloServerPlugin` implementing `requestDidStart`)
|
|
92
|
+
- Yoga plugins (`useAuth`, custom `envelop` plugins)
|
|
93
|
+
- Custom context resolvers setting `context.user` from auth headers
|
|
94
|
+
|
|
95
|
+
### Graceful Exit — No GraphQL Endpoints
|
|
96
|
+
|
|
97
|
+
If no GraphQL schema files, resolver definitions, or GraphQL framework imports are detected, skip Phase 1b and Phase 2b entirely. Output a summary note and zero gap entries for GraphQL.
|
|
98
|
+
|
|
99
|
+
## Phase 1c: gRPC Endpoint Discovery
|
|
100
|
+
|
|
101
|
+
Catalog all gRPC endpoints. For each endpoint, record: service name, RPC method name, request/response message types, streaming type (unary/server/client/bidirectional), interceptor chain.
|
|
102
|
+
|
|
103
|
+
### Proto Service Parsing
|
|
104
|
+
|
|
105
|
+
- `.proto` files with `service` blocks defining `rpc` methods
|
|
106
|
+
- `stream` annotations on request or response types (server streaming, client streaming, bidirectional streaming)
|
|
107
|
+
- `package` declarations for service namespace
|
|
108
|
+
|
|
109
|
+
### Server Interceptor Detection
|
|
110
|
+
|
|
111
|
+
| Language | Interceptor Pattern |
|
|
112
|
+
|---|---|
|
|
113
|
+
| Java | `ServerInterceptor` interface, `@GrpcService` (Spring gRPC), interceptor registry |
|
|
114
|
+
| Go | `grpc.UnaryInterceptor()`, `grpc.StreamInterceptor()`, `grpc.ChainUnaryInterceptor()` |
|
|
115
|
+
| Python | `grpc.server_interceptor`, `intercept_service()` |
|
|
116
|
+
| Node/TS | `addService()` calls, `@GrpcMethod` decorators (NestJS), `grpc-js` server options |
|
|
117
|
+
|
|
118
|
+
### gRPC Middleware Chain Detection
|
|
119
|
+
|
|
120
|
+
- Interceptor chains in server setup (ordered middleware)
|
|
121
|
+
- `@GrpcMethod` decorators with guard annotations (NestJS)
|
|
122
|
+
- Metadata extractors for authentication tokens
|
|
123
|
+
- Health check service registration (`grpc.health.v1`)
|
|
124
|
+
|
|
125
|
+
### Graceful Exit — No gRPC Endpoints
|
|
126
|
+
|
|
127
|
+
If no `.proto` files, gRPC server setup, or gRPC framework imports are detected, skip Phase 1c and Phase 2c entirely. Output a summary note and zero gap entries for gRPC.
|
|
128
|
+
|
|
129
|
+
## Phase 1d: Mixed-Protocol Detection
|
|
130
|
+
|
|
131
|
+
A single codebase may expose REST + GraphQL + gRPC endpoints simultaneously. When multiple protocols are detected:
|
|
132
|
+
|
|
133
|
+
1. Scan all three protocols independently
|
|
134
|
+
2. Identify shared authentication middleware (e.g., a JWT validator used by both Express routes and Apollo context)
|
|
135
|
+
3. Do not double-count shared middleware as separate gaps — if auth middleware covers both REST and GraphQL, count it once
|
|
136
|
+
4. Note in findings when multiple protocols share infrastructure
|
|
137
|
+
|
|
54
138
|
## Phase 2: Security Gap Detection — Application Rules
|
|
55
139
|
|
|
56
140
|
### 1. Missing Authentication Middleware (AC3a)
|
|
@@ -82,6 +166,99 @@ Sensitive data exposure is `high` severity.
|
|
|
82
166
|
|
|
83
167
|
Detect POST/PUT/PATCH/DELETE endpoints that accept a request body but have no input validation. Missing input validation is `high` severity.
|
|
84
168
|
|
|
169
|
+
## Phase 2b: GraphQL Security Gap Detection
|
|
170
|
+
|
|
171
|
+
### 1. Queries/Mutations Missing Auth Directives
|
|
172
|
+
|
|
173
|
+
Detect GraphQL operations missing authentication. Look for:
|
|
174
|
+
- Resolvers without `@auth`, `@authenticated`, `@HasPermission`, or `@UseGuards(AuthGuard)` directives
|
|
175
|
+
- Schema types without `@auth` directive when other types have it (inconsistent protection)
|
|
176
|
+
- Mutation resolvers with no authorization checks are `critical` severity
|
|
177
|
+
- Query resolvers returning non-public data without auth are `high` severity
|
|
178
|
+
|
|
179
|
+
### 2. Introspection Enabled in Production
|
|
180
|
+
|
|
181
|
+
Detect GraphQL introspection configuration that may leak schema in production:
|
|
182
|
+
- `introspection: true` in Apollo Server config without `NODE_ENV` conditional
|
|
183
|
+
- Missing introspection disable in production configuration
|
|
184
|
+
- Yoga/Mercurius default introspection without explicit disable
|
|
185
|
+
|
|
186
|
+
Introspection in production is `medium` severity.
|
|
187
|
+
|
|
188
|
+
### 3. Mutations Without Authorization Checks
|
|
189
|
+
|
|
190
|
+
Detect mutation resolvers that lack authorization logic:
|
|
191
|
+
- Mutation handlers with no permission checks, guard decorators, or authorization middleware
|
|
192
|
+
- `graphql-shield` rules that allow mutations without role checks
|
|
193
|
+
- NestJS mutations without `@UseGuards()` when other mutations have guards
|
|
194
|
+
|
|
195
|
+
Missing mutation authorization is `critical` severity.
|
|
196
|
+
|
|
197
|
+
### 4. Field-Level Authorization Gaps
|
|
198
|
+
|
|
199
|
+
Detect sensitive fields exposed without field-level auth controls:
|
|
200
|
+
- Fields named `email`, `password`, `ssn`, `token`, `secret`, `creditCard` without `@Authorized` or field-level resolvers
|
|
201
|
+
- Resolver types exposing sensitive nested objects without per-field permission checks
|
|
202
|
+
- User types exposing admin-only fields to all authenticated users
|
|
203
|
+
|
|
204
|
+
Field-level authorization gaps are `high` severity.
|
|
205
|
+
|
|
206
|
+
### 5. GraphQL Federation and Schema Stitching
|
|
207
|
+
|
|
208
|
+
When Apollo Federation or schema stitching is detected:
|
|
209
|
+
- Note gateway-level auth that may mask per-service gaps
|
|
210
|
+
- Flag subgraph services that rely solely on gateway auth without their own validation
|
|
211
|
+
- Detect `@external` fields without authorization in the owning subgraph
|
|
212
|
+
|
|
213
|
+
Federation auth gaps are `medium` severity with a note about gateway-level coverage.
|
|
214
|
+
|
|
215
|
+
## Phase 2c: gRPC Security Gap Detection
|
|
216
|
+
|
|
217
|
+
### 1. Services Missing Auth Interceptors
|
|
218
|
+
|
|
219
|
+
Detect gRPC server setup without authentication interceptors:
|
|
220
|
+
- Server initialization without `AuthInterceptor` or equivalent in the interceptor chain
|
|
221
|
+
- Services registered via `addService` with no auth middleware applied
|
|
222
|
+
- Spring gRPC services without `@GrpcService` security configuration
|
|
223
|
+
|
|
224
|
+
Missing auth interceptor is `critical` severity.
|
|
225
|
+
|
|
226
|
+
### 2. Unary RPCs Without Authorization Metadata
|
|
227
|
+
|
|
228
|
+
Detect unary RPC methods that do not validate authorization metadata:
|
|
229
|
+
- `rpc` method handlers that do not extract or validate `metadata` authorization headers
|
|
230
|
+
- Handler functions that skip token/credential validation from gRPC metadata
|
|
231
|
+
- Methods that do not check caller identity or roles from request context
|
|
232
|
+
|
|
233
|
+
Missing unary authorization is `high` severity.
|
|
234
|
+
|
|
235
|
+
### 3. Streaming RPCs Without Per-Message Auth
|
|
236
|
+
|
|
237
|
+
Detect bidirectional and server streaming RPCs without per-message authentication:
|
|
238
|
+
- Stream handlers that authenticate only at connection start but not per-message
|
|
239
|
+
- Bidirectional streams without per-message auth validation
|
|
240
|
+
- Server streaming RPCs that do not re-validate authorization on long-lived connections
|
|
241
|
+
|
|
242
|
+
Missing stream auth is `high` severity.
|
|
243
|
+
|
|
244
|
+
### 4. TLS Configuration Gaps
|
|
245
|
+
|
|
246
|
+
Detect insecure gRPC transport configuration:
|
|
247
|
+
- `grpc.insecure_port` usage in non-development configuration
|
|
248
|
+
- `ServerCredentials.createInsecure()` in production server setup
|
|
249
|
+
- Missing TLS certificate configuration in production gRPC servers
|
|
250
|
+
- Plaintext gRPC channels in production client configuration
|
|
251
|
+
|
|
252
|
+
Insecure TLS is `critical` severity.
|
|
253
|
+
|
|
254
|
+
### 5. gRPC Reflection Enabled in Production
|
|
255
|
+
|
|
256
|
+
Detect gRPC reflection service that may expose service definitions:
|
|
257
|
+
- `grpc.reflection.v1alpha` or `grpc.reflection.v1` service registered without environment guard
|
|
258
|
+
- Reflection service enabled unconditionally (similar risk to GraphQL introspection)
|
|
259
|
+
|
|
260
|
+
Reflection in production is `medium` severity.
|
|
261
|
+
|
|
85
262
|
## Phase 3: False-Positive Mitigation — Inherited Auth
|
|
86
263
|
|
|
87
264
|
Before flagging an endpoint as "missing authentication middleware," trace the middleware chain upward:
|
|
@@ -106,6 +283,18 @@ Before flagging an endpoint as "missing authentication middleware," trace the mi
|
|
|
106
283
|
- `r.Use(JWTAuth())` — app-level
|
|
107
284
|
- `group := r.Group("/api"); group.Use(AuthMiddleware())` — group-level
|
|
108
285
|
|
|
286
|
+
#### GraphQL Inherited Auth
|
|
287
|
+
- Apollo Server `context` function that validates JWT and sets `context.user` — app-level
|
|
288
|
+
- `graphql-shield` rule tree applied via `applyMiddleware` — schema-level
|
|
289
|
+
- NestJS `@UseGuards(AuthGuard)` on resolver class — class-level
|
|
290
|
+
- Apollo Federation gateway-level auth that validates before routing to subgraphs — gateway-level
|
|
291
|
+
|
|
292
|
+
#### gRPC Inherited Auth
|
|
293
|
+
- Global auth interceptor registered via `grpc.UnaryInterceptor(authInterceptor)` — server-level
|
|
294
|
+
- `ServerInterceptor` added to server builder interceptor chain — server-level
|
|
295
|
+
- Per-service interceptor applied at `addService` — service-level
|
|
296
|
+
- TLS mutual authentication (mTLS) at transport level — transport-level
|
|
297
|
+
|
|
109
298
|
## Phase 4: Infrastructure Security Patterns (E12-S6)
|
|
110
299
|
|
|
111
300
|
**Apply ONLY when {project_type} is `infrastructure` or `platform`.**
|
|
@@ -180,11 +369,50 @@ gap:
|
|
|
180
369
|
evidence:
|
|
181
370
|
file: "relative/path/to/file"
|
|
182
371
|
line: 42
|
|
372
|
+
protocol: "rest"
|
|
183
373
|
recommendation: "Actionable fix — add middleware, validate input, filter response"
|
|
184
374
|
verified_by: "machine-detected"
|
|
185
375
|
confidence: "{high|medium|low}"
|
|
186
376
|
```
|
|
187
377
|
|
|
378
|
+
**Protocol field:** Every gap entry MUST include a `protocol` field in the evidence section indicating which API protocol the finding applies to: `rest`, `graphql`, or `grpc`. This enables downstream consumers to filter and route findings by protocol.
|
|
379
|
+
|
|
380
|
+
#### GraphQL Gap Example
|
|
381
|
+
|
|
382
|
+
```yaml
|
|
383
|
+
gap:
|
|
384
|
+
id: "GAP-SECURITY-015"
|
|
385
|
+
category: "security-endpoint"
|
|
386
|
+
severity: "critical"
|
|
387
|
+
title: "Mutation resolver missing authorization checks"
|
|
388
|
+
description: "createUser mutation has no @auth directive or guard. Any authenticated user can create accounts."
|
|
389
|
+
evidence:
|
|
390
|
+
file: "src/graphql/resolvers/user.resolver.ts"
|
|
391
|
+
line: 42
|
|
392
|
+
protocol: "graphql"
|
|
393
|
+
recommendation: "Add @UseGuards(AuthGuard, RolesGuard) or @auth directive to the createUser mutation."
|
|
394
|
+
verified_by: "machine-detected"
|
|
395
|
+
confidence: "high"
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
#### gRPC Gap Example
|
|
399
|
+
|
|
400
|
+
```yaml
|
|
401
|
+
gap:
|
|
402
|
+
id: "GAP-SECURITY-022"
|
|
403
|
+
category: "security-endpoint"
|
|
404
|
+
severity: "critical"
|
|
405
|
+
title: "gRPC server missing auth interceptor in production config"
|
|
406
|
+
description: "Server setup uses grpc.insecure_port without TLS. No auth interceptor in interceptor chain."
|
|
407
|
+
evidence:
|
|
408
|
+
file: "cmd/server/main.go"
|
|
409
|
+
line: 58
|
|
410
|
+
protocol: "grpc"
|
|
411
|
+
recommendation: "Add TLS credentials and an auth interceptor to the gRPC server configuration."
|
|
412
|
+
verified_by: "machine-detected"
|
|
413
|
+
confidence: "high"
|
|
414
|
+
```
|
|
415
|
+
|
|
188
416
|
### Confidence Classification
|
|
189
417
|
|
|
190
418
|
- **high** — exact pattern match (e.g., no auth decorator/annotation on a `@PostMapping` handler)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Gap Entry Schema
|
|
2
2
|
|
|
3
|
-
> **Version:** 1.
|
|
4
|
-
> **Story:** E11-S1, E12-S5
|
|
3
|
+
> **Version:** 1.2.0
|
|
4
|
+
> **Story:** E11-S1, E12-S5, E11-S18
|
|
5
5
|
> **Traces to:** FR-111, FR-123, US-38, ADR-021, ADR-022
|
|
6
6
|
>
|
|
7
7
|
> Standardized output schema for brownfield scan subagents (E11).
|
|
@@ -22,6 +22,7 @@ description: "<string>"
|
|
|
22
22
|
evidence:
|
|
23
23
|
file: "<relative-path>"
|
|
24
24
|
line: <number-or-range>
|
|
25
|
+
protocol: "<string>" # Optional
|
|
25
26
|
recommendation: "<string>"
|
|
26
27
|
verified_by: "<agent-id>"
|
|
27
28
|
confidence: "<enum>"
|
|
@@ -36,7 +37,7 @@ confidence: "<enum>"
|
|
|
36
37
|
| `severity` | enum | yes | Impact level — must be one of the 5 allowed values (see Severity Enum) |
|
|
37
38
|
| `title` | string | yes | Short summary of the gap (max 80 characters) |
|
|
38
39
|
| `description` | string | yes | Detailed explanation of the gap, what it means, and why it matters |
|
|
39
|
-
| `evidence` | object | yes | Source code evidence (see Evidence Object) |
|
|
40
|
+
| `evidence` | object | yes | Source code evidence with required `file` and `line` sub-fields, plus optional `protocol` sub-field (see Evidence Object) |
|
|
40
41
|
| `recommendation` | string | yes | Actionable fix or remediation guidance |
|
|
41
42
|
| `verified_by` | string | yes | ID of the scan agent that produced this finding (e.g., `dead-code-analyzer`, `config-scanner`) |
|
|
42
43
|
| `confidence` | enum | yes | Agent's confidence in the finding accuracy (see Confidence Enum) |
|
|
@@ -95,6 +96,7 @@ The `evidence` field is a composite object grouping source location data:
|
|
|
95
96
|
evidence:
|
|
96
97
|
file: "src/services/auth.ts" # Relative path from project root (non-empty string)
|
|
97
98
|
line: 42 # Single line number
|
|
99
|
+
protocol: "rest" # Optional. Protocol type
|
|
98
100
|
```
|
|
99
101
|
|
|
100
102
|
Or with a line range:
|
|
@@ -105,10 +107,19 @@ evidence:
|
|
|
105
107
|
line: "15-28" # Line range (start-end)
|
|
106
108
|
```
|
|
107
109
|
|
|
110
|
+
Or without the optional protocol field (backward compatible):
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
evidence:
|
|
114
|
+
file: "src/utils/helper.ts"
|
|
115
|
+
line: 10
|
|
116
|
+
```
|
|
117
|
+
|
|
108
118
|
| Sub-field | Type | Required | Constraints |
|
|
109
119
|
|-----------|------|----------|-------------|
|
|
110
120
|
| `file` | string | yes | Relative path from project root. Must be non-empty. |
|
|
111
121
|
| `line` | number or string | yes | Single line number (integer) or range as `"start-end"` string |
|
|
122
|
+
| `protocol` | string | no | Optional. One of `rest`, `graphql`, `grpc`, `websocket`, or any custom string. Omit if not applicable. When present, must be a non-empty string. |
|
|
112
123
|
|
|
113
124
|
## ID Format
|
|
114
125
|
|
|
@@ -138,10 +149,17 @@ All fields listed in the Field Reference are **required** — a gap entry with a
|
|
|
138
149
|
- `evidence.line` must be a positive integer or a range string matching `^\d+-\d+$`
|
|
139
150
|
- `title` should not exceed 80 characters
|
|
140
151
|
- `verified_by` must be a non-empty string identifying the scan agent
|
|
152
|
+
- `evidence.protocol` when present, must be a non-empty string
|
|
141
153
|
|
|
142
154
|
### Required vs Optional
|
|
143
155
|
|
|
144
|
-
All 9 fields (`id`, `category`, `severity`, `title`, `description`, `evidence`, `recommendation`, `verified_by`, `confidence`) are **required**. There are no optional fields in the base schema.
|
|
156
|
+
All 9 top-level fields (`id`, `category`, `severity`, `title`, `description`, `evidence`, `recommendation`, `verified_by`, `confidence`) are **required**. There are no optional top-level fields in the base schema.
|
|
157
|
+
|
|
158
|
+
The `evidence` object contains one optional sub-field: `protocol`. This is the first optional sub-field in the schema. Existing gap entries that omit `protocol` remain fully valid.
|
|
159
|
+
|
|
160
|
+
### Optional Field Validation
|
|
161
|
+
|
|
162
|
+
The `protocol` sub-field of the `evidence` object is not enum-validated. It accepts any non-empty string when present. Recommended canonical values are `rest`, `graphql`, `grpc`, and `websocket`, but custom strings (e.g., `mqtt`, `soap`, `amqp`) are also accepted without schema changes. When `protocol` is omitted entirely, the gap entry remains valid (backward compatible). When present, an empty string is invalid.
|
|
145
163
|
|
|
146
164
|
## Budget Control
|
|
147
165
|
|
|
@@ -174,6 +192,23 @@ verified_by: "config-scanner"
|
|
|
174
192
|
confidence: "high"
|
|
175
193
|
```
|
|
176
194
|
|
|
195
|
+
### Application Category Example with Protocol
|
|
196
|
+
|
|
197
|
+
```yaml
|
|
198
|
+
id: "GAP-security-endpoint-001"
|
|
199
|
+
category: "security-endpoint"
|
|
200
|
+
severity: "high"
|
|
201
|
+
title: "Unprotected admin route exposes user management API"
|
|
202
|
+
description: "The /api/admin/users endpoint has no authentication middleware applied."
|
|
203
|
+
evidence:
|
|
204
|
+
file: "src/routes/admin.ts"
|
|
205
|
+
line: 15
|
|
206
|
+
protocol: "rest"
|
|
207
|
+
recommendation: "Add authentication middleware to all /api/admin/* routes."
|
|
208
|
+
verified_by: "security-scanner"
|
|
209
|
+
confidence: "high"
|
|
210
|
+
```
|
|
211
|
+
|
|
177
212
|
### Infrastructure Category Examples
|
|
178
213
|
|
|
179
214
|
```yaml
|