Merge pull request #1 from inputobjects/fixme/wordpress-feedback

WordPress Plugin submission feedback

Incorporating feedback from WordPress team:

    Fix Stable Tag/Version to match.
    Escape output/echo statements accordingly; previously applied escaping on option fetch not sufficient.
    Validate user input with explicit (albeit globally rendered) error messages.

Version bumped to 1.0.1
This commit is contained in:
Jarek Rozanski 2021-12-29 02:14:00 +01:00 committed by GitHub
commit db6109e748
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 187 additions and 105 deletions

View file

@ -4,7 +4,7 @@ Tags: web analytics, tracking, web traffic, analytics
Requires at least: 5.2
Tested up to: 5.8.2
Requires PHP: 7.2
Stable tag: trunk
Stable tag: 1.0.1
License: GPLv2
Easily add Wide Angle Analytics tracker script to your WordPress site. You can quickly configure your web analytics tracker script.

View file

@ -18,7 +18,7 @@ class WideAngleConfig {
function generateHeaderScript() {
$script = <<<EOD
<link href="{$this->trackerDomain}/script/{$this->siteId}.js" ref="prefetch"/>
<link href="https://{$this->trackerDomain}/script/{$this->siteId}.js" ref="prefetch"/>
EOD;
return $script;
}
@ -29,7 +29,7 @@ EOD;
$script = <<<EOD
<script async defer
src="{$this->trackerDomain}/script/{$this->siteId}.js"
src="https://{$this->trackerDomain}/script/{$this->siteId}.js"
data-waa-ignore-hash="{$this->ignoreHash}"
$includeParamsAttribute
$pathExlusionsAttribute></script>

View file

@ -1,9 +1,12 @@
<?php
include_once( $this->plugin->folder . '/types/WideAngleExclusion.php' );
include_once( $this->plugin->folder . '/types/WideAngleValidated.php' );
class WideAngleHelpers {
const WAA_SEPARTOR = "|:";
private const includeParamRequestKeyPattern = "/^waa_inc_params_(\d{1,2})$/";
private const includeParamRequestValuePattern = "/^[A-Za-z0-9_-]{1,128}$/";
private const excludePathRequestKeyPattern = "/^waa_exc_path_(\d{1,2})_type$/";
function normalizeBoolean($value) {
$trimmed = trim($value);
@ -18,8 +21,65 @@ class WideAngleHelpers {
}
}
function normalizeTrackerDomain($domain) {
return "https://" . parse_url($domain, PHP_URL_HOST);
function validateIgnoreHashFlag($name, $ignoreHash) {
if(filter_var($ignoreHash, FILTER_VALIDATE_BOOLEAN)) {
return WideAngleValidated::createValid($name, $ignoreHash, "true");
} else {
return WideAngleValidated::createValid($name, $ignoreHash, "false");
}
}
function validateSiteId($name, $siteId) {
if(preg_match("/^[a-zA-Z0-9]{10,24}$/", $siteId)) {
return WideAngleValidated::createValid($name, $siteId, strtoupper(trim($siteId)));
} else {
return WideAngleValidated::createInvalid($name, $siteId, "Site ID is expected to consist only letters and digits. It must be at least 10 character long and no longer than 24.");
}
}
function validateTrackerDomain($name, $domain) {
if(filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) {
return WideAngleValidated::createValid($name, $domain, $domain);
} else {
return WideAngleValidated::createInvalid($name, $domain, "The tracked domain must be a valid domain name.");
}
}
function validateIncludeParams($name, $request) {
$params = array();
foreach($request as $requestKey => $paramValue) {
if(preg_match(self::includeParamRequestKeyPattern, $requestKey)) {
if(preg_match(self::includeParamRequestValuePattern, $paramValue)) {
array_push($params, trim($paramValue));
} else {
return WideAngleValidated::createInvalid($name, $paramValue, "Name of parameter to include in request must consint of letters, numbers and can contain _ or - sign only.");
}
}
}
return WideAngleValidated::createValid($name, $params, implode(self::WAA_SEPARTOR, $params));
}
function validateExclusionPathsRequest($name, $request) {
$exclusions = array();
foreach($request as $key => $exclusionType) {
$idx = array();
if(preg_match(self::excludePathRequestKeyPattern, $key, $idx)) {
$valueKey = "waa_exc_path_".$idx[1]."_value";
$exclusionValue = trim($request[$valueKey]);
if($exclusionValue != null) {
if(filter_var($exclusionValue, FILTER_VALIDATE_REGEXP)) {
$typedExclusion = "[" . $exclusionType . "]" . $exclusionValue;
array_push($exclusions, $typedExclusion);
} else {
$typedExclusion = "[" . $exclusionType . "]" . filter_var($exclusionValue, FILTER_SANITIZE_SPECIAL_CHARS);
array_push($exclusions, $typedExclusion);
}
}
}
}
return WideAngleValidated::createValid($name, implode(self::WAA_SEPARTOR, $exclusions), null);
}
function parseIncludeParamsSetting($params) {
@ -30,23 +90,12 @@ class WideAngleHelpers {
}
}
function parseRequestIncludeParams($request) {
$pattern = "/^waa_inc_params_(\d{1,2})$/";
$params = array();
foreach($request as $key => $value) {
if(preg_match($pattern, $key)) {
array_push($params, trim($value));
}
}
return $params;
}
function parseExclusionSetting($path) {
$flatExclusions = $path;
$exclusions = preg_split("/\|\:/", $flatExclusions);
$exclusionPattern = '/^\[([A-Za-z0-9]{1,10})\](.*)$/';
$matches = array();
$parsedExclusions = array();
$exclusions = preg_split("/\|\:/", $flatExclusions);
$exclusionPattern = '/^\[([A-Za-z0-9]{1,10})\](.*)$/';
$matches = array();
$parsedExclusions = array();
foreach($exclusions as $exclusion) {
@ -57,22 +106,6 @@ class WideAngleHelpers {
return $parsedExclusions;
}
function parseRequestExclusionPaths($request) {
$pattern = "/^waa_exc_path_(\d{1,2})_type$/";
$exclusions = array();
foreach($request as $key => $exclusionType) {
$idx = array();
if(preg_match($pattern, $key, $idx)) {
$valueKey = "waa_exc_path_".$idx[1]."_value";
$exclusionValue = trim($request[$valueKey]);
if($exclusionValue != null && $exclusionValue != "") {
$typedExclusion = "[" . $exclusionType . "]" .$exclusionValue;
array_push($exclusions, $typedExclusion);
}
}
}
return $exclusions;
}
}
?>

View file

@ -0,0 +1,49 @@
<?php
class WideAngleValidated {
private $name;
private $value;
private $normalized;
private $error;
private $isValid;
private function __construct($name, $value, $normalized, $error, $isValid) {
$this->name = $name;
$this->value = $value;
$this->normalized = $normalized;
$this->error = $error;
$this->isValid = $isValid;
}
public static function createValid($name, $value, $normalized) {
return new self($name, $value, $normalized, null, true);
}
public static function createInvalid($name, $value, $error) {
return new self($name, $value, null, $error, false);
}
public function get_name() {
return $this->name;
}
public function get_value() {
if($this->normalized !== null) {
return $this->normalized;
}
return $this->value;
}
public function get_error() {
if(!$this->isValid) {
return $this->error;
}
return null;
}
public function is_valid() {
return $this->isValid;
}
}
?>

View file

@ -7,7 +7,7 @@ $parsedIncludeParams = $this->plugin->helpers->parseIncludeParamsSetting($this-
?>
<div class="wrap">
<h2>
<?php echo $this->plugin->displayName; ?> &raquo; <?php esc_html_e( 'Settings', 'wide-angle-analytics' ); ?>
<?php echo $this->plugin->displayName; ?> &raquo; <?php esc_html( 'Settings', 'wide-angle-analytics' ); ?>
</h2>
<div>
@ -27,30 +27,39 @@ $parsedIncludeParams = $this->plugin->helpers->parseIncludeParamsSetting($this-
<?php
if ( isset( $this->message ) ) {
?>
<div class="updated fade"><p><?php echo $this->message; ?></p></div>
<div class="updated fade"><p><?php echo esc_html($this->message); ?></p></div>
<?php
}
if ( isset( $this->errorMessage ) ) {
if(is_array($this->errorMessage)) {
foreach($this->errorMessage as $error) {
?>
<div class="error fade"><p><?php echo esc_html($error); ?></p></div>
<?php
}
}
else {
?>
<div class="error fade"><p><?php echo $this->errorMessage; ?></p></div>
<div class="error fade"><p><?php echo esc_html($this->errorMessage); ?></p></div>
<?php
}
}
?>
<div>
<form action="options-general.php?page=<?php echo $this->plugin->name; ?>" method="post">
<form action="options-general.php?page=<?php echo esc_attr($this->plugin->name); ?>" method="post">
<table class="form-table" role="presentation">
<tbody>
<tr>
<th scope="row"><label>Site ID</label></th>
<td>
<input id="waa_site_id" type="text" name="waa_site_id" pattern="[A-Z0-9]{10,24}" class="regular-text" value="<?php echo $siteId ?>"/>
<input id="waa_site_id" type="text" name="waa_site_id" class="regular-text" value="<?php echo esc_attr($siteId); ?>"/>
<p class="description" id="tagline-description">A Site ID. You will find it in the Site Settings, in Wide Angle Analytics Dashboard.</p>
</td>
</tr>
<tr>
<th scope="row"><label>Tracker Domain</label></th>
<td>
<input id="waa_tracker_domain" type="url" name="waa_tracker_domain" class="regular-text code" value="<?php echo $trackerDomain ?>"/>
<input id="waa_tracker_domain" type="text" name="waa_tracker_domain" class="regular-text code" value="<?php echo esc_attr($trackerDomain); ?>"/>
<p class="description" id="tagline-description">A domain you selected for your tracker. You can check current domain in the Site Settings, in the Wide Angle Analytics. If you haven't set custom domain for your site, there is no need to change this field.</p>
</td>
</tr>
@ -62,20 +71,18 @@ $parsedIncludeParams = $this->plugin->helpers->parseIncludeParamsSetting($this-
for($i = 0; $i < sizeof($parsedExclusions); $i++) {
$exclusion = $parsedExclusions[$i];
?>
<div data-waa-exc-path="<?php echo $i; ?>" style="display: flex; flex-direction: row; margin-bottom: 0.3rem">
<select name="waa_exc_path_<?php echo $i; ?>_type" id="waa_exc_path_<?php echo $i; ?>_type">
<div data-waa-exc-path="<?php echo esc_attr($i); ?>" style="display: flex; flex-direction: row; margin-bottom: 0.3rem">
<select name="waa_exc_path_<?php echo esc_attr($i); ?>_type" id="waa_exc_path_<?php echo esc_attr($i); ?>_type">
<?php
foreach($this->plugin->exclusionTypes as $id => $label) {
?>
<option value="<?php echo $id ?>"<?php if($exclusion->get_type() == $id) echo ' selected'; ?>><?php echo $label ?></option>
<option value="<?php echo esc_attr($id); ?>"<?php if($exclusion->get_type() == $id) echo ' selected'; ?>><?php echo esc_html($label); ?></option>
<?php
}
?>
<option value="end"<?php if($exclusion->get_type() == "end") echo ' selected'; ?>>Ends with</option>
<option value="regex"<?php if($exclusion->get_type() == "regex") echo ' selected'; ?>>RegEx</option>
</select>
<input type="text" name="waa_exc_path_<?php echo $i; ?>_value" value="<?php echo $exclusion->get_value(); ?>"/>
<button data-waa-action="remove_exclusion" data-waa-exc-path="<?php echo $i; ?>" class="button button-secondary">Remove</button>
<input type="text" name="waa_exc_path_<?php echo esc_attr($i); ?>_value" value="<?php echo esc_attr($exclusion->get_value()); ?>"/>
<button data-waa-action="remove_exclusion" data-waa-exc-path="<?php echo esc_attr($i); ?>" class="button button-secondary">Remove</button>
</div>
<?php
}
@ -93,9 +100,9 @@ $parsedIncludeParams = $this->plugin->helpers->parseIncludeParamsSetting($this-
for($i = 0; $i < sizeof($parsedIncludeParams); $i++) {
$param = $parsedIncludeParams[$i];
?>
<div data-waa-inc-params="<?php echo $i; ?>" style="display: flex; flex-direction: row; margin-bottom: 0.3rem">
<input type="text" name="waa_inc_params_<?php echo $i; ?>" value="<?php echo $param; ?>" pattern="[A-Za-z0-9_-]{1,128}"/>
<button data-waa-action="remove_param" data-waa-inc-params="<?php echo $i; ?>" class="button button-secondary">Remove</button>
<div data-waa-inc-params="<?php echo esc_attr($i); ?>" style="display: flex; flex-direction: row; margin-bottom: 0.3rem">
<input type="text" name="waa_inc_params_<?php echo esc_attr($i); ?>" value="<?php echo esc_attr($param); ?>"/>
<button data-waa-action="remove_param" data-waa-inc-params="<?php echo esc_attr($i); ?>" class="button button-secondary">Remove</button>
</div>
<?php
}
@ -129,11 +136,11 @@ $parsedIncludeParams = $this->plugin->helpers->parseIncludeParamsSetting($this-
<pre style="padding: 1rem; border: 1px solid;">
&lt;head&gt;
&lt;!-- .. --&gt;
<b><?php echo $this->settings[self::WAA_CONF_GENERATED_HEADER_SCRIPT]; ?></b>
<b><?php echo esc_html($this->settings[self::WAA_CONF_GENERATED_HEADER_SCRIPT]); ?></b>
&lt;/head&gt;
&lt;!-- .. --&gt;
<b><?php echo $this->settings[self::WAA_CONF_GENERATED_FOOTER_SCRIPT]; ?></b>
<b><?php echo esc_html($this->settings[self::WAA_CONF_GENERATED_FOOTER_SCRIPT]); ?></b>
</pre>
</code>
</div>

View file

@ -5,7 +5,7 @@
Description: Easily enable and configure Wide Angle Analytics on your Wordpress site
Author: Wide Angle Analytics by Input Objects GmbH
Author URI: https://wideangle.co
Version: 1.0.0
Version: 1.0.1
Requires at least: 5.2
Requires PHP: 7.2
License: GPL v2
@ -15,7 +15,6 @@
<?php
class WideAngleAnalytics {
const WAA_SEPARTOR = "|:";
const WAA_CONF_SITE_ID = "waa_site_id";
const WAA_CONF_TRACKER_DOMAIN = "waa_tracker_domain";
const WAA_CONF_EXC_PATHS = "waa_exc_path";
@ -112,32 +111,45 @@ class WideAngleAnalytics {
} elseif ( ! wp_verify_nonce( $_REQUEST[ $this->plugin->name . '_nonce' ], $this->plugin->name ) ) {
$this->errorMessage = __( 'Invalid nonce specified. Settings NOT saved.', $this->plugin->name );
} else {
$waaSiteId = $_REQUEST['waa_site_id'];
$waaTrackerDomain = $this->plugin->helpers->normalizeTrackerDomain(trim($_REQUEST['waa_tracker_domain']));
$waaIgnoreHash = $this->plugin->helpers->normalizeBoolean($_REQUEST['waa_ignore_hash']);
$waaExclusionPaths = implode(self::WAA_SEPARTOR, $this->plugin->helpers->parseRequestExclusionPaths($_REQUEST));
$waaIncParams = implode(self::WAA_SEPARTOR, $this->plugin->helpers->parseRequestIncludeParams($_REQUEST));
$waaSiteId = $this->plugin->helpers->validateSiteId(self::WAA_CONF_SITE_ID, $_REQUEST['waa_site_id']);
$waaTrackerDomain = $this->plugin->helpers->validateTrackerDomain(self::WAA_CONF_TRACKER_DOMAIN, $_REQUEST['waa_tracker_domain']);
$waaIgnoreHash = $this->plugin->helpers->validateIgnoreHashFlag(self::WAA_CONF_IGNORE_HASH, $_REQUEST['waa_ignore_hash']);
$waaIncParams = $this->plugin->helpers->validateIncludeParams(self::WAA_CONF_INC_PARAMS, $_REQUEST);
$waaExclusionPaths = $this->plugin->helpers->validateExclusionPathsRequest(self::WAA_CONF_EXC_PATHS, $_REQUEST);
include_once( $this->plugin->folder . '/types/WideAngleConfig.php' );
$config = new WideAngleConfig($waaSiteId, $waaTrackerDomain, $waaIgnoreHash, $waaExclusionPaths, $waaIncParams);
include_once( $this->plugin->folder . '/types/WideAngleConfig.php');
$merged = array($waaSiteId, $waaTrackerDomain, $waaIgnoreHash, $waaIncParams, $waaExclusionPaths);
$errors = array();
foreach($merged as $validated) {
if(!$validated->is_valid()) {
array_push($errors, $validated->get_error());
}
}
update_option(self::WAA_CONF_GENERATED_FOOTER_SCRIPT, $config->generateFooterScript());
update_option(self::WAA_CONF_GENERATED_HEADER_SCRIPT, $config->generateHeaderScript());
update_option(self::WAA_CONF_SITE_ID, $waaSiteId );
update_option(self::WAA_CONF_TRACKER_DOMAIN, $waaTrackerDomain);
update_option(self::WAA_CONF_IGNORE_HASH, $waaIgnoreHash );
update_option(self::WAA_CONF_EXC_PATHS, $waaExclusionPaths );
update_option(self::WAA_CONF_INC_PARAMS, $waaIncParams);
if(count($errors) === 0) {
$config = new WideAngleConfig($waaSiteId->get_value(), $waaTrackerDomain->get_value(), $waaIgnoreHash->get_value(), $waaExclusionPaths->get_value(), $waaIncParams->get_value());
update_option(self::WAA_CONF_GENERATED_FOOTER_SCRIPT, $config->generateFooterScript());
update_option(self::WAA_CONF_GENERATED_HEADER_SCRIPT, $config->generateHeaderScript());
update_option(self::WAA_CONF_SITE_ID, $waaSiteId->get_value());
update_option(self::WAA_CONF_TRACKER_DOMAIN, $waaTrackerDomain->get_value());
update_option(self::WAA_CONF_IGNORE_HASH, $waaIgnoreHash->get_value());
update_option(self::WAA_CONF_EXC_PATHS, $waaExclusionPaths->get_value());
update_option(self::WAA_CONF_INC_PARAMS, $waaIncParams->get_value());
$this->message = __('Settings updated', $this->plugin->name);
} else {
$this->errorMessage = $errors;
}
}
}
$this->settings = array(
self::WAA_CONF_SITE_ID => esc_html(get_option( self::WAA_CONF_SITE_ID)),
self::WAA_CONF_EXC_PATHS => esc_html(get_option( self::WAA_CONF_EXC_PATHS)),
self::WAA_CONF_INC_PARAMS => esc_html(get_option( self::WAA_CONF_INC_PARAMS)),
self::WAA_CONF_TRACKER_DOMAIN => esc_html(get_option( self::WAA_CONF_TRACKER_DOMAIN)),
self::WAA_CONF_IGNORE_HASH => esc_html(get_option( self::WAA_CONF_IGNORE_HASH)),
self::WAA_CONF_GENERATED_HEADER_SCRIPT => esc_html(get_option( self::WAA_CONF_GENERATED_HEADER_SCRIPT )),
self::WAA_CONF_GENERATED_FOOTER_SCRIPT => esc_html(get_option( self::WAA_CONF_GENERATED_FOOTER_SCRIPT )),
self::WAA_CONF_SITE_ID => get_option( self::WAA_CONF_SITE_ID),
self::WAA_CONF_EXC_PATHS => get_option( self::WAA_CONF_EXC_PATHS),
self::WAA_CONF_INC_PARAMS => get_option( self::WAA_CONF_INC_PARAMS),
self::WAA_CONF_TRACKER_DOMAIN => get_option( self::WAA_CONF_TRACKER_DOMAIN),
self::WAA_CONF_IGNORE_HASH => get_option( self::WAA_CONF_IGNORE_HASH),
self::WAA_CONF_GENERATED_HEADER_SCRIPT => get_option( self::WAA_CONF_GENERATED_HEADER_SCRIPT),
self::WAA_CONF_GENERATED_FOOTER_SCRIPT => get_option( self::WAA_CONF_GENERATED_FOOTER_SCRIPT),
);
include_once( $this->plugin->folder . '/views/admin_settings.php' );
}
@ -155,32 +167,13 @@ class WideAngleAnalytics {
* - waa_footer_script
*/
function registerPluginSettings() {
register_setting( $this->plugin->name, self::WAA_CONF_SITE_ID, array(
'sanitize_callback' => 'trim',
) );
register_setting( $this->plugin->name, self::WAA_CONF_EXC_PATHS, array(
'sanitize_callback' => 'trim',
) );
register_setting( $this->plugin->name, self::WAA_CONF_INC_PARAMS, array(
'sanitize_callback' =>'trim',
'default' => ''
) );
register_setting( $this->plugin->name, self::WAA_CONF_TRACKER_DOMAIN, array(
'sanitize_callback' => array(&$this->plugin->helpers, 'normalizeTrackerDomain'),
'default' => 'https://stats.wideangle.co'
) );
register_setting( $this->plugin->name, self::WAA_CONF_IGNORE_HASH, array(
'sanitize_callback' => array(&$this->plugin->helpers, 'normalizeBoolean'),
'default' => 'false'
) );
register_setting( $this->plugin->name, self::WAA_CONF_GENERATED_HEADER_SCRIPT, array(
'sanitize_callback' => 'trim',
'default' => ''
) );
register_setting( $this->plugin->name, self::WAA_CONF_GENERATED_FOOTER_SCRIPT, array(
'sanitize_callback' => 'trim',
'default' => ''
) );
register_setting($this->plugin->name, self::WAA_CONF_SITE_ID);
register_setting($this->plugin->name, self::WAA_CONF_EXC_PATHS);
register_setting($this->plugin->name, self::WAA_CONF_INC_PARAMS);
register_setting($this->plugin->name, self::WAA_CONF_TRACKER_DOMAIN, array('default' => 'stats.wideangle.co'));
register_setting($this->plugin->name, self::WAA_CONF_IGNORE_HASH, array('default' => 'false'));
register_setting($this->plugin->name, self::WAA_CONF_GENERATED_HEADER_SCRIPT);
register_setting($this->plugin->name, self::WAA_CONF_GENERATED_FOOTER_SCRIPT);
}
}