@blamejs/blamejs-shop 0.2.11 → 0.2.13

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.
Files changed (55) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/asset-manifest.json +3 -3
  3. package/lib/storefront.js +276 -24
  4. package/lib/translations.js +2 -2
  5. package/lib/vendor/MANIFEST.json +2 -2
  6. package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +71 -23
  7. package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +2 -0
  8. package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +2 -0
  9. package/lib/vendor/blamejs/.github/workflows/ci.yml +17 -0
  10. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +18 -10
  11. package/lib/vendor/blamejs/.github/workflows/release-container.yml +29 -16
  12. package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +22 -3
  13. package/lib/vendor/blamejs/.github/zizmor.yml +29 -0
  14. package/lib/vendor/blamejs/.pinact.yaml +24 -8
  15. package/lib/vendor/blamejs/CHANGELOG.md +4 -0
  16. package/lib/vendor/blamejs/api-snapshot.json +2 -2
  17. package/lib/vendor/blamejs/lib/archive-wrap.js +1 -1
  18. package/lib/vendor/blamejs/lib/auth/jwt.js +7 -4
  19. package/lib/vendor/blamejs/lib/auth/saml.js +2 -2
  20. package/lib/vendor/blamejs/lib/cose.js +1 -1
  21. package/lib/vendor/blamejs/lib/did.js +1 -1
  22. package/lib/vendor/blamejs/lib/guard-archive.js +2 -2
  23. package/lib/vendor/blamejs/lib/guard-auth.js +5 -5
  24. package/lib/vendor/blamejs/lib/guard-cidr.js +4 -4
  25. package/lib/vendor/blamejs/lib/guard-csv.js +6 -6
  26. package/lib/vendor/blamejs/lib/guard-domain.js +4 -4
  27. package/lib/vendor/blamejs/lib/guard-email.js +6 -5
  28. package/lib/vendor/blamejs/lib/guard-filename.js +4 -4
  29. package/lib/vendor/blamejs/lib/guard-graphql.js +5 -5
  30. package/lib/vendor/blamejs/lib/guard-html.js +2 -2
  31. package/lib/vendor/blamejs/lib/guard-image.js +1 -1
  32. package/lib/vendor/blamejs/lib/guard-json.js +5 -5
  33. package/lib/vendor/blamejs/lib/guard-jsonpath.js +4 -4
  34. package/lib/vendor/blamejs/lib/guard-jwt.js +5 -5
  35. package/lib/vendor/blamejs/lib/guard-markdown.js +6 -6
  36. package/lib/vendor/blamejs/lib/guard-mime.js +6 -6
  37. package/lib/vendor/blamejs/lib/guard-oauth.js +5 -5
  38. package/lib/vendor/blamejs/lib/guard-pdf.js +1 -1
  39. package/lib/vendor/blamejs/lib/guard-regex.js +4 -4
  40. package/lib/vendor/blamejs/lib/guard-shell.js +4 -4
  41. package/lib/vendor/blamejs/lib/guard-svg.js +5 -5
  42. package/lib/vendor/blamejs/lib/guard-template.js +4 -4
  43. package/lib/vendor/blamejs/lib/guard-time.js +6 -6
  44. package/lib/vendor/blamejs/lib/guard-uuid.js +6 -6
  45. package/lib/vendor/blamejs/lib/guard-xml.js +4 -4
  46. package/lib/vendor/blamejs/lib/guard-yaml.js +5 -5
  47. package/lib/vendor/blamejs/lib/link-header.js +3 -2
  48. package/lib/vendor/blamejs/lib/mail-agent.js +4 -2
  49. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +2 -2
  50. package/lib/vendor/blamejs/lib/tsa.js +2 -1
  51. package/lib/vendor/blamejs/package.json +1 -1
  52. package/lib/vendor/blamejs/release-notes/v0.13.23.json +42 -0
  53. package/lib/vendor/blamejs/release-notes/v0.13.24.json +26 -0
  54. package/lib/vendor/blamejs/test/smoke.js +25 -0
  55. package/package.json +1 -1
@@ -50,6 +50,8 @@ jobs:
50
50
  steps:
51
51
  - name: Checkout
52
52
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
53
+ with:
54
+ persist-credentials: false
53
55
 
54
56
  - name: Install pinact (release binary + sha256 verify)
55
57
  # Install pinact directly from the upstream release binary
@@ -106,32 +108,43 @@ jobs:
106
108
  name: zizmor — workflow security audit
107
109
  runs-on: ubuntu-latest
108
110
  timeout-minutes: 5
109
- permissions:
110
- contents: read
111
- security-events: write # required for SARIF upload to Code Scanning
112
111
  steps:
113
112
  - name: Checkout
114
113
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
114
+ with:
115
+ persist-credentials: false
115
116
 
116
- - name: Install uv (zizmor distribution)
117
- uses: astral-sh/setup-uv@69d3e2780fa0ea7da961bfbb898be32a9b1d10b1 # v7.0.0
117
+ - name: Install zizmor (release binary + sha256 verify)
118
+ # Direct release-binary install instead of astral-sh/setup-uv + uvx.
119
+ # setup-uv (a third-party action) startup_failures the whole
120
+ # workflow on the current runner — the same class of failure as the
121
+ # raven-actions/actionlint composite that took down the actionlint
122
+ # job (no jobs, no logs). A run: step cannot startup_failure.
123
+ # zizmor publishes no checksums file, so the expected sha256 is
124
+ # pinned inline; a mismatch fails closed.
125
+ env:
126
+ ZIZMOR_VERSION: v1.18.0
127
+ ZIZMOR_SHA256: 8e7901319ab7b71c38d6d388a48e02ff65791e5971b2ee6577c9b5c3ab44c65f
128
+ run: |
129
+ set -euo pipefail
130
+ curl -fsSLo /tmp/zizmor.tar.gz "https://github.com/zizmorcore/zizmor/releases/download/${ZIZMOR_VERSION}/zizmor-x86_64-unknown-linux-gnu.tar.gz"
131
+ ACTUAL=$(sha256sum /tmp/zizmor.tar.gz | awk '{print $1}')
132
+ if [ "$ACTUAL" != "$ZIZMOR_SHA256" ]; then
133
+ echo "::error::zizmor tarball sha256 ${ACTUAL} does not match pinned ${ZIZMOR_SHA256}"
134
+ exit 1
135
+ fi
136
+ mkdir -p /tmp/zizmor-bin
137
+ tar -xzf /tmp/zizmor.tar.gz -C /tmp/zizmor-bin ./zizmor
138
+ sudo install -m 0755 /tmp/zizmor-bin/zizmor /usr/local/bin/zizmor
139
+ zizmor --version
118
140
 
119
141
  - name: Run zizmor (fail on level=low across every rule class)
120
- # `--min-severity low` flags every documented class. Suppression
121
- # requires an inline `# zizmor: ignore[<rule>] — <reason>` per
122
- # the spec.
142
+ # Pure run-gate: zizmor exits non-zero on any finding, failing the
143
+ # job. Inline `# zizmor: ignore[<rule>] — <reason>` is the
144
+ # suppression mechanism for reviewed exceptions.
123
145
  run: |
124
146
  set -euo pipefail
125
- uvx zizmor@1.18.0 --min-severity low --format sarif . > zizmor.sarif || EXIT=$?
126
- uvx zizmor@1.18.0 --min-severity low . || true # human-readable output for the step log
127
- if [ "${EXIT:-0}" != "0" ]; then exit "$EXIT"; fi
128
-
129
- - name: Upload SARIF to Code Scanning
130
- if: always()
131
- uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
132
- with:
133
- sarif_file: zizmor.sarif
134
- category: zizmor
147
+ zizmor --min-severity low --no-progress .github/workflows/
135
148
 
136
149
  actionlint:
137
150
  name: actionlint — YAML + expression + shellcheck on `run:` blocks
@@ -140,9 +153,44 @@ jobs:
140
153
  steps:
141
154
  - name: Checkout
142
155
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
143
-
144
- - name: Run actionlint
145
- uses: raven-actions/actionlint@205b530c5d9fa8f44ae9ed59f341a0db994aa6f8 # v2.1.2
146
156
  with:
147
- fail_on_error: true
148
- shellcheck: true
157
+ persist-credentials: false
158
+
159
+ - name: Install actionlint (release binary + sha256 verify)
160
+ # Direct release-binary install instead of the
161
+ # raven-actions/actionlint composite action. That composite (v2.1.2,
162
+ # the latest release) startup_failures on the current runner, taking
163
+ # the whole workflow down before any job runs — which is why this
164
+ # gate never ran from v0.11.21 onward. The binary path also drops the
165
+ # composite's transitive github-script / cache action surface, which
166
+ # fits the gate whose entire purpose is pinning discipline. Mirrors
167
+ # the pinact install step above.
168
+ env:
169
+ ACTIONLINT_VERSION: v1.7.12
170
+ run: |
171
+ set -euo pipefail
172
+ V="${ACTIONLINT_VERSION#v}"
173
+ curl -fsSLo /tmp/actionlint.tar.gz "https://github.com/rhysd/actionlint/releases/download/${ACTIONLINT_VERSION}/actionlint_${V}_linux_amd64.tar.gz"
174
+ curl -fsSLo /tmp/actionlint-checksums.txt "https://github.com/rhysd/actionlint/releases/download/${ACTIONLINT_VERSION}/actionlint_${V}_checksums.txt"
175
+ ACTUAL=$(sha256sum /tmp/actionlint.tar.gz | awk '{print $1}')
176
+ EXPECTED=$(grep "actionlint_${V}_linux_amd64.tar.gz\$" /tmp/actionlint-checksums.txt | awk '{print $1}')
177
+ if [ -z "$EXPECTED" ]; then
178
+ echo "::error::could not find actionlint_${V}_linux_amd64.tar.gz line in upstream checksums"
179
+ exit 1
180
+ fi
181
+ if [ "$ACTUAL" != "$EXPECTED" ]; then
182
+ echo "::error::actionlint tarball sha256 ${ACTUAL} does not match upstream-published ${EXPECTED}"
183
+ exit 1
184
+ fi
185
+ mkdir -p /tmp/actionlint-bin
186
+ tar -xzf /tmp/actionlint.tar.gz -C /tmp/actionlint-bin actionlint
187
+ sudo install -m 0755 /tmp/actionlint-bin/actionlint /usr/local/bin/actionlint
188
+ actionlint --version
189
+
190
+ - name: Run actionlint (+ shellcheck on every run block)
191
+ # shellcheck ships preinstalled on ubuntu-latest; actionlint invokes
192
+ # it automatically on each `run:` block. Non-zero exit on any finding
193
+ # keeps this a hard gate (the raven composite's fail_on_error: true).
194
+ run: |
195
+ set -euo pipefail
196
+ actionlint -color
@@ -69,6 +69,8 @@ jobs:
69
69
  steps:
70
70
  - name: Checkout
71
71
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
72
+ with:
73
+ persist-credentials: false
72
74
 
73
75
  - name: Setup Node
74
76
  uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -80,6 +80,8 @@ jobs:
80
80
  steps:
81
81
  - name: Checkout
82
82
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
83
+ with:
84
+ persist-credentials: false
83
85
 
84
86
  - name: Setup Node
85
87
  uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -52,6 +52,8 @@ jobs:
52
52
  steps:
53
53
  - name: Checkout
54
54
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
55
+ with:
56
+ persist-credentials: false
55
57
 
56
58
  - name: Set up Node 24.14.1 LTS
57
59
  uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -106,6 +108,8 @@ jobs:
106
108
  steps:
107
109
  - name: Checkout
108
110
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
111
+ with:
112
+ persist-credentials: false
109
113
 
110
114
  - name: Set up Node 24.14.1 LTS
111
115
  uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -126,6 +130,8 @@ jobs:
126
130
  steps:
127
131
  - name: Checkout
128
132
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
133
+ with:
134
+ persist-credentials: false
129
135
 
130
136
  - name: Set up Node 24.14.1 LTS
131
137
  uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -149,6 +155,8 @@ jobs:
149
155
  steps:
150
156
  - name: Checkout
151
157
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
158
+ with:
159
+ persist-credentials: false
152
160
  - name: Set up Node
153
161
  uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
154
162
  with:
@@ -168,6 +176,7 @@ jobs:
168
176
  - name: Checkout
169
177
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
170
178
  with:
179
+ persist-credentials: false
171
180
  # gitleaks scans full history on a non-shallow checkout, so an
172
181
  # accidentally-committed secret in any prior commit on the
173
182
  # branch is caught even if a later commit reverts it. The
@@ -225,6 +234,8 @@ jobs:
225
234
  steps:
226
235
  - name: Checkout
227
236
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
237
+ with:
238
+ persist-credentials: false
228
239
 
229
240
  - name: Set up Node 24.14.1 LTS
230
241
  uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -254,6 +265,8 @@ jobs:
254
265
  steps:
255
266
  - name: Checkout
256
267
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
268
+ with:
269
+ persist-credentials: false
257
270
 
258
271
  - name: Set up Node 24.14.1 LTS
259
272
  uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -294,6 +307,8 @@ jobs:
294
307
  steps:
295
308
  - name: Checkout
296
309
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
310
+ with:
311
+ persist-credentials: false
297
312
 
298
313
  - name: Run Hadolint (strict, all severities)
299
314
  # `--no-fail` is OFF; any error or warning fails the job.
@@ -336,6 +351,8 @@ jobs:
336
351
  steps:
337
352
  - name: Checkout
338
353
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
354
+ with:
355
+ persist-credentials: false
339
356
 
340
357
  - name: Detect shell scripts
341
358
  id: sh
@@ -94,6 +94,8 @@ jobs:
94
94
  steps:
95
95
  - name: Checkout
96
96
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
97
+ with:
98
+ persist-credentials: false
97
99
 
98
100
  - name: Set up Node 24.14.1 LTS
99
101
  uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -102,11 +104,14 @@ jobs:
102
104
 
103
105
  - name: Compute version + is_tag flag
104
106
  id: version
107
+ env:
108
+ EVENT_NAME: ${{ github.event_name }}
109
+ REF: ${{ github.ref }}
105
110
  run: |
106
111
  set -euo pipefail
107
112
  PKG_VERSION=$(node -e "console.log(require('./package.json').version)")
108
113
  echo "version=$PKG_VERSION" >> "$GITHUB_OUTPUT"
109
- if [ "${{ github.event_name }}" = "push" ] && [[ "${{ github.ref }}" == refs/tags/v* ]]; then
114
+ if [ "$EVENT_NAME" = "push" ] && [[ "$REF" == refs/tags/v* ]]; then
110
115
  TAG_VERSION="${GITHUB_REF#refs/tags/v}"
111
116
  echo "is_tag=true" >> "$GITHUB_OUTPUT"
112
117
  if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
@@ -132,7 +137,7 @@ jobs:
132
137
 
133
138
  - name: ShellCheck
134
139
  if: steps.shells.outputs.found == 'true'
135
- uses: ludeeus/action-shellcheck@00b27aa7cb85167568cb48a3838b75f4265f2bca # v2.0.0
140
+ uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0
136
141
  with:
137
142
  severity: error
138
143
 
@@ -229,7 +234,7 @@ jobs:
229
234
  id: subjects
230
235
  run: |
231
236
  set -euo pipefail
232
- TARBALL=$(basename "$(ls dist/*.tgz | head -1)")
237
+ TARBALL=$(basename "$(find dist -maxdepth 1 -name '*.tgz' | head -1)")
233
238
  HASHES=$(
234
239
  (cd dist && sha256sum "$TARBALL") && \
235
240
  sha256sum sbom.cdx.json sbom.vendored.cdx.json
@@ -292,7 +297,9 @@ jobs:
292
297
  # repo specifically so the SLSA tag-pin call is permitted. The
293
298
  # `slsa-action-not-pinned` codebase-patterns detector allowlists
294
299
  # this specific callsite (see test/layer-0-primitives/codebase-patterns.test.js).
295
- uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 # allow:slsa-framework-action-not-sha-pinned — SLSA's internal builder-fetch refuses non-tag refs; mitigated by slsa-framework's upstream tag-protection + immutable-release rules
300
+ # SLSA's reusable builder workflow refuses non-tag refs (cannot be
301
+ # SHA-pinned); upstream tag-protection + immutable releases mitigate.
302
+ uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 # zizmor: ignore[unpinned-uses] allow:slsa-framework-action-not-sha-pinned — SLSA's internal builder-fetch refuses non-tag refs; mitigated by slsa-framework's upstream tag-protection + immutable-release rules
296
303
  with:
297
304
  base64-subjects: "${{ needs.validate.outputs.hashes }}"
298
305
  upload-assets: false
@@ -410,6 +417,7 @@ jobs:
410
417
  # and failed immediately on the first PQC release run.
411
418
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
412
419
  with:
420
+ persist-credentials: false
413
421
  path: src
414
422
 
415
423
  - name: Release-digest sidecars (SHA-256 + SHA3-512)
@@ -424,7 +432,7 @@ jobs:
424
432
  # Both written in `<sha> <filename>` sha-sum-compatible format.
425
433
  run: |
426
434
  set -euo pipefail
427
- TARBALL=$(ls dist/*.tgz | head -1)
435
+ TARBALL=$(find dist -maxdepth 1 -name '*.tgz' | head -1)
428
436
  node src/scripts/sha3-digest.js "$TARBALL"
429
437
 
430
438
  - name: PQC sidecar — ML-DSA-65 signature of the tarball
@@ -463,7 +471,7 @@ jobs:
463
471
  echo "available=false" >> "$GITHUB_OUTPUT"
464
472
  exit 0
465
473
  fi
466
- TARBALL=$(ls dist/*.tgz | head -1)
474
+ TARBALL=$(find dist -maxdepth 1 -name '*.tgz' | head -1)
467
475
  # The signer expects keys/release-pqc-pub.json at the
468
476
  # framework root for the self-verify cross-check. Symlink
469
477
  # the checked-out src/keys/ into the working directory so
@@ -514,11 +522,11 @@ jobs:
514
522
  set -euo pipefail
515
523
  if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
516
524
  echo "::error::Release $TAG already exists. The workflow is authoritative for release creation as of v0.11.7."
517
- echo "::error::Manual `gh release create` was removed from the release workflow in v0.11.7."
518
- echo "::error::Either delete the existing release (`gh release delete $TAG`) and re-run this workflow, OR push a new tag."
525
+ echo "::error::Manual 'gh release create' was removed from the release workflow in v0.11.7."
526
+ echo "::error::Either delete the existing release with 'gh release delete $TAG' and re-run this workflow, OR push a new tag."
519
527
  exit 1
520
528
  fi
521
- TARBALL=$(ls dist/*.tgz | head -1)
529
+ TARBALL=$(find dist -maxdepth 1 -name '*.tgz' | head -1)
522
530
  # Title comes from the structured release-notes JSON's
523
531
  # `headline` field — single source of truth. Format:
524
532
  # `vX.Y.Z — <headline>`.
@@ -594,7 +602,7 @@ jobs:
594
602
  # mis-classifies a relative path containing `/` as a git
595
603
  # spec.
596
604
  run: |
597
- TARBALL=$(ls dist/*.tgz | head -1)
605
+ TARBALL=$(find dist -maxdepth 1 -name '*.tgz' | head -1)
598
606
  echo "Publishing: $TARBALL"
599
607
  npm publish "./${TARBALL}" --provenance --access public
600
608
  env:
@@ -57,6 +57,8 @@ jobs:
57
57
  steps:
58
58
  - name: Checkout
59
59
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
60
+ with:
61
+ persist-credentials: false
60
62
  - name: Set up Node 24.14.1 LTS
61
63
  uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
62
64
  with:
@@ -73,6 +75,8 @@ jobs:
73
75
  steps:
74
76
  - name: Checkout
75
77
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
78
+ with:
79
+ persist-credentials: false
76
80
  - name: Run Hadolint (strict, all severities)
77
81
  uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5 # v3.3.0
78
82
  with:
@@ -88,6 +92,8 @@ jobs:
88
92
  steps:
89
93
  - name: Checkout
90
94
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
95
+ with:
96
+ persist-credentials: false
91
97
  - name: Detect shell scripts
92
98
  id: sh
93
99
  run: |
@@ -97,7 +103,7 @@ jobs:
97
103
  echo "found=$count" >> "$GITHUB_OUTPUT"
98
104
  - name: Run ShellCheck
99
105
  if: steps.sh.outputs.found != '0'
100
- uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0
106
+ uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0
101
107
  with:
102
108
  severity: style
103
109
  format: tty
@@ -118,16 +124,18 @@ jobs:
118
124
  steps:
119
125
  - name: Checkout
120
126
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
127
+ with:
128
+ persist-credentials: false
121
129
 
122
130
  - name: Set up QEMU (for ARM64 cross-build)
123
- uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4
131
+ uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
124
132
 
125
133
  - name: Set up Docker Buildx
126
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
134
+ uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
127
135
 
128
136
  - name: Log in to GHCR
129
137
  if: ${{ !inputs.dry_run }}
130
- uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
138
+ uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
131
139
  with:
132
140
  registry: ghcr.io
133
141
  username: ${{ github.actor }}
@@ -162,7 +170,7 @@ jobs:
162
170
  # Note: load: true forces single-arch (amd64). The multi-arch
163
171
  # build happens on the post-scan push step below.
164
172
  - name: Build (local-only, for Trivy)
165
- uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
173
+ uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
166
174
  with:
167
175
  context: .
168
176
  file: examples/wiki/Dockerfile
@@ -210,7 +218,7 @@ jobs:
210
218
  - name: Push (multi-arch, after Trivy gate)
211
219
  id: push
212
220
  if: ${{ !inputs.dry_run && steps.tags.outputs.is_tag == 'true' }}
213
- uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
221
+ uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
214
222
  with:
215
223
  context: .
216
224
  file: examples/wiki/Dockerfile
@@ -236,7 +244,7 @@ jobs:
236
244
  if: ${{ !inputs.dry_run && steps.tags.outputs.is_tag == 'true' && steps.push.outputs.digest }}
237
245
  env:
238
246
  DIGEST: ${{ steps.push.outputs.digest }}
239
- run: cosign sign --yes "${{ env.IMAGE }}@${DIGEST}"
247
+ run: cosign sign --yes "${IMAGE}@${DIGEST}"
240
248
 
241
249
  - name: Generate SHA3-512 digest
242
250
  if: ${{ !inputs.dry_run && steps.tags.outputs.is_tag == 'true' && steps.push.outputs.digest }}
@@ -245,8 +253,8 @@ jobs:
245
253
  DIGEST: ${{ steps.push.outputs.digest }}
246
254
  run: |
247
255
  set -euo pipefail
248
- docker pull "${{ env.IMAGE }}@${DIGEST}"
249
- HASH=$(docker save "${{ env.IMAGE }}@${DIGEST}" | openssl dgst -sha3-512 | awk '{print $NF}')
256
+ docker pull "${IMAGE}@${DIGEST}"
257
+ HASH=$(docker save "${IMAGE}@${DIGEST}" | openssl dgst -sha3-512 | awk '{print $NF}')
250
258
  echo "SHA3-512: $HASH"
251
259
  echo "sha3=$HASH" >> "$GITHUB_OUTPUT"
252
260
  {
@@ -275,8 +283,8 @@ jobs:
275
283
  echo "|---|---|"
276
284
  echo "| **Version** | ${VERSION} |"
277
285
  echo "| **Platforms** | linux/amd64, linux/arm64 |"
278
- echo "| **Registry** | ${{ env.IMAGE }} |"
279
- echo "| **Commit** | ${{ github.sha }} |"
286
+ echo "| **Registry** | ${IMAGE} |"
287
+ echo "| **Commit** | ${GITHUB_SHA} |"
280
288
  echo "| **Dry run** | ${DRY_RUN} |"
281
289
  echo "| **Tag push** | ${IS_TAG} |"
282
290
  echo ""
@@ -297,6 +305,11 @@ jobs:
297
305
  if: ${{ !inputs.dry_run && needs.publish.outputs.digest != '' }}
298
306
  runs-on: ubuntu-latest
299
307
  timeout-minutes: 10
308
+ env:
309
+ # Hoisted so run: blocks reference $DIGEST instead of inlining the
310
+ # ${{ needs.publish.outputs.digest }} expression (template-injection
311
+ # hardening — keeps GitHub-context values out of shell-parsed text).
312
+ DIGEST: ${{ needs.publish.outputs.digest }}
300
313
  steps:
301
314
  # Smoke-test runs on a fresh runner — it doesn't inherit the
302
315
  # publish job's GHCR auth. Log in first so `docker pull` works
@@ -304,19 +317,19 @@ jobs:
304
317
  # against a public package, so this is the right shape regardless
305
318
  # of visibility.
306
319
  - name: Log in to GHCR
307
- uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
320
+ uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
308
321
  with:
309
322
  registry: ghcr.io
310
323
  username: ${{ github.actor }}
311
324
  password: ${{ secrets.GITHUB_TOKEN }}
312
325
 
313
326
  - name: Pull published image
314
- run: docker pull "${{ env.IMAGE }}@${{ needs.publish.outputs.digest }}"
327
+ run: docker pull "${IMAGE}@${DIGEST}"
315
328
 
316
329
  - name: Verify OCI labels
317
330
  run: |
318
331
  set -euo pipefail
319
- LABELS=$(docker inspect "${{ env.IMAGE }}@${{ needs.publish.outputs.digest }}" --format '{{json .Config.Labels}}')
332
+ LABELS=$(docker inspect "${IMAGE}@${DIGEST}" --format '{{json .Config.Labels}}')
320
333
  echo "$LABELS" | python3 -m json.tool
321
334
  echo "$LABELS" | python3 -c "
322
335
  import sys, json
@@ -342,7 +355,7 @@ jobs:
342
355
  --security-opt no-new-privileges:true \
343
356
  --cap-drop ALL \
344
357
  -p 3008:3008 \
345
- "${{ env.IMAGE }}@${{ needs.publish.outputs.digest }}"
358
+ "${IMAGE}@${DIGEST}"
346
359
 
347
360
  echo "Waiting for /healthz..."
348
361
  for i in $(seq 1 30); do
@@ -396,7 +409,7 @@ jobs:
396
409
  {
397
410
  echo "### Post-publish smoke ❌"
398
411
  echo ""
399
- echo "Smoke test failed for ${{ env.IMAGE }}@${{ needs.publish.outputs.digest }}."
412
+ echo "Smoke test failed for ${IMAGE}@${DIGEST}."
400
413
  echo "See the 'Capture container diagnostics on failure' step above for"
401
414
  echo "docker ps / inspect / logs output."
402
415
  } >> "$GITHUB_STEP_SUMMARY"
@@ -39,6 +39,7 @@ jobs:
39
39
  - name: Checkout (full history needed for first-parent walk)
40
40
  uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
41
41
  with:
42
+ persist-credentials: false
42
43
  fetch-depth: 0
43
44
  fetch-tags: true
44
45
 
@@ -53,10 +54,15 @@ jobs:
53
54
  - name: Resolve tag + verify chain
54
55
  env:
55
56
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57
+ EVENT_NAME: ${{ github.event_name }}
58
+ # Hoisted: a workflow_dispatch input is operator-supplied text;
59
+ # inlining ${{ github.event.inputs.tag }} into the shell is the
60
+ # template-injection vector. Reference $INPUT_TAG instead.
61
+ INPUT_TAG: ${{ github.event.inputs.tag }}
56
62
  run: |
57
63
  set -euo pipefail
58
- if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
59
- TAG="${{ github.event.inputs.tag }}"
64
+ if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
65
+ TAG="$INPUT_TAG"
60
66
  else
61
67
  TAG="${GITHUB_REF#refs/tags/}"
62
68
  fi
@@ -77,7 +83,20 @@ jobs:
77
83
  # pass that test. The strict first-parent invariant is
78
84
  # what the project's squash-merge policy actually
79
85
  # produces; check it directly.
80
- if ! git rev-list --first-parent origin/main | grep -qx "$SHA"; then
86
+ #
87
+ # Capture rev-list output first, then grep against a
88
+ # here-string. The obvious-looking
89
+ # `git rev-list --first-parent origin/main | grep -qx "$SHA"`
90
+ # is a `set -o pipefail` + `grep -q` trap: when grep -q
91
+ # finds the match it exits early and closes its stdin,
92
+ # rev-list gets SIGPIPE and exits 141, and pipefail then
93
+ # propagates 141 as the pipeline's exit. The outer
94
+ # `if !` negates 141 → enters the false-fail branch
95
+ # every time on a long first-parent chain. (False-failed
96
+ # every release v0.13.18-.22.) The here-string consumes
97
+ # rev-list to completion first; no pipe, no SIGPIPE.
98
+ FIRST_PARENT=$(git rev-list --first-parent origin/main)
99
+ if ! grep -qFx "$SHA" <<<"$FIRST_PARENT"; then
81
100
  echo "::error::tag $TAG points at $SHA which is NOT on main's first-parent history"
82
101
  echo "::error::this means the tag was created from a side-branch commit that never merged via the project's squash-merge flow"
83
102
  echo "::error::the publish workflow refuses to proceed; investigate before re-tagging"
@@ -0,0 +1,29 @@
1
+ # zizmor configuration (auto-discovered at .github/zizmor.yml).
2
+ #
3
+ # Suppressions live here rather than as inline `# zizmor: ignore[...]`
4
+ # comments because the affected lines are `uses:` references that
5
+ # already carry a pinact version comment (`# vX.Y.Z`). zizmor only
6
+ # honors an ignore pragma when `zizmor:` is the first token of the
7
+ # YAML comment; a `# vX.Y.Z # zizmor: ignore[...]` line reads as the
8
+ # comment text "vX.Y.Z # zizmor: ..." and the pragma is never seen.
9
+ # A config-file ignore is the only placement that does not collide
10
+ # with the SHA-pin version comment that pinact requires.
11
+
12
+ rules:
13
+ cache-poisoning:
14
+ # actions/setup-node carries an optional `cache:` input that, when
15
+ # set, restores a dependency cache. zizmor flags the *capability*
16
+ # whenever the workflow also has a release-class trigger
17
+ # (`push: tags: ["v*"]`), because a poisoned cache restored during a
18
+ # publishing run is high-impact. None of these workflows set a
19
+ # `cache:` input — no dependency cache is created or restored, so
20
+ # there is nothing to poison. The finding is capability-driven, not
21
+ # an observed cache.
22
+ #
23
+ # Invariant for future edits: if any step in one of these files adds
24
+ # a `cache:` input to a setup-* action, remove that file from this
25
+ # list and review the cache-poisoning exposure for real.
26
+ ignore:
27
+ - ci.yml
28
+ - npm-publish.yml
29
+ - release-container.yml
@@ -1,17 +1,33 @@
1
1
  # pinact configuration — refuses any tag-shaped `uses:` reference EXCEPT
2
- # the documented exception below.
2
+ # the documented exceptions below.
3
3
  #
4
- # The single exception is the SLSA reusable workflow. SLSA's internal
5
- # builder-fetch step refuses non-tag `BUILDER_REF` values ("Invalid ref:
6
- # <40-hex>. Expected ref of the form refs/tags/vX.Y.Z"), so the call
7
- # MUST be tag-pinned. Mitigated by slsa-framework's upstream tag-
8
- # protection + immutable-release rules. Same suppression also appears
9
- # as a per-line `# allow:slsa-framework-action-not-sha-pinned` marker in
10
- # `.github/workflows/npm-publish.yml` for the codebase-patterns gate.
4
+ # 1. The SLSA reusable workflow. SLSA's internal builder-fetch step
5
+ # refuses non-tag `BUILDER_REF` values ("Invalid ref: <40-hex>.
6
+ # Expected ref of the form refs/tags/vX.Y.Z"), so the call MUST be
7
+ # tag-pinned. Mitigated by slsa-framework's upstream tag-protection +
8
+ # immutable-release rules. Same suppression also appears as a per-line
9
+ # `# allow:slsa-framework-action-not-sha-pinned` marker in
10
+ # `.github/workflows/npm-publish.yml` for the codebase-patterns gate.
11
+ #
12
+ # 2. aquasecurity/trivy-action. The aquasecurity org enables a GitHub
13
+ # org-level IP allow-list that returns 403 to GitHub Actions runner
14
+ # IPs on the commits API, so `pinact run --verify`'s online tag
15
+ # re-resolution cannot complete from CI (it succeeds from
16
+ # unrestricted IPs). The action stays SHA-pinned in the workflow
17
+ # (auditable inline); this entry only exempts it from the online
18
+ # re-verification that the upstream org makes impossible from a
19
+ # runner.
11
20
  #
12
21
  # See npm-publish.yml comment block + memory/specs/release-v0.10.19-plan.md.
13
22
 
14
23
  version: 3
15
24
 
16
25
  ignore_actions:
26
+ # pinact v3 treats both `name` and `ref` as regular expressions, and
27
+ # both are required (a name-only entry fails config load with
28
+ # "initialize ignore_action: ref is required"). `ref: .*` matches the
29
+ # tag-pinned ref this single exception carries (currently v2.1.0).
17
30
  - name: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml
31
+ ref: .*
32
+ - name: aquasecurity/trivy-action
33
+ ref: .*
@@ -8,6 +8,10 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.13.x
10
10
 
11
+ - v0.13.24 (2026-05-28) — **`b.guard*` docs corrected: the compliance-posture opt key, the gate API, and validate return shapes.** Documentation corrections across the b.guard* family. The most consequential: the posture-selection option was documented as `compliance:` in many guards' @opts, but the working key is `compliancePosture:` — passing the documented `{ compliance: "hipaa" }` was silently ignored, so a compliance posture (e.g. HIPAA PII redaction) never activated. If you select a posture via the gate/validate/sanitize options, use `compliancePosture:`; `compliance:` had no effect. The guard docs now name the correct key uniformly. Also corrected: gate examples and prose that invoked the gate as a callable or via `.run` / `.inspect` (the gate is an object whose method is `.check(ctx)`), and validate() return shapes that listed `severities` / `summary` / `refusal` fields the function never returned (it returns `{ ok, issues }`). **Fixed:** *guard posture option is `compliancePosture:`, not `compliance:`* — Many `b.guard*` primitives documented the compliance-posture selector as `compliance: "hipaa"|"pci-dss"|"gdpr"|"soc2"` in their `@opts`, but the family resolver reads `compliancePosture:`. Passing `{ compliance: "hipaa" }` was accepted and silently ignored — the posture overlay (e.g. CSV `piiPolicy: "redact"` under HIPAA) never applied, leaving the default policy in force. The docs across the guard family now name `compliancePosture:` consistently (the key the resolver and `b.guardX.compliancePosture(name)` already used). Action: if you selected a posture with `compliance:`, switch to `compliancePosture:` — the posture was not taking effect before. · *guard gate is an object with `.check(ctx)`, not a callable* — Several guards' gate `@example`s and prose invoked the gate as a function (`g({...})`), via `.run(...)`, or via `.inspect(...)`, and a few described it as "an async function". `b.guardX.gate(opts)` returns an object whose async method is `.check(ctx)`; the examples and prose now use `.check`, so they run as written. · *guard validate() returns `{ ok, issues }`* — Several guards documented `validate()` as returning `{ ok, issues, severities }`, `{ ok, issues, summary }`, or `{ ok, issues, refusal? }`. The function returns `{ ok, issues }` (each issue carries its own `severity` / `kind`); the documented extra top-level fields were never present. The docs now state the actual shape.
12
+
13
+ - v0.13.23 (2026-05-28) — **Documentation corrected to match actual behavior across several primitives.** A set of JSDoc / doc-comment corrections where the documented contract had drifted from what the code does. No behavior changes — the implementations already behaved as now documented; only the docs were wrong. The most operator-relevant is the JWT signer doc: an expiring token signed without an explicit jti receives an auto-minted 128-bit jti (so the replay-defense path has the jti it needs), which the sign-opts doc previously denied. Also corrected: did.resolve's unsupported-method error now names did:jwk (always supported); b.cose.verify is marked stable to match its stable sign sibling and the CWT / EAT / SCITT / mdoc verifiers built on it; b.linkHeader.serialize's doc now states every parameter value is double-quoted; b.auth.saml verifyResponse's documented return shape now lists inResponseTo and issuer (both always returned); and the rate-limit custom-backend contract drops a gc member the middleware never invoked. **Fixed:** *JWT signer doc now describes the auto-minted jti on expiring tokens* — `b.auth.jwt.sign`'s opts doc claimed that omitting `jti` adds no jti. In fact, when a token carries an `exp` and no operator-supplied `jti`, the signer auto-mints a random 128-bit `jti` so a replay-protected token always carries the identifier `verify`'s replay store requires. The doc now describes this; pass an explicit `jti` for a deterministic value. Behavior is unchanged. · *`b.did.resolve` unsupported-method error now names did:jwk* — The thrown error for an unsupported DID method listed only `did:key` and `did:web`, omitting `did:jwk`, which `resolve` fully supports. The message now reads `(did:key, did:jwk, and did:web only)`. · *`b.cose.verify` marked stable* — `b.cose.verify` carried `@status experimental` while its `b.cose.sign` sibling is stable and the CWT / EAT / SCITT / mdoc verifiers that depend on it are stable and shipped. The verifier is the same maturity as the rest of the COSE_Sign1 round-trip; its status now reflects that. · *`b.linkHeader.serialize` doc matches its quoting behavior* — The doc said parameters are token-encoded when they fit RFC 7230 token grammar and double-quoted otherwise. The serializer always double-quotes every value (valid under RFC 8288, and required for space-separated multi-rel and media-type values). The doc now states that. · *`b.auth.saml` verifyResponse documented return shape lists all fields* — The prose and the example each omitted a different field that `verifyResponse` always returns. The documented shape now lists all of `nameId`, `nameIdFormat`, `sessionIndex`, `attributes`, `audience`, `inResponseTo`, and `issuer`. · *rate-limit custom-backend contract is `{ take, reset }`* — The custom-backend opts doc listed a `gc` member that the middleware never reads or invokes (the runtime contract is `take` / `reset` / `close`, and the error message already said `{ take, reset }`). The documented shape now matches; an operator-supplied `gc` was always silently ignored. · *`b.mail.agent.create` doc no longer lists consumer as a method* — The created agent's method list named `consumer`, which is not a method on the returned object — the queue consumer is the sibling export `b.mail.agent.consumer`. The doc now says so.
14
+
11
15
  - v0.13.22 (2026-05-27) — **`b.archive.read.zip.fromTrustedStream` reads a ZIP from a Readable — no longer an experimental stub.** fromTrustedStream was an experimental stub whose inspect / entries / extract methods threw, forcing callers to buffer the stream themselves and use the random-access reader. It now works, with the same shape as the tar trusted-stream reader: pass b.archive.adapters.trustedStream(readable) and the bytes are collected into a size-capped buffer (1 GiB hard ceiling) and read through the same bomb-cap, path-traversal, and entry-type decode as the random-access reader — so bombPolicy, guardProfile, entryTypePolicy, and audit all apply, and inspect / entries / extract / extractEntries all return data. This is a bounded-memory reader (the archive is held in memory under the ceiling), not zero-buffer streaming; a future forward-inflate walker shared with the tar reader would lift the ceiling. **Added:** *`b.archive.read.zip.fromTrustedStream` now reads — `inspect` / `entries` / `extract` / `extractEntries`* — The ZIP trusted-stream reader is implemented (was an experimental stub that threw). Pass `b.archive.adapters.trustedStream(readable)` to read a ZIP straight from a Node Readable without buffering it yourself. The stream is collected into a size-capped buffer (1 GiB ceiling, matching `b.archive.read.tar`'s trusted-stream reader) and decoded through the same adversarial-safe path as the random-access reader, so `bombPolicy` / `guardProfile` / `entryTypePolicy` / `audit` are honored on decode. Adversarial archives remain fully bomb-capped; "trusted" refers only to the source-size bound. A non-trusted-stream adapter is refused with `archive-read/bad-adapter`.
12
16
 
13
17
  - v0.13.21 (2026-05-27) — **`b.cose.exportKey` — serialize a public key as a COSE_Key, the inverse of `b.cose.importKey`.** b.cose could import a COSE_Key (RFC 9052 §7) into a node:crypto key for verification, but had no way to produce one — so a key used with b.cose.sign could not be shipped to a verifier in COSE form without hand-building the CBOR map. b.cose.exportKey(keyObject, opts?) closes the round-trip: it serializes an EC2 (P-256 / P-384 / P-521) or OKP (Ed25519) public key as the CBOR-encoded COSE_Key map, with optional alg and kid common parameters. A private key has its public half exported; unsupported curves / key types are refused rather than emitting a COSE_Key no verifier here would accept. The bytes round-trip through b.cose.importKey, and feed the mdoc MSO / COSE_Key header / SCITT / C2PA verification-key paths. **Added:** *`b.cose.exportKey(keyObject, { alg?, kid? })` — KeyObject → COSE_Key (RFC 9052 §7)* — Serialize a `node:crypto` public key as the CBOR-encoded COSE_Key map — the inverse of `b.cose.importKey`. Supports EC2 (P-256 / P-384 / P-521) and OKP (Ed25519), the same key types `b.cose.verify` accepts; `opts.alg` (e.g. `"ES256"`) and `opts.kid` populate the COSE_Key alg (label 3) and kid (label 2) common parameters. A private key exports its public half; unsupported curves / key types throw rather than producing a COSE_Key no verifier would accept. `b.cose.importKey(b.cbor.decode(exportKey(k)))` round-trips, so a key signed with `b.cose.sign` can be shipped to a verifier as bytes — the mdoc MSO / COSE_Key header / SCITT / C2PA verification-key paths.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 1,
3
- "frameworkVersion": "0.13.22",
4
- "createdAt": "2026-05-27T23:32:55.833Z",
3
+ "frameworkVersion": "0.13.24",
4
+ "createdAt": "2026-05-28T08:46:52.303Z",
5
5
  "exports": {
6
6
  "a2a": {
7
7
  "type": "object",
@@ -370,7 +370,7 @@ function sniffEnvelope(bytes) {
370
370
  * @signature b.archive.wrapWithPassphrase(bytes, opts)
371
371
  * @since 0.12.11
372
372
  * @status stable
373
- * @related b.archive.unwrapWithPassphrase, b.archive.wrap, b.backupCrypto
373
+ * @related b.archive.unwrapWithPassphrase, b.archive.wrap
374
374
  *
375
375
  * Wrap archive bytes in a passphrase-derived envelope. The envelope
376
376
  * wire format is the framework's standard Argon2id (RFC 9106) +