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') +})