Skip to content

Refactor/implement security measures rest api#4

Open
mvdhoek1 wants to merge 8 commits into
mainfrom
refactor/implement-security-measures-rest-api
Open

Refactor/implement security measures rest api#4
mvdhoek1 wants to merge 8 commits into
mainfrom
refactor/implement-security-measures-rest-api

Conversation

@mvdhoek1
Copy link
Copy Markdown

@mvdhoek1 mvdhoek1 commented May 21, 2026

Related: https://github.com/yardinternet/a11y-toolbar/pull/22
Make sure to merge both PR's simultaneously, otherwise the translations will fail for visitors as well.

Important fixes:

  • Translation cache generation is no longer accessible to unauthorized users.
  • Authorized users are excluded from the newly introduced rate-limiting mechanism.

To-do:
Clarify in the README's (.md and .txt) that the caching mechanism only works for logged-in users with the edit_posts capability. This resolves an issue where malicious visitors could generate translation cache entries by POSTing invalid translations, without verifying whether the submitted translations actually matched strings requiring translation.

@mvdhoek1 mvdhoek1 requested a review from YvetteNikolov May 21, 2026 12:24
@mvdhoek1 mvdhoek1 marked this pull request as ready for review May 21, 2026 12:34
@mvdhoek1 mvdhoek1 requested a review from a team as a code owner May 21, 2026 12:34
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the plugin’s REST translation endpoint by tightening authentication/authorization, adding rate limiting, and preventing unauthorized users from generating cached translations, while also adding an admin-side metabox UI for cache control and corresponding editor styles.

Changes:

  • Updated REST API security model (nonce handling, origin validation) and introduced IP-based rate limiting with a capability-based bypass for authorized users.
  • Refactored translation caching so storing cached translations is conditional (capability-driven), and added metabox controls to disable/clear cache on save.
  • Added an editor stylesheet build entry and enqueued admin assets; committed dist/ output by removing it from .gitignore.

Reviewed changes

Copilot reviewed 10 out of 17 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
webpack.config.js Adds a dedicated editor entry to build admin/editor CSS.
src/Services/TranslationService.php Adds capability-driven caching toggle and a cached-translation helper.
src/Providers/RestAPIServiceProvider.php Switches REST nonce verification to X-WP-Nonce/wp_rest.
src/Providers/MetaBoxServiceProvider.php Refactors metabox registration/rendering; adds cache clear option; renames a public filter.
src/Providers/AssetsServiceProvider.php Uses asset path helper for .asset.php, uses build dependencies, enqueues admin editor CSS.
src/helpers.php Adds path helpers (ydpl_path, ydpl_asset_path) and updates docstrings.
src/Controllers/RestAPIController.php Adds origin enforcement, rate limiting, and capability-gated caching behavior.
package-lock.json Lockfile metadata/dep graph update (e.g., package name, ajv-formats requires).
languages/yard-deepl.pot Regenerates POT with updated plugin header strings and new metabox strings.
languages/yard-deepl-nl_NL.po Updates NL translations and syncs headers/strings with regenerated POT.
dist/editor.js Built editor bundle artifact (currently empty output).
dist/editor.css Built/minified editor CSS for metabox styling.
dist/editor.asset.php Build metadata for the editor entry.
dist/editor-rtl.css Built RTL CSS for the editor entry.
assets/css/editor.css Source editor CSS for metabox styling.
.gitignore Stops ignoring dist/ so built assets are committed.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +46 to +50
$origin = $request->get_header( 'origin' );

if ( is_null( $origin ) || home_url() !== $origin ) {
return $this->set_failure_response( 403, 'Invalid origin. Origin does not match the site URL.' );
}
Comment on lines +63 to +65

// Apply rate limit check if object ID is absent or translation is not cached when an object ID is present.
if ( empty( $object_id ) || ! $this->service->object_has_cached_translation( (int) $object_id, $target_lang ) ) {
Comment on lines +121 to 125

return $remote_address;
}

/**
Comment on lines 87 to +90
public function verify_nonce(): bool
{
return wp_verify_nonce( sanitize_text_field( wp_unslash( $_SERVER['HTTP_NONCE'] ?? '' ) ), YDPL_NONCE_REST_NAME ) || is_user_logged_in();
$nonce = sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_WP_NONCE'] ?? '' ) );
return (bool) wp_verify_nonce( $nonce, 'wp_rest' );
Comment thread src/helpers.php

/**
* Add prefix for the given string.
* Generates a full plugin path by appending the given path to the base plugin URL.
Comment on lines +69 to +76
/**
* @since NEXT
*/
public function object_has_cached_translation( int $object_id, string $target_lang ): ?array
{
try {
return $this->repository->get_cached_translation( $object_id, $target_lang );
} catch ( ObjectNotFoundException $e ) {
Comment on lines 32 to 37
add_meta_box(
'yard-deepl',
__( 'Yard Deepl', 'yard-deepl' ),
array( $this, 'render_meta_box' ),
apply_filters( 'yard::deepl/disable_cache_metabox_post_types', array( 'page' ) ),
array( $this, 'render_meta_boxes' ),
apply_filters( 'yard::deepl/cache_metabox_post_types', array( 'page' ) ),
'side',
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants