@govuk-pay/cli 0.0.7 → 0.0.9

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.
@@ -0,0 +1,473 @@
1
+ version: '2.1'
2
+
3
+ services:
4
+
5
+ adminusers_db:
6
+ image: postgres:11.1
7
+ environment:
8
+ - POSTGRES_PASSWORD=mysecretpassword
9
+ mem_limit: 250M
10
+ logging:
11
+ driver: "json-file"
12
+ container_name: adminusers_db
13
+ healthcheck:
14
+ test: ["CMD-SHELL", "pg_isready"]
15
+ interval: 10s
16
+ timeout: 5s
17
+ retries: 5
18
+ volumes:
19
+ - ./postgres/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
20
+ ports:
21
+ - "19700:5432"
22
+
23
+ connector_db:
24
+ image: postgres:11.1
25
+ environment:
26
+ - POSTGRES_PASSWORD=mysecretpassword
27
+ mem_limit: 250M
28
+ logging:
29
+ driver: "json-file"
30
+ container_name: connector_db
31
+ healthcheck:
32
+ test: ["CMD-SHELL", "pg_isready"]
33
+ interval: 10s
34
+ timeout: 5s
35
+ retries: 5
36
+ volumes:
37
+ - ./postgres/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
38
+ ports:
39
+ - "19300:5432"
40
+
41
+ publicauth_db:
42
+ image: postgres:11.1
43
+ environment:
44
+ - POSTGRES_PASSWORD=mysecretpassword
45
+ mem_limit: 250M
46
+ logging:
47
+ driver: "json-file"
48
+ container_name: publicauth_db
49
+ healthcheck:
50
+ test: ["CMD-SHELL", "pg_isready"]
51
+ interval: 10s
52
+ timeout: 5s
53
+ retries: 5
54
+ volumes:
55
+ - ./postgres/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
56
+ ports:
57
+ - "19600:5432"
58
+
59
+ products_db:
60
+ image: postgres:11.1
61
+ environment:
62
+ - POSTGRES_PASSWORD=mysecretpassword
63
+ mem_limit: 250M
64
+ logging:
65
+ driver: "json-file"
66
+ container_name: products_db
67
+ healthcheck:
68
+ test: ["CMD-SHELL", "pg_isready"]
69
+ interval: 10s
70
+ timeout: 5s
71
+ retries: 5
72
+ volumes:
73
+ - ./postgres/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
74
+ ports:
75
+ - "19800:5432"
76
+
77
+ ledger_db:
78
+ image: postgres:11.1
79
+ environment:
80
+ - POSTGRES_PASSWORD=mysecretpassword
81
+ mem_limit: 250M
82
+ logging:
83
+ driver: "json-file"
84
+ container_name: ledger_db
85
+ healthcheck:
86
+ test: ["CMD-SHELL", "pg_isready"]
87
+ interval: 10s
88
+ timeout: 5s
89
+ retries: 5
90
+ volumes:
91
+ - ./postgres/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
92
+ ports:
93
+ - "20700:5432"
94
+
95
+ webhooks_db:
96
+ image: postgres:11.1
97
+ environment:
98
+ - POSTGRES_PASSWORD=mysecretpassword
99
+ mem_limit: 250M
100
+ logging:
101
+ driver: "json-file"
102
+ container_name: webhooks_db
103
+ healthcheck:
104
+ test: ["CMD-SHELL", "pg_isready"]
105
+ interval: 10s
106
+ timeout: 5s
107
+ retries: 5
108
+ volumes:
109
+ - ./postgres/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
110
+ ports:
111
+ - "20800:5432"
112
+
113
+
114
+
115
+ adminusers:
116
+ image: governmentdigitalservice/pay-adminusers:local
117
+
118
+ depends_on:
119
+ adminusers_db:
120
+ condition: service_healthy
121
+
122
+ env_file:
123
+ - services/java_app.env
124
+ - services/adminusers.env
125
+ environment:
126
+ - RUN_MIGRATION=true
127
+ - RUN_APP=true
128
+ - PORT=9700
129
+ - DISABLE_INTERNAL_HTTPS=true
130
+
131
+ - DB_HOST=adminusers_db
132
+ - DB_USER=adminusers
133
+ - DB_PASSWORD=mysecretpassword
134
+ - DB_SSL_OPTION=ssl=none
135
+
136
+
137
+
138
+
139
+
140
+
141
+
142
+
143
+ - ADMINUSERS_URL=http://adminusers:9700
144
+ - CONNECTOR_URL=http://connector:9300
145
+ - PUBLICAUTH_URL=http://publicauth:9600
146
+ - PRODUCTS_URL=http://products:18000
147
+ - LEDGER_URL=http://ledger:10700
148
+ - WEBHOOKS_URL=http://webhooks:10800
149
+ - TOOLBOX_URL=http://localhost:3040
150
+ working_dir: '/app'
151
+ mem_limit: 2G
152
+ user: root
153
+ logging:
154
+ driver: "json-file"
155
+ ports:
156
+ - "9700:9700"
157
+
158
+ container_name: adminusers
159
+
160
+ connector:
161
+ image: governmentdigitalservice/pay-connector:local
162
+
163
+ depends_on:
164
+ connector_db:
165
+ condition: service_healthy
166
+
167
+ env_file:
168
+ - services/java_app.env
169
+ - services/connector.env
170
+ environment:
171
+ - RUN_MIGRATION=true
172
+ - RUN_APP=true
173
+ - PORT=9300
174
+ - DISABLE_INTERNAL_HTTPS=true
175
+
176
+ - DB_HOST=connector_db
177
+ - DB_USER=connector
178
+ - DB_PASSWORD=mysecretpassword
179
+ - DB_SSL_OPTION=ssl=none
180
+
181
+
182
+
183
+ - AWS_ACCESS_KEY=mockAccessKey
184
+ - AWS_SECRET_ACCESS_KEY=mockSecretAccessKey
185
+
186
+
187
+
188
+ - AWS_SQS_REGION=eu-west-1
189
+ - AWS_SQS_MESSAGE_MAXIMUM_WAIT_TIME_IN_SECONDS=20
190
+ - AWS_SQS_NON_STANDARD_SERVICE_ENDPOINT=true
191
+ - AWS_SQS_ENDPOINT=http://localstack:4566
192
+ - AWS_SQS_CAPTURE_QUEUE_URL=http://localstack:4566/000000000000/pay_capture_queue
193
+ - AWS_SQS_PAYMENT_EVENT_QUEUE_URL=http://localstack:4566/000000000000/pay_event_queue
194
+ - AWS_SQS_PAYOUT_RECONCILE_QUEUE_URL=http://localstack:4566/000000000000/payout_reconcile_queue
195
+ - AWS_SQS_CONNECTOR_TASKS_QUEUE_URL=http://localstack:4566/000000000000/connector_tasks_queue
196
+
197
+
198
+
199
+
200
+ - ADMINUSERS_URL=http://adminusers:9700
201
+ - CONNECTOR_URL=http://connector:9300
202
+ - PUBLICAUTH_URL=http://publicauth:9600
203
+ - PRODUCTS_URL=http://products:18000
204
+ - LEDGER_URL=http://ledger:10700
205
+ - WEBHOOKS_URL=http://webhooks:10800
206
+ - TOOLBOX_URL=http://localhost:3040
207
+ working_dir: '/app'
208
+ mem_limit: 2G
209
+ user: root
210
+ logging:
211
+ driver: "json-file"
212
+ ports:
213
+ - "9300:9300"
214
+ - "9301:9301"
215
+ container_name: connector
216
+
217
+ publicauth:
218
+ image: governmentdigitalservice/pay-publicauth:local
219
+
220
+ depends_on:
221
+ publicauth_db:
222
+ condition: service_healthy
223
+
224
+ env_file:
225
+ - services/java_app.env
226
+ - services/publicauth.env
227
+ environment:
228
+ - RUN_MIGRATION=true
229
+ - RUN_APP=true
230
+ - PORT=9600
231
+ - DISABLE_INTERNAL_HTTPS=true
232
+
233
+ - DB_HOST=publicauth_db
234
+ - DB_USER=publicauth
235
+ - DB_PASSWORD=mysecretpassword
236
+ - DB_SSL_OPTION=ssl=none
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+ - ADMINUSERS_URL=http://adminusers:9700
246
+ - CONNECTOR_URL=http://connector:9300
247
+ - PUBLICAUTH_URL=http://publicauth:9600
248
+ - PRODUCTS_URL=http://products:18000
249
+ - LEDGER_URL=http://ledger:10700
250
+ - WEBHOOKS_URL=http://webhooks:10800
251
+ - TOOLBOX_URL=http://localhost:3040
252
+ working_dir: '/app'
253
+ mem_limit: 2G
254
+ user: root
255
+ logging:
256
+ driver: "json-file"
257
+ ports:
258
+ - "9600:9600"
259
+
260
+ container_name: publicauth
261
+
262
+ products:
263
+ image: governmentdigitalservice/pay-products:local
264
+
265
+ depends_on:
266
+ products_db:
267
+ condition: service_healthy
268
+
269
+ env_file:
270
+ - services/java_app.env
271
+ - services/products.env
272
+ environment:
273
+ - RUN_MIGRATION=true
274
+ - RUN_APP=true
275
+ - PORT=18000
276
+ - DISABLE_INTERNAL_HTTPS=true
277
+
278
+ - DB_HOST=products_db
279
+ - DB_USER=products
280
+ - DB_PASSWORD=mysecretpassword
281
+ - DB_SSL_OPTION=ssl=none
282
+
283
+
284
+
285
+
286
+
287
+
288
+
289
+
290
+ - ADMINUSERS_URL=http://adminusers:9700
291
+ - CONNECTOR_URL=http://connector:9300
292
+ - PUBLICAUTH_URL=http://publicauth:9600
293
+ - PRODUCTS_URL=http://products:18000
294
+ - LEDGER_URL=http://ledger:10700
295
+ - WEBHOOKS_URL=http://webhooks:10800
296
+ - TOOLBOX_URL=http://localhost:3040
297
+ working_dir: '/app'
298
+ mem_limit: 2G
299
+ user: root
300
+ logging:
301
+ driver: "json-file"
302
+ ports:
303
+ - "18000:18000"
304
+
305
+ container_name: products
306
+
307
+ ledger:
308
+ image: governmentdigitalservice/pay-ledger:local
309
+
310
+ depends_on:
311
+ ledger_db:
312
+ condition: service_healthy
313
+
314
+ env_file:
315
+ - services/java_app.env
316
+ - services/ledger.env
317
+ environment:
318
+ - RUN_MIGRATION=true
319
+ - RUN_APP=true
320
+ - PORT=10700
321
+ - DISABLE_INTERNAL_HTTPS=true
322
+
323
+ - DB_HOST=ledger_db
324
+ - DB_USER=ledger
325
+ - DB_PASSWORD=mysecretpassword
326
+ - DB_SSL_OPTION=ssl=none
327
+
328
+
329
+
330
+ - AWS_ACCESS_KEY=mockAccessKey
331
+ - AWS_SECRET_ACCESS_KEY=mockSecretAccessKey
332
+
333
+
334
+
335
+ - AWS_SQS_REGION=eu-west-1
336
+ - AWS_SQS_MESSAGE_MAXIMUM_WAIT_TIME_IN_SECONDS=20
337
+ - AWS_SQS_NON_STANDARD_SERVICE_ENDPOINT=true
338
+ - AWS_SQS_ENDPOINT=http://localstack:4566
339
+ - AWS_SQS_PAYMENT_EVENT_QUEUE_URL=http://localstack:4566/000000000000/pay_event_queue
340
+
341
+
342
+
343
+ - AWS_SNS_NON_STANDARD_SERVICE_ENDPOINT=true
344
+ - AWS_SNS_ENDPOINT=http://localstack:4566
345
+ - AWS_SNS_REGION=eu-west-1
346
+ - SNS_TOPIC_CARD_PAYMENT_EVENTS_ARN=arn:aws:sns:eu-west-1:000000000000:card-payment-events-topic
347
+ - SNS_TOPIC_CARD_PAYMENT_DISPUTE_EVENTS_ARN=arn:aws:sns:eu-west-1:000000000000:card-payment-dispute-events-topic
348
+
349
+
350
+ - ADMINUSERS_URL=http://adminusers:9700
351
+ - CONNECTOR_URL=http://connector:9300
352
+ - PUBLICAUTH_URL=http://publicauth:9600
353
+ - PRODUCTS_URL=http://products:18000
354
+ - LEDGER_URL=http://ledger:10700
355
+ - WEBHOOKS_URL=http://webhooks:10800
356
+ - TOOLBOX_URL=http://localhost:3040
357
+ - SNS_ENABLED=true
358
+ - PUBLISH_CARD_PAYMENT_EVENTS_TO_SNS=true
359
+ - PUBLISH_CARD_PAYMENT_DISPUTE_EVENTS_TO_SNS=true
360
+ working_dir: '/app'
361
+ mem_limit: 2G
362
+ user: root
363
+ logging:
364
+ driver: "json-file"
365
+ ports:
366
+ - "10700:10700"
367
+
368
+ container_name: ledger
369
+
370
+ webhooks:
371
+ image: governmentdigitalservice/pay-webhooks:local
372
+
373
+ depends_on:
374
+ webhooks_db:
375
+ condition: service_healthy
376
+
377
+ env_file:
378
+ - services/java_app.env
379
+ - services/webhooks.env
380
+ environment:
381
+ - RUN_MIGRATION=true
382
+ - RUN_APP=true
383
+ - PORT=10800
384
+ - DISABLE_INTERNAL_HTTPS=true
385
+
386
+ - DB_HOST=webhooks_db
387
+ - DB_USER=webhooks
388
+ - DB_PASSWORD=mysecretpassword
389
+ - DB_SSL_OPTION=ssl=none
390
+
391
+
392
+
393
+ - AWS_ACCESS_KEY=mockAccessKey
394
+ - AWS_SECRET_ACCESS_KEY=mockSecretAccessKey
395
+
396
+
397
+
398
+ - AWS_SQS_REGION=eu-west-1
399
+ - AWS_SQS_MESSAGE_MAXIMUM_WAIT_TIME_IN_SECONDS=20
400
+ - AWS_SQS_NON_STANDARD_SERVICE_ENDPOINT=true
401
+ - AWS_SQS_ENDPOINT=http://localstack:4566
402
+ - AWS_SQS_PAYMENT_EVENT_QUEUE_URL=http://localstack:4566/000000000000/webhooks-events-subscriber-queue
403
+
404
+
405
+
406
+
407
+ - ADMINUSERS_URL=http://adminusers:9700
408
+ - CONNECTOR_URL=http://connector:9300
409
+ - PUBLICAUTH_URL=http://publicauth:9600
410
+ - PRODUCTS_URL=http://products:18000
411
+ - LEDGER_URL=http://ledger:10700
412
+ - WEBHOOKS_URL=http://webhooks:10800
413
+ - TOOLBOX_URL=http://localhost:3040
414
+ working_dir: '/app'
415
+ mem_limit: 2G
416
+ user: root
417
+ logging:
418
+ driver: "json-file"
419
+ ports:
420
+ - "10800:10800"
421
+
422
+ container_name: webhooks
423
+
424
+
425
+
426
+ toolbox:
427
+ image: node:16.20.0-alpine3.17
428
+
429
+ env_file: services/toolbox.env
430
+ environment:
431
+ - SECURE_COOKIE_OFF=true
432
+ - RUN_APP=true
433
+ - DISABLE_APPMETRICS=true
434
+ - DISABLE_INTERNAL_HTTPS=true
435
+ - PORT=3040
436
+
437
+ - ADMINUSERS_URL=http://adminusers:9700
438
+
439
+ - CONNECTOR_URL=http://connector:9300
440
+
441
+ - PUBLIC_AUTH_URL=http://publicauth:9600
442
+
443
+ - PRODUCTS_URL=http://products:18000
444
+
445
+ - LEDGER_URL=http://ledger:10700
446
+
447
+ - WEBHOOKS_URL=http://webhooks:10800
448
+ - TOOLBOX_URL=http://localhost:3040
449
+ volumes:
450
+ - "$WORKSPACE/pay-toolbox:/app"
451
+ mem_limit: 1G
452
+ working_dir: '/app'
453
+ entrypoint: sh -c "npm run build:copy && npm --inspect=0.0.0.0:3041 start"
454
+ ports:
455
+ - "3040:3040"
456
+ - "3041:3041"
457
+ logging:
458
+ driver: "json-file"
459
+ container_name: toolbox
460
+
461
+
462
+
463
+ localstack:
464
+ image: localstack/localstack:3
465
+ container_name: localstack
466
+ environment:
467
+ - EAGER_SERVICE_LOADING=1
468
+ - SERVICES=sns,sqs
469
+ ports:
470
+ - "4566:4566" # All AWS services exposed on this port
471
+ volumes:
472
+ - /Users/jonathan.harden/gitdevel/github.com/alphagov/pay-infra/cli/lib/pay_cli/commands/local/files/localstack/init-aws.sh:/etc/localstack/init/ready.d/init-aws.sh
473
+
@@ -14,6 +14,7 @@ require 'pathname'
14
14
  REQUIRES_COMPILATION = %w[frontend products-ui selfservice toolbox].freeze
15
15
 
16
16
  class PayCLI::Commands::Local < Thor
17
+ warn "🏠 Local Pay for local people\n\n"
17
18
  map "up" => :launch
18
19
  desc 'launch | up [--cluster <cluster>] [--local <app>] [--proxy] [--apps] [--rebuild]',
19
20
  'Launch a predefined cluster, or a user specified list of apps.' \
@@ -171,7 +172,7 @@ class PayCLI::Commands::Local < Thor
171
172
  def user
172
173
  warn '🏗️🏗 Constructing useful things for you...'
173
174
  gateway_account_ids = []
174
-
175
+
175
176
  service = AppClient::create_service()
176
177
  service_id = service.fetch('external_id')
177
178
 
@@ -189,7 +190,7 @@ class PayCLI::Commands::Local < Thor
189
190
  end
190
191
  end
191
192
  end
192
-
193
+
193
194
  warn '🤓 Creating admin user for service'
194
195
  user = AppClient::create_user(gateway_account_ids: gateway_account_ids)
195
196
  warn '😎 Creating read only user for service'
@@ -41,6 +41,7 @@ do
41
41
  done
42
42
 
43
43
  ACCOUNT="${ENVIRONMENT%%-*}"
44
+ PAY_ENV_NAME=$(sed -E 's/-[0-9]+$//' <<<"$ENVIRONMENT")
44
45
  KEY_LOCATION="${TMPDIR}rds_tunnel_temp_key"
45
46
 
46
47
  function cleanup() {
@@ -54,6 +55,22 @@ function cleanup() {
54
55
 
55
56
  trap cleanup EXIT
56
57
 
58
+ PROMPT_TEXT="Do you require read-only access, or write-access to the database? [R/w]: "
59
+ read -r -p "$PROMPT_TEXT" READONLY_INPUT
60
+ while [ "$READONLY_INPUT" != "R" ] && [ "$READONLY_INPUT" != "r" ] && [ "$READONLY_INPUT" != "W" ] && [ "$READONLY_INPUT" != "w" ] && [ "$READONLY_INPUT" != "" ]; do
61
+ echo
62
+ read -r -p "$PROMPT_TEXT" READONLY_INPUT
63
+ done
64
+ echo
65
+
66
+ if [ "$READONLY_INPUT" == "w" ] || [ "$READONLY_INPUT" == "W" ]; then
67
+ echo "Database write access requested"
68
+ READONLY=0
69
+ else
70
+ echo "Database read-only access requested"
71
+ READONLY=1
72
+ fi
73
+
57
74
  echo "Finding bastion host"
58
75
  read -r bastion_instance_id availability_zone <<< "$(aws-vault exec "$ACCOUNT" -- \
59
76
  aws autoscaling describe-auto-scaling-groups | \
@@ -132,18 +149,27 @@ if ! aws-vault exec "$ACCOUNT" -- \
132
149
  fi
133
150
 
134
151
  SOURCE_DIRECTORY=$(dirname "$BASH_SOURCE")
135
- DB_USER=$(yq eval ".value.$ENVIRONMENT.$APP.DB_USER" < "${SOURCE_DIRECTORY}/../config/secrets.yml")
152
+
153
+ if [ "$READONLY" -eq 1 ]; then
154
+ DB_USER=$(yq eval ".value.$ENVIRONMENT.$APP.DB_SUPPORT_USER_READONLY" < "${SOURCE_DIRECTORY}/../config/secrets.yml")
155
+ DB_USER_SECRET_NAME="aws/rds/support_readonly_users/${PAY_ENV_NAME}/${DB_USER}" # pragma: allowlist secret
156
+ PAY_SECRETS_PASSWORD_NAME="DB_SUPPORT_PASSWORD_READONLY" # pragma: allowlist secret
157
+ else
158
+ DB_USER=$(yq eval ".value.$ENVIRONMENT.$APP.DB_USER" < "${SOURCE_DIRECTORY}/../config/secrets.yml")
159
+ DB_USER_SECRET_NAME="aws/rds/application_users/${ACCOUNT}/${DB_USER}" # pragma: allowlist secret
160
+ PAY_SECRETS_PASSWORD_NAME="DB_PASSWORD" # pragma: allowlist secret
161
+ fi
136
162
 
137
163
  echo -e "Connected tunnel to $APP RDS database in $ENVIRONMENT on port 65432\n"
138
164
  echo "Copy DB credentials to clipboard (in another window) using pay-low-pass:"
139
- echo -e " pay-low-pass aws/rds/application_users/$ACCOUNT/$DB_USER | pbcopy\n"
165
+ echo -e " pay-low-pass $DB_USER_SECRET_NAME | pbcopy\n" # pragma: allowlist secret
140
166
  echo "Alternatively, fetch credentials from pay secrets:"
141
- echo -e " pay secrets fetch $ENVIRONMENT $APP DB_PASSWORD | pbcopy\n"
167
+ echo -e " pay secrets fetch $ENVIRONMENT $APP $PAY_SECRETS_PASSWORD_NAME | pbcopy\n" # pragma: allowlist secret
142
168
  echo "Open psql with:"
143
169
  echo -e " psql -h localhost -p 65432 -U $DB_USER -d $APP\n"
144
170
  echo "Alternatively connect using docker instead of needing psql installed locally and set the password automatically using pay-low-pass:"
145
171
  echo -e " docker run --rm -ti postgres:${engine_version}-alpine psql --host docker.for.mac.localhost --port 65432 --user $DB_USER --dbname $APP\n"
146
172
  echo "Or even more conveniently connect using a docker container and set the password automatically using pay-low-pass:"
147
- echo -e " docker run -e \"PGPASSWORD=\$(pay-low-pass aws/rds/application_users/${ACCOUNT}/${DB_USER})\" --rm -ti postgres:${engine_version}-alpine psql --host docker.for.mac.localhost --port 65432 --user $DB_USER --dbname $APP\n"
173
+ echo -e " docker run -e \"PGPASSWORD=\$(pay-low-pass ${DB_USER_SECRET_NAME})\" --rm -ti postgres:${engine_version}-alpine psql --host docker.for.mac.localhost --port 65432 --user $DB_USER --dbname $APP\n"
148
174
  read -rsn1 -p "Press any key to close session."; echo
149
175
  ssh -O exit -S temp-ssh.sock '*'
@@ -1,91 +1,88 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
+ const stringify = require('csv-stringify/sync').stringify
3
4
 
4
- const INPUT_PATH = `${path.dirname(__filename)}/reports`
5
- const OUTPUT_PATH = `${path.dirname(__filename)}/reports`
6
- const HEADERS = ["App name", "Release", "Vulnerability", "Severity", "Package", "Fixed version"]
5
+ const INPUT_PATH = `${path.resolve(path.dirname(__filename))}/reports`
6
+ const OUTPUT_PATH = `${path.resolve(path.dirname(__filename))}/reports`
7
+
8
+ const HEADERS = ["App name", "Release", "Vulnerability", "Severity", "Status", "Package", "Fixed version"]
7
9
  const NODE_MAJOR_VERSION = process.versions.node.split('.')[0];
8
10
 
9
11
  if (NODE_MAJOR_VERSION < 16) {
10
12
  console.warn('⛔️ requires >= Node 16')
13
+ process.exit(1)
11
14
  } else {
12
15
  generate()
13
16
  }
14
17
 
15
18
  function generate() {
16
- const jsonData = []
17
19
  const date = new Date().toJSON().slice(0, 10);
18
20
  const jsonFiles = fs.readdirSync(INPUT_PATH).filter(file => path.extname(file) === '.json')
19
21
  const reportFilePath = `${OUTPUT_PATH}/vulnerability_scan_report-${date}.csv`
22
+ const violatedRules = new Map()
20
23
  let parseCount = 0
21
24
  let rowCount = 0
22
25
  try {
23
26
  jsonFiles.forEach(file => {
24
- const appAndRelease = extractAppAndRelease(file)
27
+ const appInfo = extractAppAndReleaseFromFilename(file)
25
28
  const filePath = path.join(INPUT_PATH, file)
26
29
  const data = fs.readFileSync(filePath, 'utf8')
27
30
  const parsedData = JSON.parse(data)
28
- const cveObjects = extractCVEObjects(parsedData["runs"][0]["results"], appAndRelease)
29
- jsonData.push({
30
- "CVEs": cveObjects
31
- })
32
- })
33
31
 
34
- fs.writeFileSync(reportFilePath, HEADERS.join(',') + '\n')
32
+ const appViolations = [];
33
+ violatedRules.set(`${appInfo.name}:${appInfo.release}`, appViolations)
35
34
 
36
- jsonData.forEach(data => {
37
- data["CVEs"].forEach(cve => {
38
- const row = HEADERS.map(key => cve[key]).join(',')
39
- fs.appendFileSync(reportFilePath, row + '\n')
35
+ parsedData["runs"][0]["tool"]["driver"]["rules"].forEach(
36
+ (violatedRule) => appViolations.push({
37
+ "App name": appInfo.name,
38
+ "Release": appInfo.release,
39
+ "Vulnerability": violatedRule.id,
40
+ "Severity": violatedRule.properties.cvssV3_severity,
41
+ "Status": "",
42
+ "Package": violatedRule.properties.purls.join("\n"),
43
+ "Fixed version": violatedRule.properties.fixed_version,
44
+ })
45
+ )
46
+
47
+ parseCount++
48
+ })
49
+ } catch (err) {
50
+ console.error(`Error: Failed parsing source json after successfully parsing ${parseCount} of ${jsonFiles.length} source files: `, err)
51
+ process.exit(1)
52
+ }
53
+
54
+ const csv_rows = [HEADERS];
55
+ try {
56
+ violatedRules.forEach((violations) => {
57
+ violations.forEach((violation) => {
58
+ const row = HEADERS.map(key => violation[key])
59
+ csv_rows.push(row)
40
60
  rowCount++
41
61
  })
42
- parseCount++
43
62
  })
44
63
  } catch (err) {
45
- console.error("ERROR:", err.message)
46
- } finally {
47
- console.log(`Parsed ${parseCount} of ${jsonFiles.length} JSON files, wrote ${rowCount} row(s) to ${reportFilePath}`)
64
+ console.error("Error collating results for writing to CSV: ", err)
65
+ process.exit(1)
66
+ }
67
+
68
+ try {
69
+ fs.writeFileSync(reportFilePath, stringify(csv_rows))
70
+ } catch (err) {
71
+ console.error("Error writing CSV file: ", err.message)
72
+ process.exit(1)
48
73
  }
49
- }
50
74
 
75
+ console.log(`Parsed ${parseCount} of ${jsonFiles.length} JSON files, wrote ${rowCount} row(s) to ${reportFilePath}`)
76
+ }
51
77
 
52
- function extractAppAndRelease(inputString) {
78
+ function extractAppAndReleaseFromFilename(fileName) {
53
79
  const regex = /^.*?(?=-\d+)/ // matches any characters at the beginning of the string that are followed by a dash and one or more digits
54
- const match = inputString.match(regex)
80
+ const match = fileName.match(regex)
55
81
  if (match && match[0]) {
56
- const release = inputString.replace(`${match[0]}-`, "").replace(".json", "")
57
- return [match[0], release]
82
+ const release = fileName.replace(`${match[0]}-`, "").replace(".json", "")
83
+ return { name: match[0], release }
58
84
  } else {
59
- const fallback = inputString.replace(".json", "")
60
- return [fallback, fallback] // fallback option
85
+ const fallback = fileName.replace(".json", "")
86
+ return { name: fallback, release: fallback}
61
87
  }
62
88
  }
63
-
64
- function extractCVEObjects(results, appAndRelease) {
65
- let cveObjects = []
66
- results.forEach(result => {
67
- const stringArr = result["message"]["text"].split("\n").reduce((stringArr, string) => {
68
- if (string !== "") {
69
- stringArr.push(string
70
- .split(", ")
71
- .join(" ")
72
- .trim()
73
- )
74
- }
75
- return stringArr
76
- }, [])
77
- stringArr.push(`App name: ${appAndRelease[0]}`)
78
- stringArr.push(`Release: ${appAndRelease[1]}`)
79
- cveObjects.push(convertStringArrayToCVEObj(stringArr))
80
- })
81
- return cveObjects
82
- }
83
-
84
- function convertStringArrayToCVEObj(stringArr) {
85
- const cveObj = {}
86
- stringArr.forEach(s => {
87
- const parts = s.split(':');
88
- cveObj[parts[0].trim()] = parts[1].trim()
89
- })
90
- return cveObj
91
- }