@cmssy/core 5.1.0 → 6.0.1
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/dist/index.cjs +41 -7
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +41 -7
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
var jose = require('jose');
|
|
4
4
|
var types = require('@cmssy/types');
|
|
5
|
-
var crypto$1 = require('crypto');
|
|
6
5
|
|
|
7
6
|
// src/session.ts
|
|
8
7
|
var CMSSY_SESSION_COOKIE = "cmssy_session";
|
|
@@ -1639,6 +1638,8 @@ function formatPrice(minor, currency) {
|
|
|
1639
1638
|
return `${amount.toFixed(fractionDigits(code))} ${code}`;
|
|
1640
1639
|
}
|
|
1641
1640
|
}
|
|
1641
|
+
|
|
1642
|
+
// src/verify-webhook.ts
|
|
1642
1643
|
var CmssyWebhookError = class extends Error {
|
|
1643
1644
|
constructor(message) {
|
|
1644
1645
|
super(message);
|
|
@@ -1662,13 +1663,46 @@ function parseSignatureHeader(header) {
|
|
|
1662
1663
|
}
|
|
1663
1664
|
return { timestamp, signature };
|
|
1664
1665
|
}
|
|
1666
|
+
function hexToBytes(hex) {
|
|
1667
|
+
if (hex.length % 2 !== 0 || /[^0-9a-fA-F]/.test(hex)) return null;
|
|
1668
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
1669
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
1670
|
+
bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
1671
|
+
}
|
|
1672
|
+
return bytes;
|
|
1673
|
+
}
|
|
1674
|
+
function bytesToHex(bytes) {
|
|
1675
|
+
return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1676
|
+
}
|
|
1665
1677
|
function timingSafeHexEqual(expectedHex, providedHex) {
|
|
1666
|
-
const expected =
|
|
1667
|
-
const provided =
|
|
1668
|
-
if (expected.length !== provided.length)
|
|
1669
|
-
|
|
1678
|
+
const expected = hexToBytes(expectedHex);
|
|
1679
|
+
const provided = hexToBytes(providedHex);
|
|
1680
|
+
if (!expected || !provided || expected.length !== provided.length) {
|
|
1681
|
+
return false;
|
|
1682
|
+
}
|
|
1683
|
+
let diff = 0;
|
|
1684
|
+
for (let i = 0; i < expected.length; i += 1) {
|
|
1685
|
+
diff |= (expected[i] ?? 0) ^ (provided[i] ?? 0);
|
|
1686
|
+
}
|
|
1687
|
+
return diff === 0;
|
|
1688
|
+
}
|
|
1689
|
+
async function hmacSha256Hex(secret, message) {
|
|
1690
|
+
const encoder = new TextEncoder();
|
|
1691
|
+
const key = await crypto.subtle.importKey(
|
|
1692
|
+
"raw",
|
|
1693
|
+
encoder.encode(secret),
|
|
1694
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
1695
|
+
false,
|
|
1696
|
+
["sign"]
|
|
1697
|
+
);
|
|
1698
|
+
const signature = await crypto.subtle.sign(
|
|
1699
|
+
"HMAC",
|
|
1700
|
+
key,
|
|
1701
|
+
encoder.encode(message)
|
|
1702
|
+
);
|
|
1703
|
+
return bytesToHex(new Uint8Array(signature));
|
|
1670
1704
|
}
|
|
1671
|
-
function verifyCmssyWebhook(options) {
|
|
1705
|
+
async function verifyCmssyWebhook(options) {
|
|
1672
1706
|
const { body, signatureHeader, secret } = options;
|
|
1673
1707
|
if (!signatureHeader) {
|
|
1674
1708
|
throw new CmssyWebhookError("Missing X-Cmssy-Signature header");
|
|
@@ -1682,7 +1716,7 @@ function verifyCmssyWebhook(options) {
|
|
|
1682
1716
|
if (Math.abs(now - timestamp) > toleranceMs) {
|
|
1683
1717
|
throw new CmssyWebhookError("Webhook timestamp outside tolerance");
|
|
1684
1718
|
}
|
|
1685
|
-
const expected =
|
|
1719
|
+
const expected = await hmacSha256Hex(secret, `${timestamp}.${body}`);
|
|
1686
1720
|
if (!timingSafeHexEqual(expected, signature)) {
|
|
1687
1721
|
throw new CmssyWebhookError("Webhook signature mismatch");
|
|
1688
1722
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -491,6 +491,6 @@ declare class CmssyWebhookError extends Error {
|
|
|
491
491
|
* `CmssyWebhookError` on any failure (missing/malformed header, bad
|
|
492
492
|
* signature, stale timestamp, invalid JSON) - catch it and respond 400.
|
|
493
493
|
*/
|
|
494
|
-
declare function verifyCmssyWebhook(options: VerifyCmssyWebhookOptions): CmssyWebhookEvent
|
|
494
|
+
declare function verifyCmssyWebhook(options: VerifyCmssyWebhookOptions): Promise<CmssyWebhookEvent>;
|
|
495
495
|
|
|
496
496
|
export { type AppToEditorMessage, type AuthMutationResult, type AuthTokenResult, type BoundsMessage, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CartRequestContext, type CheckoutInput, type ClickMessage, type CmssyClient, type CmssyConfig, type CmssyCspOptions, type CmssyEnvConfig, CmssyRequestError, CmssyWebhookError, DEFAULT_CMSSY_API_URL, DEFAULT_CMSSY_EDITOR_ORIGINS, type EditorToAppMessage, FORM_QUERY, type FetchLike, type FetchLikeResponse, type FetchPageOptions, type GraphqlRequestOptions, MIN_SESSION_SECRET_LENGTH, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, PRODUCTS_QUERY, PROTOCOL_VERSION, type ParentReadyMessage, type PatchMessage, type PostTarget, type QueryScopedOptions, type ReadyMessage, type RetryPolicy, SESSION_MAX_AGE_SECONDS, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, type SelectMessage, applyCmssyCsp, asBucket, assertAuthConfig, backendAddToCart, backendApplyDiscount, backendCheckout, backendClearCart, backendForgotPassword, backendGetCart, backendMergeCart, backendMyOrder, backendMyOrders, backendOrderByToken, backendProduct, backendRefresh, backendRegister, backendRemoveDiscount, backendRemoveItem, backendResetPassword, backendSetShippingMethod, backendSignIn, backendSignOut, backendSignOutEverywhere, backendUpdateItem, backendVerifyEmail, buildBlockContext, buildLocaleSwitchHref, cachedWorkspaceId, clearWorkspaceIdCache, cmssyCspHeaders, cmssySecretsMatch, collectFormIds, createCmssyClient, decodeAccessClaims, defineCmssyConfig, fetchLayouts, fetchOrderByToken, fetchPage, fetchPageById, fetchPageMeta, fetchPages, fetchProduct, fetchProducts, fetchSiteConfig, fields, formatPrice, fractionDigits, fromMinorUnits, getBlockContentForLanguage, graphqlRequest, isAccessExpired, isDevelopment, isProtocolCompatible, isVerifiedEditUrl, localeForPath, localeForPathname, localizeHref, localizeHtmlLinks, localizedPath, normalizeOrigin, normalizeSlug, openSession, parseEditorMessage, postToEditor, resolveApiUrl, resolveEditorOrigin, resolveForms, resolveInitialTarget, resolvePublicUrl, resolveSeoLocales, resolveSiteLocales, resolveWorkspaceId, sealSession, sessionCookieOptions, splitCmssyLocale, splitLocaleFromPath, toCspOrigin, toMinorUnits, toSessionPayload, verifyCmssyWebhook };
|
package/dist/index.d.ts
CHANGED
|
@@ -491,6 +491,6 @@ declare class CmssyWebhookError extends Error {
|
|
|
491
491
|
* `CmssyWebhookError` on any failure (missing/malformed header, bad
|
|
492
492
|
* signature, stale timestamp, invalid JSON) - catch it and respond 400.
|
|
493
493
|
*/
|
|
494
|
-
declare function verifyCmssyWebhook(options: VerifyCmssyWebhookOptions): CmssyWebhookEvent
|
|
494
|
+
declare function verifyCmssyWebhook(options: VerifyCmssyWebhookOptions): Promise<CmssyWebhookEvent>;
|
|
495
495
|
|
|
496
496
|
export { type AppToEditorMessage, type AuthMutationResult, type AuthTokenResult, type BoundsMessage, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CartRequestContext, type CheckoutInput, type ClickMessage, type CmssyClient, type CmssyConfig, type CmssyCspOptions, type CmssyEnvConfig, CmssyRequestError, CmssyWebhookError, DEFAULT_CMSSY_API_URL, DEFAULT_CMSSY_EDITOR_ORIGINS, type EditorToAppMessage, FORM_QUERY, type FetchLike, type FetchLikeResponse, type FetchPageOptions, type GraphqlRequestOptions, MIN_SESSION_SECRET_LENGTH, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, PRODUCTS_QUERY, PROTOCOL_VERSION, type ParentReadyMessage, type PatchMessage, type PostTarget, type QueryScopedOptions, type ReadyMessage, type RetryPolicy, SESSION_MAX_AGE_SECONDS, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, type SelectMessage, applyCmssyCsp, asBucket, assertAuthConfig, backendAddToCart, backendApplyDiscount, backendCheckout, backendClearCart, backendForgotPassword, backendGetCart, backendMergeCart, backendMyOrder, backendMyOrders, backendOrderByToken, backendProduct, backendRefresh, backendRegister, backendRemoveDiscount, backendRemoveItem, backendResetPassword, backendSetShippingMethod, backendSignIn, backendSignOut, backendSignOutEverywhere, backendUpdateItem, backendVerifyEmail, buildBlockContext, buildLocaleSwitchHref, cachedWorkspaceId, clearWorkspaceIdCache, cmssyCspHeaders, cmssySecretsMatch, collectFormIds, createCmssyClient, decodeAccessClaims, defineCmssyConfig, fetchLayouts, fetchOrderByToken, fetchPage, fetchPageById, fetchPageMeta, fetchPages, fetchProduct, fetchProducts, fetchSiteConfig, fields, formatPrice, fractionDigits, fromMinorUnits, getBlockContentForLanguage, graphqlRequest, isAccessExpired, isDevelopment, isProtocolCompatible, isVerifiedEditUrl, localeForPath, localeForPathname, localizeHref, localizeHtmlLinks, localizedPath, normalizeOrigin, normalizeSlug, openSession, parseEditorMessage, postToEditor, resolveApiUrl, resolveEditorOrigin, resolveForms, resolveInitialTarget, resolvePublicUrl, resolveSeoLocales, resolveSiteLocales, resolveWorkspaceId, sealSession, sessionCookieOptions, splitCmssyLocale, splitLocaleFromPath, toCspOrigin, toMinorUnits, toSessionPayload, verifyCmssyWebhook };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { EncryptJWT, jwtDecrypt } from 'jose';
|
|
2
2
|
export { evaluateFieldConditionGroup } from '@cmssy/types';
|
|
3
|
-
import { createHmac, timingSafeEqual } from 'crypto';
|
|
4
3
|
|
|
5
4
|
// src/session.ts
|
|
6
5
|
var CMSSY_SESSION_COOKIE = "cmssy_session";
|
|
@@ -1637,6 +1636,8 @@ function formatPrice(minor, currency) {
|
|
|
1637
1636
|
return `${amount.toFixed(fractionDigits(code))} ${code}`;
|
|
1638
1637
|
}
|
|
1639
1638
|
}
|
|
1639
|
+
|
|
1640
|
+
// src/verify-webhook.ts
|
|
1640
1641
|
var CmssyWebhookError = class extends Error {
|
|
1641
1642
|
constructor(message) {
|
|
1642
1643
|
super(message);
|
|
@@ -1660,13 +1661,46 @@ function parseSignatureHeader(header) {
|
|
|
1660
1661
|
}
|
|
1661
1662
|
return { timestamp, signature };
|
|
1662
1663
|
}
|
|
1664
|
+
function hexToBytes(hex) {
|
|
1665
|
+
if (hex.length % 2 !== 0 || /[^0-9a-fA-F]/.test(hex)) return null;
|
|
1666
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
1667
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
1668
|
+
bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
1669
|
+
}
|
|
1670
|
+
return bytes;
|
|
1671
|
+
}
|
|
1672
|
+
function bytesToHex(bytes) {
|
|
1673
|
+
return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1674
|
+
}
|
|
1663
1675
|
function timingSafeHexEqual(expectedHex, providedHex) {
|
|
1664
|
-
const expected =
|
|
1665
|
-
const provided =
|
|
1666
|
-
if (expected.length !== provided.length)
|
|
1667
|
-
|
|
1676
|
+
const expected = hexToBytes(expectedHex);
|
|
1677
|
+
const provided = hexToBytes(providedHex);
|
|
1678
|
+
if (!expected || !provided || expected.length !== provided.length) {
|
|
1679
|
+
return false;
|
|
1680
|
+
}
|
|
1681
|
+
let diff = 0;
|
|
1682
|
+
for (let i = 0; i < expected.length; i += 1) {
|
|
1683
|
+
diff |= (expected[i] ?? 0) ^ (provided[i] ?? 0);
|
|
1684
|
+
}
|
|
1685
|
+
return diff === 0;
|
|
1686
|
+
}
|
|
1687
|
+
async function hmacSha256Hex(secret, message) {
|
|
1688
|
+
const encoder = new TextEncoder();
|
|
1689
|
+
const key = await crypto.subtle.importKey(
|
|
1690
|
+
"raw",
|
|
1691
|
+
encoder.encode(secret),
|
|
1692
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
1693
|
+
false,
|
|
1694
|
+
["sign"]
|
|
1695
|
+
);
|
|
1696
|
+
const signature = await crypto.subtle.sign(
|
|
1697
|
+
"HMAC",
|
|
1698
|
+
key,
|
|
1699
|
+
encoder.encode(message)
|
|
1700
|
+
);
|
|
1701
|
+
return bytesToHex(new Uint8Array(signature));
|
|
1668
1702
|
}
|
|
1669
|
-
function verifyCmssyWebhook(options) {
|
|
1703
|
+
async function verifyCmssyWebhook(options) {
|
|
1670
1704
|
const { body, signatureHeader, secret } = options;
|
|
1671
1705
|
if (!signatureHeader) {
|
|
1672
1706
|
throw new CmssyWebhookError("Missing X-Cmssy-Signature header");
|
|
@@ -1680,7 +1714,7 @@ function verifyCmssyWebhook(options) {
|
|
|
1680
1714
|
if (Math.abs(now - timestamp) > toleranceMs) {
|
|
1681
1715
|
throw new CmssyWebhookError("Webhook timestamp outside tolerance");
|
|
1682
1716
|
}
|
|
1683
|
-
const expected =
|
|
1717
|
+
const expected = await hmacSha256Hex(secret, `${timestamp}.${body}`);
|
|
1684
1718
|
if (!timingSafeHexEqual(expected, signature)) {
|
|
1685
1719
|
throw new CmssyWebhookError("Webhook signature mismatch");
|
|
1686
1720
|
}
|