ecr-scan-verifier 0.1.1 → 0.1.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.
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env bash
2
+ # integ-docs-gate.sh
3
+ #
4
+ # PreToolUse hook. Blocks `gh pr create` and `gh pr merge` unless the
5
+ # `integ-docs` markgate marker is fresh for the current content of
6
+ # .claude/skills/integ-test/SKILL.md, test/integ/README.md, and
7
+ # scripts/integ.sh (see .markgate.yml).
8
+ #
9
+ # To unblock: edit the offending files into consistency, then run the
10
+ # /verify-integ-docs skill (which calls scripts/verify-integ-docs.sh
11
+ # and, on pass, `markgate set integ-docs`).
12
+
13
+ set -u
14
+
15
+ # Resolve repo root from script location (.claude/hooks/integ-docs-gate.sh -> repo root).
16
+ REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
17
+
18
+ cmd=$(jq -r '.tool_input.command // ""' 2>/dev/null || echo "")
19
+
20
+ # Only gate `gh pr create` and `gh pr merge` — any other Bash invocation
21
+ # passes through.
22
+ if ! printf '%s' "$cmd" | grep -qE '\bgh[[:space:]]+pr[[:space:]]+(create|merge)\b'; then
23
+ exit 0
24
+ fi
25
+
26
+ cd "$REPO" 2>/dev/null || exit 0
27
+
28
+ # Prefer mise-pinned markgate (.mise.toml) so the schema version matches
29
+ # what the rest of the repo expects. Fall back to PATH binary.
30
+ if command -v mise >/dev/null 2>&1; then
31
+ markgate=(mise exec -- markgate)
32
+ elif command -v markgate >/dev/null 2>&1; then
33
+ markgate=(markgate)
34
+ else
35
+ cat >&2 <<EOF
36
+ Blocked by integ-docs-gate: markgate is not installed.
37
+
38
+ Install via mise (preferred — matches the version pinned in .mise.toml):
39
+ mise install
40
+
41
+ Or install markgate via your packager (Homebrew, ubi, source).
42
+ EOF
43
+ exit 2
44
+ fi
45
+
46
+ if "${markgate[@]}" verify integ-docs >/dev/null 2>&1; then
47
+ exit 0
48
+ fi
49
+
50
+ cat >&2 <<EOF
51
+ Blocked by integ-docs-gate: \`integ-docs\` markgate marker is stale or missing.
52
+
53
+ The skill (.claude/skills/integ-test/SKILL.md) and README
54
+ (test/integ/README.md) document the same workflow for two different
55
+ audiences (Claude / human). If they drift, a human following the README
56
+ hits a different code path than Claude following the skill — and bugs in
57
+ one don't surface in the other.
58
+
59
+ To unblock:
60
+
61
+ 1. Run the consistency check:
62
+ ./scripts/verify-integ-docs.sh
63
+
64
+ 2. Fix any drift it reports.
65
+
66
+ 3. Flip the marker:
67
+ mise exec -- markgate set integ-docs
68
+
69
+ (Or invoke the /verify-integ-docs skill, which does steps 1+3.)
70
+
71
+ If you genuinely have an emergency PR that has nothing to do with the
72
+ integ docs (and you confirmed they didn't drift), you can also just run
73
+ \`markgate set integ-docs\` directly — the marker just records that the
74
+ files agree with whatever set-time content they had.
75
+ EOF
76
+ exit 2
@@ -0,0 +1,18 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Bash",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": ".claude/hooks/integ-docs-gate.sh",
10
+ "if": "Bash(gh pr create*) or Bash(gh pr merge*)",
11
+ "timeout": 10,
12
+ "statusMessage": "Checking integ-docs marker..."
13
+ }
14
+ ]
15
+ }
16
+ ]
17
+ }
18
+ }
@@ -0,0 +1,500 @@
1
+ ---
2
+ name: integ-test
3
+ description: Orchestrate ecr-scan-verifier integ tests against real AWS. Handles Inspector enable/disable + propagation waits, scan-on-push toggling, image signing (Notation / Cosign KMS / Cosign Public Key / ECR Managed Signing), and cleanup. Use whenever the user wants to run anything under `test/integ/`.
4
+ argument-hint: "<status|basic|enhanced|signature|signature-notation|signature-cosign-kms|signature-cosign-publickey|signature-ecr-signing|all|cleanup|cleanup-signature> [--snapshot-only] [--no-restore]"
5
+ ---
6
+
7
+ # integ-test
8
+
9
+ End-to-end orchestrator for `test/integ/` tests. Replaces the manual checklist in `test/integ/README.md`.
10
+
11
+ Most AWS-mutating primitives live in [`scripts/integ.sh`](../../../scripts/integ.sh) as shell functions. This skill **always sources that file first** and composes the functions per mode — keep primitives there, keep orchestration here.
12
+
13
+ ```bash
14
+ . scripts/integ.sh
15
+ ```
16
+
17
+ After sourcing you have: `account_id`, `default_region`, `inspector_status`, `inspector_status_all`, `inspector_enable_all`, `inspector_disable_all`, `wait_inspector_status`, `wait_inspector_status_all`, `scan_on_push_set`, `wait_enhanced_engine_warmup`, `enhanced_run_with_retry`, `signer_profile_ensure`, `cosign_minimal_signing_config`, `cleanup_signature_artifacts`, `ecr_signing_setup`, `ecr_signing_teardown`.
18
+
19
+ In a worktree (or any clone without `node_modules`), run `pnpm install --frozen-lockfile` first — the `pnpm integ:*` scripts call `tsc` directly and will fail with `sh: tsc: command not found` otherwise.
20
+
21
+ ## Integ suite layout
22
+
23
+ | Directory | Regions exercised | Required state |
24
+ | ----------- | ------------------------------- | ----------------------------------------------------------- |
25
+ | `basic/` | `us-east-1 us-east-2 us-west-2` | Enhanced scanning (Inspector) **DISABLED** in all regions |
26
+ | `enhanced/` | `us-east-1 us-east-2 us-west-2` | Enhanced scanning (Inspector) **ENABLED** in all regions |
27
+ | `signature/`| default region only | Pre-signed images + SSM/KMS prerequisites (state-agnostic) |
28
+
29
+ Signature modes are single-region (resolved from `aws configure get region`). `basic`/`enhanced` always operate on **all three** regions.
30
+
31
+ ## Default behavior: real deploy
32
+
33
+ The skill **deploys to AWS by default**. The `pnpm integ:*` scripts wrap `integ-runner`; without `--update-on-failed`, integ-runner does a *snapshot template comparison only* and never touches AWS. With `--update-on-failed` (which the skill passes by default), it actually deploys.
34
+
35
+ If you only want the cheap snapshot comparison, pass `--snapshot-only`. The skill then degenerates to a single `pnpm integ:<mode>` call and **skips every AWS-mutating step** (Inspector toggle, scan-on-push toggle, signing setup).
36
+
37
+ ## Arguments
38
+
39
+ - `status` — only triage current AWS state, propose no changes
40
+ - `basic` — switch Inspector if needed, run `pnpm integ:basic:update`, restore
41
+ - `enhanced` — switch Inspector if needed, warm engine, run `pnpm integ:enhanced:update`, restore
42
+ - `signature` — run all four signature sub-modes back-to-back
43
+ - `signature-notation` — Notation (AWS Signer) sign + run `integ.notation`
44
+ - `signature-cosign-kms` — Cosign with KMS sign + run `integ.cosign-kms`
45
+ - `signature-cosign-publickey` — Cosign keypair sign + run `integ.cosign-publickey`
46
+ - `signature-ecr-signing` — ECR Managed Signing setup + run `integ.ecr-signing`
47
+ - `all` — run **everything** (enhanced → all signatures → basic), then auto-run `cleanup`
48
+ - `cleanup` — full teardown: signature artifacts + ECR signing repo + scan-on-push reset. Inspector state left alone (flip via `basic` / `enhanced` if needed). Idempotent.
49
+ - `cleanup-signature` — narrower: only signature artifacts (SSM params, KMS key deletion, local cosign keypair). Subset of `cleanup`.
50
+
51
+ Flags:
52
+
53
+ - `--snapshot-only` — skip every AWS-mutating step; just run `pnpm integ:<mode>` (template comparison only). Mutually exclusive with the orchestration in each mode below.
54
+ - `--no-restore` — skip restoring Inspector state at the end (useful when running multiple modes back-to-back). **Ignored by `all`**, which always restores.
55
+
56
+ ### When invoked without arguments
57
+
58
+ Use `AskUserQuestion` to elicit BOTH the mode and the snapshot flag:
59
+
60
+ 1. **Target**:
61
+ - `basic`
62
+ - `enhanced`
63
+ - `signature` (all four signature sub-modes)
64
+ - `all` (everything end-to-end with auto-cleanup)
65
+ - `status` (just triage, no test run)
66
+ 2. **Run mode**:
67
+ - **Deploy** (default) — real AWS deploy, refresh snapshots
68
+ - **Snapshot-only** — template comparison only, no AWS calls
69
+
70
+ Skip both prompts if the user already gave the target. If they gave the target but not the run-mode, only ask the second question.
71
+
72
+ ## `--snapshot-only` (any mode)
73
+
74
+ Single command, no helpers, no setup. The skill exits after this:
75
+
76
+ ```bash
77
+ pnpm integ # all directories
78
+ pnpm integ:basic # or one directory
79
+ pnpm integ:enhanced
80
+ pnpm integ:signature
81
+ ```
82
+
83
+ For per-test signature snapshots:
84
+
85
+ ```bash
86
+ pnpm integ:signature --language javascript --test-regex "integ.notation.js$"
87
+ ```
88
+
89
+ Nothing else in this document applies when `--snapshot-only` is set.
90
+
91
+ ## Post-run cleanup (deploy runs only)
92
+
93
+ After ANY deploy-mode invocation (single mode or `all`), the skill must:
94
+
95
+ 1. Confirm Inspector state has been restored as documented in the mode (or, with `--no-restore`, explicitly report what was left as-is).
96
+ 2. For `all` and any `signature-*` deploy run, finish with `cleanup_signature_artifacts` from `scripts/integ.sh`. Single `signature-*` runs leave SSM/KMS in place by default so the user can chain modes — call cleanup at the very end of the session.
97
+ 3. Print the full reporting summary (see [Reporting](#reporting)) — never end silently.
98
+
99
+ ## Common preamble (deploy runs)
100
+
101
+ ```bash
102
+ . scripts/integ.sh
103
+ ACCOUNT="$(account_id)"
104
+ ORIGINAL_STATE_EAST1="$(inspector_status us-east-1)"
105
+ ORIGINAL_STATE_EAST2="$(inspector_status us-east-2)"
106
+ ORIGINAL_STATE_WEST2="$(inspector_status us-west-2)"
107
+ ```
108
+
109
+ If the three states differ, surface it — that itself is worth flagging before running anything.
110
+
111
+ Build the Lambda once up front (every `pnpm integ:*` script chains this, but doing it once avoids repeating across modes):
112
+
113
+ ```bash
114
+ pnpm tsc -p tsconfig.dev.json
115
+ (cd assets/lambda && pnpm install --frozen-lockfile && pnpm build)
116
+ ```
117
+
118
+ ## Mode: `status`
119
+
120
+ ```bash
121
+ inspector_status_all
122
+ ```
123
+
124
+ Then recommend:
125
+
126
+ - All ENABLED → `enhanced/` is ready. `basic/` requires a disable cycle.
127
+ - All DISABLED → `basic/` is ready. `enhanced/` requires an enable cycle.
128
+ - Mixed → flag as anomaly; ask before proceeding.
129
+
130
+ Exit without changing any state.
131
+
132
+ ## Mode: `basic` (deploy)
133
+
134
+ `pnpm integ:basic:update` runs **all** basic tests including `integ.scan-on-push`, so scan-on-push must be on **before** the run.
135
+
136
+ ```bash
137
+ inspector_disable_all
138
+ wait_inspector_status_all DISABLED || exit 1
139
+ scan_on_push_set true
140
+
141
+ # Run, then ALWAYS restore scan-on-push (use a trap or explicit if/then)
142
+ if pnpm integ:basic:update; then status=0; else status=$?; fi
143
+ scan_on_push_set false
144
+ [ "$status" -eq 0 ] || exit "$status"
145
+ ```
146
+
147
+ ### Restore Inspector (unless `--no-restore`)
148
+
149
+ If any region's `ORIGINAL_STATE_*` was `ENABLED`, restore that region. Simplest correct approach when all three were originally identical: re-enable everywhere if any was originally ENABLED.
150
+
151
+ ```bash
152
+ if [ "$ORIGINAL_STATE_EAST1" = "ENABLED" ] || \
153
+ [ "$ORIGINAL_STATE_EAST2" = "ENABLED" ] || \
154
+ [ "$ORIGINAL_STATE_WEST2" = "ENABLED" ]; then
155
+ inspector_enable_all
156
+ wait_inspector_status_all ENABLED || exit 1
157
+ fi
158
+ ```
159
+
160
+ ## Mode: `enhanced` (deploy)
161
+
162
+ ```bash
163
+ # Capture the "was DISABLED in any region" condition BEFORE we flip,
164
+ # because wait_enhanced_engine_warmup needs to know whether a real
165
+ # transition is happening (the engine only lags on fresh enable).
166
+ TRANSITION="ENABLED"
167
+ if [ "$ORIGINAL_STATE_EAST1" != "ENABLED" ] || \
168
+ [ "$ORIGINAL_STATE_EAST2" != "ENABLED" ] || \
169
+ [ "$ORIGINAL_STATE_WEST2" != "ENABLED" ]; then
170
+ TRANSITION="DISABLED"
171
+ fi
172
+
173
+ inspector_enable_all
174
+ wait_inspector_status_all ENABLED || exit 1
175
+
176
+ # Engine warmup: empirically 20-30 min on a fresh enable.
177
+ wait_enhanced_engine_warmup "$TRANSITION" 1200
178
+
179
+ # Up to 3 attempts × 10 min gap. Calibrated to the warmup tail —
180
+ # NOT to flaky tests. If 3 fail, stop.
181
+ MAX_ATTEMPTS=3 RETRY_GAP_SECS=600 \
182
+ enhanced_run_with_retry pnpm integ:enhanced:update || exit 1
183
+ ```
184
+
185
+ ### Restore (unless `--no-restore`)
186
+
187
+ If all three `ORIGINAL_STATE_*` were `DISABLED`, disable in all regions and poll until DISABLED. Otherwise leave as-is.
188
+
189
+ ## Signature modes — shared preamble (deploy)
190
+
191
+ ```bash
192
+ rm -rf cdk.out/ # avoid stale assets manifests from prior signature runs
193
+ REGION="$(default_region)"
194
+ REGISTRY="${ACCOUNT}.dkr.ecr.${REGION}.amazonaws.com"
195
+ REPO="cdk-hnb659fds-container-assets-${ACCOUNT}-${REGION}"
196
+ ```
197
+
198
+ `signature-ecr-signing` uses its own dedicated repo (set up via `ecr_signing_setup`).
199
+
200
+ ## Mode: `signature-notation` (deploy)
201
+
202
+ **Profile reuse**: AWS Signer profiles cannot be deleted (only canceled), and a canceled profile cannot be reused. We intentionally keep `EcrScanVerifierTestProfile` `Active` across runs. **`put-signing-profile` is NOT actually idempotent** — calling it for an existing Active profile returns `ProfileAlreadyExists`. Use `signer_profile_ensure` from `scripts/integ.sh` (or check with `get-signing-profile` first). If you find it `Canceled`, use a new name (e.g. `EcrScanVerifierTestProfile2`) and substitute throughout.
203
+
204
+ Pre-flight: `notation version` should succeed. If absent, install via the AWS Signer installer pkg (see `test/integ/README.md` → Notation install — do not improvise URLs).
205
+
206
+ ```bash
207
+ # 1. Ensure profile exists (idempotent wrapper; raw put-signing-profile is NOT)
208
+ PROFILE_ARN="$(signer_profile_ensure)" || exit 1
209
+
210
+ # 2. Synth + publish only the Docker asset (no stack deploy yet)
211
+ npx cdk synth --app 'node test/integ/signature/integ.notation.js' -o cdk.out
212
+ npx cdk-assets -p cdk.out/NotationSignatureStack.assets.json publish
213
+
214
+ # 3. Resolve the test fixture digest (NOT the Lambda function image)
215
+ ASSET_HASH=$(cat cdk.out/NotationSignatureStack.assets.json | tr ',' '\n' | \
216
+ grep '"imageTag"' | head -1 | cut -d'"' -f4)
217
+ DIGEST=$(aws ecr describe-images --repository-name "${REPO}" \
218
+ --image-ids imageTag="${ASSET_HASH}" \
219
+ --query 'imageDetails[0].imageDigest' --output text)
220
+
221
+ # 4. Sign
222
+ aws ecr get-login-password | notation login --username AWS --password-stdin "${REGISTRY}"
223
+ notation sign \
224
+ --plugin com.amazonaws.signer.notation.plugin \
225
+ --id "${PROFILE_ARN}" \
226
+ "${REGISTRY}/${REPO}@${DIGEST}"
227
+
228
+ # 5. Run
229
+ pnpm integ:signature:update --language javascript --test-regex "integ.notation.js$"
230
+ ```
231
+
232
+ **Immutable-tag recovery**: If `notation sign` fails with `tag invalid: ... already exists ... cannot be overwritten because the tag is immutable`, a previous attempt left a partial referrers index tag. The bootstrap repo uses immutable tags, so the leftover must be deleted before retrying:
233
+
234
+ ```bash
235
+ REFERRER_TAG="sha256-${DIGEST#sha256:}"
236
+ aws ecr batch-delete-image --repository-name "${REPO}" \
237
+ --image-ids imageTag="${REFERRER_TAG}"
238
+ ```
239
+
240
+ Then retry the `notation sign` step.
241
+
242
+ ## Mode: `signature-cosign-kms` (deploy)
243
+
244
+ **Rekor note**: the Lambda verifier always skips Rekor. Sign with the same skip so the test matches Lambda behavior — verification then works offline / inside VPC without internet.
245
+
246
+ Pre-flight: `cosign version` and `jq --version` should both succeed. Do NOT auto-install via brew — the user may not use brew. If either is missing, abort and tell the user which package + suggested install commands (`brew install cosign jq`, `apt install jq`, sigstore release page for cosign, etc.).
247
+
248
+ ```bash
249
+ command -v cosign >/dev/null || { echo "cosign not installed. See https://docs.sigstore.dev/cosign/installation/" >&2; exit 1; }
250
+ command -v jq >/dev/null || { echo "jq not installed." >&2; exit 1; }
251
+
252
+ KMS_KEY_ID=$(aws kms create-key \
253
+ --key-usage SIGN_VERIFY --key-spec ECC_NIST_P256 \
254
+ --query 'KeyMetadata.KeyId' --output text)
255
+ aws ssm put-parameter \
256
+ --name /ecr-scan-verifier/cosign-kms-key-id \
257
+ --value "${KMS_KEY_ID}" --type String --overwrite
258
+ KMS_KEY_ARN=$(aws kms describe-key --key-id "${KMS_KEY_ID}" \
259
+ --query 'KeyMetadata.Arn' --output text)
260
+
261
+ npx cdk synth --app 'node test/integ/signature/integ.cosign-kms.js' -o cdk.out
262
+ npx cdk-assets -p cdk.out/CosignKmsSignatureStack.assets.json publish
263
+
264
+ ASSET_HASH=$(cat cdk.out/CosignKmsSignatureStack.assets.json | tr ',' '\n' | \
265
+ grep '"imageTag"' | head -1 | cut -d'"' -f4)
266
+ DIGEST=$(aws ecr describe-images --repository-name "${REPO}" \
267
+ --image-ids imageTag="${ASSET_HASH}" \
268
+ --query 'imageDetails[0].imageDigest' --output text)
269
+
270
+ aws ecr get-login-password --region "${REGION}" | cosign login --username AWS --password-stdin "${REGISTRY}"
271
+ # cosign 3.x requires stripping rekor + oidc + ca + tsa from the signing-config
272
+ # when using --key (keyful). Leaving any of them in causes a silent hang on
273
+ # "Signing artifact...". `cosign_minimal_signing_config` does the strip.
274
+ cosign_minimal_signing_config /tmp/signing-config.json
275
+ cosign sign --signing-config /tmp/signing-config.json \
276
+ --key "awskms:///${KMS_KEY_ARN}" "${REGISTRY}/${REPO}@${DIGEST}"
277
+
278
+ pnpm integ:signature:update --language javascript --test-regex "integ.cosign-kms.js$"
279
+ ```
280
+
281
+ ## Mode: `signature-cosign-publickey` (deploy)
282
+
283
+ Same Rekor-skip rule. `COSIGN_PASSWORD=""` required on `generate-key-pair` and `sign` to avoid the interactive password prompt blocking unattended runs.
284
+
285
+ Same pre-flight as `signature-cosign-kms`: check `cosign` and `jq`, abort with install hint if missing — do not auto-brew-install.
286
+
287
+ ```bash
288
+ command -v cosign >/dev/null || { echo "cosign not installed. See https://docs.sigstore.dev/cosign/installation/" >&2; exit 1; }
289
+ command -v jq >/dev/null || { echo "jq not installed." >&2; exit 1; }
290
+
291
+ COSIGN_PASSWORD="" cosign generate-key-pair
292
+ aws ssm put-parameter \
293
+ --name /ecr-scan-verifier/cosign-public-key \
294
+ --value "$(cat cosign.pub)" --type String --overwrite
295
+
296
+ npx cdk synth --app 'node test/integ/signature/integ.cosign-publickey.js' -o cdk.out
297
+ npx cdk-assets -p cdk.out/CosignPublicKeySignatureStack.assets.json publish
298
+
299
+ ASSET_HASH=$(cat cdk.out/CosignPublicKeySignatureStack.assets.json | tr ',' '\n' | \
300
+ grep '"imageTag"' | head -1 | cut -d'"' -f4)
301
+ DIGEST=$(aws ecr describe-images --repository-name "${REPO}" \
302
+ --image-ids imageTag="${ASSET_HASH}" \
303
+ --query 'imageDetails[0].imageDigest' --output text)
304
+
305
+ aws ecr get-login-password --region "${REGION}" | cosign login --username AWS --password-stdin "${REGISTRY}"
306
+ # cosign 3.x requires stripping rekor + oidc + ca + tsa from the signing-config
307
+ # when using --key (keyful). Leaving any of them in causes a silent hang on
308
+ # "Signing artifact...". `cosign_minimal_signing_config` does the strip.
309
+ cosign_minimal_signing_config /tmp/signing-config.json
310
+ COSIGN_PASSWORD="" cosign sign --signing-config /tmp/signing-config.json \
311
+ --key cosign.key "${REGISTRY}/${REPO}@${DIGEST}"
312
+
313
+ pnpm integ:signature:update --language javascript --test-regex "integ.cosign-publickey.js$"
314
+ ```
315
+
316
+ `cosign.key` / `cosign.pub` are git-ignored. Do NOT commit them, and remove them via `cleanup-signature`.
317
+
318
+ ## Mode: `signature-ecr-signing` (deploy)
319
+
320
+ ECR Managed Signing auto-signs on push when a signing-configuration matches the repository. The integ uses `ECRDeployment` to copy a CDK asset into a signing-enabled repo so that the push triggers a signature.
321
+
322
+ **Regional gotcha**: `aws ecr put-signing-configuration` is not available in every region. If the API call returns `UnknownOperationException` or similar, abort and report — do not silently fall back.
323
+
324
+ **Permissions gotcha**: the caller needs `signer:*` including `signer:SignPayload`. Surface a friendly error on `AccessDenied`.
325
+
326
+ ```bash
327
+ ecr_signing_setup
328
+
329
+ if pnpm integ:signature:update --language javascript --test-regex "integ.ecr-signing.js$"; then
330
+ status=0
331
+ else
332
+ status=$?
333
+ fi
334
+ ecr_signing_teardown
335
+ exit "$status"
336
+ ```
337
+
338
+ ## Mode: `signature` (deploy)
339
+
340
+ Runs all four signature sub-modes back-to-back. They are state-agnostic w.r.t. Inspector, but each mutates SSM/KMS/ECR and synths into the shared `cdk.out/`, so order matters.
341
+
342
+ Failures in earlier modes do NOT short-circuit later ones — capture per-mode status and surface them all in the final report. Each mode's preamble (`rm -rf cdk.out/`, REGION/REGISTRY/REPO export) must run anew per iteration.
343
+
344
+ ```bash
345
+ sig_status=()
346
+ for mode in signature-notation signature-cosign-kms signature-cosign-publickey signature-ecr-signing; do
347
+ echo "=== Running mode: $mode ==="
348
+ if run_mode "$mode"; then
349
+ sig_status+=("$mode: PASS")
350
+ else
351
+ sig_status+=("$mode: FAIL")
352
+ fi
353
+ done
354
+ printf '%s\n' "${sig_status[@]}"
355
+ ```
356
+
357
+ After the loop, finish with `cleanup_signature_artifacts`.
358
+
359
+ ## Mode: `all` (deploy)
360
+
361
+ Runs **every** integ test end-to-end with optimal Inspector state transitions.
362
+
363
+ State-transition strategy (minimizes Inspector flips, which each cost a ~5 min poll + the ~20 min engine warmup on enable):
364
+
365
+ 1. **Enable Inspector** (if not already) and absorb the engine warmup. Serves both `enhanced` AND positions us for `signature-*` (state-agnostic but cheaper to keep ENABLED across them).
366
+ 2. **Run `enhanced`** with the 3-attempt retry loop.
367
+ 3. **Run all four `signature-*` modes**.
368
+ 4. **Flip Inspector to DISABLED** + poll.
369
+ 5. **Run `basic`** (proactive scan-on-push enable + unconditional restore).
370
+ 6. **Restore Inspector to `ORIGINAL_STATE_*`** — `all` IGNORES `--no-restore` because a both-directions sequence has no obvious "as-found" target.
371
+ 7. **Run `cleanup_signature_artifacts`** unconditionally.
372
+
373
+ Pseudocode:
374
+
375
+ ```bash
376
+ . scripts/integ.sh
377
+ ACCOUNT="$(account_id)"
378
+ ORIGINAL_STATE_EAST1="$(inspector_status us-east-1)"
379
+ ORIGINAL_STATE_EAST2="$(inspector_status us-east-2)"
380
+ ORIGINAL_STATE_WEST2="$(inspector_status us-west-2)"
381
+
382
+ pnpm tsc -p tsconfig.dev.json
383
+ (cd assets/lambda && pnpm install --frozen-lockfile && pnpm build)
384
+
385
+ # --- 1+2: enhanced ---
386
+ TRANSITION="ENABLED"
387
+ if [ "$ORIGINAL_STATE_EAST1" != "ENABLED" ] || \
388
+ [ "$ORIGINAL_STATE_EAST2" != "ENABLED" ] || \
389
+ [ "$ORIGINAL_STATE_WEST2" != "ENABLED" ]; then
390
+ TRANSITION="DISABLED"
391
+ fi
392
+ inspector_enable_all
393
+ wait_inspector_status_all ENABLED || exit 1
394
+ wait_enhanced_engine_warmup "$TRANSITION" 1200
395
+
396
+ results=()
397
+ if MAX_ATTEMPTS=3 RETRY_GAP_SECS=600 \
398
+ enhanced_run_with_retry pnpm integ:enhanced:update; then
399
+ results+=("enhanced: PASS")
400
+ else
401
+ results+=("enhanced: FAIL")
402
+ fi
403
+
404
+ # --- 3: all signatures ---
405
+ for mode in signature-notation signature-cosign-kms signature-cosign-publickey signature-ecr-signing; do
406
+ if run_signature_mode "$mode"; then
407
+ results+=("$mode: PASS")
408
+ else
409
+ results+=("$mode: FAIL")
410
+ fi
411
+ done
412
+
413
+ # --- 4+5: basic ---
414
+ inspector_disable_all
415
+ wait_inspector_status_all DISABLED || exit 1
416
+ scan_on_push_set true
417
+ if pnpm integ:basic:update; then
418
+ results+=("basic: PASS")
419
+ else
420
+ results+=("basic: FAIL")
421
+ fi
422
+ scan_on_push_set false
423
+
424
+ # --- 6: restore Inspector (always for `all`) ---
425
+ if [ "$ORIGINAL_STATE_EAST1" = "ENABLED" ] || \
426
+ [ "$ORIGINAL_STATE_EAST2" = "ENABLED" ] || \
427
+ [ "$ORIGINAL_STATE_WEST2" = "ENABLED" ]; then
428
+ inspector_enable_all
429
+ wait_inspector_status_all ENABLED || exit 1
430
+ fi
431
+
432
+ # --- 7: cleanup (full) ---
433
+ cleanup_signature_artifacts
434
+ ecr_signing_teardown
435
+ scan_on_push_set false
436
+
437
+ printf '%s\n' "${results[@]}"
438
+ ```
439
+
440
+ Wall-clock budget for a clean `all` run: roughly 45–80 minutes depending on Inspector warmup and signature retries. Plan accordingly.
441
+
442
+ ## Mode: `cleanup`
443
+
444
+ Full teardown of everything this skill can leave behind. Idempotent — safe to run any number of times, even when nothing was set up. Used as the final step of `all`, and useful on its own when interrupting a partial signature run.
445
+
446
+ What it touches:
447
+
448
+ - Signature artifacts (delegates to `cleanup_signature_artifacts`): SSM params, KMS key 7-day deletion schedule, local cosign keypair
449
+ - ECR signing dedicated repo + signing-configuration (delegates to `ecr_signing_teardown`)
450
+ - Bootstrap-repo scan-on-push reset to `false` in all three regions
451
+
452
+ What it does NOT touch:
453
+
454
+ - Inspector enable/disable state (per-account decision; flip via `basic` / `enhanced` if needed)
455
+ - `EcrScanVerifierTestProfile` AWS Signer profile (cancellation is permanent — left Active by design)
456
+ - Pushed Docker / signature artifacts in the bootstrap repo (immutable, cannot be cleaned)
457
+
458
+ ```bash
459
+ . scripts/integ.sh
460
+ cleanup_signature_artifacts
461
+ ecr_signing_teardown # idempotent (|| true on each step)
462
+ scan_on_push_set false
463
+ ```
464
+
465
+ Report what was actually removed vs what was already absent.
466
+
467
+ ## Mode: `cleanup-signature`
468
+
469
+ ```bash
470
+ cleanup_signature_artifacts
471
+ ```
472
+
473
+ The function resolves the KMS key id from SSM before deleting the param. If SSM is already gone and the id can't be recovered, it prints a WARN and skips KMS deletion — ask the user for the key id rather than guess.
474
+
475
+ ## Reporting
476
+
477
+ At the end of every run, print:
478
+
479
+ - Mode invoked + whether `--snapshot-only` was set + duration
480
+ - `ORIGINAL_STATE_*` per region and whether each was restored (or "n/a" for snapshot-only)
481
+ - Test pass/fail counts (parse from `integ-runner` output)
482
+ - Any leftover side effects (e.g. `EcrScanVerifierTestProfile` left Active intentionally; signing-configuration disabled; `cosign.key` removed)
483
+
484
+ Never end with stale state silently. If a restore step was skipped or failed, say so explicitly.
485
+
486
+ ## Important
487
+
488
+ - **Deploy is the default.** Snapshot-only is opt-in via `--snapshot-only`, which collapses every mode to a single `pnpm integ:<mode>` call with no AWS calls and no state changes.
489
+ - **Always operate on all three regions** (`us-east-1 us-east-2 us-west-2`) when toggling Inspector — partial toggles cause hard-to-debug test failures.
490
+ - **Poll with a bounded loop** (`wait_inspector_status` from `scripts/integ.sh`) — never use a bare `sleep` for state convergence.
491
+ - **`enhanced` engine lag is long**: status flipping to `ENABLED` is not the same as the scanning engine being ready. On a fresh DISABLED→ENABLED transition, sleep 1200s (20 min) before the first test attempt, then allow up to 3 total attempts with 600s (10 min) gaps. This worst-case ~40 min budget matches observed warmup. If all 3 fail, the cause is not propagation lag — stop waiting and investigate.
492
+ - **Always enable scan-on-push proactively for `basic`** — `pnpm integ:basic:update` runs the `integ.scan-on-push` test in the same suite, so toggling on after a failure is too late.
493
+ - **Never cancel `EcrScanVerifierTestProfile`** — cancellation is permanent and blocks future runs under the same name.
494
+ - **`cosign generate-key-pair` and `cosign sign` need `COSIGN_PASSWORD=""`** when run unattended — otherwise they hang on a password prompt.
495
+ - **Never commit `cosign.key` / `cosign.pub`** — they're git-ignored, keep it that way.
496
+ - **Bootstrap repo uses immutable tags** — leftover Notation referrer tags from a failed `notation sign` MUST be deleted before retrying; do not work around with a fresh tag.
497
+ - **`signature-ecr-signing` teardown is not optional** — wrap the test runner so `ecr_signing_teardown` runs even on failure.
498
+ - **`cosign sign` looks hung** — on success it emits only `Signing artifact...` to stderr and then goes silent until the OCI referrer push completes. Use `cosign sign -d` for verbose HTTP logging when debugging.
499
+ - **worktrees need `pnpm install --frozen-lockfile` first** — the `pnpm integ:*` scripts call `tsc` directly (not via `npx tsc`); without local `node_modules` the script fails with `sh: tsc: command not found`.
500
+ - When in doubt about which test to run, call `AskUserQuestion` rather than guess.
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: verify-integ-docs
3
+ description: Verify the integ-test skill, test/integ/README.md, and scripts/integ.sh stay consistent. Runs the mechanical script checks PLUS LLM-only semantic checks (mode-description agreement, code-example coherence, cross-reference resolution, depth parity, helper-arg correctness) that a regex can't catch. Sets the `integ-docs` markgate marker on full pass.
4
+ ---
5
+
6
+ # verify-integ-docs
7
+
8
+ The integ workflow is documented in three places that must agree:
9
+
10
+ | Source | Audience |
11
+ | ----------------------------------- | ---------------------- |
12
+ | `.claude/skills/integ-test/SKILL.md`| Claude Code (automated)|
13
+ | `test/integ/README.md` | humans (manual runs) |
14
+ | `scripts/integ.sh` | shared shell helpers |
15
+
16
+ Both docs are intentionally redundant — the skill drives Claude through the workflow, the README lets a human run the same workflow by hand. This skill keeps them in lockstep using **two layers**:
17
+
18
+ 1. **Mechanical** (`scripts/verify-integ-docs.sh`) — name parity, dead-helper detection, anti-pattern grep. Cheap, deterministic, runnable in CI.
19
+ 2. **Semantic** (this skill) — does each mode actually mean the same thing in both docs? Do code examples produce equivalent results? Are cross-references valid? Only an LLM that reads both docs side-by-side can answer these.
20
+
21
+ Both layers must pass before the marker flips. If only the mechanical layer matters, use `markgate run integ-docs -- ./scripts/verify-integ-docs.sh` directly instead of this skill — but you'll miss the kinds of drift listed under "Semantic checks" below.
22
+
23
+ ## Layer 1: mechanical (script)
24
+
25
+ `scripts/verify-integ-docs.sh` enforces:
26
+
27
+ 1. **All three source files exist** — deleting one without the other would leave stale references.
28
+ 2. **Mode parity** — every mode in the SKILL's `argument-hint:` frontmatter must appear in both the SKILL's `## Arguments` list AND the README's `### Modes` table.
29
+ 3. **Helper parity** — every function defined in `scripts/integ.sh` must be referenced from at least one of the two docs (catches dead helpers and rename drift).
30
+ 4. **Both docs source the helpers** — both must reference `scripts/integ.sh`.
31
+ 5. **No raw `aws signer put-signing-profile`** — it's NOT idempotent (returns `ProfileAlreadyExists` on existing Active profile). Must use `signer_profile_ensure`.
32
+ 6. **No insufficient cosign signing-config strip** — `del(.rekorTlogUrls)` alone causes cosign 3.x to silently hang on `--key` signing. Must use `cosign_minimal_signing_config` (also strips oidc / ca / tsa).
33
+ 7. **`signature-*` mode test sources exist** — every `signature-<name>` mode must have a corresponding `test/integ/signature/integ.<name>.ts`.
34
+
35
+ ```bash
36
+ ./scripts/verify-integ-docs.sh
37
+ ```
38
+
39
+ If it fails, fix the reported drift first; do NOT proceed to layer 2 or set the marker.
40
+
41
+ ## Layer 2: semantic (this skill, LLM-only)
42
+
43
+ Run these AFTER layer 1 passes. Each catches a class of drift the script cannot.
44
+
45
+ ### S1. Per-mode description agreement
46
+
47
+ For each mode in `argument-hint:`, compare:
48
+
49
+ - the SKILL's `## Arguments` line: `` - `mode` — <SKILL description> ``
50
+ - the README's `### Modes` table row: `` | `mode` | <README description> | ``
51
+
52
+ These are written for different audiences (LLM vs human), so they should NOT be byte-identical. They MUST claim the same behavior. Flag drift such as:
53
+
54
+ - **Contradictory claims**: SKILL says "switch Inspector if needed, run …, restore" but README says "Run …; no Inspector toggle" → one is wrong.
55
+ - **Coverage mismatch**: SKILL describes 3 sub-steps, README describes only 1 → README is misleading; either expand it or compress the SKILL.
56
+ - **Out-of-date defaults**: SKILL says "deploys by default" but README says "snapshot-only by default" → polarity skew.
57
+
58
+ ### S2. Code-example coherence
59
+
60
+ For each mode that has a code example in BOTH docs (notation, cosign-kms, cosign-publickey, ecr-signing), read the SKILL and README versions side-by-side and verify they would produce the same result:
61
+
62
+ - Same commands in the same order (allow trivial reordering only when results are independent).
63
+ - Same flags (e.g., `--region "${REGION}"` vs no region).
64
+ - Same helper invocations (e.g., both use `signer_profile_ensure`, not one with `aws signer put-signing-profile`).
65
+ - Same env vars (`COSIGN_PASSWORD=""` present in both, etc.).
66
+
67
+ If they diverge, ask: which is right? Usually the SKILL (more recently audited) — port the change to the README, or vice versa.
68
+
69
+ ### S3. Cross-reference resolution
70
+
71
+ Every markdown link of the form `[text](#anchor)` in SKILL.md or README.md must resolve to an actual heading in the same document. Common breaks:
72
+
73
+ - Heading renamed but the anchor link not updated.
74
+ - Anchor uses underscore where the heading slug uses hyphen.
75
+ - Link refers to a section that exists in the OTHER doc (not the same one).
76
+
77
+ Also check that `[text](../../scripts/integ.sh)` and similar relative paths actually resolve from the doc's location.
78
+
79
+ ### S4. Helper-argument correctness
80
+
81
+ For each helper from `scripts/integ.sh` that appears in either doc, confirm the call sites use valid arguments:
82
+
83
+ - `scan_on_push_set true|false` — not `enable` / `on` / `yes`.
84
+ - `wait_inspector_status_all ENABLED|DISABLED` — uppercase string literals.
85
+ - `wait_enhanced_engine_warmup ENABLED|DISABLED [secs]` — same.
86
+ - `signer_profile_ensure` — no args.
87
+
88
+ The script's helper-parity check (mechanical #3) only verifies the **name** is mentioned; it cannot check the **arguments**.
89
+
90
+ ### S5. Depth parity
91
+
92
+ For each mode, neither doc's explanation should be more than ~3× the length of the other. The two are for different audiences, but a 1-line README row plus a 50-line SKILL section means the README is silently lying about how complex the mode is. Flag and either expand the shorter side or trim the longer.
93
+
94
+ ### S6. Drift in "Important" / gotcha lists
95
+
96
+ Compare the bullet lists at the end of each doc ("Important", "Important Note", trailing tips). Any caveat present in one but missing from the other is a candidate for porting. Examples that have bitten us before:
97
+
98
+ - "Bootstrap repo uses immutable tags — must delete leftover Notation referrer tag before retrying"
99
+ - "cosign 3.x signing-config needs oidc/ca/tsa stripped, not just rekor"
100
+ - "AWS Signer profile cancellation is permanent"
101
+
102
+ If any of those appear in one doc but not the other, port them.
103
+
104
+ ## When to run
105
+
106
+ - After editing any of `.claude/skills/integ-test/SKILL.md`, `test/integ/README.md`, or `scripts/integ.sh`.
107
+ - The `integ-docs-gate.sh` PreToolUse hook blocks `gh pr create` / `gh pr merge` when the marker is stale, so a missed run surfaces at PR time.
108
+ - The `.github/workflows/verify-integ-docs.yml` workflow runs the mechanical script on every PR. **It does NOT run the semantic checks above** — those still require this skill (an LLM). CI catches the cheap drift; the skill catches the expensive drift.
109
+
110
+ ## Setting the marker
111
+
112
+ ONLY after both layer 1 (script) and layer 2 (S1–S6) pass:
113
+
114
+ ```bash
115
+ if command -v mise >/dev/null 2>&1; then
116
+ mise exec -- markgate set integ-docs
117
+ else
118
+ markgate set integ-docs
119
+ fi
120
+ ```
121
+
122
+ If layer 1 failed → fix mechanical drift, re-run. If any semantic check (S1–S6) flagged something → fix the underlying doc drift (or surface to the user when the right answer isn't obvious), THEN set the marker. Setting the marker on a failed run defeats the entire gate.
123
+
124
+ ## Recovery from drift
125
+
126
+ The most common failures and their fixes:
127
+
128
+ - **Mode missing from one doc** (mech #2): add it to the missing place. Don't remove it from the other.
129
+ - **Helper not referenced** (mech #3): either reference it from the appropriate doc OR delete it from `scripts/integ.sh` if truly unused.
130
+ - **Raw `put-signing-profile`** (mech #5): replace with `signer_profile_ensure`.
131
+ - **Raw `del(.rekorTlogUrls)`** (mech #6): replace with `cosign_minimal_signing_config /tmp/signing-config.json`.
132
+ - **Signature mode without matching test** (mech #7): either add the test (`test/integ/signature/integ.<name>.ts` + build), or remove the mode from the skill and README.
133
+ - **Per-mode description disagreement** (sem S1): pick the audience-appropriate wording for each, but make sure they describe the same behavior. Don't silently delete claims to "fix" the diff.
134
+ - **Code example divergence** (sem S2): identify which version was changed last (git blame) and port to the other.
135
+ - **Broken cross-reference** (sem S3): fix the anchor or rename the heading consistently.
136
+ - **Wrong helper arg** (sem S4): fix the call site — the helper's contract is authoritative, not the doc.
137
+ - **Depth imbalance** (sem S5): usually expand the README (the human audience needs MORE detail, not less, even though the SKILL can be terse with LLM context).
138
+ - **Caveat in one doc only** (sem S6): port it.
139
+
140
+ ## Important
141
+
142
+ - **Layer 2 is the skill's reason to exist.** If you only run the script, prefer `markgate run integ-docs -- ./scripts/verify-integ-docs.sh` and skip this skill entirely. The skill's value is doing what `grep` can't.
143
+ - **Never set the marker on a failed run.** The gate's whole point is that the marker is an audit trail saying "yes, these files agree, and I personally read them." Setting it to silence a failure is the worst possible move.
144
+ - **Adding a new helper to `scripts/integ.sh`** requires using it from at least one doc in the same change (mechanical check), AND documenting its arg contract (semantic check S4).
145
+ - **Adding a new integ test file** alone does NOT invalidate the marker (only SKILL.md / README.md / scripts/integ.sh do, per `.markgate.yml` scope). New tests typically come with README changes anyway, which trip the marker naturally.
package/.jsii CHANGED
@@ -5622,6 +5622,6 @@
5622
5622
  "symbolId": "src/signature-verification:VerificationOptions"
5623
5623
  }
5624
5624
  },
5625
- "version": "0.1.1",
5626
- "fingerprint": "6WYnUY67CfmrtryCWmZ/LFsrWjftPnD0AbNKSfJKXG8="
5625
+ "version": "0.1.2",
5626
+ "fingerprint": "EPuVTblfXSkiSlj9KwfRbHNKkJE9igTQ8Rr0HbSceZQ="
5627
5627
  }
package/.markgate.yml ADDED
@@ -0,0 +1,25 @@
1
+ # markgate configuration — https://github.com/go-to-k/markgate
2
+ #
3
+ # Gates:
4
+ # integ-docs — keeps the three integ-workflow sources in lockstep:
5
+ # .claude/skills/integ-test/SKILL.md (automated),
6
+ # test/integ/README.md (manual),
7
+ # scripts/integ.sh (shared helpers).
8
+ # Set by the /verify-integ-docs skill after
9
+ # scripts/verify-integ-docs.sh passes. The PreToolUse
10
+ # hook .claude/hooks/integ-docs-gate.sh blocks
11
+ # `gh pr create` / `gh pr merge` when stale.
12
+ #
13
+ # Scope is intentionally narrow — only the three files above. Integ
14
+ # test files under test/integ/{basic,enhanced,signature}/ are NOT in
15
+ # scope: adding a new test typically comes with a README/SKILL update
16
+ # anyway, which trips the marker naturally. Gating on test file edits
17
+ # directly would force re-verification on every test snapshot refresh.
18
+
19
+ gates:
20
+ integ-docs:
21
+ hash: files
22
+ include:
23
+ - ".claude/skills/integ-test/SKILL.md"
24
+ - "test/integ/README.md"
25
+ - "scripts/integ.sh"
package/.mise.toml ADDED
@@ -0,0 +1,9 @@
1
+ # mise tool pinning — https://mise.jdx.dev/
2
+ #
3
+ # Pinning markgate via mise ensures every contributor uses the same
4
+ # schema version. Mixing 0.3.0 and 0.3.1+ binaries silently invalidates
5
+ # each other's markers (the schema bumped in 0.3.1), so Homebrew vs
6
+ # mise drift would constantly trip the integ-docs gate for no reason.
7
+
8
+ [tools]
9
+ "ubi:go-to-k/markgate" = "0.3.3"
@@ -156,5 +156,5 @@ class EcrScanVerifier extends constructs_1.Construct {
156
156
  }
157
157
  exports.EcrScanVerifier = EcrScanVerifier;
158
158
  _a = JSII_RTTI_SYMBOL_1;
159
- EcrScanVerifier[_a] = { fqn: "ecr-scan-verifier.EcrScanVerifier", version: "0.1.1" };
159
+ EcrScanVerifier[_a] = { fqn: "ecr-scan-verifier.EcrScanVerifier", version: "0.1.2" };
160
160
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWNyLXNjYW4tdmVyaWZpZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZWNyLXNjYW4tdmVyaWZpZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSwrQkFBNEI7QUFDNUIsNkNBQWdHO0FBRWhHLCtEQUFzRDtBQUN0RCxpREFBc0Q7QUFDdEQsdURBTWdDO0FBR2hDLG1FQUF3RDtBQUN4RCwyQ0FBbUQ7QUFLbkQsbUNBQW1DO0FBa0huQzs7OztHQUlHO0FBQ0gsTUFBYSxlQUFnQixTQUFRLHNCQUFTO0lBRzVDLFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBMkI7UUFDbkUsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUVqQixJQUFJLENBQUMsZUFBZSxHQUFHLEtBQUssQ0FBQyxlQUFlLENBQUM7UUFDN0MsTUFBTSxhQUFhLEdBQUcsNkNBQTZDLENBQUM7UUFFcEUsTUFBTSxvQkFBb0IsR0FBRyxJQUFJLDhCQUFpQixDQUFDLElBQUksRUFBRSxzQkFBc0IsRUFBRTtZQUMvRSxJQUFJLEVBQUUsc0NBQXNDO1lBQzVDLGFBQWE7WUFDYixPQUFPLEVBQUUsb0JBQU8sQ0FBQyxVQUFVO1lBQzNCLE9BQU8sRUFBRSxvQkFBTyxDQUFDLFVBQVU7WUFDM0IsSUFBSSxFQUFFLHNCQUFTLENBQUMsY0FBYyxDQUFDLElBQUEsV0FBSSxFQUFDLFNBQVMsRUFBRSxrQkFBa0IsQ0FBQyxFQUFFO2dCQUNsRSxRQUFRLEVBQUUseUJBQVEsQ0FBQyxXQUFXO2dCQUM5QixVQUFVLEVBQUUsd0JBQVUsQ0FBQyxNQUFNO2FBQzlCLENBQUM7WUFDRixZQUFZLEVBQUUseUJBQVksQ0FBQyxNQUFNO1lBQ2pDLE9BQU8sRUFBRSxzQkFBUSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUM7WUFDOUIsYUFBYSxFQUFFLENBQUM7WUFDaEIsUUFBUSxFQUFFLElBQUksQ0FBQyxlQUFlO1NBQy9CLENBQUMsQ0FBQztRQUVILE1BQU0sUUFBUSxHQUFHLEtBQUssQ0FBQyxRQUFRLElBQUksUUFBUSxDQUFDO1FBRTVDLE1BQU0sZ0JBQWdCLEdBQUcsS0FBSyxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUVqRCx5REFBeUQ7UUFDekQsSUFBSSxnQkFBZ0IsQ0FBQyxRQUFRLEtBQUssZ0JBQWdCLElBQUksQ0FBQyxLQUFLLENBQUMscUJBQXFCLEVBQUUsQ0FBQztZQUNuRixNQUFNLElBQUksS0FBSyxDQUFDLDRFQUE0RSxDQUFDLENBQUM7UUFDaEcsQ0FBQztRQUVELE1BQU0sYUFBYSxHQUFHLEtBQUssQ0FBQyxjQUFjLEVBQUUsSUFBSSxDQUFDLG9CQUFvQixDQUFDLENBQUM7UUFFdkUsc0NBQXNDO1FBQ3RDLE1BQU0sVUFBVSxHQUFHLGdCQUFnQixDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsb0JBQW9CLENBQUMsQ0FBQztRQUUzRSx5QkFBeUI7UUFDekIsTUFBTSwyQkFBMkIsR0FBRyxLQUFLLENBQUMscUJBQXFCLEVBQUUsSUFBSSxDQUFDLG9CQUFvQixDQUFDLENBQUM7UUFFNUYsa0JBQWtCO1FBQ2xCLDREQUE0RDtRQUM1RCxnRUFBZ0U7UUFDaEUsTUFBTSxVQUFVLEdBQUcsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO1FBQzFDLElBQUksZ0JBQWdCLENBQUMsUUFBUSxLQUFLLGdCQUFnQixFQUFFLENBQUM7WUFDbkQsVUFBVSxDQUFDLElBQUksQ0FBQywrQkFBK0IsQ0FBQyxDQUFDO1FBQ25ELENBQUM7UUFDRCxvQkFBb0IsQ0FBQyxlQUFlLENBQ2xDLElBQUkseUJBQWUsQ0FBQztZQUNsQixPQUFPLEVBQUUsVUFBVTtZQUNuQixTQUFTLEVBQUUsQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQztTQUM1QyxDQUFDLENBQ0gsQ0FBQztRQUVGLElBQUksZ0JBQWdCLENBQUMsUUFBUSxLQUFLLFVBQVUsRUFBRSxDQUFDO1lBQzdDLG9CQUFvQixDQUFDLGVBQWUsQ0FDbEMsSUFBSSx5QkFBZSxDQUFDO2dCQUNsQixPQUFPLEVBQUUsQ0FBQyx5QkFBeUIsRUFBRSx5QkFBeUIsQ0FBQztnQkFDL0QsU0FBUyxFQUFFLENBQUMsR0FBRyxDQUFDO2FBQ2pCLENBQUMsQ0FDSCxDQUFDO1FBQ0osQ0FBQztRQUVELElBQUksZ0JBQWdCLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDL0Isb0JBQW9CLENBQUMsZUFBZSxDQUNsQyxJQUFJLHlCQUFlLENBQUM7Z0JBQ2xCLE9BQU8sRUFBRSxDQUFDLG9CQUFvQixDQUFDO2dCQUMvQixTQUFTLEVBQUUsQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQzthQUM1QyxDQUFDLENBQ0gsQ0FBQztRQUNKLENBQUM7UUFFRCxxQ0FBcUM7UUFDckMsSUFBSSwyQkFBMkIsRUFBRSxDQUFDO1lBQ2hDLG9CQUFvQixDQUFDLGVBQWUsQ0FDbEMsSUFBSSx5QkFBZSxDQUFDO2dCQUNsQixPQUFPLEVBQUUsQ0FBQywyQkFBMkIsQ0FBQztnQkFDdEMsU0FBUyxFQUFFLENBQUMsR0FBRyxDQUFDO2FBQ2pCLENBQUMsQ0FDSCxDQUFDO1lBQ0Ysb0JBQW9CLENBQUMsZUFBZSxDQUNsQyxJQUFJLHlCQUFlLENBQUM7Z0JBQ2xCLE9BQU8sRUFBRSxDQUFDLG1CQUFtQixFQUFFLDRCQUE0QixDQUFDO2dCQUM1RCxTQUFTLEVBQUUsQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQzthQUM1QyxDQUFDLENBQ0gsQ0FBQztZQUVGLElBQUksMkJBQTJCLENBQUMsSUFBSSxLQUFLLFVBQVUsRUFBRSxDQUFDO2dCQUNwRCxvQkFBb0IsQ0FBQyxlQUFlLENBQ2xDLElBQUkseUJBQWUsQ0FBQztvQkFDbEIsT0FBTyxFQUFFLENBQUMsNEJBQTRCLENBQUM7b0JBQ3ZDLFNBQVMsRUFBRSxDQUFDLEdBQUcsQ0FBQztpQkFDakIsQ0FBQyxDQUNILENBQUM7WUFDSixDQUFDO1lBQ0QsOERBQThEO1FBQ2hFLENBQUM7UUFFRCx1REFBdUQ7UUFDdkQsSUFBSSxVQUFVLEVBQUUsQ0FBQztZQUNmLG9CQUFvQixDQUFDLGVBQWUsQ0FDbEMsSUFBSSx5QkFBZSxDQUFDO2dCQUNsQixPQUFPLEVBQUUsQ0FBQyw2QkFBNkIsRUFBRSwwQkFBMEIsQ0FBQztnQkFDcEUsU0FBUyxFQUFFLENBQUMsR0FBRyxDQUFDO2FBQ2pCLENBQUMsQ0FDSCxDQUFDO1FBQ0osQ0FBQztRQUVELElBQUksS0FBSyxDQUFDLHNCQUFzQixFQUFFLENBQUM7WUFDakMsS0FBSyxDQUFDLHNCQUFzQixDQUFDLFlBQVksQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO1FBQ2xFLENBQUM7UUFFRCxNQUFNLHVCQUF1QixHQUFHLEtBQUssQ0FBQyx1QkFBdUIsSUFBSSxJQUFJLENBQUM7UUFDdEUsSUFBSSx1QkFBdUIsRUFBRSxDQUFDO1lBQzVCLG9CQUFvQixDQUFDLGVBQWUsQ0FDbEMsSUFBSSx5QkFBZSxDQUFDO2dCQUNsQixPQUFPLEVBQUUsQ0FBQywrQkFBK0IsQ0FBQztnQkFDMUMsU0FBUyxFQUFFLENBQUMsbUJBQUssQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUMsT0FBTyxDQUFDO2FBQ3BDLENBQUMsQ0FDSCxDQUFDO1FBQ0osQ0FBQztRQUVELG9GQUFvRjtRQUNwRixxQkFBTyxDQUFDLEVBQUUsQ0FBQyxtQkFBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQztZQUM3QixLQUFLLEVBQUUsQ0FBQyxJQUFJLEVBQUUsRUFBRTtnQkFDZCxJQUNFLElBQUksWUFBWSxlQUFlO29CQUMvQixJQUFJLENBQUMsZ0JBQWdCLEVBQUUsSUFBSSxDQUFDLElBQUksS0FBSyxJQUFJLENBQUMsZUFBZSxFQUFFLElBQUksQ0FBQyxJQUFJLEVBQ3BFLENBQUM7b0JBQ0QseUJBQVcsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUMsWUFBWSxDQUMvQixtREFBbUQsRUFDbkQsZ0hBQWdILENBQ2pILENBQUM7Z0JBQ0osQ0FBQztZQUNILENBQUM7U0FDRixDQUFDLENBQUM7UUFFSCxNQUFNLGdCQUFnQixHQUFHLElBQUksMkJBQVEsQ0FBQyxJQUFJLEVBQUUsVUFBVSxFQUFFO1lBQ3RELGNBQWMsRUFBRSxvQkFBb0I7U0FDckMsQ0FBQyxDQUFDO1FBRUgsTUFBTSxrQkFBa0IsR0FBK0I7WUFDckQsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSTtZQUNwQixjQUFjLEVBQUUsS0FBSyxDQUFDLFVBQVUsQ0FBQyxjQUFjO1lBQy9DLFFBQVE7WUFDUixRQUFRLEVBQUUsZ0JBQWdCLENBQUMsUUFBUTtZQUNuQyxTQUFTLEVBQUUsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsQ0FBQztZQUM3QyxRQUFRLEVBQUUsS0FBSyxDQUFDLFFBQVEsSUFBSSxDQUFDLGdCQUFRLENBQUMsUUFBUSxDQUFDO1lBQy9DLG1CQUFtQixFQUFFLE1BQU0sQ0FBQyxLQUFLLENBQUMsbUJBQW1CLElBQUksSUFBSSxDQUFDO1lBQzlELGNBQWMsRUFBRSxLQUFLLENBQUMsY0FBYyxJQUFJLEVBQUU7WUFDMUMsTUFBTSxFQUFFLGFBQWE7WUFDckIsSUFBSSxFQUFFLFVBQVU7WUFDaEIscUJBQXFCLEVBQUUsMkJBQTJCO2dCQUNoRCxDQUFDLENBQUM7b0JBQ0UsSUFBSSxFQUFFLDJCQUEyQixDQUFDLElBQUk7b0JBQ3RDLGlCQUFpQixFQUFFLDJCQUEyQixDQUFDLGlCQUFpQjtvQkFDaEUsU0FBUyxFQUFFLDJCQUEyQixDQUFDLFNBQVM7b0JBQ2hELFNBQVMsRUFBRSwyQkFBMkIsQ0FBQyxTQUFTO29CQUNoRCxjQUFjLEVBQUUsTUFBTSxDQUFDLDJCQUEyQixDQUFDLGNBQWMsQ0FBQztpQkFDbkU7Z0JBQ0gsQ0FBQyxDQUFDLFNBQVM7WUFDYix1QkFBdUIsRUFBRSxNQUFNLENBQUMsdUJBQXVCLENBQUM7WUFDeEQsYUFBYSxFQUFFLEtBQUssQ0FBQyxzQkFBc0IsRUFBRSxRQUFRO1lBQ3JELG1CQUFtQixFQUNqQixJQUFJLENBQUMsZUFBZSxFQUFFLFlBQVksSUFBSSxlQUFlLG9CQUFvQixDQUFDLFlBQVksRUFBRTtTQUMzRixDQUFDO1FBRUYsSUFBSSw0QkFBYyxDQUFDLElBQUksRUFBRSxVQUFVLEVBQUU7WUFDbkMsWUFBWSxFQUFFLHlCQUF5QjtZQUN2QyxVQUFVLEVBQUUsa0JBQWtCO1lBQzlCLFlBQVksRUFBRSxnQkFBZ0IsQ0FBQyxZQUFZO1NBQzVDLENBQUMsQ0FBQztRQUVILEtBQUssQ0FBQyxlQUFlLEVBQUUsT0FBTyxDQUFDLENBQUMsU0FBUyxFQUFFLEVBQUU7WUFDM0MsU0FBUyxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDckMsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQsZ0JBQWdCO0lBQ2hCLElBQUksZ0JBQWdCO1FBQ2xCLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQztJQUM5QixDQUFDOztBQXRMSCwwQ0F1TEMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBBbm5vdGF0aW9ucywgQXNwZWN0cywgQ3VzdG9tUmVzb3VyY2UsIER1cmF0aW9uLCBJZ25vcmVNb2RlLCBTdGFjayB9IGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCB7IElSZXBvc2l0b3J5IH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWVjcic7XG5pbXBvcnQgeyBQbGF0Zm9ybSB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1lY3ItYXNzZXRzJztcbmltcG9ydCB7IFBvbGljeVN0YXRlbWVudCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1pYW0nO1xuaW1wb3J0IHtcbiAgQXJjaGl0ZWN0dXJlLFxuICBBc3NldENvZGUsXG4gIEhhbmRsZXIsXG4gIFJ1bnRpbWUsXG4gIFNpbmdsZXRvbkZ1bmN0aW9uLFxufSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbGFtYmRhJztcbmltcG9ydCB7IElMb2dHcm91cCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1sb2dzJztcbmltcG9ydCB7IElUb3BpYyB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1zbnMnO1xuaW1wb3J0IHsgUHJvdmlkZXIgfSBmcm9tICdhd3MtY2RrLWxpYi9jdXN0b20tcmVzb3VyY2VzJztcbmltcG9ydCB7IENvbnN0cnVjdCwgSUNvbnN0cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1xuaW1wb3J0IHsgU2Nhbm5lckN1c3RvbVJlc291cmNlUHJvcHMgfSBmcm9tICcuL2N1c3RvbS1yZXNvdXJjZS1wcm9wcyc7XG5pbXBvcnQgeyBTY2FuQ29uZmlnIH0gZnJvbSAnLi9zY2FuLWNvbmZpZyc7XG5pbXBvcnQgeyBTY2FuTG9nc091dHB1dCB9IGZyb20gJy4vc2Nhbi1sb2dzLW91dHB1dCc7XG5pbXBvcnQgeyBTaWduYXR1cmVWZXJpZmljYXRpb24gfSBmcm9tICcuL3NpZ25hdHVyZS12ZXJpZmljYXRpb24nO1xuaW1wb3J0IHsgU2V2ZXJpdHkgfSBmcm9tICcuL3R5cGVzJztcblxuLyoqXG4gKiBQcm9wZXJ0aWVzIGZvciBFY3JTY2FuVmVyaWZpZXIgQ29uc3RydWN0LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEVjclNjYW5WZXJpZmllclByb3BzIHtcbiAgLyoqXG4gICAqIEVDUiBSZXBvc2l0b3J5IHRvIHNjYW4uXG4gICAqL1xuICByZWFkb25seSByZXBvc2l0b3J5OiBJUmVwb3NpdG9yeTtcblxuICAvKipcbiAgICogSW1hZ2UgdGFnIG9yIGRpZ2VzdCB0byBzY2FuLlxuICAgKlxuICAgKiBZb3UgY2FuIHNwZWNpZnkgYSB0YWcgKGUuZy4sICd2MS4wJywgJ2xhdGVzdCcpIG9yIGEgZGlnZXN0IChlLmcuLCAnc2hhMjU2OmFiYzEyMy4uLicpLlxuICAgKiBJZiB0aGUgdmFsdWUgc3RhcnRzIHdpdGggJ3NoYTI1NjonLCBpdCBpcyB0cmVhdGVkIGFzIGEgZGlnZXN0LlxuICAgKlxuICAgKiBAZGVmYXVsdCAnbGF0ZXN0J1xuICAgKi9cbiAgcmVhZG9ubHkgaW1hZ2VUYWc/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFNjYW4gY29uZmlndXJhdGlvbiDigJQgY2hvb3NlIGJhc2VkIG9uIHlvdXIgRUNSIHJlcG9zaXRvcnkvYWNjb3VudCBzZXR0aW5nczpcbiAgICpcbiAgICogLSBgU2NhbkNvbmZpZy5iYXNpYygpYCAoZGVmYXVsdDogYHN0YXJ0U2NhbjogdHJ1ZWApIOKAlCBzdGFydHMgYSBzY2FuIHZpYSB0aGUgRUNSIEFQSS5cbiAgICogICBObyBhZGRpdGlvbmFsIEVDUiBjb25maWd1cmF0aW9uIHJlcXVpcmVkLlxuICAgKiAtIGBTY2FuQ29uZmlnLmJhc2ljKHsgc3RhcnRTY2FuOiBmYWxzZSB9KWAg4oCUIHBvbGxzIGZvciBleGlzdGluZyByZXN1bHRzLlxuICAgKiAgIFJlcXVpcmVzIEJhc2ljIHNjYW4tb24tcHVzaCB0byBiZSBlbmFibGVkIG9uIHRoZSByZXBvc2l0b3J5LlxuICAgKiAtIGBTY2FuQ29uZmlnLmVuaGFuY2VkKClgIOKAlCB1c2VzIEFtYXpvbiBJbnNwZWN0b3IgZW5oYW5jZWQgc2Nhbm5pbmcuXG4gICAqICAgUmVxdWlyZXMgRW5oYW5jZWQgc2Nhbm5pbmcgdG8gYmUgZW5hYmxlZCBvbiB0aGUgYWNjb3VudC5cbiAgICpcbiAgICogSWYgdGhlIHJlcXVpcmVkIHNjYW5uaW5nIGNvbmZpZ3VyYXRpb24gaXMgbm90IGluIHBsYWNlIGFuZCBubyBwcmlvciBzY2FuIHJlc3VsdHMgZXhpc3QsXG4gICAqIHRoZSBkZXBsb3ltZW50IHdpbGwgZmFpbC5cbiAgICovXG4gIHJlYWRvbmx5IHNjYW5Db25maWc6IFNjYW5Db25maWc7XG5cbiAgLyoqXG4gICAqIFNldmVyaXR5IHRocmVzaG9sZCBmb3IgdnVsbmVyYWJpbGl0eSBkZXRlY3Rpb24uXG4gICAqXG4gICAqIElmIHZ1bG5lcmFiaWxpdGllcyBhdCBvciBhYm92ZSBhbnkgb2YgdGhlIHNwZWNpZmllZCBzZXZlcml0eSBsZXZlbHMgYXJlIGZvdW5kLFxuICAgKiB0aGUgc2NhbiB3aWxsIGJlIGNvbnNpZGVyZWQgYXMgaGF2aW5nIGZvdW5kIHZ1bG5lcmFiaWxpdGllcy5cbiAgICpcbiAgICogQGRlZmF1bHQgW1NldmVyaXR5LkNSSVRJQ0FMXVxuICAgKi9cbiAgcmVhZG9ubHkgc2V2ZXJpdHk/OiBTZXZlcml0eVtdO1xuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHRvIGZhaWwgdGhlIENsb3VkRm9ybWF0aW9uIGRlcGxveW1lbnQgaWYgdnVsbmVyYWJpbGl0aWVzIGFyZSBkZXRlY3RlZFxuICAgKiBhYm92ZSB0aGUgc2V2ZXJpdHkgdGhyZXNob2xkLlxuICAgKlxuICAgKiBAZGVmYXVsdCB0cnVlXG4gICAqL1xuICByZWFkb25seSBmYWlsT25WdWxuZXJhYmlsaXR5PzogYm9vbGVhbjtcblxuICAvKipcbiAgICogRmluZGluZyBJRHMgdG8gaWdub3JlIGR1cmluZyB2dWxuZXJhYmlsaXR5IGV2YWx1YXRpb24uXG4gICAqXG4gICAqIEZvciBiYXNpYyBzY2FubmluZzogQ1ZFIElEcyAoZS5nLiwgJ0NWRS0yMDIzLTM3OTIwJylcbiAgICogRm9yIGVuaGFuY2VkIHNjYW5uaW5nOiBmaW5kaW5nIEFSTnMgb3IgQ1ZFIElEc1xuICAgKlxuICAgKiBAZGVmYXVsdCAtIG5vIGZpbmRpbmdzIGlnbm9yZWRcbiAgICovXG4gIHJlYWRvbmx5IGlnbm9yZUZpbmRpbmdzPzogc3RyaW5nW107XG5cbiAgLyoqXG4gICAqIENvbmZpZ3VyYXRpb24gZm9yIHNjYW4gbG9ncyBvdXRwdXQuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gc2NhbiBsb2dzIG91dHB1dCB0byBkZWZhdWx0IGxvZyBncm91cCBjcmVhdGVkIGJ5IFNjYW5uZXIgTGFtYmRhLlxuICAgKi9cbiAgcmVhZG9ubHkgc2NhbkxvZ3NPdXRwdXQ/OiBTY2FuTG9nc091dHB1dDtcblxuICAvKipcbiAgICogU2lnbmF0dXJlIHZlcmlmaWNhdGlvbiBjb25maWd1cmF0aW9uIGZvciB0aGUgY29udGFpbmVyIGltYWdlLlxuICAgKlxuICAgKiBWZXJpZmllcyB0aGUgaW1hZ2Ugc2lnbmF0dXJlIGJlZm9yZSBzY2FubmluZyB1c2luZyBOb3RhdGlvbiAoQVdTIFNpZ25lcikgb3IgQ29zaWduIChTaWdzdG9yZSkuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gbm8gc2lnbmF0dXJlIHZlcmlmaWNhdGlvblxuICAgKi9cbiAgcmVhZG9ubHkgc2lnbmF0dXJlVmVyaWZpY2F0aW9uPzogU2lnbmF0dXJlVmVyaWZpY2F0aW9uO1xuXG4gIC8qKlxuICAgKiBUaGUgU2Nhbm5lciBMYW1iZGEgZnVuY3Rpb24ncyBkZWZhdWx0IGxvZyBncm91cC5cbiAgICpcbiAgICogSWYgeW91IHVzZSBFY3JTY2FuVmVyaWZpZXIgY29uc3RydWN0IG11bHRpcGxlIHRpbWVzIGluIHRoZSBzYW1lIHN0YWNrLFxuICAgKiB5b3UgbXVzdCBzcGVjaWZ5IHRoZSBzYW1lIGxvZyBncm91cCBmb3IgZWFjaCBjb25zdHJ1Y3QuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gU2Nhbm5lciBMYW1iZGEgY3JlYXRlcyB0aGUgZGVmYXVsdCBsb2cgZ3JvdXAuXG4gICAqL1xuICByZWFkb25seSBkZWZhdWx0TG9nR3JvdXA/OiBJTG9nR3JvdXA7XG5cbiAgLyoqXG4gICAqIFN1cHByZXNzIGVycm9ycyBkdXJpbmcgcm9sbGJhY2sgc2Nhbm5lciBMYW1iZGEgZXhlY3V0aW9uLlxuICAgKlxuICAgKiBAZGVmYXVsdCB0cnVlXG4gICAqL1xuICByZWFkb25seSBzdXBwcmVzc0Vycm9yT25Sb2xsYmFjaz86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIFNOUyB0b3BpYyBmb3IgdnVsbmVyYWJpbGl0eSBub3RpZmljYXRpb24uXG4gICAqXG4gICAqIFN1cHBvcnRzIEFXUyBDaGF0Ym90IG1lc3NhZ2UgZm9ybWF0LlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIG5vIG5vdGlmaWNhdGlvblxuICAgKi9cbiAgcmVhZG9ubHkgdnVsbnNOb3RpZmljYXRpb25Ub3BpYz86IElUb3BpYztcblxuICAvKipcbiAgICogQ29uc3RydWN0cyB0byBibG9jayBpZiB2dWxuZXJhYmlsaXRpZXMgYXJlIGRldGVjdGVkLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIG5vIGNvbnN0cnVjdHMgdG8gYmxvY2tcbiAgICovXG4gIHJlYWRvbmx5IGJsb2NrQ29uc3RydWN0cz86IElDb25zdHJ1Y3RbXTtcbn1cblxuLyoqXG4gKiBBIENvbnN0cnVjdCB0aGF0IHZlcmlmaWVzIGNvbnRhaW5lciBpbWFnZSBzY2FuIGZpbmRpbmdzIHdpdGggRUNSIGltYWdlIHNjYW5uaW5nLlxuICogSXQgdXNlcyBhIExhbWJkYSBmdW5jdGlvbiBhcyBhIEN1c3RvbSBSZXNvdXJjZSBwcm92aWRlciB0byBjYWxsIEVDUiBzY2FuIEFQSXNcbiAqIGFuZCBldmFsdWF0ZSBzY2FuIGZpbmRpbmdzLlxuICovXG5leHBvcnQgY2xhc3MgRWNyU2NhblZlcmlmaWVyIGV4dGVuZHMgQ29uc3RydWN0IHtcbiAgcHJpdmF0ZSByZWFkb25seSBkZWZhdWx0TG9nR3JvdXA/OiBJTG9nR3JvdXA7XG5cbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM6IEVjclNjYW5WZXJpZmllclByb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkKTtcblxuICAgIHRoaXMuZGVmYXVsdExvZ0dyb3VwID0gcHJvcHMuZGVmYXVsdExvZ0dyb3VwO1xuICAgIGNvbnN0IGxhbWJkYVB1cnBvc2UgPSAnQ3VzdG9tOjpFY3JTY2FuVmVyaWZpZXJDdXN0b21SZXNvdXJjZUxhbWJkYSc7XG5cbiAgICBjb25zdCBjdXN0b21SZXNvdXJjZUxhbWJkYSA9IG5ldyBTaW5nbGV0b25GdW5jdGlvbih0aGlzLCAnQ3VzdG9tUmVzb3VyY2VMYW1iZGEnLCB7XG4gICAgICB1dWlkOiAnYzU2Y2VlNmItNjc3NS01NDFiLWQxNzktYzE1MzVkODhhMGM4JyxcbiAgICAgIGxhbWJkYVB1cnBvc2UsXG4gICAgICBydW50aW1lOiBSdW50aW1lLkZST01fSU1BR0UsXG4gICAgICBoYW5kbGVyOiBIYW5kbGVyLkZST01fSU1BR0UsXG4gICAgICBjb2RlOiBBc3NldENvZGUuZnJvbUFzc2V0SW1hZ2Uoam9pbihfX2Rpcm5hbWUsICcuLi9hc3NldHMvbGFtYmRhJyksIHtcbiAgICAgICAgcGxhdGZvcm06IFBsYXRmb3JtLkxJTlVYX0FSTTY0LFxuICAgICAgICBpZ25vcmVNb2RlOiBJZ25vcmVNb2RlLkRPQ0tFUixcbiAgICAgIH0pLFxuICAgICAgYXJjaGl0ZWN0dXJlOiBBcmNoaXRlY3R1cmUuQVJNXzY0LFxuICAgICAgdGltZW91dDogRHVyYXRpb24uc2Vjb25kcyg5MDApLFxuICAgICAgcmV0cnlBdHRlbXB0czogMCxcbiAgICAgIGxvZ0dyb3VwOiB0aGlzLmRlZmF1bHRMb2dHcm91cCxcbiAgICB9KTtcblxuICAgIGNvbnN0IGltYWdlVGFnID0gcHJvcHMuaW1hZ2VUYWcgPz8gJ2xhdGVzdCc7XG5cbiAgICBjb25zdCBzY2FuQ29uZmlnT3V0cHV0ID0gcHJvcHMuc2NhbkNvbmZpZy5iaW5kKCk7XG5cbiAgICAvLyBWYWxpZGF0ZTogc2lnbmF0dXJlT25seSByZXF1aXJlcyBzaWduYXR1cmVWZXJpZmljYXRpb25cbiAgICBpZiAoc2NhbkNvbmZpZ091dHB1dC5zY2FuVHlwZSA9PT0gJ1NJR05BVFVSRV9PTkxZJyAmJiAhcHJvcHMuc2lnbmF0dXJlVmVyaWZpY2F0aW9uKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NjYW5Db25maWcuc2lnbmF0dXJlT25seSgpIHJlcXVpcmVzIHNpZ25hdHVyZVZlcmlmaWNhdGlvbiB0byBiZSBzcGVjaWZpZWQuJyk7XG4gICAgfVxuXG4gICAgY29uc3Qgb3V0cHV0T3B0aW9ucyA9IHByb3BzLnNjYW5Mb2dzT3V0cHV0Py5iaW5kKGN1c3RvbVJlc291cmNlTGFtYmRhKTtcblxuICAgIC8vIFNCT00gb3V0cHV0IChmcm9tIHNjYW5Db25maWdPdXRwdXQpXG4gICAgY29uc3Qgc2JvbUNvbmZpZyA9IHNjYW5Db25maWdPdXRwdXQuc2JvbU91dHB1dD8uYmluZChjdXN0b21SZXNvdXJjZUxhbWJkYSk7XG5cbiAgICAvLyBTaWduYXR1cmUgdmVyaWZpY2F0aW9uXG4gICAgY29uc3Qgc2lnbmF0dXJlVmVyaWZpY2F0aW9uQ29uZmlnID0gcHJvcHMuc2lnbmF0dXJlVmVyaWZpY2F0aW9uPy5iaW5kKGN1c3RvbVJlc291cmNlTGFtYmRhKTtcblxuICAgIC8vIEVDUiBwZXJtaXNzaW9uc1xuICAgIC8vIERlc2NyaWJlSW1hZ2VzIGlzIGFsd2F5cyByZXF1aXJlZCAoZm9yIGRpZ2VzdCByZXNvbHV0aW9uKVxuICAgIC8vIERlc2NyaWJlSW1hZ2VTY2FuRmluZGluZ3MgaXMgb25seSByZXF1aXJlZCBmb3Igc2Nhbm5pbmcgbW9kZXNcbiAgICBjb25zdCBlY3JBY3Rpb25zID0gWydlY3I6RGVzY3JpYmVJbWFnZXMnXTtcbiAgICBpZiAoc2NhbkNvbmZpZ091dHB1dC5zY2FuVHlwZSAhPT0gJ1NJR05BVFVSRV9PTkxZJykge1xuICAgICAgZWNyQWN0aW9ucy5wdXNoKCdlY3I6RGVzY3JpYmVJbWFnZVNjYW5GaW5kaW5ncycpO1xuICAgIH1cbiAgICBjdXN0b21SZXNvdXJjZUxhbWJkYS5hZGRUb1JvbGVQb2xpY3koXG4gICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgYWN0aW9uczogZWNyQWN0aW9ucyxcbiAgICAgICAgcmVzb3VyY2VzOiBbcHJvcHMucmVwb3NpdG9yeS5yZXBvc2l0b3J5QXJuXSxcbiAgICAgIH0pLFxuICAgICk7XG5cbiAgICBpZiAoc2NhbkNvbmZpZ091dHB1dC5zY2FuVHlwZSA9PT0gJ0VOSEFOQ0VEJykge1xuICAgICAgY3VzdG9tUmVzb3VyY2VMYW1iZGEuYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgICBhY3Rpb25zOiBbJ2luc3BlY3RvcjI6TGlzdENvdmVyYWdlJywgJ2luc3BlY3RvcjI6TGlzdEZpbmRpbmdzJ10sXG4gICAgICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgICAgfSksXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmIChzY2FuQ29uZmlnT3V0cHV0LnN0YXJ0U2Nhbikge1xuICAgICAgY3VzdG9tUmVzb3VyY2VMYW1iZGEuYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgICBhY3Rpb25zOiBbJ2VjcjpTdGFydEltYWdlU2NhbiddLFxuICAgICAgICAgIHJlc291cmNlczogW3Byb3BzLnJlcG9zaXRvcnkucmVwb3NpdG9yeUFybl0sXG4gICAgICAgIH0pLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBTaWduYXR1cmUgdmVyaWZpY2F0aW9uIHBlcm1pc3Npb25zXG4gICAgaWYgKHNpZ25hdHVyZVZlcmlmaWNhdGlvbkNvbmZpZykge1xuICAgICAgY3VzdG9tUmVzb3VyY2VMYW1iZGEuYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgICBhY3Rpb25zOiBbJ2VjcjpHZXRBdXRob3JpemF0aW9uVG9rZW4nXSxcbiAgICAgICAgICByZXNvdXJjZXM6IFsnKiddLFxuICAgICAgICB9KSxcbiAgICAgICk7XG4gICAgICBjdXN0b21SZXNvdXJjZUxhbWJkYS5hZGRUb1JvbGVQb2xpY3koXG4gICAgICAgIG5ldyBQb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICAgIGFjdGlvbnM6IFsnZWNyOkJhdGNoR2V0SW1hZ2UnLCAnZWNyOkdldERvd25sb2FkVXJsRm9yTGF5ZXInXSxcbiAgICAgICAgICByZXNvdXJjZXM6IFtwcm9wcy5yZXBvc2l0b3J5LnJlcG9zaXRvcnlBcm5dLFxuICAgICAgICB9KSxcbiAgICAgICk7XG5cbiAgICAgIGlmIChzaWduYXR1cmVWZXJpZmljYXRpb25Db25maWcudHlwZSA9PT0gJ05PVEFUSU9OJykge1xuICAgICAgICBjdXN0b21SZXNvdXJjZUxhbWJkYS5hZGRUb1JvbGVQb2xpY3koXG4gICAgICAgICAgbmV3IFBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgICAgICBhY3Rpb25zOiBbJ3NpZ25lcjpHZXRSZXZvY2F0aW9uU3RhdHVzJ10sXG4gICAgICAgICAgICByZXNvdXJjZXM6IFsnKiddLFxuICAgICAgICAgIH0pLFxuICAgICAgICApO1xuICAgICAgfVxuICAgICAgLy8gQ29zaWduIEtNUyBwZXJtaXNzaW9ucyBhcmUgZ3JhbnRlZCBieSBrZXkuZ3JhbnQoKSBpbiBiaW5kKClcbiAgICB9XG5cbiAgICAvLyBTQk9NIGV4cG9ydCBwZXJtaXNzaW9ucyAoSW5zcGVjdG9yIENyZWF0ZVNib21FeHBvcnQpXG4gICAgaWYgKHNib21Db25maWcpIHtcbiAgICAgIGN1c3RvbVJlc291cmNlTGFtYmRhLmFkZFRvUm9sZVBvbGljeShcbiAgICAgICAgbmV3IFBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgICAgYWN0aW9uczogWydpbnNwZWN0b3IyOkNyZWF0ZVNib21FeHBvcnQnLCAnaW5zcGVjdG9yMjpHZXRTYm9tRXhwb3J0J10sXG4gICAgICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgICAgfSksXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmIChwcm9wcy52dWxuc05vdGlmaWNhdGlvblRvcGljKSB7XG4gICAgICBwcm9wcy52dWxuc05vdGlmaWNhdGlvblRvcGljLmdyYW50UHVibGlzaChjdXN0b21SZXNvdXJjZUxhbWJkYSk7XG4gICAgfVxuXG4gICAgY29uc3Qgc3VwcHJlc3NFcnJvck9uUm9sbGJhY2sgPSBwcm9wcy5zdXBwcmVzc0Vycm9yT25Sb2xsYmFjayA/PyB0cnVlO1xuICAgIGlmIChzdXBwcmVzc0Vycm9yT25Sb2xsYmFjaykge1xuICAgICAgY3VzdG9tUmVzb3VyY2VMYW1iZGEuYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgICBhY3Rpb25zOiBbJ2Nsb3VkZm9ybWF0aW9uOkRlc2NyaWJlU3RhY2tzJ10sXG4gICAgICAgICAgcmVzb3VyY2VzOiBbU3RhY2sub2YodGhpcykuc3RhY2tJZF0sXG4gICAgICAgIH0pLFxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBDaGVjayBmb3IgZGVmYXVsdExvZ0dyb3VwIGNvbnNpc3RlbmN5IGFjcm9zcyBtdWx0aXBsZSBpbnN0YW5jZXMgaW4gdGhlIHNhbWUgc3RhY2tcbiAgICBBc3BlY3RzLm9mKFN0YWNrLm9mKHRoaXMpKS5hZGQoe1xuICAgICAgdmlzaXQ6IChub2RlKSA9PiB7XG4gICAgICAgIGlmIChcbiAgICAgICAgICBub2RlIGluc3RhbmNlb2YgRWNyU2NhblZlcmlmaWVyICYmXG4gICAgICAgICAgbm9kZS5fZGVmYXVsdExvZ0dyb3VwPy5ub2RlLnBhdGggIT09IHRoaXMuZGVmYXVsdExvZ0dyb3VwPy5ub2RlLnBhdGhcbiAgICAgICAgKSB7XG4gICAgICAgICAgQW5ub3RhdGlvbnMub2YodGhpcykuYWRkV2FybmluZ1YyKFxuICAgICAgICAgICAgJ0BlY3Itc2Nhbi12ZXJpZmllcjpkdXBsaWNhdGVMYW1iZGFEZWZhdWx0TG9nR3JvdXAnLFxuICAgICAgICAgICAgXCJZb3UgaGF2ZSB0byBzZXQgdGhlIHNhbWUgbG9nIGdyb3VwIGZvciAnZGVmYXVsdExvZ0dyb3VwJyBmb3IgZWFjaCBFY3JTY2FuVmVyaWZpZXIgY29uc3RydWN0IGluIHRoZSBzYW1lIHN0YWNrLlwiLFxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBjb25zdCB2ZXJpZmllclByb3ZpZGVyID0gbmV3IFByb3ZpZGVyKHRoaXMsICdQcm92aWRlcicsIHtcbiAgICAgIG9uRXZlbnRIYW5kbGVyOiBjdXN0b21SZXNvdXJjZUxhbWJkYSxcbiAgICB9KTtcblxuICAgIGNvbnN0IHZlcmlmaWVyUHJvcGVydGllczogU2Nhbm5lckN1c3RvbVJlc291cmNlUHJvcHMgPSB7XG4gICAgICBhZGRyOiB0aGlzLm5vZGUuYWRkcixcbiAgICAgIHJlcG9zaXRvcnlOYW1lOiBwcm9wcy5yZXBvc2l0b3J5LnJlcG9zaXRvcnlOYW1lLFxuICAgICAgaW1hZ2VUYWcsXG4gICAgICBzY2FuVHlwZTogc2NhbkNvbmZpZ091dHB1dC5zY2FuVHlwZSxcbiAgICAgIHN0YXJ0U2NhbjogU3RyaW5nKHNjYW5Db25maWdPdXRwdXQuc3RhcnRTY2FuKSxcbiAgICAgIHNldmVyaXR5OiBwcm9wcy5zZXZlcml0eSA/PyBbU2V2ZXJpdHkuQ1JJVElDQUxdLFxuICAgICAgZmFpbE9uVnVsbmVyYWJpbGl0eTogU3RyaW5nKHByb3BzLmZhaWxPblZ1bG5lcmFiaWxpdHkgPz8gdHJ1ZSksXG4gICAgICBpZ25vcmVGaW5kaW5nczogcHJvcHMuaWdub3JlRmluZGluZ3MgPz8gW10sXG4gICAgICBvdXRwdXQ6IG91dHB1dE9wdGlvbnMsXG4gICAgICBzYm9tOiBzYm9tQ29uZmlnLFxuICAgICAgc2lnbmF0dXJlVmVyaWZpY2F0aW9uOiBzaWduYXR1cmVWZXJpZmljYXRpb25Db25maWdcbiAgICAgICAgPyB7XG4gICAgICAgICAgICB0eXBlOiBzaWduYXR1cmVWZXJpZmljYXRpb25Db25maWcudHlwZSxcbiAgICAgICAgICAgIHRydXN0ZWRJZGVudGl0aWVzOiBzaWduYXR1cmVWZXJpZmljYXRpb25Db25maWcudHJ1c3RlZElkZW50aXRpZXMsXG4gICAgICAgICAgICBwdWJsaWNLZXk6IHNpZ25hdHVyZVZlcmlmaWNhdGlvbkNvbmZpZy5wdWJsaWNLZXksXG4gICAgICAgICAgICBrbXNLZXlBcm46IHNpZ25hdHVyZVZlcmlmaWNhdGlvbkNvbmZpZy5rbXNLZXlBcm4sXG4gICAgICAgICAgICBmYWlsT25VbnNpZ25lZDogU3RyaW5nKHNpZ25hdHVyZVZlcmlmaWNhdGlvbkNvbmZpZy5mYWlsT25VbnNpZ25lZCksXG4gICAgICAgICAgfVxuICAgICAgICA6IHVuZGVmaW5lZCxcbiAgICAgIHN1cHByZXNzRXJyb3JPblJvbGxiYWNrOiBTdHJpbmcoc3VwcHJlc3NFcnJvck9uUm9sbGJhY2spLFxuICAgICAgdnVsbnNUb3BpY0FybjogcHJvcHMudnVsbnNOb3RpZmljYXRpb25Ub3BpYz8udG9waWNBcm4sXG4gICAgICBkZWZhdWx0TG9nR3JvdXBOYW1lOlxuICAgICAgICB0aGlzLmRlZmF1bHRMb2dHcm91cD8ubG9nR3JvdXBOYW1lID8/IGAvYXdzL2xhbWJkYS8ke2N1c3RvbVJlc291cmNlTGFtYmRhLmZ1bmN0aW9uTmFtZX1gLFxuICAgIH07XG5cbiAgICBuZXcgQ3VzdG9tUmVzb3VyY2UodGhpcywgJ1Jlc291cmNlJywge1xuICAgICAgcmVzb3VyY2VUeXBlOiAnQ3VzdG9tOjpFY3JTY2FuVmVyaWZpZXInLFxuICAgICAgcHJvcGVydGllczogdmVyaWZpZXJQcm9wZXJ0aWVzLFxuICAgICAgc2VydmljZVRva2VuOiB2ZXJpZmllclByb3ZpZGVyLnNlcnZpY2VUb2tlbixcbiAgICB9KTtcblxuICAgIHByb3BzLmJsb2NrQ29uc3RydWN0cz8uZm9yRWFjaCgoY29uc3RydWN0KSA9PiB7XG4gICAgICBjb25zdHJ1Y3Qubm9kZS5hZGREZXBlbmRlbmN5KHRoaXMpO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqIEBpbnRlcm5hbCAqL1xuICBnZXQgX2RlZmF1bHRMb2dHcm91cCgpOiBJTG9nR3JvdXAgfCB1bmRlZmluZWQge1xuICAgIHJldHVybiB0aGlzLmRlZmF1bHRMb2dHcm91cDtcbiAgfVxufVxuIl19
@@ -30,7 +30,7 @@ class SbomOutput {
30
30
  }
31
31
  exports.SbomOutput = SbomOutput;
32
32
  _a = JSII_RTTI_SYMBOL_1;
33
- SbomOutput[_a] = { fqn: "ecr-scan-verifier.SbomOutput", version: "0.1.1" };
33
+ SbomOutput[_a] = { fqn: "ecr-scan-verifier.SbomOutput", version: "0.1.2" };
34
34
  class SbomOutputImpl extends SbomOutput {
35
35
  constructor(props, format) {
36
36
  super();
@@ -44,7 +44,7 @@ class ScanConfig {
44
44
  }
45
45
  exports.ScanConfig = ScanConfig;
46
46
  _a = JSII_RTTI_SYMBOL_1;
47
- ScanConfig[_a] = { fqn: "ecr-scan-verifier.ScanConfig", version: "0.1.1" };
47
+ ScanConfig[_a] = { fqn: "ecr-scan-verifier.ScanConfig", version: "0.1.2" };
48
48
  class BasicScanConfig extends ScanConfig {
49
49
  constructor(options) {
50
50
  super();
@@ -43,7 +43,7 @@ class ScanLogsOutput {
43
43
  }
44
44
  exports.ScanLogsOutput = ScanLogsOutput;
45
45
  _a = JSII_RTTI_SYMBOL_1;
46
- ScanLogsOutput[_a] = { fqn: "ecr-scan-verifier.ScanLogsOutput", version: "0.1.1" };
46
+ ScanLogsOutput[_a] = { fqn: "ecr-scan-verifier.ScanLogsOutput", version: "0.1.2" };
47
47
  class CloudWatchLogsOutput extends ScanLogsOutput {
48
48
  constructor(options) {
49
49
  super();
@@ -47,7 +47,7 @@ class SignatureVerification {
47
47
  }
48
48
  exports.SignatureVerification = SignatureVerification;
49
49
  _a = JSII_RTTI_SYMBOL_1;
50
- SignatureVerification[_a] = { fqn: "ecr-scan-verifier.SignatureVerification", version: "0.1.1" };
50
+ SignatureVerification[_a] = { fqn: "ecr-scan-verifier.SignatureVerification", version: "0.1.2" };
51
51
  class NotationSignatureVerification extends SignatureVerification {
52
52
  constructor(options) {
53
53
  super();
package/package.json CHANGED
@@ -60,7 +60,7 @@
60
60
  "publishConfig": {
61
61
  "access": "public"
62
62
  },
63
- "version": "0.1.1",
63
+ "version": "0.1.2",
64
64
  "types": "lib/index.d.ts",
65
65
  "stability": "stable",
66
66
  "jsii": {
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env bash
2
+ # Shared helpers for ecr-scan-verifier integ test orchestration.
3
+ #
4
+ # Source this from a shell or from the /integ-test skill:
5
+ # . scripts/integ.sh
6
+ #
7
+ # Functions are intentionally small and idempotent so the skill can chain
8
+ # them without re-deriving region lists, account ids, or polling loops.
9
+
10
+ set -u
11
+
12
+ REGIONS=(us-east-1 us-east-2 us-west-2)
13
+
14
+ account_id() {
15
+ aws sts get-caller-identity --query Account --output text
16
+ }
17
+
18
+ default_region() {
19
+ aws configure get region
20
+ }
21
+
22
+ # --- Inspector (Enhanced scanning) ------------------------------------------
23
+
24
+ inspector_status() {
25
+ local region="$1"
26
+ aws inspector2 batch-get-account-status \
27
+ --region "$region" \
28
+ --query 'accounts[0].resourceState.ecr.status' \
29
+ --output text
30
+ }
31
+
32
+ inspector_status_all() {
33
+ for region in "${REGIONS[@]}"; do
34
+ echo "$region: $(inspector_status "$region")"
35
+ done
36
+ }
37
+
38
+ inspector_enable_all() {
39
+ for region in "${REGIONS[@]}"; do
40
+ aws inspector2 enable --resource-types ECR --region "$region"
41
+ done
42
+ }
43
+
44
+ inspector_disable_all() {
45
+ for region in "${REGIONS[@]}"; do
46
+ aws inspector2 disable --resource-types ECR --region "$region"
47
+ done
48
+ }
49
+
50
+ # Poll one region until inspector status equals $target. Cap at 5 min so we
51
+ # fail loudly on stuck transitions instead of hanging.
52
+ wait_inspector_status() {
53
+ local region="$1" target="$2" i=0
54
+ while [ "$(inspector_status "$region")" != "$target" ]; do
55
+ i=$((i + 1))
56
+ if [ "$i" -ge 20 ]; then
57
+ echo "ERROR: $region did not reach $target after 5 min" >&2
58
+ return 1
59
+ fi
60
+ sleep 15
61
+ done
62
+ }
63
+
64
+ wait_inspector_status_all() {
65
+ local target="$1"
66
+ for region in "${REGIONS[@]}"; do
67
+ wait_inspector_status "$region" "$target" || return 1
68
+ done
69
+ }
70
+
71
+ # --- scan-on-push on the CDK bootstrap asset repo ---------------------------
72
+
73
+ # Usage: scan_on_push_set true|false
74
+ scan_on_push_set() {
75
+ local enabled="$1"
76
+ local account
77
+ account="$(account_id)"
78
+ for region in "${REGIONS[@]}"; do
79
+ aws ecr put-image-scanning-configuration \
80
+ --repository-name "cdk-hnb659fds-container-assets-${account}-${region}" \
81
+ --image-scanning-configuration "scanOnPush=${enabled}" \
82
+ --region "$region"
83
+ done
84
+ }
85
+
86
+ # --- Enhanced engine warmup (DISABLED -> ENABLED) ---------------------------
87
+ #
88
+ # `batch-get-account-status` flipping to ENABLED is not the same as the
89
+ # scanning engine being ready — empirically the lag is often 20-30 min on
90
+ # a fresh enable. wait_enhanced_engine_warmup absorbs that with a long
91
+ # initial sleep; enhanced_run_with_retry then retries the test up to N
92
+ # more times with a fixed gap between attempts.
93
+
94
+ # Initial wait after a DISABLED -> ENABLED transition. Skip if already
95
+ # enabled (no transition happened).
96
+ # Args: $1 = ORIGINAL_STATE (ENABLED|DISABLED), $2 = seconds (default 1200)
97
+ wait_enhanced_engine_warmup() {
98
+ local original_state="$1" secs="${2:-1200}"
99
+ if [ "$original_state" = "DISABLED" ]; then
100
+ echo "Inspector engine warmup: sleeping ${secs}s after fresh enable..."
101
+ sleep "$secs"
102
+ fi
103
+ }
104
+
105
+ # Run `$@` (the test command). On failure, sleep $RETRY_GAP_SECS and retry,
106
+ # up to $MAX_ATTEMPTS total attempts. Defaults: 3 attempts, 600s gap.
107
+ # Tuned for "Inspector engine still warming up" — NOT for catching flaky
108
+ # tests. If real findings change, surface them; don't burn time retrying.
109
+ enhanced_run_with_retry() {
110
+ local max="${MAX_ATTEMPTS:-3}" gap="${RETRY_GAP_SECS:-600}" attempt=1
111
+ while true; do
112
+ echo "Attempt ${attempt}/${max}: $*"
113
+ if "$@"; then
114
+ return 0
115
+ fi
116
+ if [ "$attempt" -ge "$max" ]; then
117
+ echo "ERROR: failed after ${max} attempts across ~$((max * gap / 60)) min of waits." >&2
118
+ echo " Stop waiting — the cause is not propagation lag." >&2
119
+ return 1
120
+ fi
121
+ echo "Attempt ${attempt} failed. Sleeping ${gap}s before retry..."
122
+ sleep "$gap"
123
+ attempt=$((attempt + 1))
124
+ done
125
+ }
126
+
127
+ # --- Signature test cleanup -------------------------------------------------
128
+ #
129
+ # Cleans up shared signature-test artifacts. AWS Signer profile is left
130
+ # Active on purpose (cancellation is permanent and would block future runs).
131
+
132
+ cleanup_signature_artifacts() {
133
+ local key_id
134
+ key_id="$(aws ssm get-parameter --name /ecr-scan-verifier/cosign-kms-key-id \
135
+ --query 'Parameter.Value' --output text 2>/dev/null || true)"
136
+
137
+ aws ssm delete-parameter --name /ecr-scan-verifier/cosign-kms-key-id 2>/dev/null || true
138
+ aws ssm delete-parameter --name /ecr-scan-verifier/cosign-public-key 2>/dev/null || true
139
+
140
+ if [ -n "$key_id" ]; then
141
+ aws kms schedule-key-deletion --key-id "$key_id" --pending-window-in-days 7
142
+ else
143
+ echo "WARN: KMS key id not in SSM. Ask the user before scheduling deletion." >&2
144
+ fi
145
+
146
+ rm -f cosign.key cosign.pub
147
+ }
148
+
149
+ # --- signature-ecr-signing repo setup / teardown ----------------------------
150
+
151
+ ECR_SIGNING_REPO_NAME="${ECR_SIGNING_REPO_NAME:-ecr-scan-verifier-integ-ecr-signing}"
152
+ SIGNER_PROFILE_NAME="${SIGNER_PROFILE_NAME:-EcrScanVerifierTestProfile}"
153
+
154
+ # Idempotent-ish wrapper. `put-signing-profile` is NOT actually idempotent
155
+ # (returns ProfileAlreadyExists for an existing Active profile), so check
156
+ # first and only create if missing. Echoes the ARN on stdout.
157
+ # Returns non-zero if the profile cannot be ensured — callers using
158
+ # `var=$(signer_profile_ensure)` should also check `[ -n "$var" ]` since
159
+ # command substitution swallows the inner exit code by default.
160
+ signer_profile_ensure() {
161
+ local arn
162
+ arn="$(aws signer get-signing-profile --profile-name "$SIGNER_PROFILE_NAME" \
163
+ --query 'arn' --output text 2>/dev/null || true)"
164
+ if [ -z "$arn" ] || [ "$arn" = "None" ]; then
165
+ aws signer put-signing-profile \
166
+ --profile-name "$SIGNER_PROFILE_NAME" \
167
+ --platform-id Notation-OCI-SHA384-ECDSA >&2 || return 1
168
+ arn="$(aws signer get-signing-profile --profile-name "$SIGNER_PROFILE_NAME" \
169
+ --query 'arn' --output text 2>/dev/null || true)"
170
+ fi
171
+ if [ -z "$arn" ] || [ "$arn" = "None" ]; then
172
+ echo "ERROR: signer_profile_ensure: could not resolve ARN for $SIGNER_PROFILE_NAME" >&2
173
+ return 1
174
+ fi
175
+ echo "$arn"
176
+ }
177
+
178
+ # Build the cosign signing-config the Lambda verifier expects: no rekor
179
+ # (transparency log), no fulcio CA, no OIDC, no TSA. cosign 3.x will try
180
+ # keyless flows even with --key if these fields are present, which manifests
181
+ # as a hung "Signing artifact..." with no further output. Stripping them
182
+ # lets a `--key`-based sign complete.
183
+ cosign_minimal_signing_config() {
184
+ local out="${1:-/tmp/signing-config.json}"
185
+ curl -fsSL https://raw.githubusercontent.com/sigstore/root-signing/refs/heads/main/targets/signing_config.v0.2.json | \
186
+ jq 'del(.rekorTlogUrls, .oidcUrls, .caUrls, .tsaUrls)' > "$out"
187
+ echo "$out"
188
+ }
189
+
190
+ ecr_signing_setup() {
191
+ local region profile_arn
192
+ region="$(default_region)"
193
+ profile_arn="$(signer_profile_ensure)"
194
+ aws ecr create-repository --repository-name "$ECR_SIGNING_REPO_NAME" 2>/dev/null || true
195
+
196
+ local cfg=/tmp/ecr-scan-verifier-signing-config.json
197
+ cat > "$cfg" <<EOF
198
+ {
199
+ "rules": [
200
+ {
201
+ "signingProfileArn": "${profile_arn}",
202
+ "repositoryFilters": [
203
+ { "filter": "${ECR_SIGNING_REPO_NAME}", "filterType": "WILDCARD_MATCH" }
204
+ ]
205
+ }
206
+ ]
207
+ }
208
+ EOF
209
+ aws ecr put-signing-configuration --region "$region" \
210
+ --signing-configuration "file://${cfg}"
211
+ aws ecr get-signing-configuration --region "$region"
212
+ }
213
+
214
+ ecr_signing_teardown() {
215
+ local region cfg=/tmp/ecr-scan-verifier-signing-config-empty.json
216
+ region="$(default_region)"
217
+ printf '{ "rules": [] }\n' > "$cfg"
218
+ aws ecr put-signing-configuration --region "$region" \
219
+ --signing-configuration "file://${cfg}" || true
220
+ aws ecr delete-repository --repository-name "$ECR_SIGNING_REPO_NAME" --force || true
221
+ }
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env bash
2
+ # verify-integ-docs.sh
3
+ #
4
+ # Verifies that the three sources documenting the integ workflow stay in
5
+ # sync with each other:
6
+ #
7
+ # .claude/skills/integ-test/SKILL.md — automated (Claude Code skill)
8
+ # test/integ/README.md — manual (human-readable docs)
9
+ # scripts/integ.sh — shell helpers shared by both
10
+ #
11
+ # Both docs must exist (skill = automated, README = manual; deleting one
12
+ # would leave the other lying about its counterpart). Both must mention
13
+ # every helper that lives in scripts/integ.sh, and both must agree on
14
+ # the mode list.
15
+ #
16
+ # Run via the /verify-integ-docs skill, which flips the `integ-docs`
17
+ # markgate marker after this passes.
18
+
19
+ set -u
20
+
21
+ SKILL=".claude/skills/integ-test/SKILL.md"
22
+ README="test/integ/README.md"
23
+ HELPERS="scripts/integ.sh"
24
+
25
+ fails=0
26
+ fail() { echo "FAIL: $*" >&2; fails=$((fails + 1)); }
27
+ ok() { echo "ok: $*"; }
28
+
29
+ # --- 1. Source files exist --------------------------------------------------
30
+
31
+ for f in "$SKILL" "$README" "$HELPERS"; do
32
+ if [ ! -f "$f" ]; then
33
+ fail "missing source file: $f"
34
+ fi
35
+ done
36
+ [ "$fails" -eq 0 ] || { echo "Aborting; source files missing." >&2; exit 1; }
37
+ ok "all three source files exist"
38
+
39
+ # --- 2. Mode parity ---------------------------------------------------------
40
+ # Every mode in the skill's argument-hint must appear in both:
41
+ # - the SKILL's `## Arguments` list (one `- \`mode\` — desc` line each)
42
+ # - the README's `### Modes` table (one `| \`mode\` |` row each)
43
+
44
+ modes_line=$(grep -E '^argument-hint:' "$SKILL" | head -1)
45
+ modes=$(printf '%s\n' "$modes_line" | grep -oE '<[^>]+>' | head -1 | tr -d '<>' | tr '|' '\n')
46
+
47
+ if [ -z "$modes" ]; then
48
+ fail "could not parse modes from SKILL argument-hint"
49
+ else
50
+ for mode in $modes; do
51
+ grep -qE "^- \`$mode\`" "$SKILL" \
52
+ || fail "mode '$mode' in argument-hint but missing from SKILL '## Arguments' list"
53
+ grep -qE "\| \`$mode\` \|" "$README" \
54
+ || fail "mode '$mode' in argument-hint but missing from README modes table"
55
+ done
56
+ [ "$fails" -gt 0 ] || ok "all argument-hint modes present in SKILL Arguments + README table"
57
+ fi
58
+
59
+ # --- 3. Helper function parity ---------------------------------------------
60
+ # Every shell function defined in scripts/integ.sh should be referenced
61
+ # from at least one of SKILL/README (catches dead helpers and rename drift).
62
+
63
+ functions=$(grep -E '^[a-z_][a-z0-9_]*\(\)' "$HELPERS" | sed 's/().*//')
64
+ if [ -z "$functions" ]; then
65
+ fail "could not parse any functions from $HELPERS"
66
+ else
67
+ for fn in $functions; do
68
+ if ! grep -qw "$fn" "$SKILL" && ! grep -qw "$fn" "$README"; then
69
+ fail "helper '$fn' defined in $HELPERS but not referenced from SKILL or README"
70
+ fi
71
+ done
72
+ [ "$fails" -gt 0 ] || ok "every helper function is referenced from at least one doc"
73
+ fi
74
+
75
+ # --- 4. Both docs reference scripts/integ.sh -------------------------------
76
+
77
+ for f in "$SKILL" "$README"; do
78
+ grep -q "scripts/integ.sh" "$f" \
79
+ || fail "$f does not reference scripts/integ.sh"
80
+ done
81
+ [ "$fails" -gt 0 ] || ok "both docs reference scripts/integ.sh"
82
+
83
+ # --- 5. No raw `aws signer put-signing-profile` ----------------------------
84
+ # Raw form is NOT idempotent (returns ProfileAlreadyExists on existing
85
+ # Active profile). Use signer_profile_ensure instead.
86
+
87
+ for f in "$SKILL" "$README"; do
88
+ if grep -nE "aws signer put-signing-profile" "$f" >/dev/null; then
89
+ fail "$f has raw 'aws signer put-signing-profile' — use signer_profile_ensure (raw form is NOT idempotent)"
90
+ fi
91
+ done
92
+
93
+ # --- 6. No insufficient signing-config jq strip ----------------------------
94
+ # cosign 3.x silently hangs when --key is combined with a signing-config
95
+ # that still has oidc/ca/tsa fields. The correct strip is rekor+oidc+ca+tsa,
96
+ # which lives in cosign_minimal_signing_config.
97
+
98
+ for f in "$SKILL" "$README"; do
99
+ # Match `jq 'del(.rekorTlogUrls)'` (the OLD broken strip) — but not
100
+ # `del(.rekorTlogUrls, .oidcUrls, .caUrls, .tsaUrls)` (the helper's correct strip).
101
+ if grep -E "del\(\\.rekorTlogUrls\)" "$f" >/dev/null; then
102
+ fail "$f has insufficient 'del(.rekorTlogUrls)' — use cosign_minimal_signing_config (also strips oidc/ca/tsa)"
103
+ fi
104
+ done
105
+
106
+ # --- 7. signature mode list matches integ.signature/integ.<name>.ts -------
107
+ # For every signature-* mode, the referenced integ test source must exist.
108
+ # Check .ts (the source) not .js (the build artifact) — a fresh clone
109
+ # without `pnpm tsc` would otherwise false-fail.
110
+
111
+ for mode in $(printf '%s\n' "$modes" | grep '^signature-'); do
112
+ # signature-notation -> test/integ/signature/integ.notation.ts
113
+ fname=$(printf '%s' "$mode" | sed 's/^signature-//')
114
+ testfile="test/integ/signature/integ.${fname}.ts"
115
+ if [ ! -f "$testfile" ]; then
116
+ fail "mode '$mode' references missing test source $testfile"
117
+ fi
118
+ done
119
+
120
+ # --- 8. Final report -------------------------------------------------------
121
+
122
+ if [ "$fails" -gt 0 ]; then
123
+ echo "" >&2
124
+ echo "verify-integ-docs: $fails failure(s). Fix above before setting integ-docs marker." >&2
125
+ exit 1
126
+ fi
127
+
128
+ echo ""
129
+ echo "verify-integ-docs: all consistency checks passed."