vtk 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,535 @@
1
+ #!/bin/bash
2
+ #
3
+ # Credential Audit Script
4
+ # =======================
5
+ #
6
+ # Audits which credentials are present on this machine and provides rotation
7
+ # instructions for each. Run this after a suspected or confirmed security incident.
8
+ #
9
+ # WHAT THIS CHECKS:
10
+ #
11
+ # NPM:
12
+ # - ~/.npmrc (auth tokens)
13
+ # - $NPM_TOKEN, $NPM_CONFIG_TOKEN environment variables
14
+ #
15
+ # AWS:
16
+ # - ~/.aws/credentials, ~/.aws/config
17
+ # - $AWS_ACCESS_KEY_ID, $AWS_SECRET_ACCESS_KEY
18
+ #
19
+ # GCP:
20
+ # - ~/.config/gcloud/application_default_credentials.json
21
+ # - $GOOGLE_APPLICATION_CREDENTIALS
22
+ #
23
+ # Azure:
24
+ # - ~/.azure/ directory
25
+ # - $AZURE_CLIENT_SECRET, $AZURE_TENANT_ID
26
+ #
27
+ # GitHub:
28
+ # - ~/.config/gh/hosts.yml (GitHub CLI)
29
+ # - .git/config (stored credentials)
30
+ # - $GITHUB_TOKEN, $GH_TOKEN
31
+ #
32
+ # Other:
33
+ # - SSH keys (~/.ssh/)
34
+ # - Git credentials (~/.git-credentials)
35
+ # - Docker config (~/.docker/config.json)
36
+ # - Kubernetes config (~/.kube/config)
37
+ # - Sensitive environment variables
38
+ #
39
+ # EXIT CODES:
40
+ # 0 - No credentials found
41
+ # 1 - Credentials found (rotation recommended)
42
+ #
43
+ # USAGE:
44
+ # ./credential-audit.sh # Standard output
45
+ # ./credential-audit.sh --verbose # Show all checks (even clean)
46
+ # ./credential-audit.sh --json # JSON output
47
+ #
48
+ # References:
49
+ # - EERT Playbooks: https://department-of-veterans-affairs.github.io/eert/
50
+ #
51
+ # Author: Eric Boehs / EERT (with Claude Code)
52
+ # Version: 1.0.0
53
+ # Date: December 2025
54
+ #
55
+
56
+ set -e
57
+
58
+ # Parse arguments
59
+ JSON=false
60
+ VERBOSE=false
61
+ for arg in "$@"; do
62
+ case $arg in
63
+ --json|-j) JSON=true ;;
64
+ --verbose|-v) VERBOSE=true ;;
65
+ --help|-h)
66
+ echo "Usage: $0 [--verbose|-v] [--json|-j]"
67
+ echo " --verbose Show all checks including clean ones"
68
+ echo " --json JSON output format"
69
+ exit 0
70
+ ;;
71
+ esac
72
+ done
73
+
74
+ # Results tracking
75
+ ROTATION_INSTRUCTIONS=()
76
+ TOTAL_FOUND=0
77
+
78
+ # Colors (disabled in json mode)
79
+ if [ "$JSON" = false ] && [ -t 1 ]; then
80
+ RED='\033[0;31m'
81
+ YELLOW='\033[0;33m'
82
+ GREEN='\033[0;32m'
83
+ CYAN='\033[0;36m'
84
+ BOLD='\033[1m'
85
+ DIM='\033[2m'
86
+ NC='\033[0m'
87
+ else
88
+ RED='' YELLOW='' GREEN='' CYAN='' BOLD='' DIM='' NC=''
89
+ fi
90
+
91
+ # Logging functions
92
+ log() {
93
+ if [ "$JSON" = false ]; then
94
+ echo -e "$@"
95
+ fi
96
+ }
97
+
98
+ log_verbose() {
99
+ if [ "$VERBOSE" = true ] && [ "$JSON" = false ]; then
100
+ echo -e "$@"
101
+ fi
102
+ }
103
+
104
+ log_found() {
105
+ local service="$1"
106
+ local location="$2"
107
+ local instruction="$3"
108
+ ROTATION_INSTRUCTIONS+=("$service|$location|$instruction")
109
+ ((TOTAL_FOUND++)) || true
110
+ }
111
+
112
+ # Header
113
+ log ""
114
+ log "${BOLD}Credential Audit${NC}"
115
+ log "${DIM}Checking for credentials that may need rotation...${NC}"
116
+ log ""
117
+
118
+ ###########################################
119
+ # NPM CREDENTIALS
120
+ ###########################################
121
+
122
+ log "${BOLD}NPM${NC}"
123
+ NPM_FOUND=0
124
+
125
+ # Check ~/.npmrc
126
+ if [ -f "$HOME/.npmrc" ]; then
127
+ if grep -qiE "//.*:_authToken=|_auth=|authToken" "$HOME/.npmrc" 2>/dev/null; then
128
+ log " ${RED}[FOUND]${NC} ~/.npmrc contains auth tokens"
129
+ log_found "NPM" "~/.npmrc" "npm token revoke <token> && npm login"
130
+ NPM_FOUND=1
131
+ else
132
+ log_verbose " ${GREEN}[CLEAN]${NC} ~/.npmrc exists but no tokens found"
133
+ fi
134
+ else
135
+ log_verbose " ${DIM}[SKIP]${NC} ~/.npmrc not found"
136
+ fi
137
+
138
+ # Check NPM environment variables
139
+ if [ -n "$NPM_TOKEN" ]; then
140
+ log " ${RED}[FOUND]${NC} \$NPM_TOKEN is set"
141
+ log_found "NPM" "\$NPM_TOKEN" "Revoke token in npm account settings, regenerate and update env"
142
+ NPM_FOUND=1
143
+ fi
144
+
145
+ if [ -n "$NPM_CONFIG_TOKEN" ]; then
146
+ log " ${RED}[FOUND]${NC} \$NPM_CONFIG_TOKEN is set"
147
+ log_found "NPM" "\$NPM_CONFIG_TOKEN" "Revoke token in npm account settings, regenerate and update env"
148
+ NPM_FOUND=1
149
+ fi
150
+
151
+ if [ "$NPM_FOUND" -eq 0 ]; then
152
+ log " ${DIM}None found${NC}"
153
+ fi
154
+
155
+ log ""
156
+
157
+ ###########################################
158
+ # AWS CREDENTIALS
159
+ ###########################################
160
+
161
+ log "${BOLD}AWS${NC}"
162
+ AWS_FOUND=0
163
+
164
+ # Check ~/.aws/credentials
165
+ if [ -f "$HOME/.aws/credentials" ]; then
166
+ log " ${RED}[FOUND]${NC} ~/.aws/credentials"
167
+ log_found "AWS" "~/.aws/credentials" "aws iam delete-access-key && aws iam create-access-key"
168
+ AWS_FOUND=1
169
+ else
170
+ log_verbose " ${DIM}[SKIP]${NC} ~/.aws/credentials not found"
171
+ fi
172
+
173
+ # Check ~/.aws/config (may contain SSO or role info)
174
+ if [ -f "$HOME/.aws/config" ]; then
175
+ if grep -qE "aws_access_key_id|aws_secret_access_key" "$HOME/.aws/config" 2>/dev/null; then
176
+ log " ${RED}[FOUND]${NC} ~/.aws/config contains access keys"
177
+ log_found "AWS" "~/.aws/config" "Remove keys from config, use aws configure"
178
+ AWS_FOUND=1
179
+ else
180
+ log_verbose " ${GREEN}[CLEAN]${NC} ~/.aws/config exists (no embedded keys)"
181
+ fi
182
+ else
183
+ log_verbose " ${DIM}[SKIP]${NC} ~/.aws/config not found"
184
+ fi
185
+
186
+ # Check AWS environment variables
187
+ if [ -n "$AWS_ACCESS_KEY_ID" ]; then
188
+ log " ${RED}[FOUND]${NC} \$AWS_ACCESS_KEY_ID is set"
189
+ log_found "AWS" "\$AWS_ACCESS_KEY_ID" "Rotate key in IAM console, update env"
190
+ AWS_FOUND=1
191
+ fi
192
+
193
+ if [ -n "$AWS_SECRET_ACCESS_KEY" ]; then
194
+ log " ${RED}[FOUND]${NC} \$AWS_SECRET_ACCESS_KEY is set"
195
+ log_found "AWS" "\$AWS_SECRET_ACCESS_KEY" "Rotate key in IAM console, update env"
196
+ AWS_FOUND=1
197
+ fi
198
+
199
+ if [ -n "$AWS_SESSION_TOKEN" ]; then
200
+ log " ${YELLOW}[FOUND]${NC} \$AWS_SESSION_TOKEN is set (temporary)"
201
+ log_found "AWS" "\$AWS_SESSION_TOKEN" "Wait for expiration or re-authenticate with aws sso login"
202
+ AWS_FOUND=1
203
+ fi
204
+
205
+ if [ "$AWS_FOUND" -eq 0 ]; then
206
+ log " ${DIM}None found${NC}"
207
+ fi
208
+
209
+ log ""
210
+
211
+ ###########################################
212
+ # GCP CREDENTIALS
213
+ ###########################################
214
+
215
+ log "${BOLD}GCP${NC}"
216
+ GCP_FOUND=0
217
+
218
+ # Check Application Default Credentials
219
+ ADC_PATH="$HOME/.config/gcloud/application_default_credentials.json"
220
+ if [ -f "$ADC_PATH" ]; then
221
+ log " ${RED}[FOUND]${NC} Application Default Credentials"
222
+ log_found "GCP" "$ADC_PATH" "gcloud auth application-default revoke && gcloud auth application-default login"
223
+ GCP_FOUND=1
224
+ else
225
+ log_verbose " ${DIM}[SKIP]${NC} ADC not found"
226
+ fi
227
+
228
+ # Check gcloud auth
229
+ if [ -d "$HOME/.config/gcloud" ] && [ -f "$HOME/.config/gcloud/credentials.db" ]; then
230
+ log " ${RED}[FOUND]${NC} gcloud credentials.db"
231
+ log_found "GCP" "~/.config/gcloud/credentials.db" "gcloud auth revoke --all && gcloud auth login"
232
+ GCP_FOUND=1
233
+ fi
234
+
235
+ # Check GOOGLE_APPLICATION_CREDENTIALS
236
+ if [ -n "$GOOGLE_APPLICATION_CREDENTIALS" ]; then
237
+ if [ -f "$GOOGLE_APPLICATION_CREDENTIALS" ]; then
238
+ log " ${RED}[FOUND]${NC} \$GOOGLE_APPLICATION_CREDENTIALS points to: $GOOGLE_APPLICATION_CREDENTIALS"
239
+ log_found "GCP" "\$GOOGLE_APPLICATION_CREDENTIALS" "Rotate service account key in GCP Console"
240
+ GCP_FOUND=1
241
+ else
242
+ log_verbose " ${DIM}[SKIP]${NC} \$GOOGLE_APPLICATION_CREDENTIALS set but file doesn't exist"
243
+ fi
244
+ fi
245
+
246
+ if [ "$GCP_FOUND" -eq 0 ]; then
247
+ log " ${DIM}None found${NC}"
248
+ fi
249
+
250
+ log ""
251
+
252
+ ###########################################
253
+ # AZURE CREDENTIALS
254
+ ###########################################
255
+
256
+ log "${BOLD}Azure${NC}"
257
+ AZURE_FOUND=0
258
+
259
+ # Check ~/.azure directory
260
+ if [ -d "$HOME/.azure" ]; then
261
+ if [ -f "$HOME/.azure/accessTokens.json" ] || [ -f "$HOME/.azure/azureProfile.json" ]; then
262
+ log " ${RED}[FOUND]${NC} ~/.azure/ contains auth tokens"
263
+ log_found "Azure" "~/.azure/" "az logout && az login"
264
+ AZURE_FOUND=1
265
+ else
266
+ log_verbose " ${GREEN}[CLEAN]${NC} ~/.azure/ exists but no tokens found"
267
+ fi
268
+ else
269
+ log_verbose " ${DIM}[SKIP]${NC} ~/.azure/ not found"
270
+ fi
271
+
272
+ # Check Azure environment variables
273
+ if [ -n "$AZURE_CLIENT_SECRET" ]; then
274
+ log " ${RED}[FOUND]${NC} \$AZURE_CLIENT_SECRET is set"
275
+ log_found "Azure" "\$AZURE_CLIENT_SECRET" "Rotate client secret in Azure AD app registration"
276
+ AZURE_FOUND=1
277
+ fi
278
+
279
+ if [ -n "$AZURE_CLIENT_ID" ] && [ -n "$AZURE_TENANT_ID" ]; then
280
+ log " ${YELLOW}[INFO]${NC} Azure service principal env vars configured"
281
+ fi
282
+
283
+ if [ "$AZURE_FOUND" -eq 0 ]; then
284
+ log " ${DIM}None found${NC}"
285
+ fi
286
+
287
+ log ""
288
+
289
+ ###########################################
290
+ # GITHUB CREDENTIALS
291
+ ###########################################
292
+
293
+ log "${BOLD}GitHub${NC}"
294
+ GITHUB_FOUND=0
295
+
296
+ # Check GitHub CLI
297
+ if [ -f "$HOME/.config/gh/hosts.yml" ]; then
298
+ log " ${RED}[FOUND]${NC} GitHub CLI authenticated (~/.config/gh/hosts.yml)"
299
+ log_found "GitHub" "~/.config/gh/hosts.yml" "gh auth logout && gh auth login"
300
+ GITHUB_FOUND=1
301
+ else
302
+ log_verbose " ${DIM}[SKIP]${NC} GitHub CLI not authenticated"
303
+ fi
304
+
305
+ # Check for GITHUB_TOKEN / GH_TOKEN
306
+ if [ -n "$GITHUB_TOKEN" ]; then
307
+ log " ${RED}[FOUND]${NC} \$GITHUB_TOKEN is set"
308
+ log_found "GitHub" "\$GITHUB_TOKEN" "Revoke token at github.com/settings/tokens, regenerate"
309
+ GITHUB_FOUND=1
310
+ fi
311
+
312
+ if [ -n "$GH_TOKEN" ]; then
313
+ log " ${RED}[FOUND]${NC} \$GH_TOKEN is set"
314
+ log_found "GitHub" "\$GH_TOKEN" "Revoke token at github.com/settings/tokens, regenerate"
315
+ GITHUB_FOUND=1
316
+ fi
317
+
318
+ # Check .git/config for stored credentials (in home dir)
319
+ if [ -f "$HOME/.gitconfig" ]; then
320
+ if grep -qE "helper.*store|credential.*=.*https" "$HOME/.gitconfig" 2>/dev/null; then
321
+ log " ${YELLOW}[WARN]${NC} ~/.gitconfig uses credential store"
322
+ log_found "GitHub" "~/.gitconfig" "git config --global --unset credential.helper (if using store)"
323
+ GITHUB_FOUND=1
324
+ fi
325
+ fi
326
+
327
+ # Check .git-credentials
328
+ if [ -f "$HOME/.git-credentials" ]; then
329
+ log " ${RED}[FOUND]${NC} ~/.git-credentials (plaintext credentials)"
330
+ log_found "GitHub" "~/.git-credentials" "rm ~/.git-credentials && regenerate PATs"
331
+ GITHUB_FOUND=1
332
+ fi
333
+
334
+ if [ "$GITHUB_FOUND" -eq 0 ]; then
335
+ log " ${DIM}None found${NC}"
336
+ fi
337
+
338
+ log ""
339
+
340
+ ###########################################
341
+ # SSH KEYS
342
+ ###########################################
343
+
344
+ log "${BOLD}SSH${NC}"
345
+ SSH_FOUND=0
346
+
347
+ if [ -d "$HOME/.ssh" ]; then
348
+ # Count private keys (files without .pub extension that aren't config/known_hosts)
349
+ PRIVATE_KEYS=$(find "$HOME/.ssh" -type f ! -name "*.pub" ! -name "known_hosts*" ! -name "config" ! -name "authorized_keys" 2>/dev/null | wc -l | tr -d ' ')
350
+ if [ "$PRIVATE_KEYS" -gt 0 ]; then
351
+ log " ${RED}[FOUND]${NC} $PRIVATE_KEYS SSH private key(s) in ~/.ssh/"
352
+ log_found "SSH" "~/.ssh/" "ssh-keygen -t ed25519, update public keys on all services"
353
+ SSH_FOUND=1
354
+
355
+ # List the keys
356
+ if [ "$VERBOSE" = true ]; then
357
+ find "$HOME/.ssh" -type f ! -name "*.pub" ! -name "known_hosts*" ! -name "config" ! -name "authorized_keys" 2>/dev/null | while read -r key; do
358
+ log " - $(basename "$key")"
359
+ done
360
+ fi
361
+ else
362
+ log_verbose " ${DIM}[SKIP]${NC} No SSH private keys found"
363
+ fi
364
+ else
365
+ log_verbose " ${DIM}[SKIP]${NC} ~/.ssh/ not found"
366
+ fi
367
+
368
+ if [ "$SSH_FOUND" -eq 0 ]; then
369
+ log " ${DIM}None found${NC}"
370
+ fi
371
+
372
+ log ""
373
+
374
+ ###########################################
375
+ # DOCKER CREDENTIALS
376
+ ###########################################
377
+
378
+ log "${BOLD}Docker${NC}"
379
+ DOCKER_FOUND=0
380
+
381
+ if [ -f "$HOME/.docker/config.json" ]; then
382
+ if grep -q "auth" "$HOME/.docker/config.json" 2>/dev/null; then
383
+ log " ${RED}[FOUND]${NC} ~/.docker/config.json contains auth"
384
+ log_found "Docker" "~/.docker/config.json" "docker logout && docker login"
385
+ DOCKER_FOUND=1
386
+ else
387
+ log_verbose " ${GREEN}[CLEAN]${NC} ~/.docker/config.json exists (no auth)"
388
+ fi
389
+ else
390
+ log_verbose " ${DIM}[SKIP]${NC} ~/.docker/config.json not found"
391
+ fi
392
+
393
+ if [ "$DOCKER_FOUND" -eq 0 ]; then
394
+ log " ${DIM}None found${NC}"
395
+ fi
396
+
397
+ log ""
398
+
399
+ ###########################################
400
+ # KUBERNETES CREDENTIALS
401
+ ###########################################
402
+
403
+ log "${BOLD}Kubernetes${NC}"
404
+ K8S_FOUND=0
405
+
406
+ if [ -f "$HOME/.kube/config" ]; then
407
+ log " ${RED}[FOUND]${NC} ~/.kube/config"
408
+ log_found "Kubernetes" "~/.kube/config" "Rotate cluster credentials, re-run az aks get-credentials or equivalent"
409
+ K8S_FOUND=1
410
+ else
411
+ log_verbose " ${DIM}[SKIP]${NC} ~/.kube/config not found"
412
+ fi
413
+
414
+ if [ "$K8S_FOUND" -eq 0 ]; then
415
+ log " ${DIM}None found${NC}"
416
+ fi
417
+
418
+ log ""
419
+
420
+ ###########################################
421
+ # SENSITIVE ENVIRONMENT VARIABLES
422
+ ###########################################
423
+
424
+ log "${BOLD}Environment Variables${NC}"
425
+
426
+ # Count sensitive env vars (excluding ones we already checked)
427
+ SENSITIVE_PATTERNS="token|secret|password|credential|api.?key|auth"
428
+ EXCLUDED_VARS="NPM_TOKEN|NPM_CONFIG_TOKEN|AWS_ACCESS_KEY_ID|AWS_SECRET_ACCESS_KEY|AWS_SESSION_TOKEN|GITHUB_TOKEN|GH_TOKEN|GOOGLE_APPLICATION_CREDENTIALS|AZURE_CLIENT_SECRET"
429
+ SENSITIVE_VARS=$(env | grep -iE "$SENSITIVE_PATTERNS" | grep -vE "^($EXCLUDED_VARS)=" 2>/dev/null || true)
430
+ SENSITIVE_COUNT=$(echo "$SENSITIVE_VARS" | grep -c . 2>/dev/null || echo 0)
431
+
432
+ if [ "$SENSITIVE_COUNT" -gt 0 ]; then
433
+ log " ${YELLOW}[FOUND]${NC} $SENSITIVE_COUNT additional sensitive env var(s)"
434
+ log_found "Environment" "shell environment" "Check ~/.zshrc, ~/.bashrc, or shell profile for secrets"
435
+
436
+ if [ "$VERBOSE" = true ]; then
437
+ echo "$SENSITIVE_VARS" | while read -r line; do
438
+ VAR_NAME=$(echo "$line" | cut -d= -f1)
439
+ log " - \$$VAR_NAME"
440
+ done
441
+ fi
442
+ else
443
+ log " ${DIM}None found${NC}"
444
+ fi
445
+
446
+ log ""
447
+
448
+ ###########################################
449
+ # SUMMARY
450
+ ###########################################
451
+
452
+ log "${BOLD}========================================${NC}"
453
+
454
+ if [ "$TOTAL_FOUND" -gt 0 ]; then
455
+ log "${RED}${BOLD} CREDENTIALS FOUND: $TOTAL_FOUND${NC}"
456
+ log "${BOLD}========================================${NC}"
457
+ log ""
458
+ log "${BOLD}Rotation Instructions:${NC}"
459
+ log ""
460
+
461
+ # Group by service
462
+ CURRENT_SERVICE=""
463
+ for entry in "${ROTATION_INSTRUCTIONS[@]}"; do
464
+ SERVICE=$(echo "$entry" | cut -d'|' -f1)
465
+ LOCATION=$(echo "$entry" | cut -d'|' -f2)
466
+ INSTRUCTION=$(echo "$entry" | cut -d'|' -f3)
467
+
468
+ if [ "$SERVICE" != "$CURRENT_SERVICE" ]; then
469
+ log "${CYAN}$SERVICE:${NC}"
470
+ CURRENT_SERVICE="$SERVICE"
471
+ fi
472
+ log " ${DIM}$LOCATION${NC}"
473
+ log " ${BOLD}$INSTRUCTION${NC}"
474
+ log ""
475
+ done
476
+
477
+ log "${YELLOW}${BOLD}IMPORTANT:${NC} If your machine was compromised, assume ALL of these"
478
+ log "credentials were exfiltrated. Rotate them immediately."
479
+ log ""
480
+ log "${DIM}Note: This list is not exhaustive. You may need to rotate other"
481
+ log "credentials not detected by this scan (e.g., database passwords,"
482
+ log "API keys in config files, or service-specific tokens).${NC}"
483
+ log ""
484
+ log "See: ${CYAN}https://department-of-veterans-affairs.github.io/eert/${NC}"
485
+
486
+ EXIT_CODE=1
487
+ else
488
+ log "${GREEN}${BOLD} NO CREDENTIALS FOUND${NC}"
489
+ log "${BOLD}========================================${NC}"
490
+ log ""
491
+ log "No credential files or environment variables were detected."
492
+ log "This machine has minimal credential exposure risk."
493
+
494
+ EXIT_CODE=0
495
+ fi
496
+
497
+ log ""
498
+
499
+ ###########################################
500
+ # JSON OUTPUT
501
+ ###########################################
502
+
503
+ if [ "$JSON" = true ]; then
504
+ echo "{"
505
+ echo " \"status\": \"$([ "$TOTAL_FOUND" -gt 0 ] && echo 'CREDENTIALS_FOUND' || echo 'CLEAN')\","
506
+ echo " \"credentials_found\": $TOTAL_FOUND,"
507
+ echo " \"credentials\": ["
508
+
509
+ FIRST=true
510
+ for entry in "${ROTATION_INSTRUCTIONS[@]}"; do
511
+ SERVICE=$(echo "$entry" | cut -d'|' -f1)
512
+ LOCATION=$(echo "$entry" | cut -d'|' -f2)
513
+ INSTRUCTION=$(echo "$entry" | cut -d'|' -f3)
514
+
515
+ # Escape for JSON
516
+ LOCATION="${LOCATION//\\/\\\\}"
517
+ LOCATION="${LOCATION//\"/\\\"}"
518
+ INSTRUCTION="${INSTRUCTION//\\/\\\\}"
519
+ INSTRUCTION="${INSTRUCTION//\"/\\\"}"
520
+
521
+ if [ "$FIRST" = true ]; then
522
+ FIRST=false
523
+ else
524
+ echo ","
525
+ fi
526
+ printf " {\"service\": \"%s\", \"location\": \"%s\", \"rotation\": \"%s\"}" "$SERVICE" "$LOCATION" "$INSTRUCTION"
527
+ done
528
+
529
+ echo ""
530
+ echo " ],"
531
+ echo " \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
532
+ echo "}"
533
+ fi
534
+
535
+ exit $EXIT_CODE