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
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,7 @@ private function doSkipReason($current_user, $ct_comment_done)
apbct_is_user_enable() === false ||
$this->apbct->settings['forms__comments_test'] == 0 ||
$ct_comment_done ||
(isset($_SERVER['HTTP_REFERER']) && stripos($_SERVER['HTTP_REFERER'], 'page=wysija_campaigns&action=editTemplate') !== false) ||
(isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/wp-admin/') !== false)
is_admin()
) {
return __FILE__ . ' -> ' . __FUNCTION__ . '():' . __LINE__;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,66 @@ class EmailEncoderShortCode extends \Cleantalk\ApbctWP\ShortCode
*/
protected $public_name;

/**
* @var string Wrapper template overridden by child classes as the placeholder source.
*/
protected $exclusion_wrapper = '';

// Placeholder nonce for ensuring unique placeholders per render.
protected $placeholder_nonce = '';

/**
* Build a placeholder for the given counter using the child's $exclusion_wrapper as a template.
* Lazy-initialises the per-render nonce so replacements survive isolated render passes.
*
* @param int $counter
*
* @return string
*/
protected function buildPlaceholder($counter)
{
if ($this->placeholder_nonce === '') {
$this->placeholder_nonce = $this->generatePlaceholderNonce();
}

$wrapper = (string)$this->exclusion_wrapper;
$placeholder = preg_replace(
'/EE\_\d+/',
'EE_' . (string)$counter . '_' . $this->placeholder_nonce,
$wrapper
);

return is_null($placeholder) ? $wrapper : $placeholder;
}

/**
* Rotates the placeholder nonce to ensure unique placeholders for subsequent renders.
* @return void
*/
protected function rotatePlaceholderNonce()
{
$this->placeholder_nonce = $this->generatePlaceholderNonce();
}

/**
* Generates a new high-entropy nonce for placeholders.
* @return string
*/
protected function generatePlaceholderNonce()
{
if (function_exists('random_bytes')) {
try {
return bin2hex(random_bytes(16));
} catch (\Exception $e) {
// fall through to WP fallback
}
}
if (function_exists('wp_generate_password')) {
return strtolower(wp_generate_password(32, false));
}
return substr(hash('sha256', uniqid((string)mt_rand(), true)), 0, 32);
}

/**
* Process only this encoder's shortcode tags in the content.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,7 @@ public function changeContentBeforeEncoderModify($content)
// Extract shortcode content to protect it from email encoding, supports sc attributes(!)
$shortcode_exist_pattern = sprintf('/(\[%s(?:\s[^\]]*)?\])([\s\S]*?)(\[\/%s\])/s', $this->public_name, $this->public_name);
$content = preg_replace_callback($shortcode_exist_pattern, function ($matches) {
$placeholder = preg_replace('/EE\_\d+/', 'EE_' . (string)$this->shortcode_counter++, $this->exclusion_wrapper);
if (is_null($placeholder)) {
$placeholder = $this->exclusion_wrapper;
}
$placeholder = $this->buildPlaceholder($this->shortcode_counter++);
if (isset($matches[1], $matches[2], $matches[3])) {
$prefix = $matches[1];
$entity = $matches[2];
Expand Down Expand Up @@ -166,5 +163,6 @@ public function resetShortcodeReplacements()
{
$this->shortcode_replacements = array();
$this->shortcode_counter = 0;
$this->rotatePlaceholderNonce();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,7 @@ protected function shouldDeferPlaceholderRestore()
*/
protected function createPlaceholder($content)
{
$placeholder = preg_replace('/EE\_\d+/', 'EE_' . (string)$this->shortcode_counter++, $this->exclusion_wrapper);
if (is_null($placeholder)) {
$placeholder = $this->exclusion_wrapper;
}
$placeholder = $this->buildPlaceholder($this->shortcode_counter++);
$this->shortcode_replacements[$placeholder] = $content;

return $placeholder;
Expand All @@ -291,6 +288,7 @@ public function resetShortcodeReplacements()
{
$this->shortcode_replacements = array();
$this->shortcode_counter = 0;
$this->rotatePlaceholderNonce();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ public function testChangeContentBeforeEncoderModifyReplacesShortcodesWithPlaceh
$content = 'Some content with [apbct_encode_data]Test content[/apbct_encode_data]';
$result = $this->shortcode->changeContentBeforeEncoderModify($content);

$this->assertStringContainsString('%%APBCT_SHORT_CODE_INCLUDE_EE_0%%', $result);
$this->assertArrayHasKey('%%APBCT_SHORT_CODE_INCLUDE_EE_0%%', $this->shortcode->shortcode_replacements);
$this->assertRegExp('/%%APBCT_SHORT_CODE_INCLUDE_EE_0_[a-f0-9]+%%/', $result);
$this->assertCount(1, $this->shortcode->shortcode_replacements);
}

public function testChangeContentBeforeEncoderModifyUsesPlaceholdersWhenDecoderCookieSet()
Expand All @@ -113,8 +113,8 @@ public function testChangeContentBeforeEncoderModifyUsesPlaceholdersWhenDecoderC
$content = 'Some content with [apbct_encode_data]Test content[/apbct_encode_data]';
$result = $this->shortcode->changeContentBeforeEncoderModify($content);

$this->assertStringContainsString('%%APBCT_SHORT_CODE_INCLUDE_EE_0%%', $result);
$this->assertArrayHasKey('%%APBCT_SHORT_CODE_INCLUDE_EE_0%%', $this->shortcode->shortcode_replacements);
$this->assertRegExp('/%%APBCT_SHORT_CODE_INCLUDE_EE_0_[a-f0-9]+%%/', $result);
$this->assertCount(1, $this->shortcode->shortcode_replacements);
}

public function testChangeContentBeforeEncoderModifyUsesPlaceholdersWhenGlobalEmailEncodingDisabled()
Expand All @@ -128,7 +128,7 @@ public function testChangeContentBeforeEncoderModifyUsesPlaceholdersWhenGlobalEm
$content = '<p>[apbct_encode_data]Test content[/apbct_encode_data]</p>';
$result = $shortcode->changeContentBeforeEncoderModify($content);

$this->assertStringContainsString('%%APBCT_SHORT_CODE_INCLUDE_EE_0%%', $result);
$this->assertRegExp('/%%APBCT_SHORT_CODE_INCLUDE_EE_0_[a-f0-9]+%%/', $result);
$this->assertStringContainsString('<p>', $result);
}

Expand Down Expand Up @@ -174,8 +174,8 @@ public function testShortcodeOutsideHtmlIsProcessed()

$result = $this->shortcode->changeContentBeforeEncoderModify($content);

$this->assertStringContainsString(
'%%APBCT_SHORT_CODE_INCLUDE_EE_0%%',
$this->assertRegExp(
'/%%APBCT_SHORT_CODE_INCLUDE_EE_0_[a-f0-9]+%%/',
$result
);

Expand All @@ -191,8 +191,10 @@ public function testMultipleShortcodesAreHandled()

$result = $this->shortcode->changeContentBeforeEncoderModify($content);

$this->assertStringContainsString('%%APBCT_SHORT_CODE_INCLUDE_EE_0%%', $result);
$this->assertStringContainsString('%%APBCT_SHORT_CODE_INCLUDE_EE_1%%', $result);
// both placeholders in the same render pass must share the same nonce
preg_match('/%%APBCT_SHORT_CODE_INCLUDE_EE_0_([a-f0-9]+)%%/', $result, $matches);
$this->assertNotEmpty($matches);
$this->assertStringContainsString('%%APBCT_SHORT_CODE_INCLUDE_EE_1_' . $matches[1] . '%%', $result);
}

public function testHtmlAttributeBreakPayloadDoesNotExplode()
Expand Down Expand Up @@ -234,8 +236,8 @@ public function testShortcodeWithAttributesIsProcessed()

$result = $this->shortcode->changeContentBeforeEncoderModify($content);

$this->assertStringContainsString(
'%%APBCT_SHORT_CODE_INCLUDE_EE_0%%',
$this->assertRegExp(
'/%%APBCT_SHORT_CODE_INCLUDE_EE_0_[a-f0-9]+%%/',
$result
);

Expand Down Expand Up @@ -271,7 +273,7 @@ public function testPlaceholderNeverAppearsInsideHtmlAttribute()

$result = $this->shortcode->changeContentBeforeEncoderModify($content);

$this->assertStringNotContainsString('%%APBCT_SHORT_CODE_INCLUDE_EE_0%%', $result);
$this->assertNotRegExp('/%%APBCT_SHORT_CODE_INCLUDE_EE_0(_[a-f0-9]+)?%%/', $result);
}

public function testCallbackEscapesReplacingText()
Expand Down
Loading