Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ CTAブロック ] ホワイトリストのURLのみiframeがエスケープされてしまうのを修正しました #1165

Merged
merged 18 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion inc/call-to-action/package/block/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -274,5 +274,5 @@ function veu_cta_block_callback( $attributes, $content ) {
}
}

return wp_kses_post( $content );
return Vk_Call_To_Action::safe_kses_post( $content );
}
62 changes: 58 additions & 4 deletions inc/call-to-action/package/class-vk-call-to-action.php
Original file line number Diff line number Diff line change
Expand Up @@ -478,10 +478,7 @@ 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 ブロックで <script>alert(0)</script> 入れられ標準でXSSは実行可能なので、ここでは処理していない。
return do_blocks( do_shortcode( $content ) );
return self::safe_kses_post( do_blocks( do_shortcode( $content ) ) );
}

/**
Expand Down Expand Up @@ -724,6 +721,63 @@ 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.*?src=["\'](.*?)["\'].*?>.*?<\/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,
);
$tags['style'] = array(
'type' => true,
);
}
return $tags;
}
}

Vk_Call_To_Action::init();
Expand Down
2 changes: 1 addition & 1 deletion inc/call-to-action/package/widget-call-to-action.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
59 changes: 59 additions & 0 deletions tests/test-cta.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,63 @@ 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(
'name' => 'allow google',
'input' => '<iframe src="https://www.google.com/maps/embed?pb=!1m18"></iframe>',
'expected' => '<iframe src="https://www.google.com/maps/embed?pb=!1m18"></iframe>',
),
array(
'name' => 'allow iframe youtube',
'input' => '<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ"></iframe>',
'expected' => '<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ"></iframe>',
),
array(
'name' => 'allow iframe openstreetmap',
'input' => '<iframe src="https://www.openstreetmap.org/export/embed.html"></iframe>',
'expected' => '<iframe src="https://www.openstreetmap.org/export/embed.html"></iframe>',
),
array(
'name' => 'allow iframe vimeo',
'input' => '<iframe src="https://player.vimeo.com/video/123456789"></iframe>',
'expected' => '<iframe src="https://player.vimeo.com/video/123456789"></iframe>',
),
array(
'name' => 'escape iframe',
'input' => '<iframe src="https://www.vektor-inc.co.jp/"></iframe>',
'expected' => '',
),
array(
'name' => 'class and style',
'input' => '<div class="class-name" style="margin-bottom:3rem">CTA</div>',
'expected' => '<div class="class-name" style="margin-bottom:3rem">CTA</div>',
),
array(
'name' => 'allow style tag',
'input' => '<div>CTA</div><style>.target-class { background-image: url(http://localhost:8888/image.jpg);}</style>',
'expected' => '<div>CTA</div><style>.target-class { background-image: url(http://localhost:8888/image.jpg);}</style>',
),
array(
'name' => 'allow style tag with media query',
'input' => '<div>CTA</div><style>@media screen and (max-width: 575.98px) {.target-class {
background-image: url(http://localhost:8888/image.jpg);
background-position: 50% 50%!important;}}</style>',
'expected' => '<div>CTA</div><style>@media screen and (max-width: 575.98px) {.target-class {
background-image: url(http://localhost:8888/image.jpg);
background-position: 50% 50%!important;}}</style>',
),
);

foreach ( $test_cases as $case ) {
$actual = Vk_Call_To_Action::safe_kses_post( $case['input'] );
$this->assertEquals( $case['expected'], $actual, $case['name'] );
}
}
}
Loading