@tightknitai/block-kitchen 0.10.12 → 0.10.14
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/README.md +1 -1
- package/dist/helpers.cjs +47 -5
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +5 -3
- package/dist/helpers.d.ts +5 -3
- package/dist/helpers.js +47 -5
- package/dist/helpers.js.map +1 -1
- package/dist/index.cjs +125 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +125 -48
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1838,6 +1838,7 @@ function groupValidatorErrors(errors, blocks) {
|
|
|
1838
1838
|
var SAFE_LINK_PROTOCOLS = /^(https?|ircs?|mailto|tel|sms|xmpp)$/i;
|
|
1839
1839
|
var SAFE_IMAGE_PROTOCOLS = /^(https?)$/i;
|
|
1840
1840
|
var SAFE_IMAGE_DATA_PREFIX = /^data:image\/(?:png|jpeg|jpg|gif|webp|avif|x-icon|vnd\.microsoft\.icon)(?:;[a-z0-9-]+(?:=[^,;]*)?)*,/i;
|
|
1841
|
+
var SAFE_EMBED_PROTOCOLS = /^(https?)$/i;
|
|
1841
1842
|
function getScheme(value) {
|
|
1842
1843
|
const trimmed = value.replace(/^\s+/, "");
|
|
1843
1844
|
const colon = trimmed.indexOf(":");
|
|
@@ -1893,16 +1894,54 @@ function isSafeImageSrc(value) {
|
|
|
1893
1894
|
}
|
|
1894
1895
|
return SAFE_IMAGE_PROTOCOLS.test(scheme);
|
|
1895
1896
|
}
|
|
1897
|
+
function isSafeEmbedSrc(value) {
|
|
1898
|
+
if (typeof value !== "string") {
|
|
1899
|
+
return false;
|
|
1900
|
+
}
|
|
1901
|
+
if (value.length === 0) {
|
|
1902
|
+
return true;
|
|
1903
|
+
}
|
|
1904
|
+
const scheme = getScheme(value);
|
|
1905
|
+
if (scheme === null) {
|
|
1906
|
+
return false;
|
|
1907
|
+
}
|
|
1908
|
+
return SAFE_EMBED_PROTOCOLS.test(scheme);
|
|
1909
|
+
}
|
|
1896
1910
|
function sanitizeHref(value) {
|
|
1897
1911
|
return isSafeHref(value) ? value ?? "" : "";
|
|
1898
1912
|
}
|
|
1899
1913
|
function sanitizeImageSrc(value) {
|
|
1900
1914
|
return isSafeImageSrc(value) ? value ?? "" : "";
|
|
1901
1915
|
}
|
|
1916
|
+
function sanitizeEmbedSrc(value) {
|
|
1917
|
+
return isSafeEmbedSrc(value) ? value ?? "" : "";
|
|
1918
|
+
}
|
|
1902
1919
|
|
|
1903
1920
|
// src/lib/sanitize-blocks.ts
|
|
1904
|
-
var
|
|
1905
|
-
var
|
|
1921
|
+
var EMBED_KEYS = /* @__PURE__ */ new Set(["video_url"]);
|
|
1922
|
+
var URL_KEY = /(^|_)(url|uri|link|href|src)$/;
|
|
1923
|
+
var MAYBE_URL_KEY = /url|uri|link|href|src/i;
|
|
1924
|
+
var IMAGE_KEY_HINT = /image|thumb|icon|avatar|logo|photo|picture/;
|
|
1925
|
+
var PROP_BAG_KEY = /(^|_)props$/;
|
|
1926
|
+
function normalizeKey(key) {
|
|
1927
|
+
return key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
1928
|
+
}
|
|
1929
|
+
function isRendererPropBag(key) {
|
|
1930
|
+
return PROP_BAG_KEY.test(normalizeKey(key));
|
|
1931
|
+
}
|
|
1932
|
+
function classifyUrlKey(key) {
|
|
1933
|
+
if (!MAYBE_URL_KEY.test(key)) {
|
|
1934
|
+
return null;
|
|
1935
|
+
}
|
|
1936
|
+
const normalized = normalizeKey(key);
|
|
1937
|
+
if (EMBED_KEYS.has(normalized)) {
|
|
1938
|
+
return "embed";
|
|
1939
|
+
}
|
|
1940
|
+
if (!URL_KEY.test(normalized)) {
|
|
1941
|
+
return null;
|
|
1942
|
+
}
|
|
1943
|
+
return IMAGE_KEY_HINT.test(normalized) ? "image" : "href";
|
|
1944
|
+
}
|
|
1906
1945
|
var RETRIEVAL_ONLY_KEYS = /* @__PURE__ */ new Map([
|
|
1907
1946
|
// Covers both the image block and the image element — both `type: 'image'`.
|
|
1908
1947
|
["image", /* @__PURE__ */ new Set(["image_width", "image_height", "image_bytes", "fallback", "is_animated"])],
|
|
@@ -1930,7 +1969,7 @@ function sanitizeValue(value) {
|
|
|
1930
1969
|
const dropKeys = typeof src.type === "string" ? RETRIEVAL_ONLY_KEYS.get(src.type) : void 0;
|
|
1931
1970
|
let copy = null;
|
|
1932
1971
|
for (const key of Object.keys(src)) {
|
|
1933
|
-
if (dropKeys?.has(key)) {
|
|
1972
|
+
if (dropKeys?.has(key) || isRendererPropBag(key)) {
|
|
1934
1973
|
copy ??= { ...src };
|
|
1935
1974
|
delete copy[key];
|
|
1936
1975
|
continue;
|
|
@@ -1938,10 +1977,13 @@ function sanitizeValue(value) {
|
|
|
1938
1977
|
const original = src[key];
|
|
1939
1978
|
let next = original;
|
|
1940
1979
|
if (typeof original === "string") {
|
|
1941
|
-
|
|
1980
|
+
const kind = classifyUrlKey(key);
|
|
1981
|
+
if (kind === "href") {
|
|
1942
1982
|
next = sanitizeHref(original);
|
|
1943
|
-
} else if (
|
|
1983
|
+
} else if (kind === "image") {
|
|
1944
1984
|
next = sanitizeImageSrc(original);
|
|
1985
|
+
} else if (kind === "embed") {
|
|
1986
|
+
next = sanitizeEmbedSrc(original);
|
|
1945
1987
|
}
|
|
1946
1988
|
} else if (typeof original === "object" && original !== null) {
|
|
1947
1989
|
next = sanitizeValue(original);
|
|
@@ -3254,6 +3296,7 @@ function ChannelCombobox({
|
|
|
3254
3296
|
)
|
|
3255
3297
|
] });
|
|
3256
3298
|
}
|
|
3299
|
+
var EMBED_SELECTOR = "iframe[src], embed[src], object[data], source[src], video[src], audio[src], track[src]";
|
|
3257
3300
|
function SlackBlockPreview({
|
|
3258
3301
|
block,
|
|
3259
3302
|
hooks,
|
|
@@ -3286,6 +3329,17 @@ function SlackBlockPreview({
|
|
|
3286
3329
|
img.setAttribute("data-bk-blocked-src", "1");
|
|
3287
3330
|
}
|
|
3288
3331
|
}
|
|
3332
|
+
for (const el of root.querySelectorAll(EMBED_SELECTOR)) {
|
|
3333
|
+
const attr = el.tagName === "OBJECT" ? "data" : "src";
|
|
3334
|
+
if (!isSafeEmbedSrc(el.getAttribute(attr))) {
|
|
3335
|
+
el.removeAttribute(attr);
|
|
3336
|
+
el.setAttribute("data-bk-blocked-src", "1");
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
for (const frame of root.querySelectorAll("iframe[srcdoc]")) {
|
|
3340
|
+
frame.removeAttribute("srcdoc");
|
|
3341
|
+
frame.setAttribute("data-bk-blocked-srcdoc", "1");
|
|
3342
|
+
}
|
|
3289
3343
|
});
|
|
3290
3344
|
return (
|
|
3291
3345
|
// The library scopes all of its CSS under `#slack_blocks_to_jsx` and
|
|
@@ -9138,7 +9192,14 @@ function SourcesField({
|
|
|
9138
9192
|
] })
|
|
9139
9193
|
] });
|
|
9140
9194
|
}
|
|
9195
|
+
function UnsafeUrlNote({ children }) {
|
|
9196
|
+
return /* @__PURE__ */ jsx("p", { className: "mt-1 text-[11px] text-destructive", children });
|
|
9197
|
+
}
|
|
9141
9198
|
function VideoEditor({ block, onChange }) {
|
|
9199
|
+
const videoUrlIsUnsafe = (block.video_url ?? "").length > 0 && !isSafeEmbedSrc(block.video_url);
|
|
9200
|
+
const thumbnailIsUnsafe = (block.thumbnail_url ?? "").length > 0 && !isSafeImageSrc(block.thumbnail_url);
|
|
9201
|
+
const titleUrlIsUnsafe = (block.title_url ?? "").length > 0 && !isSafeHref(block.title_url);
|
|
9202
|
+
const providerIconIsUnsafe = (block.provider_icon_url ?? "").length > 0 && !isSafeImageSrc(block.provider_icon_url);
|
|
9142
9203
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
9143
9204
|
/* @__PURE__ */ jsx(EditorField, { label: "Title", help: "Up to 199 characters. Shown above the embedded player.", htmlFor: "video-title", children: /* @__PURE__ */ jsx(
|
|
9144
9205
|
Input,
|
|
@@ -9162,50 +9223,62 @@ function VideoEditor({ block, onChange }) {
|
|
|
9162
9223
|
onChange: (e) => onChange({ ...block, alt_text: e.target.value })
|
|
9163
9224
|
}
|
|
9164
9225
|
) }),
|
|
9165
|
-
/* @__PURE__ */
|
|
9226
|
+
/* @__PURE__ */ jsxs(
|
|
9166
9227
|
EditorField,
|
|
9167
9228
|
{
|
|
9168
9229
|
label: "Video URL",
|
|
9169
9230
|
help: "HTTPS embed URL. Must match an unfurl domain on your Slack app.",
|
|
9170
9231
|
htmlFor: "video-url",
|
|
9171
|
-
children:
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9232
|
+
children: [
|
|
9233
|
+
/* @__PURE__ */ jsx(
|
|
9234
|
+
Input,
|
|
9235
|
+
{
|
|
9236
|
+
id: "video-url",
|
|
9237
|
+
type: "url",
|
|
9238
|
+
value: block.video_url ?? "",
|
|
9239
|
+
placeholder: "e.g. https://www.youtube.com/embed/dQw4w9WgXcQ",
|
|
9240
|
+
onChange: (e) => onChange({ ...block, video_url: e.target.value }),
|
|
9241
|
+
"aria-invalid": videoUrlIsUnsafe || void 0
|
|
9242
|
+
}
|
|
9243
|
+
),
|
|
9244
|
+
videoUrlIsUnsafe && /* @__PURE__ */ jsx(UnsafeUrlNote, { children: "The embedded player only accepts a full http(s) URL. This URL will be stripped before send and preview." })
|
|
9245
|
+
]
|
|
9181
9246
|
}
|
|
9182
9247
|
),
|
|
9183
|
-
/* @__PURE__ */
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9248
|
+
/* @__PURE__ */ jsxs(EditorField, { label: "Thumbnail URL", help: "Preview image shown before the video loads.", htmlFor: "video-thumbnail", children: [
|
|
9249
|
+
/* @__PURE__ */ jsx(
|
|
9250
|
+
Input,
|
|
9251
|
+
{
|
|
9252
|
+
id: "video-thumbnail",
|
|
9253
|
+
type: "url",
|
|
9254
|
+
value: block.thumbnail_url ?? "",
|
|
9255
|
+
placeholder: "e.g. https://example.com/thumb.png",
|
|
9256
|
+
onChange: (e) => onChange({ ...block, thumbnail_url: e.target.value }),
|
|
9257
|
+
"aria-invalid": thumbnailIsUnsafe || void 0
|
|
9258
|
+
}
|
|
9259
|
+
),
|
|
9260
|
+
thumbnailIsUnsafe && /* @__PURE__ */ jsx(UnsafeUrlNote, { children: "Only http(s) image URLs are allowed. This URL will be stripped before send and preview." })
|
|
9261
|
+
] }),
|
|
9262
|
+
/* @__PURE__ */ jsxs(
|
|
9194
9263
|
EditorField,
|
|
9195
9264
|
{
|
|
9196
9265
|
label: "Title link URL",
|
|
9197
9266
|
help: "Optional. HTTPS link the title text opens (the non-embed page).",
|
|
9198
9267
|
htmlFor: "video-title-url",
|
|
9199
|
-
children:
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9268
|
+
children: [
|
|
9269
|
+
/* @__PURE__ */ jsx(
|
|
9270
|
+
Input,
|
|
9271
|
+
{
|
|
9272
|
+
id: "video-title-url",
|
|
9273
|
+
type: "url",
|
|
9274
|
+
value: block.title_url ?? "",
|
|
9275
|
+
placeholder: "e.g. https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
|
9276
|
+
onChange: (e) => onChange({ ...block, title_url: e.target.value || void 0 }),
|
|
9277
|
+
"aria-invalid": titleUrlIsUnsafe || void 0
|
|
9278
|
+
}
|
|
9279
|
+
),
|
|
9280
|
+
titleUrlIsUnsafe && /* @__PURE__ */ jsx(UnsafeUrlNote, { children: "Only http(s), mailto, tel, sms, and xmpp links are allowed. This URL will be stripped before send and preview." })
|
|
9281
|
+
]
|
|
9209
9282
|
}
|
|
9210
9283
|
),
|
|
9211
9284
|
/* @__PURE__ */ jsx(EditorField, { label: "Description", help: "Optional. Up to 199 characters.", htmlFor: "video-description", children: /* @__PURE__ */ jsx(
|
|
@@ -9249,22 +9322,26 @@ function VideoEditor({ block, onChange }) {
|
|
|
9249
9322
|
)
|
|
9250
9323
|
}
|
|
9251
9324
|
),
|
|
9252
|
-
/* @__PURE__ */
|
|
9325
|
+
/* @__PURE__ */ jsxs(
|
|
9253
9326
|
EditorField,
|
|
9254
9327
|
{
|
|
9255
9328
|
label: "Provider icon URL",
|
|
9256
9329
|
help: "Optional. Small icon shown next to the provider name.",
|
|
9257
9330
|
htmlFor: "video-provider-icon",
|
|
9258
|
-
children:
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9331
|
+
children: [
|
|
9332
|
+
/* @__PURE__ */ jsx(
|
|
9333
|
+
Input,
|
|
9334
|
+
{
|
|
9335
|
+
id: "video-provider-icon",
|
|
9336
|
+
type: "url",
|
|
9337
|
+
value: block.provider_icon_url ?? "",
|
|
9338
|
+
placeholder: "e.g. https://example.com/youtube-favicon.png",
|
|
9339
|
+
onChange: (e) => onChange({ ...block, provider_icon_url: e.target.value || void 0 }),
|
|
9340
|
+
"aria-invalid": providerIconIsUnsafe || void 0
|
|
9341
|
+
}
|
|
9342
|
+
),
|
|
9343
|
+
providerIconIsUnsafe && /* @__PURE__ */ jsx(UnsafeUrlNote, { children: "Only http(s) image URLs are allowed. This URL will be stripped before send and preview." })
|
|
9344
|
+
]
|
|
9268
9345
|
}
|
|
9269
9346
|
)
|
|
9270
9347
|
] });
|