Skip to content
Open
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
11 changes: 11 additions & 0 deletions obfx_modules/beaver-widgets/inc/common-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@
// Require custom field.
require_once $module_directory . '/custom-fields/number-field/number_field.php';

/**
* Sanitize html tags.
*
* @param string $tag HTML tag name.
*
* @return string
*/
function themeisle_sanitize_tag( $tag ) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The identical sink is still open in the post-grid widget — modules/post-grid/includes/frontend.php L63-64 interpolates $settings->title_tag straight into the_title() with no escaping at all. Same module, same vector as themeisle#2058, so closing the issue on the pricing table alone leaves it exploitable.

Please route post-grid through this helper too. Its select also offers span and div (post-grid.php L150-165), so the allowed list and the fallback need to be parameters:

function themeisle_sanitize_tag( $tag, $allowed = array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p' ), $default = 'h2' ) {
	return in_array( $tag, $allowed, true ) ? $tag : $default;
}

That would also let sanitize_tag() in elementor-extra-widgets/widgets/elementor/pricing-table.php L1179 drop its copy of the same list.

return in_array( $tag, array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p' ), true ) ? $tag : 'h1';
}

/**
* Function to return padding controls.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@

$class_to_add = $settings->card_layout === 'yes' ? 'obfx-card' : '';

$plan_title_tag = themeisle_sanitize_tag( $settings->plan_title_tag );
$plan_subtitle_tag = themeisle_sanitize_tag( $settings->plan_subtitle_tag );

echo '<div class="obfx-pricing-plan ' . esc_attr( $class_to_add ) . '">';
echo '<div class="obfx-pricing-header">';
echo '<' . esc_attr( $settings->plan_title_tag ) . ' class="obfx-plan-title text-center">' . wp_kses_post( $settings->plan_title ) . '</' . esc_attr( $settings->plan_title_tag ) . '>';
echo '<' . esc_attr( $settings->plan_subtitle_tag ) . ' class="obfx-plan-subtitle text-center">' . wp_kses_post( $settings->plan_subtitle ) . '</' . esc_attr( $settings->plan_subtitle_tag ) . '>';
echo '<' . esc_html( $plan_title_tag ) . ' class="obfx-plan-title text-center">' . wp_kses_post( $settings->plan_title ) . '</' . esc_html( $plan_title_tag ) . '>';
echo '<' . esc_html( $plan_subtitle_tag ) . ' class="obfx-plan-subtitle text-center">' . wp_kses_post( $settings->plan_subtitle ) . '</' . esc_html( $plan_subtitle_tag ) . '>';
echo '</div>';
echo '<div class="obfx-pricing-price text-center">';
switch ( $settings->currency_position ) {
Expand Down
132 changes: 132 additions & 0 deletions tests/test-module-beaver-widgets.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php
/**
* WordPress unit test plugin.
*
* @package Orbit_Fox
* @subpackage Orbit_Fox/tests
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
*/
class Test_Module_Beaver_Widgets extends WP_UnitTestCase {

/**
* Path to the pricing table frontend template.
*
* @var string
*/
protected $pricing_table_template;

public function setUp(): void {
parent::setUp();

// The common functions file resolves its own path through the module instance,
// so it has to be loaded with the module as $this. Beaver Builder is not needed
// for the helpers themselves.
$module = new Beaver_Widgets_OBFX_Module();
$load_common = Closure::bind(
function () {
require_once $this->get_dir() . '/inc/common-functions.php';
},
$module,
'Beaver_Widgets_OBFX_Module'
);
$load_common();

$this->pricing_table_template = dirname( dirname( __FILE__ ) ) . '/obfx_modules/beaver-widgets/modules/pricing-table/includes/frontend.php';
}

/**
* Render the pricing table frontend template.
*
* @param array $args Settings to overwrite the defaults with.
*
* @return string
*/
protected function render_pricing_table( $args = array() ) {
$settings = (object) array_merge(
array(
'card_layout' => 'yes',
'plan_title' => 'Plan title',
'plan_title_tag' => 'h2',
'plan_subtitle' => 'Plan subtitle',
'plan_subtitle_tag' => 'p',
'price' => '50',
'currency' => '$',
'currency_position' => 'after',
'period' => '/mo',
'features' => array(),
'text' => 'Buy now',
'link' => 'https://example.com',
),
$args
);

ob_start();
include $this->pricing_table_template;

return ob_get_clean();
}

/**
* Supported tags should be returned unchanged.
*
* @covers ::themeisle_sanitize_tag
*/
public function test_sanitize_tag_keeps_supported_tags() {
foreach ( array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p' ) as $tag ) {
$this->assertEquals( $tag, themeisle_sanitize_tag( $tag ) );
}
}

/**
* Anything that is not a supported tag should fall back to a safe default.
*
* @covers ::themeisle_sanitize_tag
*/
public function test_sanitize_tag_falls_back_for_unsupported_tags() {
$unsupported = array(
'h1 onmouseover=alert(document.cookie)',
'p onclick=alert(1)',
'h2 class="evil"',
'script',
'div',
'H1',
'',
);

foreach ( $unsupported as $tag ) {
$this->assertEquals( 'h1', themeisle_sanitize_tag( $tag ), 'Unsupported tag: ' . $tag );
}
}

/**
* The pricing table should render the configured heading tags.
*/
public function test_pricing_table_renders_configured_tags() {
$output = $this->render_pricing_table(
array(
'plan_title_tag' => 'h3',
'plan_subtitle_tag' => 'h4',
)
);

$this->assertStringContainsString( '<h3 class="obfx-plan-title text-center">Plan title</h3>', $output );
$this->assertStringContainsString( '<h4 class="obfx-plan-subtitle text-center">Plan subtitle</h4>', $output );
}
Comment thread
girishpanchal30 marked this conversation as resolved.

/**
* The pricing table should reject unsafe heading tags.
*/
public function test_pricing_table_sanitizes_unsupported_tags() {
$output = $this->render_pricing_table(
array(
'plan_title_tag' => 'h3 onmouseover=alert(1)',
'plan_subtitle_tag' => 'script',
)
);

$this->assertStringContainsString( '<h1 class="obfx-plan-title text-center">Plan title</h1>', $output );
$this->assertStringContainsString( '<h1 class="obfx-plan-subtitle text-center">Plan subtitle</h1>', $output );
$this->assertStringNotContainsString( 'onmouseover', $output );
$this->assertStringNotContainsString( '<script', $output );
}
}
3 changes: 3 additions & 0 deletions tests/test-module-post-duplicator.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ public function tearDown(): void {
public function setUp(): void {
parent::setUp();

// WP_UnitTestCase resets the current user on tear down, so the capability
// checks need an administrator again for every test.
wp_set_current_user( 1 );

// Create a mock loader
$this->loader = $this->createMock('Orbit_Fox_Loader');
Expand Down
Loading