JPKCom Harden & Trim

features-privacy.php

Privacy features

Author enumeration, REST users, oEmbed author, users sitemap, Browse Happy, dashboard events, local avatars, commenter IPs, embed privacy, remote editor content and the core AI client. All of them are global: they protect anonymous visitors or act on data and requests where no user context exists, so the exempt roles do not apply. Every hook name and priority below was checked against the WordPress 7.1.3 source.

Tags
since
1.0.0

Table of Contents

Functions

jpkcom_harden_trim_register_privacy_features()  : void
Register the hooks of the active privacy features.
jpkcom_harden_trim_is_author_enum_request()  : bool
Whether a request is an author enumeration probe.
jpkcom_harden_trim_requested_url()  : string
The requested URL exactly as core's redirect_canonical() builds it.
jpkcom_harden_trim_author_is_only_query_arg()  : bool
Whether `author` is the only argument of the requested URL's query.
jpkcom_harden_trim_activitypub_answers_author()  : bool
Whether the ActivityPub plugin itself answers this `?author=N` request.
jpkcom_harden_trim_author_enum_exempt()  : bool
Whether an author enumeration probe is let through.
jpkcom_harden_trim_author_enum_guard_template()  : mixed
`template_include` callback (PHP_INT_MAX): the exempted probe must get ActivityPub's own document.
jpkcom_harden_trim_block_author_enum()  : void
`template_redirect` callback: answer author enumeration probes with a 404.
jpkcom_harden_trim_is_users_route()  : bool
Whether a REST route is the users collection or anything below it.
jpkcom_harden_trim_filter_rest_users()  : mixed
`rest_pre_dispatch` callback: 401 for unauthenticated users requests.
jpkcom_harden_trim_anonymize_oembed()  : array<string, mixed>
Replace the author in oEmbed data by the site.
jpkcom_harden_trim_filter_oembed_response_data()  : mixed
`oembed_response_data` callback.
jpkcom_harden_trim_filter_sitemap_provider()  : mixed
`wp_sitemaps_add_provider` callback: drop the users provider.
jpkcom_harden_trim_is_browse_happy_url()  : bool
Whether a URL is the Browse Happy API (http or https).
jpkcom_harden_trim_filter_browse_happy()  : mixed
`pre_http_request` callback: short-circuit the Browse Happy request.
jpkcom_harden_trim_remove_events_widget()  : void
`wp_dashboard_setup` callback: remove "WordPress Events and News".
jpkcom_harden_trim_local_avatar_data()  : array<string, mixed>
Point avatar data at the plugin's local SVG.
jpkcom_harden_trim_filter_avatar_data()  : mixed
`pre_get_avatar_data` callback: local avatar instead of Gravatar.
jpkcom_harden_trim_anonymize_ip()  : string
Anonymise an IP address: IPv4 to /24, IPv6 to /48.
jpkcom_harden_trim_filter_comment_ip()  : mixed
`pre_comment_user_ip` callback.
jpkcom_harden_trim_filter_comment_flood()  : mixed
`wp_is_comment_flood` callback: flood check against the anonymised IP.
jpkcom_harden_trim_filter_comment_agent()  : mixed
`pre_comment_user_agent` callback: store no user agent.
jpkcom_harden_trim_privacy_embed_src()  : string
Rewrite one iframe src for privacy.
jpkcom_harden_trim_privacy_embed_html()  : string
Rewrite the src of every YouTube and Vimeo iframe in a piece of HTML.
jpkcom_harden_trim_filter_embed_privacy()  : mixed
`embed_oembed_html` callback.
jpkcom_harden_trim_filter_cover_embed()  : mixed
`render_block_core/cover` callback for embedded background videos.
jpkcom_harden_trim_filter_oembed_proxy_response()  : mixed
`rest_post_dispatch` callback: rewrite the oEmbed proxy's preview HTML.
jpkcom_harden_trim_filter_editor_settings()  : mixed
`block_editor_settings_all` callback: hide the Openverse media category.
jpkcom_harden_trim_unregister_google_fonts()  : void
`init` callback: unregister core's Google Fonts collection.

Functions

jpkcom_harden_trim_register_privacy_features()

Register the hooks of the active privacy features.

jpkcom_harden_trim_register_privacy_features(array<string, mixed> $settings) : void
Parameters
$settings : array<string, mixed>

Effective settings.

Tags
since
1.0.0

jpkcom_harden_trim_is_author_enum_request()

Whether a request is an author enumeration probe.

jpkcom_harden_trim_is_author_enum_request(array<string, mixed> $query, bool $logged_in, string $permalink_structure, bool $is_admin) : bool

Plain permalinks are left alone (there ?author=N is the real author link), as are logged-in users and the admin. Whether a probe is exempt (ActivityPub) is decided separately, see jpkcom_harden_trim_author_enum_exempt().

Parameters
$query : array<string, mixed>

Parsed request query vars.

$logged_in : bool

Whether a user is logged in.

$permalink_structure : string

The permalink structure option.

$is_admin : bool

Whether this is an admin request.

Tags
since
1.0.0
Return values
bool —

True for an anonymous ?author=N request with pretty permalinks.

jpkcom_harden_trim_requested_url()

The requested URL exactly as core's redirect_canonical() builds it.

jpkcom_harden_trim_requested_url() : string

7.1.3 canonical.php:69-73: scheme from is_ssl(), then the raw Host header and the raw REQUEST_URI, concatenated unchecked; no Host header, no URL. This is the $requested_url ActivityPub's Router::redirect_canonical() parses, so a Host header such as example.com# moves the whole REQUEST_URI into the fragment there. A non-string server value (no web SAPI sends one) gives '' as well.

Tags
since
1.0.0
Return values
string —

The URL, or '' when core builds none.

jpkcom_harden_trim_author_is_only_query_arg()

Whether `author` is the only argument of the requested URL's query.

jpkcom_harden_trim_author_is_only_query_arg(string $requested_url) : bool

Mirrors ActivityPub's own check in Router::redirect_canonical() (ActivityPub 9.3.1, class-router.php:300-330), input included: it takes the query of core's $requested_url (see jpkcom_harden_trim_requested_url(), not REQUEST_URI alone) with wp_parse_url(), parses it with wp_parse_args(), ignores activitypub and stamp, and suppresses core's redirect to /author// only when exactly one argument, author, remains. No query (also one hidden in a fragment by the Host header) or any further argument (also one WordPress ignores) lets that redirect through.

Parameters
$requested_url : string

The requested URL as core builds it.

Tags
since
1.0.0
Return values
bool —

True for ?author=N, optionally with activitypub/stamp.

jpkcom_harden_trim_activitypub_answers_author()

Whether the ActivityPub plugin itself answers this `?author=N` request.

jpkcom_harden_trim_activitypub_answers_author(array<string, mixed> $query, string $requested_url) : bool

ActivityPub (wordpress.org slug activitypub, read against 9.3.1) answers ?author=N with the actor document only when all of these hold, each decided by its own public API, never by a header test of ours:

  • \Activitypub\is_activitypub_request() (functions-request.php:70): ?activitypub, or an Accept header whose highest-quality type is activity+json or ld+json with the ActivityStreams profile (q-aware, q=0 refuses), plus its filter. Otherwise it does not suppress core's canonical redirect (class-router.php:301).
  • \Activitypub\should_negotiate_content() (functions-request.php:79): otherwise it renders the theme template (class-router.php:92).
  • author is the only argument in the query of the requested URL as core builds it from the Host header and REQUEST_URI (see jpkcom_harden_trim_author_is_only_query_arg()); otherwise core's redirect goes through.
  • \Activitypub\user_can_activitypub( N ) (functions-user.php:90): a user who is no actor gets no transformer (transformer/class-factory.php:109) and the theme renders the author archive.
  • The request reaches template_include, where ActivityPub renders its document (class-router.php:24). Core's template loader answers robots, favicon, feeds and trackbacks before that filter (template-loader.php:40-62), and sitemaps exit on template_redirect; a feed path such as /feed/?author=N keeps the raw query at author alone, yet core serves the author's feed (display name, by default the login). Any of their query vars (feed, tb, robots, favicon, sitemap, sitemap-stylesheet), from path, query or POST body, refuses.

The functions are looked up at call time. If any of them is missing (ActivityPub not loaded, or a version with a different API), the answer is false: an unknown ActivityPub is not trusted to answer.

Parameters
$query : array<string, mixed>

Parsed request query vars.

$requested_url : string

The requested URL as core builds it (jpkcom_harden_trim_requested_url()).

Tags
since
1.0.0
Return values
bool —

True when ActivityPub answers the request with its own document.

jpkcom_harden_trim_author_enum_exempt()

Whether an author enumeration probe is let through.

jpkcom_harden_trim_author_enum_exempt(array<string, mixed> $query, string $accept[, string $request_uri = '' ][, string $requested_url = '' ]) : bool

By default only when the ActivityPub plugin itself answers this very request with the actor document (see jpkcom_harden_trim_activitypub_answers_author()): then ?author=N is the actor ID other servers resolve, and ActivityPub publishes the actor's name by design. Every other probe, whatever its Accept header, gets the 404, because core would redirect it to /author//.

Parameters
$query : array<string, mixed>

Parsed request query vars.

$accept : string

The Accept request header (passed to the filter).

$request_uri : string = ''

The raw REQUEST_URI (passed to the filter).

$requested_url : string = ''

The requested URL as core builds it (jpkcom_harden_trim_requested_url()); '' never exempts.

Tags
since
1.0.0
Return values
bool —

True when the probe must not get the 404.

jpkcom_harden_trim_author_enum_guard_template()

`template_include` callback (PHP_INT_MAX): the exempted probe must get ActivityPub's own document.

jpkcom_harden_trim_author_enum_guard_template(mixed $template) : mixed

Added only for a request that was exempted because ActivityPub answers it. ActivityPub still falls back to the theme template on some paths it decides only while rendering: under Authorized Fetch an unsigned request gets status 401 and the theme template (9.3.1 class-router.php:211-215), which for ?author=N is the author archive with the user's name and nicename. ActivityPub's own templates live in its plugin directory (ACTIVITYPUB_PLUGIN_DIR); any other template turns the response into the 404 the probe would have got.

Parameters
$template : mixed

Template path chosen so far.

Tags
since
1.0.0
Return values
mixed —

The template, or the theme's 404 template.

jpkcom_harden_trim_block_author_enum()

`template_redirect` callback: answer author enumeration probes with a 404.

jpkcom_harden_trim_block_author_enum() : void

Reads the parsed request ($wp->query_vars), not $wp_query: WP_Query fills its own author from author_name on pretty author archives, which must keep working. The query is emptied (see jpkcom_harden_trim_force_404()), so the theme's 404 template renders and neither a feed nor a fallback index lists the author's posts; redirect_canonical() sees is_404() and has nothing to guess from.

Tags
since
1.0.0

jpkcom_harden_trim_is_users_route()

Whether a REST route is the users collection or anything below it.

jpkcom_harden_trim_is_users_route(string $route) : bool

Case-insensitive like core's route matching; a trailing slash is tolerated.

The anchor must keep core's semantics: WP_REST_Server::match_request_to_handler() (7.1.3 class-wp-rest-server.php:1172) matches with '@^' . $route . '$@i', no D modifier, so its $ also matches before a trailing "\n", and rest_api_loaded() only strips slashes (untrailingslashit()). Core therefore serves ?rest_route=/wp/v2/users%0A as the user list. Do not "tighten" this to \z: that would let exactly that request through.

Parameters
$route : string

REST route, e.g. /wp/v2/users/1.

Tags
since
1.0.0
Return values
bool —

True for /wp/v2/users and /wp/v2/users/….

jpkcom_harden_trim_filter_rest_users()

`rest_pre_dispatch` callback: 401 for unauthenticated users requests.

jpkcom_harden_trim_filter_rest_users(mixed $result[, mixed $server = null ][, mixed $request = null ]) : mixed

Decided at dispatch time, after cookie+nonce and application-password authentication. An error set earlier (rate limit, another plugin) is kept; any other short-circuit result is replaced.

Parameters
$result : mixed

Short-circuit result so far (null to continue).

$server : mixed = null

REST server (unused).

$request : mixed = null

The request.

Tags
since
1.0.0
Return values
mixed —

The result, or a WP_Error with status 401.

jpkcom_harden_trim_anonymize_oembed()

Replace the author in oEmbed data by the site.

jpkcom_harden_trim_anonymize_oembed(array<string, mixed> $data) : array<string, mixed>
Parameters
$data : array<string, mixed>

oEmbed response data.

Tags
since
1.0.0
Return values
array<string, mixed> —

The data; only existing author keys are changed.

jpkcom_harden_trim_filter_oembed_response_data()

`oembed_response_data` callback.

jpkcom_harden_trim_filter_oembed_response_data(mixed $data) : mixed
Parameters
$data : mixed

oEmbed response data.

Tags
since
1.0.0
Return values
mixed —

The anonymised data, or the input when it is no array.

jpkcom_harden_trim_filter_sitemap_provider()

`wp_sitemaps_add_provider` callback: drop the users provider.

jpkcom_harden_trim_filter_sitemap_provider(mixed $provider[, mixed $name = '' ]) : mixed
Parameters
$provider : mixed

Sitemap provider.

$name : mixed = ''

Provider name.

Tags
since
1.0.0
Return values
mixed —

False for users (core then skips it), else the provider.

jpkcom_harden_trim_is_browse_happy_url()

Whether a URL is the Browse Happy API (http or https).

jpkcom_harden_trim_is_browse_happy_url(string $url) : bool
Parameters
$url : string

Request URL.

Tags
since
1.0.0
Return values
bool —

True for api.wordpress.org/core/browse-happy/….

jpkcom_harden_trim_filter_browse_happy()

`pre_http_request` callback: short-circuit the Browse Happy request.

jpkcom_harden_trim_filter_browse_happy(mixed $pre[, mixed $args = array() ][, mixed $url = '' ]) : mixed

The error message is never shown (core only checks is_wp_error()), so it is not translated; translating here could load the text domain early.

Parameters
$pre : mixed

Short-circuit value so far (false to continue).

$args : mixed = array()

Request arguments (unused).

$url : mixed = ''

Request URL.

Tags
since
1.0.0
Return values
mixed —

A WP_Error for the Browse Happy request, else $pre.

jpkcom_harden_trim_remove_events_widget()

`wp_dashboard_setup` callback: remove "WordPress Events and News".

jpkcom_harden_trim_remove_events_widget() : void

Without the widget its scripts never request events or news.

Tags
since
1.0.0

jpkcom_harden_trim_local_avatar_data()

Point avatar data at the plugin's local SVG.

jpkcom_harden_trim_local_avatar_data(array<string, mixed> $args) : array<string, mixed>
Parameters
$args : array<string, mixed>

Avatar data arguments.

Tags
since
1.0.0
Return values
array<string, mixed> —

The arguments with url and found_avatar set.

jpkcom_harden_trim_filter_avatar_data()

`pre_get_avatar_data` callback: local avatar instead of Gravatar.

jpkcom_harden_trim_filter_avatar_data(mixed $args) : mixed

Setting url makes get_avatar_data() return before it hashes the e-mail address. A URL another plugin already set is kept.

Parameters
$args : mixed

Avatar data arguments.

Tags
since
1.0.0
Return values
mixed —

The arguments.

jpkcom_harden_trim_anonymize_ip()

Anonymise an IP address: IPv4 to /24, IPv6 to /48.

jpkcom_harden_trim_anonymize_ip(string $ip) : string

An IPv4-mapped IPv6 address (::ffff:a.b.c.d, as dual-stack listeners report IPv4 clients) is anonymised as the IPv4 address it carries; keeping its first 48 bits would store :: for every such client. Core's sanitiser keeps commas and spaces (comment.php:2424), so a proxy list anonymises its first valid address.

Parameters
$ip : string

IP address, or a comma-separated list of them.

Tags
since
1.0.0
Return values
string —

The anonymised address, or '' when no valid IP is found.

jpkcom_harden_trim_filter_comment_ip()

`pre_comment_user_ip` callback.

jpkcom_harden_trim_filter_comment_ip(mixed $ip) : mixed

Re-checks the setting of the current site: after switch_to_blog() a comment stored on another site of the network follows that site's choice, not the one the request booted with.

Parameters
$ip : mixed

Commenter IP.

Tags
since
1.0.0
Return values
mixed —

The anonymised IP ('' for anything that is no IP), or $ip unchanged when the current site does not anonymise.

jpkcom_harden_trim_filter_comment_flood()

`wp_is_comment_flood` callback: flood check against the anonymised IP.

jpkcom_harden_trim_filter_comment_flood(mixed $is_flood[, mixed $ip = '' ][, mixed $email = '' ][, mixed $date = '' ][, mixed $avoid_die = false ]) : mixed

wp_check_comment_flood() (7.1.3 comment.php:909) looks for a recent comment with comment_author_IP = <live IP> or the same e-mail address. While comment_ip is on, stored rows hold the shortened address, so for guests only the e-mail clause would still match and a guest changing the e-mail address on every comment was never throttled. This runs the same core check once more with the shortened form, so the IP clause works again, per /24 (IPv4) or /48 (IPv6) network.

Skipped when core already found a flood, for logged-in users (core checks the user ID), when the IP is already in its stored form, and when the site removed core's flood check.

Parameters
$is_flood : mixed

Flood verdict so far.

$ip : mixed = ''

Live commenter IP.

$email : mixed = ''

Commenter e-mail address.

$date : mixed = ''

GMT date of the new comment.

$avoid_die : mixed = false

Whether to return instead of calling wp_die().

Tags
since
1.0.0
Return values
mixed —

The verdict.

jpkcom_harden_trim_filter_comment_agent()

`pre_comment_user_agent` callback: store no user agent.

jpkcom_harden_trim_filter_comment_agent(mixed $agent) : mixed
Parameters
$agent : mixed

Commenter user agent.

Tags
since
1.0.0
Return values
mixed —

'' while the current site anonymises comments, else $agent.

jpkcom_harden_trim_privacy_embed_src()

Rewrite one iframe src for privacy.

jpkcom_harden_trim_privacy_embed_src(string $src) : string

YouTube (www. or bare host) moves to youtube-nocookie.com; a Vimeo player URL gets dnt=1, and an existing dnt parameter with any other value (dnt=0 keeps tracking on) is set to 1. The separator follows the src's own escaping (&amp;, &#038; or &) and the parameter goes before a fragment.

Parameters
$src : string

Raw attribute value.

Tags
since
1.0.0
Return values
string —

The rewritten value.

jpkcom_harden_trim_privacy_embed_html()

Rewrite the src of every YouTube and Vimeo iframe in a piece of HTML.

jpkcom_harden_trim_privacy_embed_html(string $html) : string

Only src attributes of <iframe> tags are touched; links and text stay as they are. Idempotent.

Parameters
$html : string

Embed or block HTML.

Tags
since
1.0.0
Return values
string —

The rewritten HTML.

jpkcom_harden_trim_filter_embed_privacy()

`embed_oembed_html` callback.

jpkcom_harden_trim_filter_embed_privacy(mixed $html) : mixed
Parameters
$html : mixed

Embed HTML.

Tags
since
1.0.0
Return values
mixed —

The rewritten HTML, or the input when it is no string.

jpkcom_harden_trim_filter_cover_embed()

`render_block_core/cover` callback for embedded background videos.

jpkcom_harden_trim_filter_cover_embed(mixed $content[, mixed $block = array() ]) : mixed
Parameters
$content : mixed

Rendered block HTML.

$block : mixed = array()

Parsed block.

Tags
since
1.0.0
Return values
mixed —

The rewritten HTML for embed-video covers, else the input.

jpkcom_harden_trim_filter_oembed_proxy_response()

`rest_post_dispatch` callback: rewrite the oEmbed proxy's preview HTML.

jpkcom_harden_trim_filter_oembed_proxy_response(mixed $result[, mixed $server = null ][, mixed $request = null ]) : mixed

Covers fresh and cached proxy responses; the cached copy itself is not changed.

Parameters
$result : mixed

REST response.

$server : mixed = null

REST server (unused).

$request : mixed = null

The request.

Tags
since
1.0.0
Return values
mixed —

The response.

jpkcom_harden_trim_filter_editor_settings()

`block_editor_settings_all` callback: hide the Openverse media category.

jpkcom_harden_trim_filter_editor_settings(mixed $settings) : mixed
Parameters
$settings : mixed

Block editor settings.

Tags
since
1.0.0
Return values
mixed —

The settings.

jpkcom_harden_trim_unregister_google_fonts()

`init` callback: unregister core's Google Fonts collection.

jpkcom_harden_trim_unregister_google_fonts() : void

Checked first, because unregistering a collection that is not registered (removed by another plugin) raises _doing_it_wrong. The check goes through the public get_font_collections(): WP_Font_Library::is_collection_registered() is private (7.1.3, class-wp-font-library.php:96) and calling it is a fatal Error.

Tags
since
1.0.0
On this page

Search results