WP Manifestindependent plugin directory
manifest / editor / glutenblocks

Glutenblocks WP Registry grade D+. High-severity findings · 3 findingsWP RegistryHigh-severity findings · 3 findingsOpen the reportD+

Collection of Wordpress blocks for the Gutenberg editor.

★ 1stars
5.5kcomposer installs
v1.5.0latest · 1mo ago

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/exolnet/glutenblocks/archive/refs/heads/main.zip

On Packagist as exolnet/glutenblocks:

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

Latest Stable Version

Collection of Wordpress blocks for the Gutenberg editor.

Installation

composer require exolnet/glutenblocks

Usage

If you need to create a new Block, please use the packages/src/example as the skeleton of your new block.

Developement flow

To be able to see the components, you need to install the plugin in a Wordpress site. If you have Docker installed, you can create a new Wordpress site quickly with this command:

docker-compose -f docker-compose.yml -f docker-compose-localdev.yml up

Your Wordpress site will be available on localhost:8888. You will have to finish the Wordpress installation in the browser.

If it is the first time you start the Docker containers, you will need to enable Glutenblocks plugin from the WP admin panel.

When you are done, you can stop the docker-compose process to stop the server from working.

If you want to remove the containers from your development machine, you can run this command:

docker-compose down

Testing

Component Tests

To run your components tests, please use:

yarn test

Lint

To run linter tests (project guidelines), please use:

yarn lint
composer lint

Contributing

Please see CONTRIBUTING and CODE OF CONDUCT for details.

Security

If you discover any security related issues, please email security@exolnet.com instead of using the issue tracker.

Credits

License

Copyright © eXolnet. All rights reserved.

This code is licensed under the MIT license. Please see the license file for more information.

Releases

13 releases.

Tag
Published
v1.5.0 latest
Aug 26, 2026 1mo ago
Feb 6, 2026 8mo ago
Sep 13, 2021 5 years ago
Sep 13, 2021 5 years ago
Sep 13, 2021 5 years ago
Nov 23, 2020 5 years ago
Aug 4, 2020 6 years ago
May 6, 2020 6 years ago
Apr 14, 2020 6 years ago
Mar 20, 2020 6 years ago
Mar 20, 2020 6 years ago
Feb 12, 2020 6 years ago
Feb 6, 2020 6 years ago

These releases are tags only. The author does not attach a packaged zip, so there are no download counts to report.

D+ grade

Security

Glutenblocks 1.3.0 · audited by WP Registry

High-severity findings.

3 high
Audited release
1.3.0
Findings
3
Worst severity
high
Content hash
f886dc6deb561e455789dde8…

Findings

  • high Unauthenticated REST dumps all posts of a type as raw WP_Post objects including post_password

    information_disclosure

    register_rest_route glutenblocks/v1/gb_get_field_types/field=(?P<field>[a-zA-Z0-9-]+) is GET with permission_callback __return_true. gb_get_field_types() passes the captured slug straight to get_posts() with posts_per_page/numberposts = -1 and no post_status, suppress_filters, or current_user_can('read_post') check. get_posts() defaults to post_status=publish, which includes password-protected posts, and returns full WP_Post objects. WP_REST_Server json_encodes those public properties, so an unauthenticated client receives post_content, post_excerpt, post_author, guid, and post_password for every matching row — data the core /wp/v2/posts controller withholds from logged-out users and paginates at 100. Observable: GET /wp-json/glutenblocks/v1/gb_get_field_types/field=post (or field=page, or any hyphen/alphanumeric type slug, including non-public CPTs whose names match the regex) returns the entire collection in one response. Logged-in Editors additionally inherit WP_Query's read_private_posts OR, but the unauth password leak is the blast radius. Sibling route wp_get_post_types is the same public gate and only lists type names (used by LinkSelect); it is not separately filed.

    glutenblocks.php:53-82

    Recommendation

    Gate this route on current_user_can('edit_posts') (it exists only to populate the block editor LinkSelect). Do not return raw WP_Post objects: map ID + post_title of posts the user can read, exclude password-protected content unless current_user_can('edit_post'), and cap posts_per_page. Restrict post_type to an allowlist (or types registered with glutenblock_rest) instead of any regex match.

  • high Unauthenticated REST dumps ACF fields for any post ID and the ACF options page

    information_disclosure

    register_rest_route glutenblocks/v1/gb_get_post_attributes/id=(?P<id>[a-zA-Z0-9-]+) is GET with permission_callback __return_true. The callback is return get_fields($data['id']); with no function_exists guard, no current_user_can('read_post'), and no is_protected_meta check. ACF's get_fields() accepts numeric post IDs and the sentinels 'option'/'options' (the regex allows both). Observable when Advanced Custom Fields is active: GET /wp-json/glutenblocks/v1/gb_get_post_attributes/id=options returns the entire ACF options-page array (API keys, SMTP, license tokens, whatever the site stored there); GET .../id=123 returns every ACF field on that post, including drafts, private posts, and WooCommerce/order-like IDs, none of which the core REST or ACF's own show_in_rest controllers would expose to a logged-out client. LinkSelect.js calls this path from the editor after picking a post, which is why the route exists — it was never meant to be public. If ACF is not installed the call fatals (500); that is not a substitute for authorization.

    glutenblocks.php:63-88

    Recommendation

    Require current_user_can('edit_posts') at minimum, and current_user_can('read_post', $id) (or manage_options for option/options) before calling get_fields. Reject non-numeric IDs unless they are a documented allowlist. Return only the field keys the editor needs (or a single named attribute), never the full ACF bag. Guard with function_exists('get_fields').

  • high Contributor+ stored XSS in glutenblocks/button and glutenblocks/link dynamic render

    xss_stored

    Both blocks are dynamic (PHP render_callback on the frontend). glutenblocks_glutenblocks_button_render_callback concatenates $attributes['link'], target, className, color/shape/size/align, and text into an <a> and wrapper class with no esc_url/esc_attr/esc_html. glutenblocks_glutenblocks_register_link_render_callback does the same for href/target/type/actionText and additionally runs str_replace(['<','&rt;'], ['<','>'], $text) immediately before echoing $text inside the markup — so a Contributor without unfiltered_html can store <img src=x onerror=alert(1)> (wp_kses sees entities, not tags) and the render path turns it into a live tag. Quote-breakout also survives filter_block_kses_value, which does not encode quotes in string attributes: link=`" onclick="alert(1)`, target=`" onmouseover="alert(1)`, className/color/type similarly. Because the final HTML is generated at render time, core kses on post_content never sees the tags. Observable: JS runs in the origin of any page that contains a published (Author+) or previewed (Contributor, admin viewing pending) button/link block — session/nonce theft. packages/button/index.php and packages/link/index.php contain the same sinks but are not loaded at runtime (lib/blocks.php includes only build/glutenblocks_registers.php).

    build/glutenblocks_registers.php:100-112

    Recommendation

    Escape every interpolated value: esc_url() for href, esc_attr() for target/class/rel/type, esc_html() for button/link text and actionText. Remove the < decode (or replace it with wp_kses_post on an allowlist). Sanitize className with sanitize_html_class / a safe token list on save. Do not print javascript: or data: URLs.

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.