WP Manifestindependent plugin directory

Install

Download release.zip From the 3.0.3 release on GitHub, Dec 5, 2023

Upload it under Plugins → Add New Plugin → Upload Plugin, then activate.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/helsingborg-stad/search-notices/releases/download/3.0.3/release.zip

On Packagist as helsingborg-stad/search-notices:

composer require helsingborg-stad/search-notices
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

Enables option to add notices to search result of specific keywords

This plugin will always run before the search-loop, but there is a shortcode avabile [search_notices_print] to be used before any loop.

Releases

5 releases. Each count is every asset in that release; expand a row for the breakdown.

Tag
Published
Assets
Downloads
3.0.6 latest
Feb 23, 2026 8mo ago
tag only
—
Nov 20, 2025 11mo ago
tag only
—
Dec 5, 2023 2 years ago
release.zip
3
Sep 28, 2023 3 years ago
tag only
—
Sep 26, 2023 3 years ago
tag only
—
D+ grade

Security

Search Notices 3.0.2 · audited by WP Registry

High-severity findings.

1 high 1 medium
Audited release
3.0.2
Findings
2
Worst severity
high
Content hash
4ef3caf1deb37ec4233361a5…

Findings

  • high Contributor+ stored XSS in public search-notice markup

    xss_stored

    App::output (loop_start, [search_notices_print], search_notices/print) runs on is_search() and concatenates stored ACF repeater rows into HTML that is echoed via apply_filters('search_notices/output', $markup). Three sinks are unescaped: (1) $notice['notice_class'] is placed in a class attribute with no esc_attr; the field is an ACF select (ui/ajax/create_options off) whose admin validate_value does not require the value to be in choices — only REST validate_rest_value does — so a Contributor who can open Settings > Search notices (capability edit_posts) can POST acf[...][field_57e286dd7b5b3]=info" onfocus="alert(document.domain)" tabindex="0. ACF HTML kses/sanitize_text_field do not encode quotes, so the breakout survives save and executes for every visitor of a matching /?s= query (onfocus fires on the div). (2) $notice['notice'] is passed through strip_tags(..., '<a>'), which keeps javascript: hrefs and onclick/onmouseover on <a>. (3) buttonOutput interpolates notice_button_url into href and notice_button_text into element content with no esc_url/esc_html. Write path is ACF options (wp_options options_search_notices_*), not post_content, so core wp_filter_post_kses does not apply. Observable consequence: arbitrary JavaScript on public search results in the visitor's origin/session (including an Administrator who searches). Subscriber cannot write; Contributor+ can.

    source/php/App.php:64-102

    Recommendation

    Escape notice_class with esc_attr (or allow-list info|success|danger|warning). Replace strip_tags(..., '<a>') with wp_kses() using a tight <a href> allow-list (no event handlers, no javascript:). Echo notice_button_url with esc_url() and notice_button_text with esc_html(). Raise the options-page capability to manage_options so Contributor/Author cannot write this option.

  • medium Site-wide search notices writable with edit_posts (Contributor+)

    missing_capability

    acf_add_options_page registers Settings > Search notices with capability edit_posts (parent_slug options-general.php). WordPress admin.php dispatches ?page=search-notices before options-general.php's manage_options guard, and user_can_access_admin_page() only checks the submenu cap, so Contributor/Author can load the ACF form, obtain a valid ACF nonce, and save the search_notices repeater into wp_options. That content is then shown on every matching public search (App::getNotices → App::output). Even if output were fully escaped, an Author can plant keyword-targeted notices and notice_button_url phishing links on searches they do not otherwise control. If SEARCH_NOTICES_NETWORK is defined on multisite, App::multisiteSync on acf/save_post copies the repeater via update_field('field_57e286a77b5b1') to every get_sites() blog after only a screen-id check — still no manage_options/manage_network. Observable consequence: site-wide (and optionally network-wide) search-result defacement/phishing by any role with edit_posts.

    source/php/App.php:29-38

    Recommendation

    Set capability to manage_options (and require manage_network / a super-admin check before multisiteSync). Keep ACF nonces. Do not copy notices across blogs for users who cannot administer those blogs.

WP Registry hashes the installable build and reports on that exact bytes-for-bytes copy. Embargoed findings are withheld until they are disclosed, so a clean verdict means nothing public is outstanding. WP Manifest does not audit code itself.