WP Manifestindependent plugin directory
manifest / admin / bm-concierge

BM Concierge WP Registry grade D+. High-severity findings · 2 findingsWP RegistryHigh-severity findings · 2 findingsOpen the reportD+

Through BM Concierge we provide helpful services right from the WordPress dashboard and editor.

★ 2stars
24release downloads
1.1.1latest · 4 years ago

Install

Download bm-concierge.zip From the 1.1.1 release on GitHub, Oct 4, 2022

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

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/bernskiold/bm-concierge/releases/download/1.1.1/bm-concierge.zip
Updates itself. It ships its own updater (Plugin Update Checker), so new versions appear under Dashboard → Updates.

Through BM Concierge we provide helpful services right from the WordPress dashboard and editor. Concierge is available for all Company Cloud websites by default, and can be added to any of our WordPress customers' websites.

Supported Services

  • Proofreading
  • On-Page SEO Optimization
  • Accessibility Audit
  • Page Layout
  • Translation (Machine-Learning)

Coming soon:

  • Translation (Agency)

Hooks

The behavior of Concierge can be customized via hooks. Common usecases would be customer specific pricing, disabling concierge for some sites in a multisite, or selecting which services are available.

Disabling Concierge

If the filter bm/concierge/enabled returns false, Concierge will not be loaded for the website.

apply_filters('bm/concierge/enabled', function() {

    // Enable concierge on the sites in this list.
    $enabled_on_sites = [ 1,2,3 ];

    if ( in_array( get_current_blog_id(), $enabled_on_sites, true ) ) {
        return true;
    }

    return false;
});

Disabling Specific Concierge Services

All services are enabled by default. To disable a service, return its filter as false.

  • Proofreading: bm/concierge/services/proofreading
  • Layout: bm/concierge/services/layout
  • Accessibility: bm/concierge/services/accessibility
  • On-Page SEO: bm/concierge/services/onpage
  • Machine Translation: bm/concierge/services/machine_translation

This could be used to either disable a service completely, or selectively based on rules:

// Only allow admins to request layout services.
apply_filters('bm/concierge/services/layout', function() {
    return current_user_can( 'manage_options' );
});

Customer-Specific Pricing

By default, Concierge loads a specific API pricing file from our servers for all services.

The bm/concierge/pricing_api_url filter can be used to return a different JSON file URL, matching https://static.bernskioldmedia.com/concierge/api.json with different pricing.

It takes one argument, apart from the URL, which is the service key. This could be used to load a specific file only for a specific service.

// Load customer-specific pricing.
apply_filters('bm/concierge/pricing_api_url', function( string $url, string $service_key ) {
    return 'https://static.bernskioldmedia.com/concierge/clients/client-name.json';
});

Specifying the currency.

By default, the currency used to present prices is based on the locale. We support SEK, GBP and USD.

To override the currency and keep it the same regardless of locale, use the `` filter and return the three-letter ISO currency code.

// Force currency.
apply_filters('bm/concierge/currency', function( string $currency, string $service_key ) {
    return 'SEK';
});

Adding the machine learning API key

In order for the machine learning service to work an API key needs to be set in the environment configuration.

define( 'BM_CONCIERGE_ML_TRANSLATION_API_KEY', 'XXXXXX' );

For Bernskiold Media projects we store this in our central management system which we generate for all clients. Alternatively you can sign up to get your own API key with DeepL.

Releases

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

Tag
Published
Assets
Downloads
1.1.1 latest
Oct 4, 2022 4 years ago
bm-concierge.zip
17
Sep 21, 2022 4 years ago
bm-concierge.zip
7
Sep 5, 2021 5 years ago
tag only
—
Aug 24, 2021 5 years ago
tag only
—
Jul 27, 2021 5 years ago
tag only
—
D+ grade

Security

BM Concierge 1.1.1 · audited by WP Registry

High-severity findings.

1 high 1 medium
Audited release
1.1.1
Findings
2
Worst severity
high
Content hash
b88a8bbc2cab893fc7a2e571…

Findings

  • high Any logged-in user can overwrite arbitrary posts via the machine-translation order endpoint

    missing_capability

    Every Concierge REST route, including POST /bm-concierge/v1/order, uses permission_callback Concierge::has_logged_in_access which is only is_user_logged_in(). There is no current_user_can('edit_post') / edit_posts check anywhere on the write path. A Subscriber (or any other logged-in role) who can obtain a wp_rest nonce — wp-admin profile.php, heartbeat, or an application password — can POST JSON {type:'machine_translation', postId:<id>, targetLanguage:'DE', express:false, price:'0'}. process_order() instantiates MachineTranslation and calls order(). That method first sends the vendor/user emails, then get_translated_data() reads get_post($postId) with no capability check, translates title and HTML content with the site's BM_CONCIERGE_ML_TRANSLATION_API_KEY (DeepL tag_handling=html), and save_to_post() calls wp_update_post() on that ID. wp_update_post does not check capabilities, so this rewrites any post/page/CPT the ID refers to, including content the Subscriber cannot edit or even read (drafts). GET /services reports whether the DeepL constant is defined, so the attacker knows when the overwrite path is live. Without the key, save_to_post still runs wp_update_post({ID}) (touch/save_post hooks) but does not replace content. Unauthenticated users are blocked by the logged-in gate.

    src/Services/MachineTranslation.php:20-64

    Recommendation

    Replace has_logged_in_access with a real capability check: at minimum current_user_can('edit_post', (int) $request['postId']) on /order and the translation helpers, and current_user_can('edit_posts') on the remaining concierge routes. Refuse machine_translation orders unless the DeepL key is defined and the user can edit that specific post. Do not call wp_update_post without an edit_post check.

  • medium Any logged-in user can place paid Concierge orders and trigger vendor/user emails

    missing_capability

    POST /bm-concierge/v1/order is registered as CREATABLE with permission_callback has_logged_in_access (is_user_logged_in only). process_order() switches on attacker-controlled JSON type (proofreading, accessibility, onpage, layout, machine_translation) and calls $service->order($data) with the full JSON body. BaseService::order() merges that body with pageName/pageUrl/websiteName/current user, then wp_mail()s an HTML job ticket to support@bernskioldmedia.com (Reply-To is the current user) and a confirmation to the current user's email stating the selected service, page, delivery speed, and that the price 'will be invoiced separately'. express, price, and (for onpage) keyword are taken from the JSON and echoed unescaped into the vendor email. A Subscriber can therefore spam the vendor, spoof a billable order against the site, and burn staff time, without edit_posts or any nonce beyond the REST cookie nonce every logged-in user can mint. This is not unauthenticated and does not write options/post meta; blast radius is email + potential vendor invoicing, so medium rather than high.

    src/Rest/Concierge.php:65-69, 159-194

    Recommendation

    Gate /order (and the price/language helpers if they should stay editor-only) on current_user_can('edit_post', $post_id) or a dedicated place_concierge_order capability defaulting to editors. Ignore client-supplied price; compute it server-side. Escape order-email fields (price, keyword, titles, URLs) and do not treat REST login as authorization to create billable work.

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.