Sneak Preview: Reports 2208.15

A new reports interface is coming in the 2208.15 release based on preliminary work with the React API library. In addition to the simplified interface , the new reports provide more controls over the data table. Some new features include a date selector that auto-refreshes the data table, ability to filter rows, select which columns to display, or change the display density. The export CSV now honors the column display and filter settings. There is also a new “direct to print” option.

Here is the informal review of the new reports.

Dev Standards

git Branching Model

Master / Main

The production branch. This branch should always point to production releases – the code released to the public or in use on production systems.

Staging

The release candidate branch. Prerelease plugins or the plugins/components that are published to dashbeta for the SaaS offering.

Test / Develop

The branch being used for testing integration of ongoing development branches.

Version Identifiers

Starting in August 2022 all product versions will follow a YYMMDD.xx standard where YYMMDD is based on the development start date for primary product releases (Store Locator Plus® for WordPress, MySLP Dashboard,etc.) For related product releases (Power, Front-End, etc.) the date should try to match the main product/service it is related to but this is not a strict rule – rather a suggest to help users easily identify related releases.

The YYMMDD should be 2 digits each and xx should always start at 01 for the first revision. For example 220815.01 is the first iteration for an app that started development on 220815.

Code Formatting / Linting

JavaScript

The preferred linting library is ESLint due to the ongoing React support.

0 comments on “Analyzing AWS Scaling Group Traffic”

Analyzing AWS Scaling Group Traffic

With the re-listing of the Store Locator Plus® WordPress plugin in the WordPress plugin directory, there has been a notable increase in outward scaling of the Store Locator Plus® application cluster. Key Store Locator Plus® websites and services run on horizontally scalable cluster built on AWS Scaling Groups, AWS Load Balancer, and EC2 instances. Every night starting around midnight EST the scaling group adds one node per hour until 3AM EST at which point they start scaling back.

The AWS cluster is handling the load well, but we want to investigate in case there is something else going on. Scaling can be caused by a number of issues including network attacks, application misconfiguration, coding errors, routine bot traffic, or routine customer interaction patterns. It is best to get insight into the issue and know for certain the root cause.

This article walks through a real-time analysis of the events and traffic patterns that are triggering the scaling.

Background

The AWS Scaling Group that manages the Store Locator Plus® cluster is configured to monitor average CPU usage over time and add nodes to the cluster when the servers start to climb above 80% utilization. This is often an early indicator of impending server overload and a good baseline metric on which to base scaling events.

These events are triggered almost nightly at the same time. This typically indicates a routine scheduled process such as a site crawler via a bot (aka spider) or a scheduled routine running on a customer’s website.

The latest scaling event started at 11:55PM EST last night, so we’ll start there.

First Stop : AWS Dashboard

We want to verify our timestamps with more specificity as the email notifications are not necessarily precise.

AWS Scaling Groups

We’ll look at the AWS Scaling Groups first. We have monitoring enabled and can get a quick overview of the group activity.

We can see the instance count jump from our baseline of 2 nodes to 3 nodes almost exactly at 03:56 UTC. Remember AWS mostly notes times in UTC, which puts us at 11:55PM EST. The traffic drops back to baseline at 13:34 which is around 9:34AM EST.

We can also look at our aggregate EC2 instance metrics for all instances that are part of this scaling group. We can see the bulk of CPU usage starts to fire up around 03:48 UTC but really kicks in an hour later around 4:48 UTC before regularly grinding away from 5:48 UTC through 7:48 UTC. The pattern looks a lot like an external process, possibly a bot.

Inbound network requests on the EC2 instances reflects the same with a single 502Mbps spike starting at 05:03 UTC.

Inspecting EC2 Logs

While our load balancer is logging access to an S3 bucket, it is often difficult to locate and parse the logs with the volume of requests being pushed to the bucket every day. While there are log parsing and reporting services out there, there is a faster way to get insight into requests — looking at the local disk logs on a running EC2 instance in the cluster.

If you are lucky one of the current nodes will be running as part of the cluster that was online for the entire event. Given the span of time and amount of traffic the single node will provide a reasonable cross-section of requests, starting with logging 50% of the requests as part of the 2-node cluster to start with. We can assume it was logging at least 33% of the requests during the initial spikes as the cluster expands to 3 nodes.

Since our nodes are all running some form of web application, we want to check our web server (nginx) log files in /var/log/nginx. Keep in mind the EC2 servers are configured to be in the data center’s time zone, so the log files for our US-East-1 zone servers will be in EST. We want to look between 11:55PM EST and 9:34AM EST with a focus on the 1:48 – 6:48AM EST entries.

Bad Actor In Log Files

The nginx access log have the fingerprints of a brute force attack against the server around 01:28 EST (05:30 UTC). Many of the URLs here are known weak points in apps that may be running on a server (not ours though). These can be blocked by a Web Application Firewall update on AWS, a service that provides edge-of-cloud protection and can keep the network requests from reaching our cluster in the first place.

And a similar attack on the demo site

0 comments on “Debugging: Excess Option Lookup”

Debugging: Excess Option Lookup

An excess number of option value lookups was discovered while testing Store Locator Plus® 5.13.8 where the option/label_directions was searched multiple times for a single map render. To reduce load on servers this should only happen once, at the start of the initial map rendering.

This is not a blatant “it did something wrong” or “didn’t do something it should” type of bug. This is a performance and resource usage bug and may require some new architcture.

The manifestation:

Debugging path… investigation

Looking for label_directions in the code…

The initial search through the plugins reveals places the settings are created, set, stored, and managed but not the “JavaScript hook” being fired via /wp-json (the REST API for WordPress). So we need to look deeper.

Looking for options/ in the code…

Let’s try the start of the REST API path after the generic /wp-json/store-locator-plus/v2/ part…

We can ignore the assets/ directory… but what else is in here…

Ahhh… the slp_core.js call to slplus.rest_url + ‘options/’ + attribute looks sus…a

Solution (planned for SLP 5.14)

There was a problem in slp_core.js with value testing.

Turns out an empty value causes multiple REST calls. The following is an invalid test to see if a property exists, which is what we want here. Previously it was checking the value was SET and had a “non-falsy” value. In JavaScript lots of things are “kind of true” or “kind of false” (aka Falsy), for example the empty string “” is FALSE. That is not what we want here.

In slp_core.js we ant to replace the if (!<var>) with if (! var.hasOwnProperty()) …

Cause

The call to /v2/options/label_directions was firing once for every location because the default value for the directions label is empty (“”). This was evaluating to false, which forced the SLP JavaScript to query the server to get the value, which set it to “”.

For each location that was rendered the setting was checked… “is the label for directions set?” or more accurately “Is the label for directions set and NOT empty?”. Every time it came back saying “no, it is NOT set” or rather “It IS empty” … so the code would then go ask the REST API server for the label value.

Rinse and repeat.

0 comments on “Debugging: Shortcode Attributes Not Processing”

Debugging: Shortcode Attributes Not Processing

Adding attributes to the [slplus] shortcode will override default settings for the Store Locator Plus® plugin on a per-map basis.

We are investigating a shortcode like this:

[SLPLUS center_map_at=”Laverton Victoria ” append_to_search=”Australia” initial_radius=”1000″]

Some of these properties are not functioning as expected in the upcoming SLP 5.13.X release.

center_map_at Test

Note: This feature requires the Experience add on to be active.

Running this test on the Docker localhost, adding a shortcode with ONLY the center_map_at setting.

[slplus center_map_at=”Westford MA 01886″]

How it behaves in 5.12.X…

And in 5.13.8… it is working…

initial_radius Test

Adding initial_radius to our testing… requires some setup.

First change the SLP settings under Results to “Initial Search Radius 10” and leave Center Map At at “Charleston SC”.

The results look as expected… one location within 10 miles of Charleston…

And with center_map_at=”Mt Pleasant SC 29464″ initial_radius=”500″ … also looks as expected…

And with the append_to_search=”…” addition…

Now with the full shortcode…

[slplus center_map_at=”Mt Pleasant SC 29464″ initial_radius=”500″ append_to_search=”, United States”]

And run find locations…

The , United States is appended and appears to be working normally.

And now activating Power 5.13 and Premier 5.12 alongside Experience 5.12 and SLP 5.13.8…

It all works same as above…

Apparently this bug was fixed along the way.

0 comments on “Debugging: Location Category Not Filtering”

Debugging: Location Category Not Filtering

The User Action : Filtering Locations With Category on the Testing : Location Categories article is not limiting output to only the specified category.

Tech Overview

The front end sends a request back to WordPress via AJAX to request a list of locations. Part of that request encodes all of the form field entries, including the category drop down selection, into a query-encoded (key/value pairs with an & delimiter) string that is send in the formData property to the backend.

The category ID is not being parsed and filtered properly.

Looking At The Data Request

Via browser developer tools and the network I/O inspector, XHR filter…

HTTP Method: POST
URL: /wp-admin/admin-ajax.php
Request Payload…

You can see the formdata property in the POST body along with the encoded form data including the cat=5 entry. This is where the requests tells the back end to limit results to those that have WordPress category ID #5 attached to the location.

Diving Into The Code

On the back end the AJAX is routed through the Store Locator Plus® base plugin via the standard WordPress hooks and filters. These are setup in the SLP_AJAX class via this add_ajax_hooks method:

    /**
     * Add our AJAX hooks.
     *
     * @uses \SLP_AJAX::slp_delete_location for the AJAX 'slp_delete_location' action
     */
    public function add_ajax_hooks() {

    	// -- priv (WP logged in) and no priv (guest) AJAX calls
        add_action( 'wp_ajax_csl_ajax_search', array( $this, 'csl_ajax_search' ) );
        add_action( 'wp_ajax_nopriv_csl_ajax_search', array( $this, 'csl_ajax_search' ) );

        add_action( 'wp_ajax_csl_ajax_onload', array( $this, 'csl_ajax_onload' ) );
        add_action( 'wp_ajax_nopriv_csl_ajax_onload', array( $this, 'csl_ajax_onload' ) );

	    // -- priv only AJAX calls (WP logged in)
	    add_action( 'wp_ajax_slp_delete_location', array( $this, 'slp_delete_location' ) );
        add_action( 'wp_ajax_slp_change_option', array( $this, 'slp_change_option' ) );
    }

Where the csl_ajax_search eventually finds its way to the find_locations method.

Debugging the find_locations method it looks like the formdata variable coming in via the superglobal $_REQUEST variable is not being set properly.

Digging Into The Malformed Variables

Looking at the call stack the form data is mishandled causing the & in the query string to be encoded as &amp, this causes wp_parse_args() to set the property name incorrect as you can see in the debugging screenshot below.

Turns out the wp_kses_post() is too aggressive with the data munging and converts & to &amp; which then throws wp_parse_args() for a loop.

Instead we need to use esc_url_raw() to leave most URL entities intact.

0 comments on “Debugging : Location Category Dropdown Not Rendering”

Debugging : Location Category Dropdown Not Rendering

Read the Testing : Location Categories article for background on this feature.

The setting to display the category selector on the front end stopped working. The Power add on is active, but with SLP 5.13.X prerelease the drop down no longer appears.

This is very likely due to escaping of output that is overly aggressive on what it filters out on the front end.

Tech Overview

The base plugin renders the map , search form and results on the front end using shortcodes. The [slplus] shortcode renders the entire UX based on a combination of HTML and secondary shortcodes. These secondary shortcodes are parsed by the SLP base plugin to do things like “put the search form here” or “put the results here”. Within each of those custom shortcodes is another set of shortcodes, for example for the search form “put the address input box here” or “put the radius selector here”.

One of those additional shortcodes is provided by the Power add on to “put the category selector drop down here”.

That is not being processed.

Code Path

SLP_Power_UI.php

This attaches the method for rendering additional SLP search form features. This method is getting called.

public function add_hooks_and_filters() 
...
add_filter( 'shortcode_slp_searchelement'   , array( $this , 'process_slp_search_element_shortcode' )   );

This method is not getting called when the [slplus] shortcode is rendered…

public function process_slp_search_element_shortcode( $attributes ) {

The shortcode_slp_searchelement filter is only fired from this piece of code:

SLP_UI.php

Provided by the base plugin, this handles most of the front end initialization and execution.

This line of code is being called to apply filters…

public function create_SearchElement( $attributes, $content = null ) {
$attributes = apply_filters( 'shortcode_slp_searchelement', $attributes );

Checking Call Order

⚠️ Evaluating the call execution order, the above apply_filters is being called BEFORE the power add on has run the SLP_POWER_UI add_hooks_and_filters() , noted above, to hook onto that filter call.

Power Add Hooks & Filters

Here is the call stack when Power add_hooks_and_filters is run…

The call to SLP_BaseClass_Addon->createobject_UserInterface() is fired during the execution of the ‘wp_enqueue_scripts’ action hook as defined in base_class.addon.php in the slp_init() method…

// User Interface?
//
if ( ! empty( $this->userinterface_class_name ) ) {
add_action( 'wp_enqueue_scripts' , array( $this , 'createobject_UserInterface' ) );
}

Base Plugin Create Search Element

Here is the call stack , CALLED FIRST, for creating the search element:

slp_render_shortcode() is called to start this process per the WordPress add_shortcode() method in SLP_UI :: initialize().

That means do_shortcode() is being called very early —

This is due to a side effect of all the fuckery WordPress Core has been doing to deal with the block mode editor. In order to support new block themes (like Twenty Twenty Two) WP_Block->render() calls render_block_core_post_content() super early. Before any script enqueue.

WP Core: render_block_core_post_content() calls the “the_content” filter way earlier than non-block themes:

$content = apply_filters( 'the_content', str_replace( ']]>', ']]&gt;', $content ) );

Remedies

Lance.bio : WP Hooks & Filters Reference

Attach creatobject_UserInterface Calls To New Hook

With the block theme, firing that method via the ‘wp_enqueue_scripts’ method is too late for WP Block Themes.

There are multiple methods that could be used to hook this, but which one will be BEFORE the WP Block rendering calls the ‘the_content’ filter AND still work for older themes?

Option 1 : Hook to ‘the_content’ filter with an earlier precedence on the weight

Replace

// User Interface? 
// 
if ( ! empty( $this->userinterface_class_name ) ) {  add_action( 'wp_enqueue_scripts' , array( $this , 'createobject_UserInterface' ) ); }

With the call order weight parameter added (the default WP action weight is 10 if not specified)

// User Interface? 
// 
if ( ! empty( $this->userinterface_class_name ) ) {  add_action( 'the_content' , array( $this , 'createobject_UserInterface' ), 5 ); }
Concerns

The main concern here is that a ‘the_content’ script does not get called everywhere a ‘wp_enqueue_scripts’ hook gets called. The question is whether AJAX and REST calls run through here.

Results

Did not work, broke a lot of features and setup in the plugin.

Option 2 : Attach to an earlier hook…

Candidates…

  • block_parser_class — in function parse_blocks() probably not called on non-block themes.
  • template_include — before template_canvas.php which appears to be before template_canvas where the first line is get_the_block_template(); the assumption is template_canvas is only called for block based themes.
Concerns

‘template_include’ hook may get called too often, but it is a classic filter that has been around for a while and should work for both block and legacy templates.

Results

Seems to be working, needs more testing.

0 comments on “Testing: Location Categories”

Testing: Location Categories

Location categories provides a way to place categories into groups that can be filtered via the administrative tools and on the front end user experience.

Managing categories is provided by the Power add on.

Technical Implementation

The underlying technology uses the built-in WordPress category management system. Some of the core functionality of adding, removing, and assigning categories is managed by WordPress and thus requires additional testing whenever WordPress posts a new release.

The Power add on extend this default functionality to provide custom administrative and user interactions with location categories.

Testing Environment (July 2022)

WordPress 6+
Store Locator Plus 5.13+
Power Add On 5.11+

Related Use Cases

The use cases are based on the WordPress plugins. Most of the processes described within apply to the MySLP SaaS implementation as well.

Assumptions

WordPress environment is setup and configured including activation of the following plugins

  • Store Locator Plus®
  • SLP Power add on

In addition map service (Google API keys) should be in place and some locations should be added to the system.

A page has been created with the [slplus] shortcode present and the map is rendering properly.

Users are logged in as a WordPress administrator (the process will be slightly different for SaaS users).

Adding Categories To Locations

Objective

Create a location category and attach it to an existing location.

User Action: Add A Location Category

  1. Click on Store Locator Plus® | Categories in the WordPress sidebar menu.
  2. In the left “Add New Category Form”…
    1. Type a category name. (Restaurant)
    2. Click Add New Category
Result

The new category should appear on the category list on the page.

Category list after adding the restaurant category.

User Action: Attach Category To Location

  1. Click on Store Locator Plus® | Locations in the WordPress sidebar menu.
  2. Click the Edit action icon on the first location.
  3. Check off the box next to “Restaurants” in the Power | Categories section of the form.
  4. Click Save.
Result

The category is assigned to the location and appears on the location table.

The location with an assigned restaurant category. This view is showing fewer columns as the Screen Options was updated to removed unused columns from the table view.

User Action: Show Category Select On Front End

  1. Click on Store Locator Plus® | Settings in the WordPress sidebar menu.
  2. Change the Category Selector setting to “Single Drop Down”.
  3. Click Save
  4. On the front end, go to the page that has the [slplus] shortcode.
Result

The search form for the map should show a category drop down.

The front end page with the [slplus] shortcode, showing the category selection filter.

User Action : Filtering Locations With Category

  1. On the front end, go to the page that has the [slplus] shortcode.
  2. Enter an address, choose a category.
  3. Click “Find Locations”.
Result

Only the locations that match the search criteria, including the category filter, should be shown.

0 comments on “Debugging: Find Locations / Initial Load not working with Power add on enabled”

Debugging: Find Locations / Initial Load not working with Power add on enabled

Initial post to AJAX
Initial response

This looks like a problem with the AJAX processor on the backend. Need to look into ajax_onload.

csl_ajax_onload refs

The problem was with the Power add on trying to validate SLP location IDs when updating map markers. The intent is to NOT manipulate a map marker with an invalid ID as it can cause more problems downstream.

Resolution

DO NOT check_admin_referer() for the AJAX calls. The easiest way to check that , since we do not have an easily-accessible reference variable for “hey, I’m running an AJAX query now” is to simply ensure the nonce we want to validate even exists in the first place.

0 comments on “Debugging: Add locations w/ Power “Uncaught SyntaxError: redeclaration of const wp_data””

Debugging: Add locations w/ Power “Uncaught SyntaxError: redeclaration of const wp_data”

SLP 5.13.5 prerelease + Power 5.11 has a problem found during testing… the admin add locations breaks (probably a lot of other things as well).

The browser console:

This is related to moving to wp_add_inline_script() vs. the wp_localize_script() based on findings that indicate wp_localize_script() may a not work with block themes and it is really intended for i18n/l10n.

Further investigation finds…

store-locator-le/include/module/admin_tabs/SLP_BaseClass_Admin.php
enqueue_admin_javascript()

This is called for each add-on that needs it…


Power needs it.
handle: slppower_manage_locations
data stack:
{"ajax_nonce":"50164ddb77"}

SLP (base plugin) needs it.
handle: slp_manage_locations
data stack:
{"ajax_nonce":"50164ddb77"}

However, a code search shows that ONLY the base plugin admin JavaScript is using/referencing wp_data…

Why is Power setting up an enqueue of this file?

Code Analysis of enqueue_admin_javascript()…

This is always true:

if ( ! empty( $tab_js_settings ) ) {

Because this is always set further up in the code…

// All our JS will now have an AJAX_NONCE setting.
$tab_js_settings = array( 'ajax_nonce' => wp_create_nonce('slp_ajax'), );

They key player here “ajax_nonce” set above is ONLY ever used here… SLP base plugin admin.js… which is only called when doing a change_option. That is fired from both the base plugin and Power add on, HOWEVER… they both reference the SLP_ADMIN.change_option() method in JavaScript.

ajax_nonce usage in JavaScript is only in SLP base plugin admin.js (SLP_ADMIN) change_option() method attached to the options property (SLP_ADMIN.options.change_option).
All calls to the SLP_ADMIN.options.change_option() call

Resolution

Do not set the baseline tab_js_settings in SLP_BaseClass_Admin.php

This will stop the default (only) ajax_nonce PHP array entry from being set, effectively short-circuiting the enqueue of the script from the Power add-on.

While this fixes the issue short-term and stops overloading the tab_js_settings, if Power (or any other add on) finds it necessary to add some custom entries to the tab_js_settings array, it will break again. This is a fragile patch.

The change…

Add the handle to the JavaScript variable definition

The current setup is to hard-code the JS environment variable to “wp_data” for ALL add-ons. This will make them “fight” per the message above.

Instead of blindly referencing a generic hard-coded “wp_data” variable, let’s change that to be based on the add-on name.

For our use cases above that would do this.

For SLP base plugin wp_data => slp_data

For Power add on wp_data => slppower_data

The change…

0 comments on “WordPress Escaping and Sanitizing”

WordPress Escaping and Sanitizing

Escaping or Sanitizing General Rules

Arrays

Instead of manually detecting arrays then using array_map(), as noted below, instead use map_deep( $arrayOrScalarVar, ‘<method>’.

For example:

$cleanVar = map_deep( $_REQUEST['id'], 'santize_key');

For wp_kses_post use wp_kses_post_deep(). See below for more info.

When processing an array of values use array_map( ‘<method>’ , $arrayVar).

For example array_map( ‘sanitize_key’ , (array) $_REQUEST[‘id’] ).

Unslash Warnings

There is no need to call wp_unslash() before sanitizing inputs.

Related Resources


Securing Input (sanitizing)

sanitize_email()

Strips out all characters that are not allowable in an email.

sanitize_file_name()

sanitize_hex_color()

sanitize_hex_color_no_hash()

sanitize_html_class()

sanitize_key()

Lowercase alphanumeric characters, dashes, and underscores are allowed.

sanitize_meta()

This function applies filters that can be hooked to perform specific sanitization procedures for the particular metadata type and key. Does not sanitize anything on its own. Custom filters must be hooked in to do the work. The filter hook tag has the form “sanitize_{$meta_type}_meta_{$meta_key}“.

sanitize_mime_type()

sanitize_option()

Only sanitizes specific options known to WordPress (primarily for internal use).
After the value has been handled by the functions in the switch statement, it will be passed through a sanitize_option_$option filter.

sanitize_sql_orderby()

💙 sanitize_text_field()

From the WordPress docs…

  • Checks for invalid UTF-8,
  • Converts single < characters to entities
  • Strips all tags
  • Removes line breaks, tabs, and extra whitespace
  • Strips octets

Strips All Tags…

That means this CANNOT be used for anything that processes HTML elements.

The Code

/**
 * Sanitizes a string from user input or from the database.
 *
 * - Checks for invalid UTF-8,
 * - Converts single `<` characters to entities
 * - Strips all tags
 * - Removes line breaks, tabs, and extra whitespace
 * - Strips octets
 *
 * @param string $str String to sanitize.
 * @return string Sanitized string.
 */
function sanitize_text_field( $str ) {
	$filtered = _sanitize_text_fields( $str, false );

	/**
	 * Filters a sanitized text field string.
	 *
	 * @since 2.9.0
	 *
	 * @param string $filtered The sanitized string.
	 * @param string $str      The string prior to being sanitized.
	 */
	return apply_filters( 'sanitize_text_field', $filtered, $str );
}

💙 sanitize_textarea_field()

Like sanitize_text_field, but keeps newlines.

sanitize_title()

sanitize_title_for_query()

sanitize_title_with_dashes()

sanitize_user()

sanitize_url()

use esc_url_raw() – see below

wp_kses()

💙 wp_kses_post()

Calls wp_kses() with the ‘post’ context that automatically allows all HTML that is permitted in post content.

ℹ️ Processing an array or object? Use wp_kses_post_deep().

SLP Modifications

In SLP it allows HTML tags like Vue, etc. that are on the allowed HTML tags filter.

Converts & to &amp;

The wp_kses methods call wp_normalize_entities which bastardizes nearly all (but not ALL) occurrences of & in a string to &amp;.

That means wp_kses functions are basically useless for sanitizing query parameter strings.

The Code

 function wp_kses_post( $data ) {
    return wp_kses( $data, 'post' );
}

💙 wp_kses_post_deep()

Navigates through an array, object, or scalar, and sanitizes content for allowed HTML tags for post content.


function wp_kses_post_deep( $data ) {
    return map_deep( $data, 'wp_kses_post' );
}

Securing Output (escaping)

esc_attr()
Use on everything else that’s printed into an HTML element’s attribute.

esc_html()
Use anytime an HTML element encloses a section of data being displayed. This WILL NOT display HTML content, it is meant for being used inside HTML and will remove your HTML.

esc_js()
Use for inline Javascript.

esc_textarea() – Use this to encode text for use inside a textarea element.

esc_url()

Use on all URLs, including those in the src and href attributes of an HTML element.

Does encode things like & to wonky-ass WordPress HTML coded entities.

esc_url_raw()

Use when storing a URL in the database or in other cases where non-encoded URLs are needed.

Does NOT encode things like & to wonky-ass WordPress HTML coded entities.

esc_xml() – Use to escape XML block.

wp_kses() – See Santize above for more details.

💙 wp_kses_post() – See Sanitize above for more details.

💙 wp_kses_post_deep() – See Sanitize above for more details.


Nonces

wp_kses_data() – Alternative version of wp_kses() that allows only the HTML permitted in post comments.

wp_nonce_field( <action> , [ name = ‘_wpnonce’ ], [ referer = true ] , [ echo = true ])

– Add nonce input to a form.

check_admin_referer( <action> , [ name = ‘_wpnonce’ ])

– Check a received nonce is valid AND from an admin page.