From e065798b780f876e637be3f406c91cf4d5db54f4 Mon Sep 17 00:00:00 2001 From: zenn Date: Fri, 24 Jul 2026 16:21:22 +0900 Subject: [PATCH] =?UTF-8?q?iframe=20=EA=B2=BD=EA=B3=A0=EC=99=80=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EA=B0=95=EC=A1=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/content/ProseEmbed.vue | 1 - components/site/SitePagination.vue | 2 +- docs/map.md | 4 ++-- docs/spec.md | 4 ++-- docs/update.md | 6 ++++++ lib/markdown-iframe-embed.js | 23 +++++++++++++++++++---- package-lock.json | 4 ++-- package.json | 2 +- tests/markdown-iframe-embed.test.js | 17 +++++++++++++++++ 9 files changed, 50 insertions(+), 13 deletions(-) create mode 100644 tests/markdown-iframe-embed.test.js diff --git a/components/content/ProseEmbed.vue b/components/content/ProseEmbed.vue index aeb7561..0782fa6 100644 --- a/components/content/ProseEmbed.vue +++ b/components/content/ProseEmbed.vue @@ -241,7 +241,6 @@ watch(mastodonEmbedUrl, () => { :allow="customIframe.allow" :height="customIframe.height || 480" :width="customIframe.width || undefined" - :allowfullscreen="customIframe.allowFullscreen" loading="lazy" sandbox="allow-scripts allow-same-origin allow-popups allow-forms allow-presentation" /> diff --git a/components/site/SitePagination.vue b/components/site/SitePagination.vue index b4f48b8..61082d4 100644 --- a/components/site/SitePagination.vue +++ b/components/site/SitePagination.vue @@ -119,7 +119,7 @@ const getPageLinkTo = (page) => ({ String(value || '') .replace(/[^\w\s;*.:/-]/g, '') .trim() +/** + * iframe allow 속성과 fullscreen 권한을 정리한다. + * @param {Record} attributes - iframe 속성 객체 + * @param {string} openerAttributes - iframe 여는 태그 원본 속성 문자열 + * @returns {string} 정리된 allow 값 + */ +const normalizeAllowValue = (attributes, openerAttributes) => { + const allow = sanitizeAllowValue(attributes.allow || 'fullscreen; picture-in-picture; encrypted-media; web-share') + + if (!/\ballowfullscreen\b/i.test(openerAttributes) || /\bfullscreen\b/i.test(allow)) { + return allow + } + + return [allow, 'fullscreen'].filter(Boolean).join('; ') +} + /** * iframe HTML 코드인지 확인한다. * @param {string} value - 검사할 문자열 @@ -59,7 +75,7 @@ export const isIframeEmbedMarkup = (value) => /^\s /** * iframe HTML 코드에서 렌더링 가능한 정보만 추출한다. * @param {string} value - iframe HTML 코드 - * @returns {{ src: string, title: string, allow: string, width: string, height: string, allowFullscreen: boolean }|null} iframe 정보 + * @returns {{ src: string, title: string, allow: string, width: string, height: string }|null} iframe 정보 */ export const parseIframeEmbedMarkup = (value) => { const source = String(value || '').trim() @@ -79,9 +95,8 @@ export const parseIframeEmbedMarkup = (value) => { return { src, title: attributes.title || 'Embedded content', - allow: sanitizeAllowValue(attributes.allow || 'fullscreen; picture-in-picture; encrypted-media; web-share'), + allow: normalizeAllowValue(attributes, opener[1]), width: sanitizeSizeValue(attributes.width), - height: sanitizeSizeValue(attributes.height), - allowFullscreen: /\ballowfullscreen\b/i.test(opener[1]) + height: sanitizeSizeValue(attributes.height) } } diff --git a/package-lock.json b/package-lock.json index 2cc2f8f..a2c0cec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sori.studio", - "version": "1.11.58", + "version": "1.11.59", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sori.studio", - "version": "1.11.58", + "version": "1.11.59", "hasInstallScript": true, "dependencies": { "@nuxtjs/tailwindcss": "^6.14.0", diff --git a/package.json b/package.json index 3561e67..334fa46 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sori.studio", - "version": "1.11.58", + "version": "1.11.59", "private": true, "type": "module", "imports": { diff --git a/tests/markdown-iframe-embed.test.js b/tests/markdown-iframe-embed.test.js new file mode 100644 index 0000000..d4fd392 --- /dev/null +++ b/tests/markdown-iframe-embed.test.js @@ -0,0 +1,17 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { parseIframeEmbedMarkup } from '../lib/markdown-iframe-embed.js' + +test('iframe allowfullscreen은 allow fullscreen 권한으로 정규화한다', () => { + const parsed = parseIframeEmbedMarkup('') + + assert.equal(parsed.src, 'https://example.com/embed') + assert.match(parsed.allow, /\bfullscreen\b/) + assert.equal(Object.hasOwn(parsed, 'allowFullscreen'), false) +}) + +test('iframe allow 속성은 안전한 문자만 보존한다', () => { + const parsed = parseIframeEmbedMarkup('') + + assert.equal(parsed.allow, 'fullscreen; autoplay; bad') +})