flowlint 0.3.7 → 0.3.8
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/README.md +390 -0
- package/dist/commands/scan.js +53 -8
- package/dist/commands/scan.js.map +1 -1
- package/dist/reporters/github-actions-reporter.d.ts +30 -0
- package/dist/reporters/github-actions-reporter.js +104 -0
- package/dist/reporters/github-actions-reporter.js.map +1 -0
- package/dist/reporters/junit-reporter.d.ts +25 -0
- package/dist/reporters/junit-reporter.js +142 -0
- package/dist/reporters/junit-reporter.js.map +1 -0
- package/dist/reporters/sarif-reporter.d.ts +21 -0
- package/dist/reporters/sarif-reporter.js +125 -0
- package/dist/reporters/sarif-reporter.js.map +1 -0
- package/package.json +2 -1
package/README.md
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# FlowLint CLI
|
|
2
|
+
|
|
3
|
+
Static analysis tool for n8n workflows - detect issues early, fix them faster.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g flowlint
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or use with npx (no installation required):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx flowlint scan ./workflows
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
Scan workflows in the current directory:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
flowlint scan .
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Scan a specific directory:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
flowlint scan ./workflows
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Scan with a custom config file:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
flowlint scan . --config .flowlint.yml
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Output Formats
|
|
38
|
+
|
|
39
|
+
FlowLint supports multiple output formats for different use cases:
|
|
40
|
+
|
|
41
|
+
### Stylish (Default)
|
|
42
|
+
|
|
43
|
+
Human-readable console output with colors and formatting:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
flowlint scan .
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### JSON
|
|
50
|
+
|
|
51
|
+
Machine-readable JSON output:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
flowlint scan . --format json
|
|
55
|
+
flowlint scan . --format json --out-file report.json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### SARIF
|
|
59
|
+
|
|
60
|
+
SARIF 2.1.0 format for GitHub Code Scanning and other security platforms:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
flowlint scan . --format sarif --out-file results.sarif
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### JUnit XML
|
|
67
|
+
|
|
68
|
+
JUnit XML format for CI/CD platforms (Jenkins, GitLab CI, CircleCI, etc.):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
flowlint scan . --format junit --out-file results.xml
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### GitHub Actions
|
|
75
|
+
|
|
76
|
+
GitHub Actions workflow commands for inline annotations in workflow logs:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
flowlint scan . --format github-actions
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Command Reference
|
|
83
|
+
|
|
84
|
+
### `scan`
|
|
85
|
+
|
|
86
|
+
Scan workflow files for issues.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
flowlint scan [path] [options]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Arguments:**
|
|
93
|
+
- `path` - Directory to scan (default: current directory)
|
|
94
|
+
|
|
95
|
+
**Options:**
|
|
96
|
+
- `--config <path>` - Path to `.flowlint.yml` config file
|
|
97
|
+
- `--format <format>` - Output format: `stylish`, `json`, `sarif`, `junit`, `github-actions` (default: `stylish`)
|
|
98
|
+
- `--out-file <path>` - Write results to file (format inferred from extension or `--format`)
|
|
99
|
+
- `--fail-on-error` - Exit with code 1 if errors found
|
|
100
|
+
|
|
101
|
+
**Examples:**
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Scan current directory with default config
|
|
105
|
+
flowlint scan
|
|
106
|
+
|
|
107
|
+
# Scan specific directory
|
|
108
|
+
flowlint scan ./my-workflows
|
|
109
|
+
|
|
110
|
+
# Use custom config
|
|
111
|
+
flowlint scan --config custom-config.yml
|
|
112
|
+
|
|
113
|
+
# Output JSON to file
|
|
114
|
+
flowlint scan --format json --out-file report.json
|
|
115
|
+
|
|
116
|
+
# Generate SARIF for GitHub Code Scanning
|
|
117
|
+
flowlint scan --format sarif --out-file results.sarif
|
|
118
|
+
|
|
119
|
+
# Generate JUnit XML for Jenkins
|
|
120
|
+
flowlint scan --format junit --out-file test-results.xml
|
|
121
|
+
|
|
122
|
+
# Fail CI build if errors found
|
|
123
|
+
flowlint scan --fail-on-error
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## CI/CD Integration
|
|
127
|
+
|
|
128
|
+
### GitHub Actions
|
|
129
|
+
|
|
130
|
+
#### Option 1: SARIF Upload (Recommended)
|
|
131
|
+
|
|
132
|
+
Upload results to GitHub Code Scanning for permanent PR annotations:
|
|
133
|
+
|
|
134
|
+
```yaml
|
|
135
|
+
name: FlowLint
|
|
136
|
+
|
|
137
|
+
on:
|
|
138
|
+
pull_request:
|
|
139
|
+
paths:
|
|
140
|
+
- '**.json'
|
|
141
|
+
|
|
142
|
+
jobs:
|
|
143
|
+
flowlint:
|
|
144
|
+
runs-on: ubuntu-latest
|
|
145
|
+
permissions:
|
|
146
|
+
contents: read
|
|
147
|
+
security-events: write # Required for SARIF upload
|
|
148
|
+
steps:
|
|
149
|
+
- uses: actions/checkout@v4
|
|
150
|
+
|
|
151
|
+
- name: Run FlowLint
|
|
152
|
+
run: npx flowlint scan --format sarif --out-file results.sarif
|
|
153
|
+
continue-on-error: true # Don't fail workflow on findings
|
|
154
|
+
|
|
155
|
+
- name: Upload SARIF results
|
|
156
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
157
|
+
if: always()
|
|
158
|
+
with:
|
|
159
|
+
sarif_file: results.sarif
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Option 2: Workflow Annotations
|
|
163
|
+
|
|
164
|
+
Show findings directly in workflow logs:
|
|
165
|
+
|
|
166
|
+
```yaml
|
|
167
|
+
name: FlowLint
|
|
168
|
+
|
|
169
|
+
on:
|
|
170
|
+
pull_request:
|
|
171
|
+
paths:
|
|
172
|
+
- '**.json'
|
|
173
|
+
|
|
174
|
+
jobs:
|
|
175
|
+
flowlint:
|
|
176
|
+
runs-on: ubuntu-latest
|
|
177
|
+
steps:
|
|
178
|
+
- uses: actions/checkout@v4
|
|
179
|
+
|
|
180
|
+
- name: Run FlowLint
|
|
181
|
+
run: npx flowlint scan --format github-actions --fail-on-error
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### Option 3: Both (Best of Both Worlds)
|
|
185
|
+
|
|
186
|
+
Combine both approaches for immediate feedback and permanent annotations:
|
|
187
|
+
|
|
188
|
+
```yaml
|
|
189
|
+
name: FlowLint
|
|
190
|
+
|
|
191
|
+
on:
|
|
192
|
+
pull_request:
|
|
193
|
+
paths:
|
|
194
|
+
- '**.json'
|
|
195
|
+
|
|
196
|
+
jobs:
|
|
197
|
+
flowlint:
|
|
198
|
+
runs-on: ubuntu-latest
|
|
199
|
+
permissions:
|
|
200
|
+
contents: read
|
|
201
|
+
security-events: write
|
|
202
|
+
steps:
|
|
203
|
+
- uses: actions/checkout@v4
|
|
204
|
+
|
|
205
|
+
- name: Run FlowLint (Workflow Annotations)
|
|
206
|
+
run: npx flowlint scan --format github-actions
|
|
207
|
+
continue-on-error: true
|
|
208
|
+
|
|
209
|
+
- name: Run FlowLint (SARIF)
|
|
210
|
+
run: npx flowlint scan --format sarif --out-file results.sarif
|
|
211
|
+
continue-on-error: true
|
|
212
|
+
|
|
213
|
+
- name: Upload SARIF results
|
|
214
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
215
|
+
if: always()
|
|
216
|
+
with:
|
|
217
|
+
sarif_file: results.sarif
|
|
218
|
+
|
|
219
|
+
- name: Check for blocking issues
|
|
220
|
+
run: npx flowlint scan --fail-on-error
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### GitLab CI
|
|
224
|
+
|
|
225
|
+
```yaml
|
|
226
|
+
flowlint:
|
|
227
|
+
stage: test
|
|
228
|
+
image: node:22
|
|
229
|
+
script:
|
|
230
|
+
- npx flowlint scan --format junit --out-file flowlint-results.xml
|
|
231
|
+
artifacts:
|
|
232
|
+
when: always
|
|
233
|
+
reports:
|
|
234
|
+
junit: flowlint-results.xml
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Jenkins
|
|
238
|
+
|
|
239
|
+
```groovy
|
|
240
|
+
pipeline {
|
|
241
|
+
agent any
|
|
242
|
+
|
|
243
|
+
stages {
|
|
244
|
+
stage('FlowLint') {
|
|
245
|
+
steps {
|
|
246
|
+
sh 'npx flowlint scan --format junit --out-file flowlint-results.xml'
|
|
247
|
+
}
|
|
248
|
+
post {
|
|
249
|
+
always {
|
|
250
|
+
junit 'flowlint-results.xml'
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### CircleCI
|
|
259
|
+
|
|
260
|
+
```yaml
|
|
261
|
+
version: 2.1
|
|
262
|
+
|
|
263
|
+
jobs:
|
|
264
|
+
flowlint:
|
|
265
|
+
docker:
|
|
266
|
+
- image: cimg/node:22.0
|
|
267
|
+
steps:
|
|
268
|
+
- checkout
|
|
269
|
+
- run:
|
|
270
|
+
name: Run FlowLint
|
|
271
|
+
command: npx flowlint scan --format junit --out-file test-results/flowlint.xml
|
|
272
|
+
- store_test_results:
|
|
273
|
+
path: test-results
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Azure Pipelines
|
|
277
|
+
|
|
278
|
+
```yaml
|
|
279
|
+
trigger:
|
|
280
|
+
branches:
|
|
281
|
+
include:
|
|
282
|
+
- main
|
|
283
|
+
- develop
|
|
284
|
+
|
|
285
|
+
pool:
|
|
286
|
+
vmImage: 'ubuntu-latest'
|
|
287
|
+
|
|
288
|
+
steps:
|
|
289
|
+
- task: NodeTool@0
|
|
290
|
+
inputs:
|
|
291
|
+
versionSpec: '22.x'
|
|
292
|
+
|
|
293
|
+
- script: npx flowlint scan --format junit --out-file flowlint-results.xml
|
|
294
|
+
displayName: 'Run FlowLint'
|
|
295
|
+
|
|
296
|
+
- task: PublishTestResults@2
|
|
297
|
+
condition: always()
|
|
298
|
+
inputs:
|
|
299
|
+
testResultsFormat: 'JUnit'
|
|
300
|
+
testResultsFiles: 'flowlint-results.xml'
|
|
301
|
+
failTaskOnFailedTests: true
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Configuration
|
|
305
|
+
|
|
306
|
+
Create a `.flowlint.yml` file in your repository root:
|
|
307
|
+
|
|
308
|
+
```yaml
|
|
309
|
+
files:
|
|
310
|
+
include:
|
|
311
|
+
- '**/*.json'
|
|
312
|
+
ignore:
|
|
313
|
+
- 'node_modules/**'
|
|
314
|
+
- 'dist/**'
|
|
315
|
+
|
|
316
|
+
report:
|
|
317
|
+
annotations: true
|
|
318
|
+
summary_limit: 100
|
|
319
|
+
|
|
320
|
+
rules:
|
|
321
|
+
R1:
|
|
322
|
+
enabled: true
|
|
323
|
+
R2:
|
|
324
|
+
enabled: true
|
|
325
|
+
# ... etc
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
See [FlowLint documentation](https://flowlint.dev) for complete configuration reference.
|
|
329
|
+
|
|
330
|
+
## Exit Codes
|
|
331
|
+
|
|
332
|
+
- `0` - Success (no errors or only warnings/suggestions)
|
|
333
|
+
- `1` - Analysis found blocking issues (when using `--fail-on-error`)
|
|
334
|
+
- `2` - Runtime error (invalid config, file not found, etc.)
|
|
335
|
+
|
|
336
|
+
## Rule Severity Levels
|
|
337
|
+
|
|
338
|
+
- **must** - Blocks PR (errors) - Critical issues that must be fixed
|
|
339
|
+
- **should** - Warnings - Important issues that should be addressed
|
|
340
|
+
- **nit** - Suggestions - Minor improvements
|
|
341
|
+
|
|
342
|
+
## Supported Rules
|
|
343
|
+
|
|
344
|
+
FlowLint currently implements the following rules:
|
|
345
|
+
|
|
346
|
+
- **R1** - Rate Limit & Retry
|
|
347
|
+
- **R2** - Error Handling
|
|
348
|
+
- **R3** - Dead End Detection
|
|
349
|
+
- **R4** - Credential Validation
|
|
350
|
+
- **R5** - Idempotency
|
|
351
|
+
- **R6** - Stop & Error Nodes
|
|
352
|
+
|
|
353
|
+
See [RULES.md](https://github.com/Replikanti/flowlint-app/blob/main/RULES.md) for detailed rule documentation.
|
|
354
|
+
|
|
355
|
+
## Troubleshooting
|
|
356
|
+
|
|
357
|
+
### No workflows found
|
|
358
|
+
|
|
359
|
+
Make sure your workflow files match the glob patterns in `.flowlint.yml`:
|
|
360
|
+
|
|
361
|
+
```yaml
|
|
362
|
+
files:
|
|
363
|
+
include:
|
|
364
|
+
- '**/*.json' # Adjust pattern as needed
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### SARIF upload fails in GitHub Actions
|
|
368
|
+
|
|
369
|
+
Ensure you have the correct permissions:
|
|
370
|
+
|
|
371
|
+
```yaml
|
|
372
|
+
permissions:
|
|
373
|
+
contents: read
|
|
374
|
+
security-events: write # Required for SARIF upload
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### JUnit results not showing in CI
|
|
378
|
+
|
|
379
|
+
Make sure the file path in your CI configuration matches the `--out-file` path.
|
|
380
|
+
|
|
381
|
+
## Links
|
|
382
|
+
|
|
383
|
+
- [Documentation](https://flowlint.dev)
|
|
384
|
+
- [GitHub Repository](https://github.com/Replikanti/flowlint-app)
|
|
385
|
+
- [Issue Tracker](https://github.com/Replikanti/flowlint-app/issues)
|
|
386
|
+
- [npm Package](https://www.npmjs.com/package/flowlint)
|
|
387
|
+
|
|
388
|
+
## License
|
|
389
|
+
|
|
390
|
+
MIT
|
package/dist/commands/scan.js
CHANGED
|
@@ -19,29 +19,54 @@ const local_file_source_1 = require("../providers/local-file-source");
|
|
|
19
19
|
const local_config_provider_1 = require("../providers/local-config-provider");
|
|
20
20
|
const console_reporter_1 = require("../reporters/console-reporter");
|
|
21
21
|
const json_reporter_1 = require("../reporters/json-reporter");
|
|
22
|
+
const sarif_reporter_1 = require("../reporters/sarif-reporter");
|
|
23
|
+
const junit_reporter_1 = require("../reporters/junit-reporter");
|
|
24
|
+
const github_actions_reporter_1 = require("../reporters/github-actions-reporter");
|
|
22
25
|
const review_1 = require("../packages/review");
|
|
23
26
|
exports.scanCommand = new commander_1.Command('scan')
|
|
24
27
|
.description('Scan workflow files for issues')
|
|
25
28
|
.argument('[path]', 'Directory to scan', '.')
|
|
26
29
|
.option('--config <path>', 'Path to .flowlint.yml config file')
|
|
27
|
-
.option('--format <format>', 'Output format: stylish|json', 'stylish')
|
|
28
|
-
.option('--out-file <path>', 'Write
|
|
30
|
+
.option('--format <format>', 'Output format: stylish|json|sarif|junit|github-actions', 'stylish')
|
|
31
|
+
.option('--out-file <path>', 'Write results to file (format inferred from extension or --format)')
|
|
29
32
|
.option('--fail-on-error', 'Exit with code 1 if errors found')
|
|
30
33
|
.action(async (scanPath, options) => {
|
|
31
34
|
try {
|
|
32
35
|
const absolutePath = path_1.default.resolve(process.cwd(), scanPath);
|
|
33
36
|
const configPath = options.config ? path_1.default.resolve(process.cwd(), options.config) : undefined;
|
|
34
|
-
|
|
37
|
+
// Determine format from options or file extension
|
|
38
|
+
let format = options.format;
|
|
39
|
+
if (options.outFile && !options.format) {
|
|
40
|
+
format = inferFormatFromFilename(options.outFile);
|
|
41
|
+
}
|
|
42
|
+
// Validate format
|
|
43
|
+
const validFormats = ['stylish', 'json', 'sarif', 'junit', 'github-actions'];
|
|
44
|
+
if (!validFormats.includes(format)) {
|
|
45
|
+
console.error(`Invalid format: ${format}. Valid formats: ${validFormats.join(', ')}`);
|
|
46
|
+
process.exit(2);
|
|
47
|
+
}
|
|
35
48
|
// Initialize providers
|
|
36
49
|
const fileSource = new local_file_source_1.LocalFileSource(absolutePath);
|
|
37
50
|
const configProvider = new local_config_provider_1.LocalConfigProvider(configPath);
|
|
38
51
|
// Choose reporter based on format
|
|
39
52
|
let reporter;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
switch (format) {
|
|
54
|
+
case 'json':
|
|
55
|
+
reporter = new json_reporter_1.JsonReporter(options.outFile);
|
|
56
|
+
break;
|
|
57
|
+
case 'sarif':
|
|
58
|
+
reporter = new sarif_reporter_1.SarifReporter(options.outFile);
|
|
59
|
+
break;
|
|
60
|
+
case 'junit':
|
|
61
|
+
reporter = new junit_reporter_1.JunitReporter(options.outFile);
|
|
62
|
+
break;
|
|
63
|
+
case 'github-actions':
|
|
64
|
+
reporter = new github_actions_reporter_1.GithubActionsReporter();
|
|
65
|
+
break;
|
|
66
|
+
case 'stylish':
|
|
67
|
+
default:
|
|
68
|
+
reporter = new console_reporter_1.ConsoleReporter();
|
|
69
|
+
break;
|
|
45
70
|
}
|
|
46
71
|
// Run analysis
|
|
47
72
|
const engine = new review_1.DefaultAnalysisEngine(fileSource, configProvider, reporter);
|
|
@@ -56,4 +81,24 @@ exports.scanCommand = new commander_1.Command('scan')
|
|
|
56
81
|
process.exit(2);
|
|
57
82
|
}
|
|
58
83
|
});
|
|
84
|
+
/**
|
|
85
|
+
* Infer output format from filename extension
|
|
86
|
+
*/
|
|
87
|
+
function inferFormatFromFilename(filename) {
|
|
88
|
+
const ext = path_1.default.extname(filename).toLowerCase();
|
|
89
|
+
switch (ext) {
|
|
90
|
+
case '.json':
|
|
91
|
+
// Check if it's SARIF based on filename
|
|
92
|
+
if (filename.includes('sarif')) {
|
|
93
|
+
return 'sarif';
|
|
94
|
+
}
|
|
95
|
+
return 'json';
|
|
96
|
+
case '.sarif':
|
|
97
|
+
return 'sarif';
|
|
98
|
+
case '.xml':
|
|
99
|
+
return 'junit';
|
|
100
|
+
default:
|
|
101
|
+
return 'json'; // Default to JSON
|
|
102
|
+
}
|
|
103
|
+
}
|
|
59
104
|
//# sourceMappingURL=scan.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../../../../apps/cli/src/commands/scan.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;;;;AAEH,yCAAoC;AACpC,gDAAwB;AACxB,sEAAiE;AACjE,8EAAyE;AACzE,oEAAgE;AAChE,8DAA0D;AAC1D,4CAAwD;AAE3C,QAAA,WAAW,GAAG,IAAI,mBAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CAAC,gCAAgC,CAAC;KAC7C,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,EAAE,GAAG,CAAC;KAC5C,MAAM,CAAC,iBAAiB,EAAE,mCAAmC,CAAC;KAC9D,MAAM,
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../../../../apps/cli/src/commands/scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Actions Reporter
|
|
3
|
+
* Outputs findings using GitHub Actions workflow commands
|
|
4
|
+
* Suitable for:
|
|
5
|
+
* - GitHub Actions workflows
|
|
6
|
+
* - Immediate inline annotations in workflow logs
|
|
7
|
+
* - Complementary to SARIF (SARIF for Code Scanning, this for workflow logs)
|
|
8
|
+
*
|
|
9
|
+
* Format: ::error file={file},line={line}::{message}
|
|
10
|
+
* Docs: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions
|
|
11
|
+
*/
|
|
12
|
+
import type { AnalysisResult, Reporter } from 'packages/review/providers';
|
|
13
|
+
export declare class GithubActionsReporter implements Reporter {
|
|
14
|
+
report(results: AnalysisResult[]): Promise<void>;
|
|
15
|
+
private outputWorkflowCommand;
|
|
16
|
+
private severityToCommand;
|
|
17
|
+
private buildProperties;
|
|
18
|
+
private buildMessage;
|
|
19
|
+
private outputSummary;
|
|
20
|
+
/**
|
|
21
|
+
* Escape property values for GitHub Actions workflow commands
|
|
22
|
+
* Properties use comma as delimiter, so commas must be escaped
|
|
23
|
+
*/
|
|
24
|
+
private escapeProperty;
|
|
25
|
+
/**
|
|
26
|
+
* Escape data values for GitHub Actions workflow commands
|
|
27
|
+
* Data values support newlines but special characters must be escaped
|
|
28
|
+
*/
|
|
29
|
+
private escapeData;
|
|
30
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GitHub Actions Reporter
|
|
4
|
+
* Outputs findings using GitHub Actions workflow commands
|
|
5
|
+
* Suitable for:
|
|
6
|
+
* - GitHub Actions workflows
|
|
7
|
+
* - Immediate inline annotations in workflow logs
|
|
8
|
+
* - Complementary to SARIF (SARIF for Code Scanning, this for workflow logs)
|
|
9
|
+
*
|
|
10
|
+
* Format: ::error file={file},line={line}::{message}
|
|
11
|
+
* Docs: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.GithubActionsReporter = void 0;
|
|
15
|
+
const findings_1 = require("../packages/review/utils/findings");
|
|
16
|
+
class GithubActionsReporter {
|
|
17
|
+
async report(results) {
|
|
18
|
+
const allFindings = results.flatMap((r) => r.findings);
|
|
19
|
+
// Output workflow commands for each finding
|
|
20
|
+
for (const result of results) {
|
|
21
|
+
for (const finding of result.findings) {
|
|
22
|
+
this.outputWorkflowCommand(finding);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Output summary
|
|
26
|
+
this.outputSummary(allFindings);
|
|
27
|
+
}
|
|
28
|
+
outputWorkflowCommand(finding) {
|
|
29
|
+
const command = this.severityToCommand(finding.severity);
|
|
30
|
+
const properties = this.buildProperties(finding);
|
|
31
|
+
const message = this.buildMessage(finding);
|
|
32
|
+
console.log(`::${command} ${properties}::${message}`);
|
|
33
|
+
}
|
|
34
|
+
severityToCommand(severity) {
|
|
35
|
+
switch (severity) {
|
|
36
|
+
case 'must':
|
|
37
|
+
return 'error';
|
|
38
|
+
case 'should':
|
|
39
|
+
return 'warning';
|
|
40
|
+
case 'nit':
|
|
41
|
+
return 'notice';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
buildProperties(finding) {
|
|
45
|
+
const props = [];
|
|
46
|
+
props.push(`file=${this.escapeProperty(finding.path)}`);
|
|
47
|
+
if (finding.line) {
|
|
48
|
+
props.push(`line=${finding.line}`);
|
|
49
|
+
}
|
|
50
|
+
props.push(`title=${this.escapeProperty(finding.rule)}`);
|
|
51
|
+
return props.join(',');
|
|
52
|
+
}
|
|
53
|
+
buildMessage(finding) {
|
|
54
|
+
let message = finding.message;
|
|
55
|
+
if (finding.nodeId) {
|
|
56
|
+
message += ` (Node: ${finding.nodeId})`;
|
|
57
|
+
}
|
|
58
|
+
if (finding.raw_details) {
|
|
59
|
+
// GitHub Actions workflow commands support newlines in messages
|
|
60
|
+
message += `\n\n${finding.raw_details}`;
|
|
61
|
+
}
|
|
62
|
+
if (finding.documentationUrl) {
|
|
63
|
+
message += `\n\nSee: ${finding.documentationUrl}`;
|
|
64
|
+
}
|
|
65
|
+
return this.escapeData(message);
|
|
66
|
+
}
|
|
67
|
+
outputSummary(findings) {
|
|
68
|
+
const summary = (0, findings_1.countFindingsBySeverity)(findings);
|
|
69
|
+
console.log();
|
|
70
|
+
console.log('::group::FlowLint Summary');
|
|
71
|
+
if (summary.total === 0) {
|
|
72
|
+
console.log('✓ No issues found');
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
console.log(`Total findings: ${summary.total}`);
|
|
76
|
+
if (summary.must > 0) {
|
|
77
|
+
console.log(` - Errors (must-fix): ${summary.must}`);
|
|
78
|
+
}
|
|
79
|
+
if (summary.should > 0) {
|
|
80
|
+
console.log(` - Warnings (should-fix): ${summary.should}`);
|
|
81
|
+
}
|
|
82
|
+
if (summary.nit > 0) {
|
|
83
|
+
console.log(` - Notices (nit): ${summary.nit}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
console.log('::endgroup::');
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Escape property values for GitHub Actions workflow commands
|
|
90
|
+
* Properties use comma as delimiter, so commas must be escaped
|
|
91
|
+
*/
|
|
92
|
+
escapeProperty(value) {
|
|
93
|
+
return value.replace(/%/g, '%25').replace(/,/g, '%2C').replace(/:/g, '%3A');
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Escape data values for GitHub Actions workflow commands
|
|
97
|
+
* Data values support newlines but special characters must be escaped
|
|
98
|
+
*/
|
|
99
|
+
escapeData(value) {
|
|
100
|
+
return value.replace(/%/g, '%25').replace(/\r/g, '%0D').replace(/\n/g, '%0A');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
exports.GithubActionsReporter = GithubActionsReporter;
|
|
104
|
+
//# sourceMappingURL=github-actions-reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-actions-reporter.js","sourceRoot":"","sources":["../../../../../apps/cli/src/reporters/github-actions-reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JUnit Reporter
|
|
3
|
+
* Outputs findings in JUnit XML format
|
|
4
|
+
* Suitable for:
|
|
5
|
+
* - Jenkins
|
|
6
|
+
* - GitLab CI
|
|
7
|
+
* - CircleCI
|
|
8
|
+
* - Azure Pipelines
|
|
9
|
+
* - Other CI/CD platforms that support JUnit test results
|
|
10
|
+
*
|
|
11
|
+
* Each workflow file becomes a test suite
|
|
12
|
+
* Each finding becomes a test failure
|
|
13
|
+
*/
|
|
14
|
+
import type { AnalysisResult, Reporter } from 'packages/review/providers';
|
|
15
|
+
export declare class JunitReporter implements Reporter {
|
|
16
|
+
private outputFile?;
|
|
17
|
+
constructor(outputFile?: string | undefined);
|
|
18
|
+
report(results: AnalysisResult[]): Promise<void>;
|
|
19
|
+
private buildJunitXml;
|
|
20
|
+
private addTestSuite;
|
|
21
|
+
private addTestCase;
|
|
22
|
+
private buildFailureMessage;
|
|
23
|
+
private buildFailureDetails;
|
|
24
|
+
private severityToFailureType;
|
|
25
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* JUnit Reporter
|
|
4
|
+
* Outputs findings in JUnit XML format
|
|
5
|
+
* Suitable for:
|
|
6
|
+
* - Jenkins
|
|
7
|
+
* - GitLab CI
|
|
8
|
+
* - CircleCI
|
|
9
|
+
* - Azure Pipelines
|
|
10
|
+
* - Other CI/CD platforms that support JUnit test results
|
|
11
|
+
*
|
|
12
|
+
* Each workflow file becomes a test suite
|
|
13
|
+
* Each finding becomes a test failure
|
|
14
|
+
*/
|
|
15
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
16
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
17
|
+
};
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.JunitReporter = void 0;
|
|
20
|
+
const fs_1 = __importDefault(require("fs"));
|
|
21
|
+
const xmlbuilder2_1 = require("xmlbuilder2");
|
|
22
|
+
class JunitReporter {
|
|
23
|
+
constructor(outputFile) {
|
|
24
|
+
this.outputFile = outputFile;
|
|
25
|
+
}
|
|
26
|
+
async report(results) {
|
|
27
|
+
const xml = this.buildJunitXml(results);
|
|
28
|
+
if (this.outputFile) {
|
|
29
|
+
// Write to file
|
|
30
|
+
fs_1.default.writeFileSync(this.outputFile, xml, 'utf8');
|
|
31
|
+
console.log(`✓ JUnit report written to ${this.outputFile}`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// Print to stdout
|
|
35
|
+
console.log(xml);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
buildJunitXml(results) {
|
|
39
|
+
const timestamp = new Date().toISOString();
|
|
40
|
+
// Count total tests: each finding is a test, or 1 passing test if no findings
|
|
41
|
+
const totalTests = results.reduce((sum, r) => sum + (r.findings.length === 0 ? 1 : r.findings.length), 0);
|
|
42
|
+
const totalFailures = results.reduce((sum, r) => sum + r.findings.length, 0);
|
|
43
|
+
const totalTime = 0; // We don't track execution time
|
|
44
|
+
// Create root element
|
|
45
|
+
const root = (0, xmlbuilder2_1.create)({ version: '1.0', encoding: 'UTF-8' })
|
|
46
|
+
.ele('testsuites', {
|
|
47
|
+
name: 'FlowLint',
|
|
48
|
+
tests: totalTests,
|
|
49
|
+
failures: totalFailures,
|
|
50
|
+
errors: 0,
|
|
51
|
+
time: totalTime,
|
|
52
|
+
timestamp,
|
|
53
|
+
});
|
|
54
|
+
// Add a test suite for each file
|
|
55
|
+
for (const result of results) {
|
|
56
|
+
this.addTestSuite(root, result);
|
|
57
|
+
}
|
|
58
|
+
return root.end({ prettyPrint: true });
|
|
59
|
+
}
|
|
60
|
+
addTestSuite(parent, result) {
|
|
61
|
+
const { file, findings } = result;
|
|
62
|
+
// If no findings, create one passing test case
|
|
63
|
+
const testCount = findings.length === 0 ? 1 : findings.length;
|
|
64
|
+
const failureCount = findings.length; // All findings are failures
|
|
65
|
+
const testsuite = parent.ele('testsuite', {
|
|
66
|
+
name: file.path,
|
|
67
|
+
tests: testCount,
|
|
68
|
+
failures: failureCount,
|
|
69
|
+
errors: 0,
|
|
70
|
+
time: 0,
|
|
71
|
+
timestamp: new Date().toISOString(),
|
|
72
|
+
});
|
|
73
|
+
// If no findings, add a passing test
|
|
74
|
+
if (findings.length === 0) {
|
|
75
|
+
testsuite.ele('testcase', {
|
|
76
|
+
name: 'No issues found',
|
|
77
|
+
classname: file.path,
|
|
78
|
+
time: 0,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// Add a test case for each finding
|
|
83
|
+
for (const finding of findings) {
|
|
84
|
+
this.addTestCase(testsuite, finding, file.path);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
addTestCase(parent, finding, filePath) {
|
|
89
|
+
const testcase = parent.ele('testcase', {
|
|
90
|
+
name: finding.rule,
|
|
91
|
+
classname: filePath,
|
|
92
|
+
time: 0,
|
|
93
|
+
});
|
|
94
|
+
// Add failure element
|
|
95
|
+
const failureMessage = this.buildFailureMessage(finding);
|
|
96
|
+
const failureType = this.severityToFailureType(finding.severity);
|
|
97
|
+
testcase.ele('failure', {
|
|
98
|
+
message: failureMessage,
|
|
99
|
+
type: failureType,
|
|
100
|
+
}).txt(this.buildFailureDetails(finding));
|
|
101
|
+
}
|
|
102
|
+
buildFailureMessage(finding) {
|
|
103
|
+
const location = finding.line ? `:${finding.line}` : '';
|
|
104
|
+
return `[${finding.severity.toUpperCase()}] ${finding.message} (${finding.path}${location})`;
|
|
105
|
+
}
|
|
106
|
+
buildFailureDetails(finding) {
|
|
107
|
+
const parts = [];
|
|
108
|
+
parts.push(`Rule: ${finding.rule}`);
|
|
109
|
+
parts.push(`Severity: ${finding.severity.toUpperCase()}`);
|
|
110
|
+
parts.push(`File: ${finding.path}`);
|
|
111
|
+
if (finding.line) {
|
|
112
|
+
parts.push(`Line: ${finding.line}`);
|
|
113
|
+
}
|
|
114
|
+
if (finding.nodeId) {
|
|
115
|
+
parts.push(`Node ID: ${finding.nodeId}`);
|
|
116
|
+
}
|
|
117
|
+
parts.push('');
|
|
118
|
+
parts.push(finding.message);
|
|
119
|
+
if (finding.raw_details) {
|
|
120
|
+
parts.push('');
|
|
121
|
+
parts.push('Details:');
|
|
122
|
+
parts.push(finding.raw_details);
|
|
123
|
+
}
|
|
124
|
+
if (finding.documentationUrl) {
|
|
125
|
+
parts.push('');
|
|
126
|
+
parts.push(`Documentation: ${finding.documentationUrl}`);
|
|
127
|
+
}
|
|
128
|
+
return parts.join('\n');
|
|
129
|
+
}
|
|
130
|
+
severityToFailureType(severity) {
|
|
131
|
+
switch (severity) {
|
|
132
|
+
case 'must':
|
|
133
|
+
return 'FlowLintError';
|
|
134
|
+
case 'should':
|
|
135
|
+
return 'FlowLintWarning';
|
|
136
|
+
case 'nit':
|
|
137
|
+
return 'FlowLintSuggestion';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
exports.JunitReporter = JunitReporter;
|
|
142
|
+
//# sourceMappingURL=junit-reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"junit-reporter.js","sourceRoot":"","sources":["../../../../../apps/cli/src/reporters/junit-reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SARIF Reporter
|
|
3
|
+
* Outputs findings in SARIF 2.1.0 format
|
|
4
|
+
* Suitable for:
|
|
5
|
+
* - GitHub Code Scanning
|
|
6
|
+
* - Azure DevOps
|
|
7
|
+
* - Security scanners and analysis platforms
|
|
8
|
+
* - CI/CD pipelines
|
|
9
|
+
*
|
|
10
|
+
* Spec: https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
|
|
11
|
+
*/
|
|
12
|
+
import type { AnalysisResult, Reporter } from 'packages/review/providers';
|
|
13
|
+
export declare class SarifReporter implements Reporter {
|
|
14
|
+
private outputFile?;
|
|
15
|
+
constructor(outputFile?: string | undefined);
|
|
16
|
+
report(results: AnalysisResult[]): Promise<void>;
|
|
17
|
+
private buildSarifLog;
|
|
18
|
+
private extractUniqueRules;
|
|
19
|
+
private findingToSarifResult;
|
|
20
|
+
private severityToSarifLevel;
|
|
21
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SARIF Reporter
|
|
4
|
+
* Outputs findings in SARIF 2.1.0 format
|
|
5
|
+
* Suitable for:
|
|
6
|
+
* - GitHub Code Scanning
|
|
7
|
+
* - Azure DevOps
|
|
8
|
+
* - Security scanners and analysis platforms
|
|
9
|
+
* - CI/CD pipelines
|
|
10
|
+
*
|
|
11
|
+
* Spec: https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
|
|
12
|
+
*/
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.SarifReporter = void 0;
|
|
18
|
+
const fs_1 = __importDefault(require("fs"));
|
|
19
|
+
class SarifReporter {
|
|
20
|
+
constructor(outputFile) {
|
|
21
|
+
this.outputFile = outputFile;
|
|
22
|
+
}
|
|
23
|
+
async report(results) {
|
|
24
|
+
const sarifLog = this.buildSarifLog(results);
|
|
25
|
+
const json = JSON.stringify(sarifLog, null, 2);
|
|
26
|
+
if (this.outputFile) {
|
|
27
|
+
// Write to file
|
|
28
|
+
fs_1.default.writeFileSync(this.outputFile, json, 'utf8');
|
|
29
|
+
console.log(`✓ SARIF report written to ${this.outputFile}`);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// Print to stdout
|
|
33
|
+
console.log(json);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
buildSarifLog(results) {
|
|
37
|
+
const allFindings = results.flatMap((r) => r.findings);
|
|
38
|
+
// Extract unique rules
|
|
39
|
+
const uniqueRules = this.extractUniqueRules(allFindings);
|
|
40
|
+
// Build artifacts list (analyzed files)
|
|
41
|
+
const artifacts = results.map((r) => ({
|
|
42
|
+
location: {
|
|
43
|
+
uri: r.file.path,
|
|
44
|
+
},
|
|
45
|
+
}));
|
|
46
|
+
// Build SARIF results
|
|
47
|
+
const sarifResults = [];
|
|
48
|
+
for (const result of results) {
|
|
49
|
+
for (const finding of result.findings) {
|
|
50
|
+
sarifResults.push(this.findingToSarifResult(finding));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
version: '2.1.0',
|
|
55
|
+
$schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
|
|
56
|
+
runs: [
|
|
57
|
+
{
|
|
58
|
+
tool: {
|
|
59
|
+
driver: {
|
|
60
|
+
name: 'FlowLint',
|
|
61
|
+
version: '0.3.8',
|
|
62
|
+
informationUri: 'https://flowlint.dev',
|
|
63
|
+
rules: uniqueRules,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
results: sarifResults,
|
|
67
|
+
artifacts,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
extractUniqueRules(findings) {
|
|
73
|
+
const ruleMap = new Map();
|
|
74
|
+
for (const finding of findings) {
|
|
75
|
+
if (!ruleMap.has(finding.rule)) {
|
|
76
|
+
ruleMap.set(finding.rule, {
|
|
77
|
+
id: finding.rule,
|
|
78
|
+
shortDescription: {
|
|
79
|
+
text: finding.message,
|
|
80
|
+
},
|
|
81
|
+
helpUri: finding.documentationUrl,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return Array.from(ruleMap.values());
|
|
86
|
+
}
|
|
87
|
+
findingToSarifResult(finding) {
|
|
88
|
+
const result = {
|
|
89
|
+
ruleId: finding.rule,
|
|
90
|
+
level: this.severityToSarifLevel(finding.severity),
|
|
91
|
+
message: {
|
|
92
|
+
text: finding.raw_details ? `${finding.message}\n\n${finding.raw_details}` : finding.message,
|
|
93
|
+
},
|
|
94
|
+
locations: [
|
|
95
|
+
{
|
|
96
|
+
physicalLocation: {
|
|
97
|
+
artifactLocation: {
|
|
98
|
+
uri: finding.path,
|
|
99
|
+
},
|
|
100
|
+
region: finding.line ? { startLine: finding.line } : undefined,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
// Add nodeId as property if present
|
|
106
|
+
if (finding.nodeId) {
|
|
107
|
+
result.properties = {
|
|
108
|
+
nodeId: finding.nodeId,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
severityToSarifLevel(severity) {
|
|
114
|
+
switch (severity) {
|
|
115
|
+
case 'must':
|
|
116
|
+
return 'error';
|
|
117
|
+
case 'should':
|
|
118
|
+
return 'warning';
|
|
119
|
+
case 'nit':
|
|
120
|
+
return 'note';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
exports.SarifReporter = SarifReporter;
|
|
125
|
+
//# sourceMappingURL=sarif-reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif-reporter.js","sourceRoot":"","sources":["../../../../../apps/cli/src/reporters/sarif-reporter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowlint",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "Static analysis tool for n8n workflows - detect issues early, fix them faster",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"micromatch": "^4.0.8",
|
|
28
28
|
"pino": "^10.1.0",
|
|
29
29
|
"pino-pretty": "^13.1.2",
|
|
30
|
+
"xmlbuilder2": "^4.0.3",
|
|
30
31
|
"yaml": "^2.4.0"
|
|
31
32
|
},
|
|
32
33
|
"keywords": [
|