From 7cda9c72c6a1c21d3f966123a4db4f1780b4832a Mon Sep 17 00:00:00 2001 From: mtdkei Date: Fri, 31 Jan 2025 18:34:58 +0900 Subject: [PATCH 01/11] Add veu_cta_block_safe_kses_post --- inc/call-to-action/package/block/index.php | 48 +++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/inc/call-to-action/package/block/index.php b/inc/call-to-action/package/block/index.php index 3f7fe2d6..21e047c8 100644 --- a/inc/call-to-action/package/block/index.php +++ b/inc/call-to-action/package/block/index.php @@ -273,5 +273,51 @@ function veu_cta_block_callback( $attributes, $content ) { } } - return wp_kses_post( $content ); + return veu_cta_block_safe_kses_post( $content ); +} + +// ksesから特定のiframeを除外 +function veu_allow_custom_iframes( $tags, $context ) { + if ( 'post' === $context ) { // 'post' は wp_kses_post() に適用されるコンテキスト + $tags['iframe'] = array( + 'src' => true, + 'width' => true, + 'height' => true, + 'style' => true, + 'allowfullscreen' => true, + 'loading' => true, + 'sandbox' => true, + ); + } + return $tags; +} + +function veu_cta_block_safe_kses_post( $content ) { + // 許可する iframe の URL パターン(正規表現) + $allowed_iframe_patterns = array( + '/https:\/\/(www\.)?google\.com\//i', // Google Maps + '/https:\/\/(www\.)?youtube\.com\//i', // YouTube + '/https:\/\/www\.openstreetmap\.org\//i', // OpenStreetMap + '/https:\/\/player\.vimeo\.com\//i', // Vimeo + ); + + // 許可リストに該当する iframe が含まれているかチェック + $should_allow_iframe = false; + foreach ( $allowed_iframe_patterns as $pattern ) { + if ( preg_match( $pattern, $content ) ) { + $should_allow_iframe = true; + break; + } + } + + // 許可対象の iframe がある場合のみフィルターを適用 + if ( $should_allow_iframe ) { + add_filter( 'wp_kses_allowed_html', 'veu_allow_custom_iframes', 10, 2 ); + $content = wp_kses_post( $content ); + remove_filter( 'wp_kses_allowed_html', 'veu_allow_custom_iframes', 10, 2 ); + } else { + $content = wp_kses_post( $content ); + } + + return $content; } From 3ecebe6b636546abafa7c54176b93d6ec50cac9d Mon Sep 17 00:00:00 2001 From: mtdkei Date: Fri, 31 Jan 2025 18:37:51 +0900 Subject: [PATCH 02/11] Delete: 'post' --- inc/call-to-action/package/block/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/call-to-action/package/block/index.php b/inc/call-to-action/package/block/index.php index 21e047c8..1e913d51 100644 --- a/inc/call-to-action/package/block/index.php +++ b/inc/call-to-action/package/block/index.php @@ -278,7 +278,7 @@ function veu_cta_block_callback( $attributes, $content ) { // ksesから特定のiframeを除外 function veu_allow_custom_iframes( $tags, $context ) { - if ( 'post' === $context ) { // 'post' は wp_kses_post() に適用されるコンテキスト + if ( $context ) { $tags['iframe'] = array( 'src' => true, 'width' => true, From 50a424fe23e59abda4d50d0ae88cc762651a125d Mon Sep 17 00:00:00 2001 From: mtdkei Date: Mon, 3 Feb 2025 13:48:02 +0900 Subject: [PATCH 03/11] Fix: Allow only whitelisted iframes in CTA block --- inc/call-to-action/package/block/index.php | 30 ++++++++++++---------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/inc/call-to-action/package/block/index.php b/inc/call-to-action/package/block/index.php index 1e913d51..09c9975e 100644 --- a/inc/call-to-action/package/block/index.php +++ b/inc/call-to-action/package/block/index.php @@ -293,7 +293,6 @@ function veu_allow_custom_iframes( $tags, $context ) { } function veu_cta_block_safe_kses_post( $content ) { - // 許可する iframe の URL パターン(正規表現) $allowed_iframe_patterns = array( '/https:\/\/(www\.)?google\.com\//i', // Google Maps '/https:\/\/(www\.)?youtube\.com\//i', // YouTube @@ -301,22 +300,25 @@ function veu_cta_block_safe_kses_post( $content ) { '/https:\/\/player\.vimeo\.com\//i', // Vimeo ); - // 許可リストに該当する iframe が含まれているかチェック - $should_allow_iframe = false; - foreach ( $allowed_iframe_patterns as $pattern ) { - if ( preg_match( $pattern, $content ) ) { - $should_allow_iframe = true; - break; + preg_match_all( '/.*?<\/iframe>/i', $content, $matches ); + + $valid_iframes = array(); + + foreach ( $matches[1] as $index => $iframe_src ) { + foreach ( $allowed_iframe_patterns as $pattern ) { + if ( preg_match( $pattern, $iframe_src ) ) { + $valid_iframes[ $matches[0][ $index ] ] = $matches[0][ $index ]; // 元の `iframe` タグを保存 + break; + } } } - // 許可対象の iframe がある場合のみフィルターを適用 - if ( $should_allow_iframe ) { - add_filter( 'wp_kses_allowed_html', 'veu_allow_custom_iframes', 10, 2 ); - $content = wp_kses_post( $content ); - remove_filter( 'wp_kses_allowed_html', 'veu_allow_custom_iframes', 10, 2 ); - } else { - $content = wp_kses_post( $content ); + add_filter( 'wp_kses_allowed_html', 'veu_allow_custom_iframes', 10, 2 ); + $content = wp_kses_post( $content ); + remove_filter( 'wp_kses_allowed_html', 'veu_allow_custom_iframes', 10, 2 ); + + foreach ( $valid_iframes as $original_iframe ) { + $content = str_replace( '[iframe-placeholder]', $original_iframe, $content ); } return $content; From 5315942878f5842965e21e53377d5c3a19d3b629 Mon Sep 17 00:00:00 2001 From: mtdkei Date: Mon, 3 Feb 2025 13:58:06 +0900 Subject: [PATCH 04/11] Add changelog --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index d6693868..f477496e 100644 --- a/readme.txt +++ b/readme.txt @@ -81,6 +81,7 @@ e.g. == Changelog == +[ Bug fix ][ CTA ] Fixed iframes escaping only whitelisted URLs [ Design Bug Fix ][ Share button ]Resolved an issue where `gap` in the `.veu_socialSet` component was not applying proper spacing between elements. [ Specification change ] Fixed the zoom-out toggle not always displaying in the editor toolbar (updated blocks.json API version from 2 to 3). From 7a1f358814808456cc96b2ccb1af964e870a76a4 Mon Sep 17 00:00:00 2001 From: mtdkei Date: Mon, 3 Feb 2025 14:04:45 +0900 Subject: [PATCH 05/11] fix: rename valid_iframes to allowed_iframes. --- inc/call-to-action/package/block/index.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/inc/call-to-action/package/block/index.php b/inc/call-to-action/package/block/index.php index 09c9975e..29e47674 100644 --- a/inc/call-to-action/package/block/index.php +++ b/inc/call-to-action/package/block/index.php @@ -302,12 +302,12 @@ function veu_cta_block_safe_kses_post( $content ) { preg_match_all( '/.*?<\/iframe>/i', $content, $matches ); - $valid_iframes = array(); + $allowed_iframes = array(); foreach ( $matches[1] as $index => $iframe_src ) { foreach ( $allowed_iframe_patterns as $pattern ) { if ( preg_match( $pattern, $iframe_src ) ) { - $valid_iframes[ $matches[0][ $index ] ] = $matches[0][ $index ]; // 元の `iframe` タグを保存 + $allowed_iframes[ $matches[0][ $index ] ] = $matches[0][ $index ]; // 元の `iframe` タグを保存 break; } } @@ -317,7 +317,7 @@ function veu_cta_block_safe_kses_post( $content ) { $content = wp_kses_post( $content ); remove_filter( 'wp_kses_allowed_html', 'veu_allow_custom_iframes', 10, 2 ); - foreach ( $valid_iframes as $original_iframe ) { + foreach ( $allowed_iframes as $original_iframe ) { $content = str_replace( '[iframe-placeholder]', $original_iframe, $content ); } From a17e9e77bf91ee41e519fa81d7b877f5463c7f18 Mon Sep 17 00:00:00 2001 From: mtdkei Date: Mon, 3 Feb 2025 14:38:38 +0900 Subject: [PATCH 06/11] Add test for iframe --- package-lock.json | 22 +++++++------- tests/e2e/cta.spec.ts | 67 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5236e87b..74f9a131 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3006,12 +3006,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.0.tgz", - "integrity": "sha512-/QYft5VArOrGRP5pgkrfKksqsKA6CEFyGQ/gjNe6q0y4tZ1aaPfq4gIjudr1s3D+pXyrPRdsy4opKDrjBabE5w==", + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz", + "integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==", "dev": true, "dependencies": { - "playwright": "1.46.0" + "playwright": "1.50.1" }, "bin": { "playwright": "cli.js" @@ -20278,12 +20278,12 @@ } }, "node_modules/playwright": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz", - "integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==", + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz", + "integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==", "dev": true, "dependencies": { - "playwright-core": "1.46.0" + "playwright-core": "1.50.1" }, "bin": { "playwright": "cli.js" @@ -20296,9 +20296,9 @@ } }, "node_modules/playwright-core": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz", - "integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==", + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz", + "integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==", "dev": true, "bin": { "playwright-core": "cli.js" diff --git a/tests/e2e/cta.spec.ts b/tests/e2e/cta.spec.ts index 6ca90d6f..6487f9db 100644 --- a/tests/e2e/cta.spec.ts +++ b/tests/e2e/cta.spec.ts @@ -134,4 +134,69 @@ test('CTA', async ({ page }) => { await page.waitForTimeout(500); // wait the "Move to trash" button await page.getByRole('button', { name: 'Move to trash' }).click(); -}); \ No newline at end of file +}); + +test('CTA with Allowed Iframe', async ({ page }) => { + // ログイン + await page.goto('http://localhost:8889/wp-login.php'); + await page.getByLabel('Username or Email Address').fill('admin'); + await page.getByLabel('Password', { exact: true }).fill('password'); + await page.getByLabel('Password', { exact: true }).press('Enter'); + + // 新しいCTA作成 + await page.goto('http://localhost:8889/wp-admin/post-new.php?post_type=cta'); + await page.getByRole('textbox', { name: 'Add title' }).fill('Allowed Iframe CTA'); + await page.getByRole('textbox', { name: 'Add title' }).press('Enter'); + + // Googleマップのiframeを追加 + const allowed_iframe = ``; + await page.getByRole('document', { name: 'Empty block; start writing or type forward slash to choose a block' }).first().fill(allowed_iframe); + + // CTAを公開 + await page.getByRole('region', { name: 'Editor top bar' }).getByRole('button', { name: 'Publish' }).click(); + await page.getByRole('button', { name: 'Publish' }).nth(1).click(); + await page.waitForTimeout(1000); + + // CTA作成を確認 + await page.goto('http://localhost:8889/wp-admin/edit.php?post_type=cta'); + await expect(page.locator('table.wp-list-table')).toContainText('Allowed Iframe CTA'); + + // 新規投稿を作成し、CTAブロックを追加 + await page.goto('http://localhost:8889/wp-admin/post-new.php'); + await page.getByRole('textbox', { name: 'Add title' }).fill('Post with Allowed Iframe'); + await page.getByRole('button', { name: 'Add block' }).click(); + await page.getByPlaceholder('Search').fill('cta'); + await page.getByRole('option', { name: 'CTA' }).click(); + + // CTAを選択 + await page.locator('#veu-cta-block-select').selectOption({ label: 'Allowed Iframe CTA' }); + + // CTAが表示されることを確認 + await expect(page.locator('.veu-cta-block')).toContainText('Allowed Iframe CTA'); + + // 投稿を公開 + await page.getByRole('region', { name: 'Editor top bar' }).getByRole('button', { name: 'Publish' }).click(); + await page.getByRole('button', { name: 'Publish' }).nth(1).click(); + await page.waitForTimeout(1000); + + // フロントエンドで投稿を確認 + const postURL = await page.url(); + await page.goto(postURL); + + // 許可されたiframeが表示されるかチェック + const iframeLocator = page.frameLocator('iframe[src*="https://www.google.com/maps/embed"]'); + await expect(iframeLocator.locator('body')).toBeVisible(); + + // 許可されていない iframe は存在しないことを確認 + await expect(page.locator('iframe:not([src*="https://www.google.com/maps/embed"])')).toHaveCount(0); + + // テスト完了後に削除 + await page.goto('http://localhost:8889/wp-admin/edit.php'); + await page.getByRole('link', { name: '“Post with Allowed Iframe” (Edit)' }).click(); + await page.getByRole('button', { name: 'Move to trash' }).click(); + await page.waitForTimeout(500); + + await page.goto('http://localhost:8889/wp-admin/edit.php?post_type=cta'); + await page.getByRole('link', { name: '“Allowed Iframe CTA” (Edit)' }).click(); + await page.getByRole('button', { name: 'Move to trash' }).click(); +}); From 216fde2b0bb2542ead6a910db3d51361db97c2f1 Mon Sep 17 00:00:00 2001 From: mtdkei Date: Mon, 3 Feb 2025 15:37:47 +0900 Subject: [PATCH 07/11] Revert "Add test for iframe" This reverts commit a17e9e77bf91ee41e519fa81d7b877f5463c7f18. --- package-lock.json | 22 +++++++------- tests/e2e/cta.spec.ts | 67 +------------------------------------------ 2 files changed, 12 insertions(+), 77 deletions(-) diff --git a/package-lock.json b/package-lock.json index 74f9a131..5236e87b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3006,12 +3006,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.50.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz", - "integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==", + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.0.tgz", + "integrity": "sha512-/QYft5VArOrGRP5pgkrfKksqsKA6CEFyGQ/gjNe6q0y4tZ1aaPfq4gIjudr1s3D+pXyrPRdsy4opKDrjBabE5w==", "dev": true, "dependencies": { - "playwright": "1.50.1" + "playwright": "1.46.0" }, "bin": { "playwright": "cli.js" @@ -20278,12 +20278,12 @@ } }, "node_modules/playwright": { - "version": "1.50.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz", - "integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==", + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz", + "integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==", "dev": true, "dependencies": { - "playwright-core": "1.50.1" + "playwright-core": "1.46.0" }, "bin": { "playwright": "cli.js" @@ -20296,9 +20296,9 @@ } }, "node_modules/playwright-core": { - "version": "1.50.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz", - "integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==", + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz", + "integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==", "dev": true, "bin": { "playwright-core": "cli.js" diff --git a/tests/e2e/cta.spec.ts b/tests/e2e/cta.spec.ts index 6487f9db..6ca90d6f 100644 --- a/tests/e2e/cta.spec.ts +++ b/tests/e2e/cta.spec.ts @@ -134,69 +134,4 @@ test('CTA', async ({ page }) => { await page.waitForTimeout(500); // wait the "Move to trash" button await page.getByRole('button', { name: 'Move to trash' }).click(); -}); - -test('CTA with Allowed Iframe', async ({ page }) => { - // ログイン - await page.goto('http://localhost:8889/wp-login.php'); - await page.getByLabel('Username or Email Address').fill('admin'); - await page.getByLabel('Password', { exact: true }).fill('password'); - await page.getByLabel('Password', { exact: true }).press('Enter'); - - // 新しいCTA作成 - await page.goto('http://localhost:8889/wp-admin/post-new.php?post_type=cta'); - await page.getByRole('textbox', { name: 'Add title' }).fill('Allowed Iframe CTA'); - await page.getByRole('textbox', { name: 'Add title' }).press('Enter'); - - // Googleマップのiframeを追加 - const allowed_iframe = ``; - await page.getByRole('document', { name: 'Empty block; start writing or type forward slash to choose a block' }).first().fill(allowed_iframe); - - // CTAを公開 - await page.getByRole('region', { name: 'Editor top bar' }).getByRole('button', { name: 'Publish' }).click(); - await page.getByRole('button', { name: 'Publish' }).nth(1).click(); - await page.waitForTimeout(1000); - - // CTA作成を確認 - await page.goto('http://localhost:8889/wp-admin/edit.php?post_type=cta'); - await expect(page.locator('table.wp-list-table')).toContainText('Allowed Iframe CTA'); - - // 新規投稿を作成し、CTAブロックを追加 - await page.goto('http://localhost:8889/wp-admin/post-new.php'); - await page.getByRole('textbox', { name: 'Add title' }).fill('Post with Allowed Iframe'); - await page.getByRole('button', { name: 'Add block' }).click(); - await page.getByPlaceholder('Search').fill('cta'); - await page.getByRole('option', { name: 'CTA' }).click(); - - // CTAを選択 - await page.locator('#veu-cta-block-select').selectOption({ label: 'Allowed Iframe CTA' }); - - // CTAが表示されることを確認 - await expect(page.locator('.veu-cta-block')).toContainText('Allowed Iframe CTA'); - - // 投稿を公開 - await page.getByRole('region', { name: 'Editor top bar' }).getByRole('button', { name: 'Publish' }).click(); - await page.getByRole('button', { name: 'Publish' }).nth(1).click(); - await page.waitForTimeout(1000); - - // フロントエンドで投稿を確認 - const postURL = await page.url(); - await page.goto(postURL); - - // 許可されたiframeが表示されるかチェック - const iframeLocator = page.frameLocator('iframe[src*="https://www.google.com/maps/embed"]'); - await expect(iframeLocator.locator('body')).toBeVisible(); - - // 許可されていない iframe は存在しないことを確認 - await expect(page.locator('iframe:not([src*="https://www.google.com/maps/embed"])')).toHaveCount(0); - - // テスト完了後に削除 - await page.goto('http://localhost:8889/wp-admin/edit.php'); - await page.getByRole('link', { name: '“Post with Allowed Iframe” (Edit)' }).click(); - await page.getByRole('button', { name: 'Move to trash' }).click(); - await page.waitForTimeout(500); - - await page.goto('http://localhost:8889/wp-admin/edit.php?post_type=cta'); - await page.getByRole('link', { name: '“Allowed Iframe CTA” (Edit)' }).click(); - await page.getByRole('button', { name: 'Move to trash' }).click(); -}); +}); \ No newline at end of file From 5638aedbb58cf159d4e84c6476c04a2a63f09f01 Mon Sep 17 00:00:00 2001 From: mtdkei Date: Tue, 4 Feb 2025 10:08:35 +0900 Subject: [PATCH 08/11] Fix: safe_kses_post --- inc/call-to-action/package/block/index.php | 50 +---------------- .../package/class-vk-call-to-action.php | 56 ++++++++++++++++++- .../package/widget-call-to-action.php | 2 +- 3 files changed, 57 insertions(+), 51 deletions(-) diff --git a/inc/call-to-action/package/block/index.php b/inc/call-to-action/package/block/index.php index 29e47674..5a95cbb5 100644 --- a/inc/call-to-action/package/block/index.php +++ b/inc/call-to-action/package/block/index.php @@ -273,53 +273,5 @@ function veu_cta_block_callback( $attributes, $content ) { } } - return veu_cta_block_safe_kses_post( $content ); -} - -// ksesから特定のiframeを除外 -function veu_allow_custom_iframes( $tags, $context ) { - if ( $context ) { - $tags['iframe'] = array( - 'src' => true, - 'width' => true, - 'height' => true, - 'style' => true, - 'allowfullscreen' => true, - 'loading' => true, - 'sandbox' => true, - ); - } - return $tags; -} - -function veu_cta_block_safe_kses_post( $content ) { - $allowed_iframe_patterns = array( - '/https:\/\/(www\.)?google\.com\//i', // Google Maps - '/https:\/\/(www\.)?youtube\.com\//i', // YouTube - '/https:\/\/www\.openstreetmap\.org\//i', // OpenStreetMap - '/https:\/\/player\.vimeo\.com\//i', // Vimeo - ); - - preg_match_all( '/.*?<\/iframe>/i', $content, $matches ); - - $allowed_iframes = array(); - - foreach ( $matches[1] as $index => $iframe_src ) { - foreach ( $allowed_iframe_patterns as $pattern ) { - if ( preg_match( $pattern, $iframe_src ) ) { - $allowed_iframes[ $matches[0][ $index ] ] = $matches[0][ $index ]; // 元の `iframe` タグを保存 - break; - } - } - } - - add_filter( 'wp_kses_allowed_html', 'veu_allow_custom_iframes', 10, 2 ); - $content = wp_kses_post( $content ); - remove_filter( 'wp_kses_allowed_html', 'veu_allow_custom_iframes', 10, 2 ); - - foreach ( $allowed_iframes as $original_iframe ) { - $content = str_replace( '[iframe-placeholder]', $original_iframe, $content ); - } - - return $content; + return Vk_Call_To_Action::safe_kses_post( $content ); } diff --git a/inc/call-to-action/package/class-vk-call-to-action.php b/inc/call-to-action/package/class-vk-call-to-action.php index ed4b51ae..93214975 100644 --- a/inc/call-to-action/package/class-vk-call-to-action.php +++ b/inc/call-to-action/package/class-vk-call-to-action.php @@ -481,7 +481,7 @@ public static function render_cta_content( $id ) { // wp_kses_post でエスケープすると outerブロックが出力するstyle属性を無効化されるので使わないように. // 出力時にエスケープしたいが、wp_kses_post だと style属性が無効化される / wp_kses でも allow_html で opacity を許可しても無視・削除される。 // 結局本文欄はどのみち HTML ブロックで 入れられ標準でXSSは実行可能なので、ここでは処理していない。 - return do_blocks( do_shortcode( $content ) ); + return self::safe_kses_post( do_blocks( do_shortcode( $content ) ) ); } /** @@ -724,6 +724,60 @@ public static function render_configPage() { include __DIR__ . '/view-adminsetting.php'; } + + public static function safe_kses_post( $content ) { + $allowed_iframe_patterns = array( + '/https:\/\/(www\.)?google\.com\//i', + '/https:\/\/(www\.)?youtube\.com\//i', + '/https:\/\/www\.openstreetmap\.org\//i', + '/https:\/\/player\.vimeo\.com\//i', + ); + + // すべての iframe タグを検索 + preg_match_all( '/.*?<\/iframe>/i', $content, $matches ); + + $allowed_iframes = array(); + $disallowed_iframes = array(); + + foreach ( $matches[1] as $index => $iframe_src ) { + $allowed = false; + foreach ( $allowed_iframe_patterns as $pattern ) { + if ( preg_match( $pattern, $iframe_src ) ) { + $allowed_iframes[ $matches[0][ $index ] ] = $matches[0][ $index ]; // 許可リストに追加 + $allowed = true; + break; + } + } + if ( ! $allowed ) { + $disallowed_iframes[] = $matches[0][ $index ]; // 非許可リストに追加 + } + } + + // すべての iframe を一旦削除 + $content = str_replace( $disallowed_iframes, '', $content ); + + // HTMLのフィルタリング + add_filter( 'wp_kses_allowed_html', array( __CLASS__, 'allow_custom_iframes' ), 10, 2 ); + $content = wp_kses_post( $content ); + remove_filter( 'wp_kses_allowed_html', array( __CLASS__, 'allow_custom_iframes' ), 10, 2 ); + + return $content; + } + + public static function allow_custom_iframes( $tags, $context ) { + if ( $context ) { + $tags['iframe'] = array( + 'src' => true, + 'width' => true, + 'height' => true, + 'style' => true, + 'allowfullscreen' => true, + 'loading' => true, + 'sandbox' => true, + ); + } + return $tags; + } } Vk_Call_To_Action::init(); diff --git a/inc/call-to-action/package/widget-call-to-action.php b/inc/call-to-action/package/widget-call-to-action.php index 66e0e859..e338681f 100644 --- a/inc/call-to-action/package/widget-call-to-action.php +++ b/inc/call-to-action/package/widget-call-to-action.php @@ -43,7 +43,7 @@ function widget( $args, $instance ) { if ( $instance['id'] == 'random' ) { $instance['id'] = Vk_Call_To_Action::cta_id_random(); } - echo Vk_Call_To_Action::render_cta_content( $instance['id'] ); + echo Vk_Call_To_Action::safe_kses_post( Vk_Call_To_Action::render_cta_content( $instance['id'] ) ); echo $args['after_widget']; } return; From 640b23a590fadb5d5d79e9e167aabebede33a083 Mon Sep 17 00:00:00 2001 From: mtdkei Date: Tue, 4 Feb 2025 10:21:59 +0900 Subject: [PATCH 09/11] Add test_safe_kses_post --- tests/test-cta.php | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/test-cta.php b/tests/test-cta.php index e12083b7..3f2efd27 100644 --- a/tests/test-cta.php +++ b/tests/test-cta.php @@ -265,4 +265,39 @@ function test_render_cta_content() { $this->assertEquals( $value['expected'], $actual ); } } + + /** + * Test safe_kses_post() + * + * @return void + */ + function test_safe_kses_post() { + $test_cases = array( + array( + 'input' => '', + 'expected' => '', + ), + array( + 'input' => '', + 'expected' => '', + ), + array( + 'input' => '', + 'expected' => '', + ), + array( + 'input' => '', + 'expected' => '', + ), + array( + 'input' => '', + 'expected' => '', + ), + ); + + foreach ( $test_cases as $case ) { + $actual = Vk_Call_To_Action::safe_kses_post( $case['input'] ); + $this->assertEquals( $case['expected'], $actual ); + } + } } From cf306488542a741ea6ea6c4912a1f2af529beb2a Mon Sep 17 00:00:00 2001 From: kurudrive Date: Thu, 6 Feb 2025 12:05:23 +0900 Subject: [PATCH 10/11] =?UTF-8?q?[=20WIP=20]=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test-cta.php | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/test-cta.php b/tests/test-cta.php index 3f2efd27..97ee27c4 100644 --- a/tests/test-cta.php +++ b/tests/test-cta.php @@ -274,30 +274,54 @@ function test_render_cta_content() { function test_safe_kses_post() { $test_cases = array( array( + 'name' => 'allow google', 'input' => '', 'expected' => '', ), array( + 'name' => 'allow iframe youtube', 'input' => '', 'expected' => '', ), array( + 'name' => 'allow iframe openstreetmap', 'input' => '', 'expected' => '', ), array( + 'name' => 'allow iframe vimeo', 'input' => '', 'expected' => '', ), array( + 'name' => 'escape iframe', 'input' => '', 'expected' => '', ), + array( + 'name' => 'class and style', + 'input' => '
CTA
', + 'expected' => '
CTA
', + ), + array( + 'name' => 'allow style tag', + 'input' => '
CTA
', + 'expected' => '
CTA
', + ), + // array( + // 'name' => 'allow style tag', + // 'input' => '
CTA
', + // 'expected' => '
CTA
', + // ), ); foreach ( $test_cases as $case ) { $actual = Vk_Call_To_Action::safe_kses_post( $case['input'] ); - $this->assertEquals( $case['expected'], $actual ); + $this->assertEquals( $case['expected'], $actual, $case['name'] ); } } } From 9dff7f62042a7d65aa3bf84533a2eab3338969de Mon Sep 17 00:00:00 2001 From: kurudrive Date: Thu, 6 Feb 2025 14:58:56 +0900 Subject: [PATCH 11/11] =?UTF-8?q?CTA=20=E3=82=A8=E3=82=B9=E3=82=B1?= =?UTF-8?q?=E3=83=BC=E3=83=97=E8=AA=BF=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../package/class-vk-call-to-action.php | 6 +++--- tests/test-cta.php | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/inc/call-to-action/package/class-vk-call-to-action.php b/inc/call-to-action/package/class-vk-call-to-action.php index 93214975..afd01f2a 100644 --- a/inc/call-to-action/package/class-vk-call-to-action.php +++ b/inc/call-to-action/package/class-vk-call-to-action.php @@ -478,9 +478,6 @@ public static function render_cta_content( $id ) { // リセットしないと$postが改変されたままでコメント欄が表示されなくなるなどの弊害が発生する. wp_reset_postdata(); - // wp_kses_post でエスケープすると outerブロックが出力するstyle属性を無効化されるので使わないように. - // 出力時にエスケープしたいが、wp_kses_post だと style属性が無効化される / wp_kses でも allow_html で opacity を許可しても無視・削除される。 - // 結局本文欄はどのみち HTML ブロックで 入れられ標準でXSSは実行可能なので、ここでは処理していない。 return self::safe_kses_post( do_blocks( do_shortcode( $content ) ) ); } @@ -775,6 +772,9 @@ public static function allow_custom_iframes( $tags, $context ) { 'loading' => true, 'sandbox' => true, ); + $tags['style'] = array( + 'type' => true, + ); } return $tags; } diff --git a/tests/test-cta.php b/tests/test-cta.php index 97ee27c4..4811b53e 100644 --- a/tests/test-cta.php +++ b/tests/test-cta.php @@ -308,15 +308,15 @@ function test_safe_kses_post() { 'input' => '
CTA
', 'expected' => '
CTA
', ), - // array( - // 'name' => 'allow style tag', - // 'input' => '
CTA
', - // 'expected' => '
CTA
', - // ), + array( + 'name' => 'allow style tag with media query', + 'input' => '
CTA
', + 'expected' => '
CTA
', + ), ); foreach ( $test_cases as $case ) {