@blamejs/blamejs-shop 0.5.6 → 0.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/cost-layers.js +86 -49
- package/lib/plan-changes.js +14 -0
- package/lib/storefront.js +30 -17
- package/lib/vendor/MANIFEST.json +31 -15
- package/lib/vendor/blamejs/.github/workflows/release-container.yml +2 -2
- package/lib/vendor/blamejs/CHANGELOG.md +2 -0
- package/lib/vendor/blamejs/api-snapshot.json +2 -2
- package/lib/vendor/blamejs/examples/wiki/package-lock.json +1 -1
- package/lib/vendor/blamejs/lib/content-credentials.js +43 -2
- package/lib/vendor/blamejs/lib/guard-sql.js +29 -0
- package/lib/vendor/blamejs/lib/mcp.js +37 -4
- package/lib/vendor/blamejs/lib/metrics.js +15 -3
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +9 -2
- package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +33 -2
- package/lib/vendor/blamejs/lib/safe-buffer.js +11 -1
- package/lib/vendor/blamejs/package-lock.json +2 -2
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.3.json +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials-coverage.test.js +319 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cycle2-bugfixes.test.js +142 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-sql-coverage.test.js +432 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp-coverage.test.js +443 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-coverage.test.js +395 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-tus-upload-coverage.test.js +402 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-safe-yaml-coverage.test.js +292 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,10 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.5.x
|
|
10
10
|
|
|
11
|
+
- v0.5.8 (2026-07-03) — **Inventory cost-layer consumption and subscription plan-change execution are now correct under concurrent requests; vendored framework refreshed to 0.16.3.** Two ledger-atomicity fixes for concurrent requests, plus a vendored framework security refresh. Cost-layer (COGS) consumption debited each inventory layer with an unguarded read-modify-write against a snapshot, so two overlapping sales or write-offs of the same SKU could both draw from the same layer — driving a layer's remaining quantity negative, stranding a later layer, and understating cost of goods sold. Each layer debit is now an atomic guarded claim (the decrement only applies while the layer still holds the planned quantity); a losing claim reverts its own partial work and re-plans against a fresh snapshot, and the cost attribution rows are written only after every layer in the plan is claimed, so the combined consumption is exact regardless of interleaving. Separately, an immediate subscription plan change wrote its history row as executed before claiming the plan transition, so a double-submitted or retried change that lost the transition claim left a phantom executed row — carrying charge and credit amounts even though no plan moved, no invoice was recorded, and no credit was issued. The losing change now voids that row, so subscription history and any revenue rollup over executed changes reflect only changes that actually happened. The vendored framework is refreshed to 0.16.3, which redacts credential-shaped values in the rendered /metrics exposition (they were masked only in the internal cardinality key), closes a SQL-guard fragment-mode bypass for PostgreSQL dollar-quoted strings, and fails closed on MCP tool input whose JSON Schema omits an explicit object type — all inherited without shop code changes. **Changed:** *Vendored framework refreshed to 0.16.3* — Refreshes the framework the shop builds on from 0.16.2 to 0.16.3, a security and robustness patch across the observability, SQL-guard, MCP, YAML-parser, content-credentials, and resumable-upload primitives. Most consequential: a credential-shaped metric label value was redacted only in the internal cardinality key, not in the rendered exposition, so a raw token on a label leaked to every /metrics scrape — redaction now covers the rendered labels; and the SQL guard's raw-fragment escape hatch now rejects a PostgreSQL dollar-quoted string and a lone statement terminator, closing a bypass of the placeholder-binding floor. The minimum Node.js engine is unchanged at 24.18.0. **Fixed:** *Concurrent cost-layer consumption no longer double-debits a layer* — FIFO, LIFO, and weighted-average COGS consumption planned each layer debit from a snapshot and then applied an unconditional decrement, so two concurrent consumptions of the same SKU could both debit the same front layer — driving its remaining quantity negative and understating COGS while a later layer was left stranded. Each debit is now an atomic guarded claim that only applies while the layer still holds the planned quantity; a losing claim reverts the attempt's earlier claims and re-plans against a re-read of the active layers, within a bounded retry. Cost attribution rows are persisted only after every layer in the plan is claimed, so a mid-plan contention loss leaves no orphaned rows and the summed consumption and attributed cost are exact. · *A lost immediate plan-change claim voids its row instead of leaving a phantom executed change* — An immediate subscription plan change inserted its history row as executed and then claimed the plan transition with a conditional update. A double-submit or retry could have two changes both insert an executed row while only one wins the transition; the loser previously returned leaving a phantom executed change with a charge and credit amount even though no plan moved, no proration invoice was recorded, and no store credit was issued. The losing change now voids its row (cancelled, reason recorded), matching how the same path already handles a failed processor push, so history and any rollup over executed changes count only changes that actually settled.
|
|
12
|
+
|
|
13
|
+
- v0.5.7 (2026-07-03) — **Storefront redirect targets route through the framework open-redirect guard, closing a TAB-injection open redirect on return_to / redirect_to.** Closes an open-redirect (CWE-601) in the storefront's post-action redirects. The cookie-consent submit, currency and locale switchers, and the wishlist and compare toggles validated their return_to / redirect_to target with a hand-rolled leading-slash regex that accepted a horizontal TAB (0x09) at the second byte. A user agent strips ASCII TAB, CR, and LF from a URL before resolving it, so a target like /<TAB>/evil.example collapsed to the protocol-relative //evil.example and navigated the shopper off-origin under the shop's trust — a phishing bounce. Every request-derived redirect target now routes through the framework's b.safeRedirect.resolve primitive (exposed once as a shared same-origin helper), which rejects every control character including TAB, protocol-relative //host and \\host forms, and full URLs — only genuine same-origin relative paths survive; anything else falls back to a safe default. The two guards that already rejected control characters are consolidated onto the same primitive so there is one redirect-safety path to reason about, and a codebase check refuses any future hand-rolled leading-slash redirect guard. No operator action and no configuration change. **Security:** *Open-redirect on storefront return_to / redirect_to closed* — The post-action redirect guards (cookie consent, currency switch, locale switch, wishlist toggle, compare toggle, announcement dismiss) accepted a leading-slash target whose second byte was a control character. A horizontal TAB there is stripped by the browser before URL parsing, turning /<TAB>/evil.example into the protocol-relative //evil.example — an off-origin redirect an attacker could use to launder a phishing link through the shop's origin. All these targets now resolve through b.safeRedirect.resolve, which rejects control characters (TAB, CR, LF, NUL), protocol-relative and backslash forms, and off-origin absolute URLs; an invalid target falls back to a same-origin default. A codebase-patterns check prevents a hand-rolled leading-slash redirect guard from reappearing.
|
|
14
|
+
|
|
11
15
|
- v0.5.6 (2026-07-03) — **Minimum Node is now 24.18.0; the vendored framework refresh brings a broad batch of request-path and mail-authentication security hardening.** Refreshes the vendored framework to 0.16.2 and raises the minimum supported Node.js to 24.18.0 — operators must run Node 24.18.0 or newer before upgrading. The floor moves with the framework, which requires 24.18.0 for its bundled SQLite 3.53.1, a backup-path fix, and the native OpenSSL 3.5 SLH-DSA surface. The refresh carries a large batch of security and correctness fixes that harden the request lifecycle the storefront and account portal already run on, at no code cost to the shop: the router refuses an encoded path separator or null byte and decodes the request path once, so a path-scoped security gate and the resource it guards can no longer disagree; res.redirect rejects a horizontal TAB that user agents strip before URL parsing, closing an open-redirect bypass; OAuth refresh-token and step-up replay gates treat any truthy store result as seen and bind grants to the authenticated principal; SAML Single-Logout fails closed when a verification key arrives without its algorithm; the mail-authentication builders (Authentication-Results, delivery-status, ARC) refuse CR/LF/NUL header injection and SPF evaluation fails closed on a malformed ip4 CIDR; file uploads enforce per-upload ownership and reject path-traversal ids; recursive parsers refuse pathologically deep input instead of overflowing the stack; and shared-cache counters (lockout, byte quotas, rate caps) accumulate with an atomic compare-and-set so distributed requests cannot lose increments. Every Node-version pin — engines, .nvmrc, the container base image, CI, and the docs — is synced to the new floor from the framework's own engines constraint. **Changed:** *Minimum Node.js raised to 24.18.0* — engines.node is now >=24.18.0 (was >=24.16.0). Operators must run Node 24.18.0 or newer. The floor is dictated by the vendored framework, which requires 24.18.0 for its bundled SQLite 3.53.1, a database backup-path fix, and the native OpenSSL 3.5 SLH-DSA sign/verify surface. The .nvmrc, the container base image, the CI runner pins, and the README/architecture docs are all synced to the new floor. · *Vendored framework refreshed to 0.16.2* — The framework the shop builds on is refreshed from 0.15.38 to 0.16.2. Beyond the request-path and mail hardening in this release's summary, the refresh includes: the router rejects percent-encoded path separators so a security middleware and the route it guards agree on the path; ARC and DKIM verification reject forged or body-length-truncated signatures; OCSP validation binds the response to the certificate's issuer; status-list verification binds the token type against replay; and OAuth authorization-URL builders refuse operator parameters that collide with framework-managed ones. These land in the request lifecycle the storefront and account portal already compose, with no shop code change required.
|
|
12
16
|
|
|
13
17
|
- v0.5.5 (2026-06-27) — **Customers can change their subscription plan — proration-aware upgrades and downgrades from the account portal.** Adds a self-serve plan-change surface to the customer subscription portal. From /account/subscriptions a customer can switch an active subscription to any other plan in the same currency: the page previews the proration for each candidate plan and offers two timings. An immediate change applies now and prorates the rest of the current period — an upgrade is charged the prorated difference (recorded on the subscription invoice ledger), and a downgrade is issued the unused remainder as store credit. A next-billing-cycle change is queued and applied automatically when the period rolls over, with the new plan starting clean and no proration. The transition is exactly-once: the immediate path claims the plan switch atomically before recording the charge or credit, and the scheduled path claims each due change with a single guarded update, so a retry or an overlapping scheduler run cannot double-charge or double-credit. A downgrade that owes a credit is never applied without paying it — if the credit write fails the change rolls back and retries. The surface mounts automatically when subscriptions and store credit are composed (the default for a configured storefront); the scheduler runs on the existing cron, and the tables it uses already shipped, so there are no schema changes and no operator action is required. **Added:** *Self-serve subscription plan changes with proration* — Customers can change the plan on an active subscription from /account/subscriptions/:id/change. The page lists every plan in the subscription's currency (excluding the current and archived plans) with a proration preview, and offers an immediate switch or a switch at the next billing date. An immediate upgrade charges the prorated difference for the rest of the period and records it on the subscription invoice ledger; an immediate downgrade issues the unused remainder of the outgoing plan as store credit. A still-pending change is shown on the subscription with a one-click cancel. Cross-currency, same-plan, and archived-plan requests are refused server-side, and the surface enforces subscription ownership before any read or write. · *Scheduler applies next-billing-cycle changes automatically* — A change queued for the next billing cycle is applied without operator involvement: a minute-cadence tick flips every due change to its new plan, records the proration, and issues any owed credit. Each due change is claimed with a single guarded update so an overlapping or retried run cannot apply it twice, and a change that owes a credit is left queued rather than applied if no credit can be issued — the credit is never dropped. The scheduler is inert on a deploy without subscriptions or store credit composed. **Changed:** *Dunning step failures are logged with their cause* — When a dunning step (reminder send, retry-charge record, pause, or cancel) fails, the schedule still advances and the step is recorded as failed — but the underlying error is now written to the server log instead of being discarded, so an operator can see why a step failed rather than only that it did.
|
package/lib/asset-manifest.json
CHANGED
package/lib/cost-layers.js
CHANGED
|
@@ -388,6 +388,21 @@ function create(opts) {
|
|
|
388
388
|
|
|
389
389
|
var method = await _methodFor(sku);
|
|
390
390
|
var direction = method === "lifo" ? "lifo" : "fifo";
|
|
391
|
+
// Consuming is a check-then-write against a snapshot of the active
|
|
392
|
+
// layers, so it runs under a bounded claim-retry loop. Each planned
|
|
393
|
+
// layer debit is an ATOMIC guarded UPDATE (`... AND quantity_remaining
|
|
394
|
+
// >= take`); the cost attribution rows are written only after EVERY
|
|
395
|
+
// layer in the plan has been claimed. If a concurrent consumer moved a
|
|
396
|
+
// layer since the snapshot, the losing attempt reverts its own winning
|
|
397
|
+
// claims — a compensating add restores exactly what it subtracted — and
|
|
398
|
+
// re-reads. Without the guard, two concurrent consumes of the same SKU
|
|
399
|
+
// both debit the same front layer(s) from a stale snapshot, driving
|
|
400
|
+
// quantity_remaining negative and misstating COGS.
|
|
401
|
+
var MAX_CLAIM_ATTEMPTS = 8;
|
|
402
|
+
var consumed, totalCogs, currency;
|
|
403
|
+
var attributionTs = requested;
|
|
404
|
+
|
|
405
|
+
for (var attempt = 1; ; attempt += 1) {
|
|
391
406
|
var layers = await _activeLayers(sku, direction);
|
|
392
407
|
|
|
393
408
|
// Pre-flight: confirm on-hand >= requested. No partial
|
|
@@ -403,7 +418,7 @@ function create(opts) {
|
|
|
403
418
|
// same currency. Mixed currencies are refused at the primitive
|
|
404
419
|
// layer (the operator reconciles via a manual receipt rather
|
|
405
420
|
// than getting a mysteriously-FX-blended COGS row).
|
|
406
|
-
|
|
421
|
+
currency = layers[0].currency;
|
|
407
422
|
for (var c = 1; c < layers.length; c += 1) {
|
|
408
423
|
if (layers[c].currency !== currency) {
|
|
409
424
|
throw new TypeError("cost-layers.consumeForSale: layers for sku " +
|
|
@@ -412,14 +427,11 @@ function create(opts) {
|
|
|
412
427
|
}
|
|
413
428
|
}
|
|
414
429
|
|
|
415
|
-
//
|
|
416
|
-
//
|
|
417
|
-
//
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
var consumed = [];
|
|
421
|
-
var totalCogs = 0;
|
|
422
|
-
var attributionTs = requested;
|
|
430
|
+
// Build the debit PLAN for this attempt without writing anything.
|
|
431
|
+
// Each entry: { layer_id, take, attrs: [{ qty, unit_cost_minor }] }.
|
|
432
|
+
// `planCogs` is the line's total COGS in minor units.
|
|
433
|
+
var plan = [];
|
|
434
|
+
var planCogs = 0;
|
|
423
435
|
|
|
424
436
|
if (method === "weighted_average") {
|
|
425
437
|
// Compute weighted-average unit cost in minor units. Round
|
|
@@ -493,11 +505,6 @@ function create(opts) {
|
|
|
493
505
|
if (attrCogs > residual) attrCogs = residual;
|
|
494
506
|
residual -= attrCogs;
|
|
495
507
|
}
|
|
496
|
-
// Debit the layer by the full consumed quantity (one update).
|
|
497
|
-
await query(
|
|
498
|
-
"UPDATE cost_layers SET quantity_remaining = quantity_remaining - ?1 WHERE id = ?2",
|
|
499
|
-
[dRow.qty, dRow.layer.id],
|
|
500
|
-
);
|
|
501
508
|
// Attribute the debit's cost so `qty * unit_cost_minor` summed over
|
|
502
509
|
// its attribution rows reconstructs attrCogs EXACTLY — the COGS
|
|
503
510
|
// reports (cogsForOrder / cogsForPeriod) recover each line's cost
|
|
@@ -513,60 +520,90 @@ function create(opts) {
|
|
|
513
520
|
var floorUnit = dRow.qty > 0 ? Math.floor(attrCogs / dRow.qty) : avgUnitCost;
|
|
514
521
|
var rem = dRow.qty > 0 ? attrCogs - (floorUnit * dRow.qty) : 0;
|
|
515
522
|
var parts = [];
|
|
516
|
-
if (dRow.qty - rem > 0) parts.push({ qty: dRow.qty - rem,
|
|
517
|
-
if (rem > 0) parts.push({ qty: rem,
|
|
518
|
-
|
|
519
|
-
var attrId = b.uuid.v7();
|
|
520
|
-
await query(
|
|
521
|
-
"INSERT INTO cogs_attributions " +
|
|
522
|
-
"(id, order_id, line_id, sku, layer_id, qty, unit_cost_minor, " +
|
|
523
|
-
"currency, reversed, reversal_reason, occurred_at) " +
|
|
524
|
-
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, 0, NULL, ?9)",
|
|
525
|
-
[attrId, orderId, lineId, sku, dRow.layer.id, parts[p].qty, parts[p].unit, currency, attributionTs],
|
|
526
|
-
);
|
|
527
|
-
consumed.push({
|
|
528
|
-
layer_id: dRow.layer.id,
|
|
529
|
-
qty: parts[p].qty,
|
|
530
|
-
unit_cost_minor: parts[p].unit,
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
totalCogs += attrCogs;
|
|
523
|
+
if (dRow.qty - rem > 0) parts.push({ qty: dRow.qty - rem, unit_cost_minor: floorUnit });
|
|
524
|
+
if (rem > 0) parts.push({ qty: rem, unit_cost_minor: floorUnit + 1 });
|
|
525
|
+
plan.push({ layer_id: dRow.layer.id, take: dRow.qty, attrs: parts });
|
|
534
526
|
}
|
|
535
|
-
|
|
536
|
-
// above, but defensive). Push it onto the last attribution.
|
|
537
|
-
if (residual !== 0 && consumed.length) {
|
|
538
|
-
totalCogs += residual;
|
|
539
|
-
}
|
|
540
|
-
totalCogs = lineCogs;
|
|
527
|
+
planCogs = lineCogs;
|
|
541
528
|
} else {
|
|
542
|
-
// FIFO / LIFO: walk layers in order, debit each
|
|
543
|
-
// the requested quantity is satisfied.
|
|
529
|
+
// FIFO / LIFO: walk layers in order, planning a debit against each
|
|
530
|
+
// one until the requested quantity is satisfied.
|
|
544
531
|
var remaining = quantity;
|
|
545
532
|
for (var f = 0; f < layers.length && remaining > 0; f += 1) {
|
|
546
533
|
var lay = layers[f];
|
|
547
534
|
var take = lay.quantity_remaining;
|
|
548
535
|
if (take > remaining) take = remaining;
|
|
536
|
+
plan.push({
|
|
537
|
+
layer_id: lay.id,
|
|
538
|
+
take: take,
|
|
539
|
+
attrs: [{ qty: take, unit_cost_minor: lay.unit_cost_minor }],
|
|
540
|
+
});
|
|
541
|
+
planCogs += take * lay.unit_cost_minor;
|
|
542
|
+
remaining -= take;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Claim every planned layer atomically. A guarded decrement loses (0
|
|
547
|
+
// rows) when a concurrent consumer has drawn the layer below `take`
|
|
548
|
+
// since this attempt's snapshot was read.
|
|
549
|
+
var claimed = [];
|
|
550
|
+
var lostClaim = false;
|
|
551
|
+
for (var g = 0; g < plan.length; g += 1) {
|
|
552
|
+
var claimRes = await query(
|
|
553
|
+
"UPDATE cost_layers SET quantity_remaining = quantity_remaining - ?1 " +
|
|
554
|
+
"WHERE id = ?2 AND quantity_remaining >= ?1",
|
|
555
|
+
[plan[g].take, plan[g].layer_id],
|
|
556
|
+
);
|
|
557
|
+
if (b.sql.casWon(claimRes).won) {
|
|
558
|
+
claimed.push(plan[g]);
|
|
559
|
+
} else {
|
|
560
|
+
lostClaim = true;
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (lostClaim) {
|
|
566
|
+
// Revert this attempt's winning claims — a compensating add restores
|
|
567
|
+
// exactly what was subtracted — then re-read the layers and re-plan.
|
|
568
|
+
for (var rv = 0; rv < claimed.length; rv += 1) {
|
|
549
569
|
await query(
|
|
550
|
-
"UPDATE cost_layers SET quantity_remaining = quantity_remaining
|
|
551
|
-
[take,
|
|
570
|
+
"UPDATE cost_layers SET quantity_remaining = quantity_remaining + ?1 WHERE id = ?2",
|
|
571
|
+
[claimed[rv].take, claimed[rv].layer_id],
|
|
552
572
|
);
|
|
553
|
-
|
|
573
|
+
}
|
|
574
|
+
if (attempt >= MAX_CLAIM_ATTEMPTS) {
|
|
575
|
+
throw new Error("cost-layers.consumeForSale: layer contention on sku " +
|
|
576
|
+
JSON.stringify(sku) + " — claim retries exhausted after " + attempt + " attempts");
|
|
577
|
+
}
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Every planned layer was claimed — persist the attribution rows. The
|
|
582
|
+
// row set (layer / qty / unit_cost_minor) is identical to the pre-fix
|
|
583
|
+
// interleaved write; only the ordering (claim-all, then insert-all)
|
|
584
|
+
// changed so a lost mid-plan claim reverts cleanly with no orphan rows.
|
|
585
|
+
consumed = [];
|
|
586
|
+
totalCogs = planCogs;
|
|
587
|
+
for (var pj = 0; pj < plan.length; pj += 1) {
|
|
588
|
+
for (var pa = 0; pa < plan[pj].attrs.length; pa += 1) {
|
|
589
|
+
var attrRow = plan[pj].attrs[pa];
|
|
590
|
+
var attrRowId = b.uuid.v7();
|
|
554
591
|
await query(
|
|
555
592
|
"INSERT INTO cogs_attributions " +
|
|
556
593
|
"(id, order_id, line_id, sku, layer_id, qty, unit_cost_minor, " +
|
|
557
594
|
"currency, reversed, reversal_reason, occurred_at) " +
|
|
558
595
|
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, 0, NULL, ?9)",
|
|
559
|
-
[
|
|
596
|
+
[attrRowId, orderId, lineId, sku, plan[pj].layer_id, attrRow.qty, attrRow.unit_cost_minor, currency, attributionTs],
|
|
560
597
|
);
|
|
561
598
|
consumed.push({
|
|
562
|
-
layer_id:
|
|
563
|
-
qty:
|
|
564
|
-
unit_cost_minor:
|
|
599
|
+
layer_id: plan[pj].layer_id,
|
|
600
|
+
qty: attrRow.qty,
|
|
601
|
+
unit_cost_minor: attrRow.unit_cost_minor,
|
|
565
602
|
});
|
|
566
|
-
totalCogs += take * lay.unit_cost_minor;
|
|
567
|
-
remaining -= take;
|
|
568
603
|
}
|
|
569
604
|
}
|
|
605
|
+
break;
|
|
606
|
+
} // end bounded claim-retry loop
|
|
570
607
|
|
|
571
608
|
return {
|
|
572
609
|
consumed_layers: consumed,
|
package/lib/plan-changes.js
CHANGED
|
@@ -635,6 +635,20 @@ function create(opts) {
|
|
|
635
635
|
periodEnd: sub.current_period_end,
|
|
636
636
|
planChangeId: id,
|
|
637
637
|
});
|
|
638
|
+
} else {
|
|
639
|
+
// Lost the transition claim — a concurrent executeChange (double-submit
|
|
640
|
+
// or retry) already flipped this subscription's plan. This row was
|
|
641
|
+
// inserted 'executed' before the claim, but no plan moved, no credit
|
|
642
|
+
// was issued, and no invoice was recorded under it. Void it rather than
|
|
643
|
+
// leave a phantom 'executed' history row that carries money columns yet
|
|
644
|
+
// reflects nothing — a naive revenue rollup
|
|
645
|
+
// (SUM(first_charge_minor) WHERE status='executed') would double-count
|
|
646
|
+
// it. Mirrors the stripe-push-failed / credit-settlement-failed voids.
|
|
647
|
+
await query(
|
|
648
|
+
"UPDATE subscription_plan_changes SET status = 'cancelled', cancelled_at = ?1, " +
|
|
649
|
+
"cancel_reason = ?2 WHERE id = ?3",
|
|
650
|
+
[_now(), "transition-lost", id],
|
|
651
|
+
);
|
|
638
652
|
}
|
|
639
653
|
|
|
640
654
|
return await _getById(id);
|
package/lib/storefront.js
CHANGED
|
@@ -59,6 +59,25 @@ var _localeAls = new AsyncLocalStorage();
|
|
|
59
59
|
// the client only ever sees a generic message + the correlating id.
|
|
60
60
|
var _log = b.log.create({});
|
|
61
61
|
|
|
62
|
+
// Same-origin redirect-target guard shared by every post-action redirect
|
|
63
|
+
// (return_to / redirect_to / next). Delegates to the framework open-redirect
|
|
64
|
+
// (CWE-601) primitive rather than a hand-rolled path regex: it rejects
|
|
65
|
+
// protocol-relative targets ("//host", "\\host"), refuses full URLs (no
|
|
66
|
+
// base/allowlist is passed, so only same-origin relatives survive), and
|
|
67
|
+
// rejects EVERY control char including a horizontal TAB. A hand-rolled
|
|
68
|
+
// `/^\/[^/]/` guard misses the TAB: a user agent strips ASCII TAB/CR/LF from
|
|
69
|
+
// a URL before resolving it, so a `Location: /\t/evil.example` collapses to
|
|
70
|
+
// the protocol-relative `//evil.example` and navigates off-origin. Returns
|
|
71
|
+
// the safe same-origin path (query/fragment preserved) or `fallback` — pass
|
|
72
|
+
// "" as the fallback to use it as an accepted/rejected sentinel.
|
|
73
|
+
function _sameOriginRedirect(raw, fallback) {
|
|
74
|
+
var fb = typeof fallback === "string" ? fallback : "/";
|
|
75
|
+
// A pathological over-length target (header bloat) falls back before the
|
|
76
|
+
// primitive even parses it.
|
|
77
|
+
if (typeof raw === "string" && raw.length > 2048) return fb;
|
|
78
|
+
return b.safeRedirect.resolve(raw, { fallback: fb });
|
|
79
|
+
}
|
|
80
|
+
|
|
62
81
|
// Generic 500 for an auth/ceremony route: log the real error server-side
|
|
63
82
|
// (correlated by the framework request id) and return a fixed message to
|
|
64
83
|
// the client so no internal error string (stack frame, DB column, vault
|
|
@@ -12485,10 +12504,7 @@ function mount(router, deps) {
|
|
|
12485
12504
|
router.post("/announcements/:slug/dismiss", async function (req, res) {
|
|
12486
12505
|
var slug = (req.params && typeof req.params.slug === "string") ? req.params.slug : "";
|
|
12487
12506
|
var body = req.body || {};
|
|
12488
|
-
var to = (
|
|
12489
|
-
if (to.charAt(0) !== "/" || to.charAt(1) === "/" || to.indexOf("\\") !== -1 || /[\x00-\x1f\x7f]/.test(to)) {
|
|
12490
|
-
to = "/";
|
|
12491
|
-
}
|
|
12507
|
+
var to = _sameOriginRedirect(body.return_to, "/");
|
|
12492
12508
|
if (/^[a-z0-9-]{1,64}$/.test(slug)) {
|
|
12493
12509
|
// Durable record (best-effort — keyed on the cart session id; absent
|
|
12494
12510
|
// a session the cookie still carries the dismissal for this browser).
|
|
@@ -13125,13 +13141,9 @@ function mount(router, deps) {
|
|
|
13125
13141
|
// setting a cookie, so a hostile link can't 500 the route.
|
|
13126
13142
|
router.get("/locale", function (req, res) {
|
|
13127
13143
|
var q = req.query || {};
|
|
13128
|
-
|
|
13129
|
-
//
|
|
13130
|
-
|
|
13131
|
-
// back to the home path.
|
|
13132
|
-
if (to.charAt(0) !== "/" || to.charAt(1) === "/" || to.indexOf("\\") !== -1 || /[\x00-\x1f\x7f]/.test(to)) {
|
|
13133
|
-
to = "/";
|
|
13134
|
-
}
|
|
13144
|
+
// Same-origin path only (rejects protocol-relative "//", backslash, and
|
|
13145
|
+
// control chars including TAB); anything else falls back to home.
|
|
13146
|
+
var to = _sameOriginRedirect(q.to, "/");
|
|
13135
13147
|
var lang = (typeof q.lang === "string") ? q.lang : "";
|
|
13136
13148
|
if (lang && LOCALE_COOKIE_RE.test(lang) && lang.length <= LOCALE_COOKIE_MAX && supportedTags.indexOf(lang) !== -1) {
|
|
13137
13149
|
_setLocaleCookie(res, lang);
|
|
@@ -13898,7 +13910,7 @@ function mount(router, deps) {
|
|
|
13898
13910
|
// slash, no scheme / host / protocol-relative `//`) so the switcher
|
|
13899
13911
|
// can't be turned into an open redirect.
|
|
13900
13912
|
var rawTo = typeof body.redirect_to === "string" ? body.redirect_to : "/";
|
|
13901
|
-
var to = (
|
|
13913
|
+
var to = _sameOriginRedirect(rawTo, "/");
|
|
13902
13914
|
if (chosen === _ccyBase || _ccyOptions.indexOf(chosen) === -1) {
|
|
13903
13915
|
_clearCurrencyCookie(res);
|
|
13904
13916
|
} else {
|
|
@@ -14445,7 +14457,8 @@ function mount(router, deps) {
|
|
|
14445
14457
|
// failure still lands the shopper somewhere sane.
|
|
14446
14458
|
var rt = (req.body || {}).return_to;
|
|
14447
14459
|
var dest = null;
|
|
14448
|
-
|
|
14460
|
+
var safeRt = _sameOriginRedirect(rt, "");
|
|
14461
|
+
if (safeRt) dest = safeRt;
|
|
14449
14462
|
|
|
14450
14463
|
var notice;
|
|
14451
14464
|
try {
|
|
@@ -17801,10 +17814,11 @@ function mount(router, deps) {
|
|
|
17801
17814
|
var okKind = removed ? "removed" : "added";
|
|
17802
17815
|
var rt = (req.body || {}).return_to;
|
|
17803
17816
|
var dest;
|
|
17804
|
-
|
|
17817
|
+
var safeRt = _sameOriginRedirect(rt, "");
|
|
17818
|
+
if (safeRt) {
|
|
17805
17819
|
// Only thread the success marker when returning to the wishlist
|
|
17806
17820
|
// page (the PDP heart toggle is its own visible cue).
|
|
17807
|
-
dest =
|
|
17821
|
+
dest = safeRt.indexOf("/account/wishlist") === 0 ? "/account/wishlist?ok=" + okKind : safeRt;
|
|
17808
17822
|
} else {
|
|
17809
17823
|
var product = null;
|
|
17810
17824
|
try { product = await deps.catalog.products.get(productId); } catch (_e) { product = null; }
|
|
@@ -22131,8 +22145,7 @@ function mount(router, deps) {
|
|
|
22131
22145
|
// refused), capping length defensively. Anything else collapses to the
|
|
22132
22146
|
// safe default the caller passes.
|
|
22133
22147
|
function _consentReturnTo(raw, fallback) {
|
|
22134
|
-
|
|
22135
|
-
return fallback;
|
|
22148
|
+
return _sameOriginRedirect(raw, fallback);
|
|
22136
22149
|
}
|
|
22137
22150
|
|
|
22138
22151
|
// Translate a posted choice + per-category checkboxes into the four
|
package/lib/vendor/MANIFEST.json
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
"_about": "blamejs.shop vendors a single framework — blamejs — which itself bundles every server-side crypto/identity dependency. The transitive packages blamejs ships are surfaced in its own MANIFEST.json at lib/vendor/blamejs/lib/vendor/MANIFEST.json — Trivy / Grype rely on that nested data for CVE attribution.",
|
|
4
4
|
"packages": {
|
|
5
5
|
"blamejs": {
|
|
6
|
-
"version": "0.16.
|
|
7
|
-
"tag": "v0.16.
|
|
6
|
+
"version": "0.16.3",
|
|
7
|
+
"tag": "v0.16.3",
|
|
8
8
|
"license": "Apache-2.0",
|
|
9
9
|
"author": "blamejs contributors",
|
|
10
10
|
"source": "https://github.com/blamejs/blamejs",
|
|
@@ -838,6 +838,7 @@
|
|
|
838
838
|
"release-notes/v0.16.0.json": "lib/vendor/blamejs/release-notes/v0.16.0.json",
|
|
839
839
|
"release-notes/v0.16.1.json": "lib/vendor/blamejs/release-notes/v0.16.1.json",
|
|
840
840
|
"release-notes/v0.16.2.json": "lib/vendor/blamejs/release-notes/v0.16.2.json",
|
|
841
|
+
"release-notes/v0.16.3.json": "lib/vendor/blamejs/release-notes/v0.16.3.json",
|
|
841
842
|
"release-notes/v0.2.x.json": "lib/vendor/blamejs/release-notes/v0.2.x.json",
|
|
842
843
|
"release-notes/v0.3.x.json": "lib/vendor/blamejs/release-notes/v0.3.x.json",
|
|
843
844
|
"release-notes/v0.4.x.json": "lib/vendor/blamejs/release-notes/v0.4.x.json",
|
|
@@ -1100,6 +1101,7 @@
|
|
|
1100
1101
|
"test/layer-0-primitives/config-drift.test.js": "lib/vendor/blamejs/test/layer-0-primitives/config-drift.test.js",
|
|
1101
1102
|
"test/layer-0-primitives/config.test.js": "lib/vendor/blamejs/test/layer-0-primitives/config.test.js",
|
|
1102
1103
|
"test/layer-0-primitives/consent-purposes.test.js": "lib/vendor/blamejs/test/layer-0-primitives/consent-purposes.test.js",
|
|
1104
|
+
"test/layer-0-primitives/content-credentials-coverage.test.js": "lib/vendor/blamejs/test/layer-0-primitives/content-credentials-coverage.test.js",
|
|
1103
1105
|
"test/layer-0-primitives/content-credentials.test.js": "lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js",
|
|
1104
1106
|
"test/layer-0-primitives/content-digest.test.js": "lib/vendor/blamejs/test/layer-0-primitives/content-digest.test.js",
|
|
1105
1107
|
"test/layer-0-primitives/cors.test.js": "lib/vendor/blamejs/test/layer-0-primitives/cors.test.js",
|
|
@@ -1132,6 +1134,7 @@
|
|
|
1132
1134
|
"test/layer-0-primitives/csrf-protect.test.js": "lib/vendor/blamejs/test/layer-0-primitives/csrf-protect.test.js",
|
|
1133
1135
|
"test/layer-0-primitives/csv.test.js": "lib/vendor/blamejs/test/layer-0-primitives/csv.test.js",
|
|
1134
1136
|
"test/layer-0-primitives/cwt.test.js": "lib/vendor/blamejs/test/layer-0-primitives/cwt.test.js",
|
|
1137
|
+
"test/layer-0-primitives/cycle2-bugfixes.test.js": "lib/vendor/blamejs/test/layer-0-primitives/cycle2-bugfixes.test.js",
|
|
1135
1138
|
"test/layer-0-primitives/daemon.test.js": "lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js",
|
|
1136
1139
|
"test/layer-0-primitives/daily-byte-quota.test.js": "lib/vendor/blamejs/test/layer-0-primitives/daily-byte-quota.test.js",
|
|
1137
1140
|
"test/layer-0-primitives/dane.test.js": "lib/vendor/blamejs/test/layer-0-primitives/dane.test.js",
|
|
@@ -1235,6 +1238,7 @@
|
|
|
1235
1238
|
"test/layer-0-primitives/guard-saga-config.test.js": "lib/vendor/blamejs/test/layer-0-primitives/guard-saga-config.test.js",
|
|
1236
1239
|
"test/layer-0-primitives/guard-smtp-command.test.js": "lib/vendor/blamejs/test/layer-0-primitives/guard-smtp-command.test.js",
|
|
1237
1240
|
"test/layer-0-primitives/guard-snapshot-envelope.test.js": "lib/vendor/blamejs/test/layer-0-primitives/guard-snapshot-envelope.test.js",
|
|
1241
|
+
"test/layer-0-primitives/guard-sql-coverage.test.js": "lib/vendor/blamejs/test/layer-0-primitives/guard-sql-coverage.test.js",
|
|
1238
1242
|
"test/layer-0-primitives/guard-stream-args.test.js": "lib/vendor/blamejs/test/layer-0-primitives/guard-stream-args.test.js",
|
|
1239
1243
|
"test/layer-0-primitives/guard-svg.test.js": "lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js",
|
|
1240
1244
|
"test/layer-0-primitives/guard-tenant-id.test.js": "lib/vendor/blamejs/test/layer-0-primitives/guard-tenant-id.test.js",
|
|
@@ -1324,12 +1328,15 @@
|
|
|
1324
1328
|
"test/layer-0-primitives/mail-store.test.js": "lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js",
|
|
1325
1329
|
"test/layer-0-primitives/mail-unsubscribe.test.js": "lib/vendor/blamejs/test/layer-0-primitives/mail-unsubscribe.test.js",
|
|
1326
1330
|
"test/layer-0-primitives/mail.test.js": "lib/vendor/blamejs/test/layer-0-primitives/mail.test.js",
|
|
1331
|
+
"test/layer-0-primitives/mcp-coverage.test.js": "lib/vendor/blamejs/test/layer-0-primitives/mcp-coverage.test.js",
|
|
1327
1332
|
"test/layer-0-primitives/mcp-tool-registry.test.js": "lib/vendor/blamejs/test/layer-0-primitives/mcp-tool-registry.test.js",
|
|
1328
1333
|
"test/layer-0-primitives/mcp.test.js": "lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js",
|
|
1329
1334
|
"test/layer-0-primitives/mdoc.test.js": "lib/vendor/blamejs/test/layer-0-primitives/mdoc.test.js",
|
|
1335
|
+
"test/layer-0-primitives/metrics-coverage.test.js": "lib/vendor/blamejs/test/layer-0-primitives/metrics-coverage.test.js",
|
|
1330
1336
|
"test/layer-0-primitives/metrics-shadow-registry.test.js": "lib/vendor/blamejs/test/layer-0-primitives/metrics-shadow-registry.test.js",
|
|
1331
1337
|
"test/layer-0-primitives/metrics-snapshot.test.js": "lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js",
|
|
1332
1338
|
"test/layer-0-primitives/middleware-compose-pipeline.test.js": "lib/vendor/blamejs/test/layer-0-primitives/middleware-compose-pipeline.test.js",
|
|
1339
|
+
"test/layer-0-primitives/middleware-tus-upload-coverage.test.js": "lib/vendor/blamejs/test/layer-0-primitives/middleware-tus-upload-coverage.test.js",
|
|
1333
1340
|
"test/layer-0-primitives/mime-parse.test.js": "lib/vendor/blamejs/test/layer-0-primitives/mime-parse.test.js",
|
|
1334
1341
|
"test/layer-0-primitives/money.test.js": "lib/vendor/blamejs/test/layer-0-primitives/money.test.js",
|
|
1335
1342
|
"test/layer-0-primitives/mtls-ca-paths.test.js": "lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-paths.test.js",
|
|
@@ -1367,6 +1374,7 @@
|
|
|
1367
1374
|
"test/layer-0-primitives/output-header-hardening.test.js": "lib/vendor/blamejs/test/layer-0-primitives/output-header-hardening.test.js",
|
|
1368
1375
|
"test/layer-0-primitives/pagination.test.js": "lib/vendor/blamejs/test/layer-0-primitives/pagination.test.js",
|
|
1369
1376
|
"test/layer-0-primitives/parser-verify-hardening.test.js": "lib/vendor/blamejs/test/layer-0-primitives/parser-verify-hardening.test.js",
|
|
1377
|
+
"test/layer-0-primitives/parsers-safe-yaml-coverage.test.js": "lib/vendor/blamejs/test/layer-0-primitives/parsers-safe-yaml-coverage.test.js",
|
|
1370
1378
|
"test/layer-0-primitives/parsers-standalone.test.js": "lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js",
|
|
1371
1379
|
"test/layer-0-primitives/passkey-real-vectors.test.js": "lib/vendor/blamejs/test/layer-0-primitives/passkey-real-vectors.test.js",
|
|
1372
1380
|
"test/layer-0-primitives/passkey.test.js": "lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js",
|
|
@@ -1552,7 +1560,7 @@
|
|
|
1552
1560
|
".github/workflows/ci.yml": "sha256:c9cd093403b410d14f6cf61bfc864b8862cc0d8f5c8dbf01a979881a4204fe83",
|
|
1553
1561
|
".github/workflows/codeql.yml": "sha256:9622f111e4da9028dc94507fe5a7c9ee4cd19dfcfbffeed1edbefde284133fea",
|
|
1554
1562
|
".github/workflows/npm-publish.yml": "sha256:cac26b6d72101e882a59d8d25fb6977973e1f8d67c87b491297712fca479b93d",
|
|
1555
|
-
".github/workflows/release-container.yml": "sha256:
|
|
1563
|
+
".github/workflows/release-container.yml": "sha256:89c6722c30f1a69f2367086f46eeb213fe384f0aa44a44e81f64866f4c19a6e2",
|
|
1556
1564
|
".github/workflows/scorecard.yml": "sha256:8a3a8a25eafe6e7c965983fe550a3e7a945386a5e9e88341c801b10b504d94be",
|
|
1557
1565
|
".github/workflows/sha-to-tag-verify.yml": "sha256:6ed546bdb518514c79a69aa9afe87c92903a8663afb57d4d07b5e81fbda8b844",
|
|
1558
1566
|
".github/zizmor.yml": "sha256:fe08e66c5763dfb4edf1ea33e715f04e514b1baceb3a242241d8a60793db51d2",
|
|
@@ -1562,7 +1570,7 @@
|
|
|
1562
1570
|
".npmrc": "sha256:66f104e7d07c496d2d0409988225e8c0e4ceb8d247dbcac3be75b2128d20ce66",
|
|
1563
1571
|
".pinact.yaml": "sha256:0213ffda55961dc49b64c0a5dfa3c0567419633b1499d57eaf7c8d842d7da6c7",
|
|
1564
1572
|
"ARCHITECTURE.md": "sha256:9b1c8d2b1b7a41838eb348b0a008e4b4369718fd72bfe2974b37155f7536d35b",
|
|
1565
|
-
"CHANGELOG.md": "sha256:
|
|
1573
|
+
"CHANGELOG.md": "sha256:4fa997c021b50ee725c66753a8ee6ea86b26fa4233101dd9070d5702b1adaed9",
|
|
1566
1574
|
"CODE_OF_CONDUCT.md": "sha256:148a281960fff7c2fe6554dab66da572c72245ddeb00b0d14811558397bff386",
|
|
1567
1575
|
"CONTRIBUTING.md": "sha256:3e4575c3cd57e72317773a80faabcabb611133ace5ee523db4b8d631e80bdaa8",
|
|
1568
1576
|
"GOVERNANCE.md": "sha256:906df6afb1f552b27b9acb50f7f96c47b917a2f1021cd4e987dbf4ee0e0a821b",
|
|
@@ -1573,7 +1581,7 @@
|
|
|
1573
1581
|
"README.md": "sha256:e1c6d401d81c959a0f264d73b3640a26465921f0ceb1103be6936816d99d21b4",
|
|
1574
1582
|
"ROADMAP.md": "sha256:d66be1de21603794608395c57ecc17381b673452ea74159bd805c85684821ecd",
|
|
1575
1583
|
"SECURITY.md": "sha256:e47271ef5094205c0ce38c10c8674c42a59b7210311f0e27ecc028c7a387c2cc",
|
|
1576
|
-
"api-snapshot.json": "sha256:
|
|
1584
|
+
"api-snapshot.json": "sha256:765b6929a2fde973e67fb49fd49942d4053eabd6e858b0b5f3942cf06e3400ec",
|
|
1577
1585
|
"assets/BlameJS_Logo.png": "sha256:3c65699753c771b48ef9ac7f45bb40815ec19a23afcdd0cd30ef4601bbbe293e",
|
|
1578
1586
|
"assets/BlameJS_Logo.svg": "sha256:dda44f3fb1343d5de9db6b1fcdb75fc649c57e7a99a8e8239fcf852e3841e1a8",
|
|
1579
1587
|
"bench/README.md": "sha256:74202f2507fd840bfc1ac6c681975d9273cf36cca6e0f72655f138337304033c",
|
|
@@ -1632,7 +1640,7 @@
|
|
|
1632
1640
|
"examples/wiki/lib/source-doc-parser.js": "sha256:e39a1701dbe6940987b11e9318e535039e8a5d1dbc67ba7b7e6bac4ef87ef638",
|
|
1633
1641
|
"examples/wiki/lib/symbol-index.js": "sha256:abd133b4aa8e582cf72b7c92fafe7f342b2d06d1a9f3d1082eb0c4df8a743b0e",
|
|
1634
1642
|
"examples/wiki/migrations/0001-pages-schema.js": "sha256:b4459ee6d1d4f1130dbf0c7a22b39ffa0f972a7ca3a0ef19215abe4bf35e46a5",
|
|
1635
|
-
"examples/wiki/package-lock.json": "sha256:
|
|
1643
|
+
"examples/wiki/package-lock.json": "sha256:ceb2632eea43f0b167da990fee70ae16dbd24577673c0f5e0f9c3c0781ce1170",
|
|
1636
1644
|
"examples/wiki/package.json": "sha256:8efb9ee3012107f9631ce23e7846ff2b10cadf643ec449e7940de496728aaedb",
|
|
1637
1645
|
"examples/wiki/public/img/blamejs-logo.png": "sha256:3c65699753c771b48ef9ac7f45bb40815ec19a23afcdd0cd30ef4601bbbe293e",
|
|
1638
1646
|
"examples/wiki/public/img/blamejs-logo.svg": "sha256:dda44f3fb1343d5de9db6b1fcdb75fc649c57e7a99a8e8239fcf852e3841e1a8",
|
|
@@ -1904,7 +1912,7 @@
|
|
|
1904
1912
|
"lib/config.js": "sha256:7f28de45331b39de8bb3b9281ef0641c4b3fdf928088a22fdb51ecac2a1c21d7",
|
|
1905
1913
|
"lib/consent.js": "sha256:0eb93f281d05ac35895cd1e0b8bf7c0cf121bf6c72901f844b52028c6bd3a14c",
|
|
1906
1914
|
"lib/constants.js": "sha256:31897caa2604b53c5365e9e054373b29c753c226beefd3a47e515a9f441c105c",
|
|
1907
|
-
"lib/content-credentials.js": "sha256:
|
|
1915
|
+
"lib/content-credentials.js": "sha256:df200fe8945fcbf4783c81a52b552dc9fc5630fdc4674275a1521f88175bbeb8",
|
|
1908
1916
|
"lib/content-digest.js": "sha256:ae9052c1d3f54a314c3d25b2e45a173459efc12a470b5b632b8c31db0e89c5ff",
|
|
1909
1917
|
"lib/cookies.js": "sha256:5230ca525e8d618a0cb9df607235b5fd194b2138915e8089dc4ea3824aa92675",
|
|
1910
1918
|
"lib/cose.js": "sha256:89c8672c0a5a019bc70e7889f4441a8ba6a6a3a52d288f0515199982046810cc",
|
|
@@ -2014,7 +2022,7 @@
|
|
|
2014
2022
|
"lib/guard-shell.js": "sha256:bc866a05afba17004ce5ce7497d884d0a59fbec3aa8b81c76840eeddd2c3b9a3",
|
|
2015
2023
|
"lib/guard-smtp-command.js": "sha256:a761c4f5d5d8f5083339e095dca2ede035330b760735bcf76693b9e6484ab4ae",
|
|
2016
2024
|
"lib/guard-snapshot-envelope.js": "sha256:13eab8a92ef38c9d0a58adf30844a0a41030e9d0b67e447480a2d21c6db052ff",
|
|
2017
|
-
"lib/guard-sql.js": "sha256:
|
|
2025
|
+
"lib/guard-sql.js": "sha256:fef254470a15a7b9be78300c8fa86d5dd26ae88f654f5230f5f3051597f6f108",
|
|
2018
2026
|
"lib/guard-stream-args.js": "sha256:3d20d6a57639c38fb406b40540d4bd738387ec2f1eca3e63c3a586a75527e61a",
|
|
2019
2027
|
"lib/guard-svg.js": "sha256:c9fbca850273d43fddaf6232949b963fbe48cfa73068540fb30aee8ce8d6f017",
|
|
2020
2028
|
"lib/guard-template.js": "sha256:861a566fd25a7abaf541c0e7875f8dc30ae5b34832ee81ee37f1ee26ae96b58b",
|
|
@@ -2107,9 +2115,9 @@
|
|
|
2107
2115
|
"lib/markup-escape.js": "sha256:7cb287780b9bb8a5ab37b1f8c36427df432331def15e2ef4d63c4b29809357ee",
|
|
2108
2116
|
"lib/markup-tokenizer.js": "sha256:b02048184fb1a55e410cdf15049935b3f1ab4895c595b0bd2a6589bcdab9f87e",
|
|
2109
2117
|
"lib/mcp-tool-registry.js": "sha256:57722b488a0977704997acbb61d2fad13155040007516f996b6b574c64d482ee",
|
|
2110
|
-
"lib/mcp.js": "sha256:
|
|
2118
|
+
"lib/mcp.js": "sha256:d0d40b1ee3cc4d66d249f6ad15ba5e1bdb0db66c689a940206c25a1d51a2c694",
|
|
2111
2119
|
"lib/mdoc.js": "sha256:536d6ad327e15f11f3279781f93a779bffeddfec06222b0e601728f905561d21",
|
|
2112
|
-
"lib/metrics.js": "sha256:
|
|
2120
|
+
"lib/metrics.js": "sha256:f12e30587a8a2c7f74be480dbb7c8f5e11a8e2ddca644cb88a225367168b241e",
|
|
2113
2121
|
"lib/middleware/age-gate.js": "sha256:ef9113093151494eca51bbcd60bd334aa2e8865969e2f3ad34cc6d8ce180fc25",
|
|
2114
2122
|
"lib/middleware/ai-act-disclosure.js": "sha256:e6f5bb9aab37bcec5ecef2ec5b3688ed0eb069eb61279d297e1ddd8460bc4568",
|
|
2115
2123
|
"lib/middleware/api-encrypt.js": "sha256:48c59f52ffc9b05e38c58e193e2df743b62c3f80ac61336bf2c4abade18437a7",
|
|
@@ -2164,7 +2172,7 @@
|
|
|
2164
2172
|
"lib/middleware/sse.js": "sha256:5bf672e6a9f2988719b820ed8d67bda34b9b7a4d54d170620614ed24bc688bfc",
|
|
2165
2173
|
"lib/middleware/trace-log-correlation.js": "sha256:abf2339adddc2a2b3f61b26e9c1cb6ebffae5fc02ca51bf9ee002df99dfe622f",
|
|
2166
2174
|
"lib/middleware/trace-propagate.js": "sha256:ddfebd2947bca77be6023fd4833bd80f8d03bef7ca2d63c7fc27f447108cd451",
|
|
2167
|
-
"lib/middleware/tus-upload.js": "sha256:
|
|
2175
|
+
"lib/middleware/tus-upload.js": "sha256:0e85b893fdd1e081a3d32fd6a1e47ddf56730c7e855c670b1dfdb50ed7dd6e46",
|
|
2168
2176
|
"lib/middleware/web-app-manifest.js": "sha256:d2e50696b4c49d4a089a79b7514fb89fe4c765b9e7edfd9bc7e0e3bf31ca1d23",
|
|
2169
2177
|
"lib/migration-files.js": "sha256:6381dd46fdcb5796ac094ec9353ce6c4996d56f826f70bc4939349b934a7cef1",
|
|
2170
2178
|
"lib/migrations.js": "sha256:229a067f388db89d74d7f1c6b2a5b4c48840966bf90d918fc786f489995e4b83",
|
|
@@ -2218,7 +2226,7 @@
|
|
|
2218
2226
|
"lib/parsers/safe-ini.js": "sha256:58287b28aa6f0dbeb0f45526e671790a8647b3b3e4c78ae8961042e11ff22ae9",
|
|
2219
2227
|
"lib/parsers/safe-toml.js": "sha256:148e874454ae1ed6f882f450b38e08e6b73b64b100b499b5eaa739c0fd8064be",
|
|
2220
2228
|
"lib/parsers/safe-xml.js": "sha256:82a3bb0c0e08d7437c478d74a50b7ba949a2e090c8e4600f5f18c69de681a48a",
|
|
2221
|
-
"lib/parsers/safe-yaml.js": "sha256:
|
|
2229
|
+
"lib/parsers/safe-yaml.js": "sha256:bb91ee487c4599333d7d87ed2aa670bea939ed776108eed5f8e92093e2d36353",
|
|
2222
2230
|
"lib/permissions.js": "sha256:c18f53a53c4e9b0c9a212bc39703000ea844a7beb407d19f82455bb1d4a48cdf",
|
|
2223
2231
|
"lib/pick.js": "sha256:7e4f5aca12ed39311afbb71885332884cb7fbaeb02dd9a2d6ecf9cb53dba1b57",
|
|
2224
2232
|
"lib/pipl-cn.js": "sha256:4855a8653ef6c91fc2dec0a1c997ac7ba4d38ec737add5f77a5733ee2d56b1b2",
|
|
@@ -2254,7 +2262,7 @@
|
|
|
2254
2262
|
"lib/router.js": "sha256:1225dbe9a51d846bbfdaa29041b19675b4d0c01084b70e9ae795026c0b382651",
|
|
2255
2263
|
"lib/safe-archive.js": "sha256:43a81f432e1cf9f294cbcf60636514cf2a5f3c67ec8e36aa8f8521c4cc657d39",
|
|
2256
2264
|
"lib/safe-async.js": "sha256:4095c238c1fb0ca788946c757aea2371d832648865ef1810bd2f3a95dbe30bf4",
|
|
2257
|
-
"lib/safe-buffer.js": "sha256:
|
|
2265
|
+
"lib/safe-buffer.js": "sha256:881cf46b90f17f0e36d360a627aa4d10b9d9928472fd2254ae8ddff7bdf49789",
|
|
2258
2266
|
"lib/safe-decompress.js": "sha256:f06a4d1e31453a94b6722cbb305111b10d0a683fe36c186a174954f45f4a521a",
|
|
2259
2267
|
"lib/safe-dns.js": "sha256:e1c2c381bae7fa7e0ee2b84a7f09870b95760a007aa0ae0793679f78982cb902",
|
|
2260
2268
|
"lib/safe-ical.js": "sha256:ad3cc9810d538aba86fed57b2e4bc79eaa8760bc9ef146dae898b1d496058ea9",
|
|
@@ -2348,8 +2356,8 @@
|
|
|
2348
2356
|
"oss-fuzz/projects/blamejs/README.md": "sha256:ae13b7bb79ed8d69b1b3276e5562807a0349fb6e6b7d11cf1f683aad1eafdb4b",
|
|
2349
2357
|
"oss-fuzz/projects/blamejs/build.sh": "sha256:0ced1cf21782c97be7f8d74faf5e27a308b60b2f858836fb5ca3b8c4e939a8f7",
|
|
2350
2358
|
"oss-fuzz/projects/blamejs/project.yaml": "sha256:59f2cb83aa622325a175b77416fe155be15b70a9c798bd1a78bba05763b1b03d",
|
|
2351
|
-
"package-lock.json": "sha256:
|
|
2352
|
-
"package.json": "sha256:
|
|
2359
|
+
"package-lock.json": "sha256:e09ec4b072f8f1f38ddb0b59b855e0ca1bca293a9568daa4be3e765dd237047e",
|
|
2360
|
+
"package.json": "sha256:ed80932595218a5a1da1c3f405a1dfc989ab3910f27b39df5f15b601f95225e9",
|
|
2353
2361
|
"release-notes/v0.0.x.json": "sha256:7a49819f30068ee119000cad7010194882bb8bfaa12acbdab4dfc066efb7982f",
|
|
2354
2362
|
"release-notes/v0.1.x.json": "sha256:6742a8c17f947c5cb76f69dead7eea86b942d80621d914b774ba5488e09937e5",
|
|
2355
2363
|
"release-notes/v0.10.x.json": "sha256:fe498045daf88337bd3d987e5964aa42c99a50e1685b6f09e51f698b8687726f",
|
|
@@ -2361,6 +2369,7 @@
|
|
|
2361
2369
|
"release-notes/v0.16.0.json": "sha256:70def9578a6ae184dfb2f82c2f2b1bda15c4d9b8f987f0047da7f68f06a7e41b",
|
|
2362
2370
|
"release-notes/v0.16.1.json": "sha256:67f5833cf682266b0ea93d410fdeedfa427e80781532d15e64c7d440dfefa95c",
|
|
2363
2371
|
"release-notes/v0.16.2.json": "sha256:54d139821c154cffcd5b87b2cad1cff696f1db2af49742a144cd86c3194b3ece",
|
|
2372
|
+
"release-notes/v0.16.3.json": "sha256:0de1e0284e41575b8480d26f99b49d9dea550ef035f5c45e0b1723620d84f49d",
|
|
2364
2373
|
"release-notes/v0.2.x.json": "sha256:985e27ff5de04cfc7869a3986dd0b9f0fcdfcddfb67d3fae3f4ae70856722d7e",
|
|
2365
2374
|
"release-notes/v0.3.x.json": "sha256:e2db5eae66977b272bb185cad668386afb8fd33998a17c22eb6e411c0f8ca588",
|
|
2366
2375
|
"release-notes/v0.4.x.json": "sha256:c3d19cb9c50a976432fc0bb612c87741d8728ae37562b501e6f1eccd01dd574d",
|
|
@@ -2623,6 +2632,7 @@
|
|
|
2623
2632
|
"test/layer-0-primitives/config-drift.test.js": "sha256:75371225fef23f472e26444e672c7822668e0400407e05652bc73f3a368b97aa",
|
|
2624
2633
|
"test/layer-0-primitives/config.test.js": "sha256:6adc4278b64f960ea2649779d7048c90ff54dbd849832c088700fbdca150ef94",
|
|
2625
2634
|
"test/layer-0-primitives/consent-purposes.test.js": "sha256:a1669c7fb9c999ced399a4d93dccf5195008c422f28a669c25a9832f2114ce2d",
|
|
2635
|
+
"test/layer-0-primitives/content-credentials-coverage.test.js": "sha256:81a1942e6306e69159d3da8da97c55e70e3c66d19d8d6ad187e6f2a627e57ddc",
|
|
2626
2636
|
"test/layer-0-primitives/content-credentials.test.js": "sha256:10014ff2e44cd5185d6c3401cb797db4813c4aa794cd01f07a221953cd4f029f",
|
|
2627
2637
|
"test/layer-0-primitives/content-digest.test.js": "sha256:7b28731f3806576e04669a62919be1d79df3881b1b80b6eaf7cf101a2aa56c91",
|
|
2628
2638
|
"test/layer-0-primitives/cors.test.js": "sha256:74b0feee302861b3a6385cf43dfb286b008a10c37794e7074f34236124d924f2",
|
|
@@ -2655,6 +2665,7 @@
|
|
|
2655
2665
|
"test/layer-0-primitives/csrf-protect.test.js": "sha256:4b7d07492a1f1a218aa8106a387401cd3d6f3b43d3c6833ee4db5386bc2bc712",
|
|
2656
2666
|
"test/layer-0-primitives/csv.test.js": "sha256:1367035130388f4cf75c9bc03cc88b4d4ac1aca3fb6c62d7a98c34d256f89e7a",
|
|
2657
2667
|
"test/layer-0-primitives/cwt.test.js": "sha256:2e992ab7445db02581275ae5fd8b38de210e21f38a1594f27942f1bb1f9495e4",
|
|
2668
|
+
"test/layer-0-primitives/cycle2-bugfixes.test.js": "sha256:d23919a13e58a84d61a1a3d2eb95ec6f9d24ab94e3f2221e7002e07438d48341",
|
|
2658
2669
|
"test/layer-0-primitives/daemon.test.js": "sha256:5e75c56ad03579293561ce8708a4289f2b8967a6b28bec31d5e3138bc49c17b3",
|
|
2659
2670
|
"test/layer-0-primitives/daily-byte-quota.test.js": "sha256:f8ac102047451f6dd04050096674b739f012117b1845215bf4f1b47df25be72f",
|
|
2660
2671
|
"test/layer-0-primitives/dane.test.js": "sha256:0fff14dfa609fa04a30f6a5a644288bcfafb3292044797cdc89674e5dc9485e2",
|
|
@@ -2758,6 +2769,7 @@
|
|
|
2758
2769
|
"test/layer-0-primitives/guard-saga-config.test.js": "sha256:f35eb034a7b53cb7f3219998d546834cfc2b0c7b6af71a5f6d127e59152f8648",
|
|
2759
2770
|
"test/layer-0-primitives/guard-smtp-command.test.js": "sha256:a31c1f379ff873253e165359ad251a6574b8aff532b6c177e0af1c52caca7702",
|
|
2760
2771
|
"test/layer-0-primitives/guard-snapshot-envelope.test.js": "sha256:7fb6a0c76febd6576ca47c0712eb8f2db4305e7000fbee1450f98450a58caae9",
|
|
2772
|
+
"test/layer-0-primitives/guard-sql-coverage.test.js": "sha256:8ce355a7ff8b1923090b2f2409a0a0726a418221eb4b901920216f387784ecde",
|
|
2761
2773
|
"test/layer-0-primitives/guard-stream-args.test.js": "sha256:7c7401b88eccfbd50964d41c826a1f75be3ac3bdb4caa1ac929add180ca01976",
|
|
2762
2774
|
"test/layer-0-primitives/guard-svg.test.js": "sha256:057b281464de02d03e5e1a5c76a0a7efb23904bbcc7be43095924d6a73e5bbfe",
|
|
2763
2775
|
"test/layer-0-primitives/guard-tenant-id.test.js": "sha256:4083f32ea58e4b350fd6b40e7ada751e4b213ae4a03b85500505863fb72d800a",
|
|
@@ -2847,12 +2859,15 @@
|
|
|
2847
2859
|
"test/layer-0-primitives/mail-store.test.js": "sha256:80f98a73baf628158df0bc879ca9f35c4c534e3e5562e2a25fae41b0b5a1bbc1",
|
|
2848
2860
|
"test/layer-0-primitives/mail-unsubscribe.test.js": "sha256:b745189403f7574e4845c2366a05fcdb9a3fcaf21c6930e2fe53ac94f1d1dec8",
|
|
2849
2861
|
"test/layer-0-primitives/mail.test.js": "sha256:c83e3d7a9d22ad072104ac6d3c360a2b272f16f364ad8206b5eff7043fe97e48",
|
|
2862
|
+
"test/layer-0-primitives/mcp-coverage.test.js": "sha256:b27f9f19be15c0f5308d1fe88534bd979d4b84439f1a6ecb117bc085cae4626b",
|
|
2850
2863
|
"test/layer-0-primitives/mcp-tool-registry.test.js": "sha256:57bc395acf4e7bac1bd4385ba6bf3d749ecee20dacd20b97ae1a6ca7b439a201",
|
|
2851
2864
|
"test/layer-0-primitives/mcp.test.js": "sha256:21d45e7d498b5d7404294709b0031fa1882505ee81681090190aaeeece066a20",
|
|
2852
2865
|
"test/layer-0-primitives/mdoc.test.js": "sha256:d89818a74820c8729d9c9b6024311468fcc72c3333474a5658f57fa79f514e07",
|
|
2866
|
+
"test/layer-0-primitives/metrics-coverage.test.js": "sha256:1ee32f903567bc9451d7f0257b3fb830a438283e0714c5ee38af37ec5cdeffef",
|
|
2853
2867
|
"test/layer-0-primitives/metrics-shadow-registry.test.js": "sha256:60e3a71c5ec83f83901a9484db09079d1d8e32768538fb953fd3732ce1c15076",
|
|
2854
2868
|
"test/layer-0-primitives/metrics-snapshot.test.js": "sha256:57b4686c1d472c07e85ee32ab8f08e7ba9332ccdb1e6000363be87815e4eef32",
|
|
2855
2869
|
"test/layer-0-primitives/middleware-compose-pipeline.test.js": "sha256:012ccb0976f5fddbc6ef370a7c2dabed7c24a9619e0cfb0ea1532551b40fc0c0",
|
|
2870
|
+
"test/layer-0-primitives/middleware-tus-upload-coverage.test.js": "sha256:c8a54bc3116804a8ca6b7a8d8ddce75de6771af421af531b5c8043918e8bffed",
|
|
2856
2871
|
"test/layer-0-primitives/mime-parse.test.js": "sha256:d6d843a250c6dccac9c125668de5cb3885bf043a0c24eb176ab871d9cadbe878",
|
|
2857
2872
|
"test/layer-0-primitives/money.test.js": "sha256:76fb9d1071d72cdf7f26bbd0a69dc5329a5a35f436d52b0a041c30afef602831",
|
|
2858
2873
|
"test/layer-0-primitives/mtls-ca-paths.test.js": "sha256:e770ce593b1f7c271be6aea5c4245fbda32ba22b66466c9cff9c35b0886262e9",
|
|
@@ -2890,6 +2905,7 @@
|
|
|
2890
2905
|
"test/layer-0-primitives/output-header-hardening.test.js": "sha256:3c31c56bb1ac63783c40832cf35d0748c801ad7a74e385fc8d46f63ad2907206",
|
|
2891
2906
|
"test/layer-0-primitives/pagination.test.js": "sha256:19364aa49ce2808b5ab20cc16d59ffe6dc41a2d05ff868cdd83b076f0adb7793",
|
|
2892
2907
|
"test/layer-0-primitives/parser-verify-hardening.test.js": "sha256:cba2f34efc631efe95c5cc10ffb8af75d4fbd7f8586520149cf6f05b96bede19",
|
|
2908
|
+
"test/layer-0-primitives/parsers-safe-yaml-coverage.test.js": "sha256:5717cc0b7d36264f40f833d3284245c30f09573942218731702450c7f1341633",
|
|
2893
2909
|
"test/layer-0-primitives/parsers-standalone.test.js": "sha256:21ea49f2722bf595f4d04491f08626dcbee6d1be4241667a33ac6f9450671ef7",
|
|
2894
2910
|
"test/layer-0-primitives/passkey-real-vectors.test.js": "sha256:5ac058d841807fe15a9d44f823e6cfbcf028efbedd14c3cc7e902eb5e31aaa92",
|
|
2895
2911
|
"test/layer-0-primitives/passkey.test.js": "sha256:a46e1f12c969efaa3d1fd04bcc248a0d8f80f976f52404cb772683d56091a80d",
|
|
@@ -135,7 +135,7 @@ jobs:
|
|
|
135
135
|
|
|
136
136
|
- name: Log in to GHCR
|
|
137
137
|
if: ${{ !inputs.dry_run }}
|
|
138
|
-
uses: docker/login-action@
|
|
138
|
+
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
|
|
139
139
|
with:
|
|
140
140
|
registry: ghcr.io
|
|
141
141
|
username: ${{ github.actor }}
|
|
@@ -340,7 +340,7 @@ jobs:
|
|
|
340
340
|
# against a public package, so this is the right shape regardless
|
|
341
341
|
# of visibility.
|
|
342
342
|
- name: Log in to GHCR
|
|
343
|
-
uses: docker/login-action@
|
|
343
|
+
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
|
|
344
344
|
with:
|
|
345
345
|
registry: ghcr.io
|
|
346
346
|
username: ${{ github.actor }}
|