WP Manifestindependent plugin directory
manifest / updates / press-sync

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

The easiest way to synchronize posts, media, users and more between two WordPress sites.

★ 27stars
0.7.3.1-no-releaselatest · never

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/themarcusbattle/press-sync/archive/refs/heads/master.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

Press Sync by Marcus Battle

An easy and straightforward way to syncrchonize users, posts and more between multiple WordPress sites. Activate the plugin on both sites that you want to sync content with and you immediately will have a connection to push content to.

Uses the WP-API. Custom Post Type Support. Currently supports push only. Alpha Version. Use at own risk.

For support, email marcus@marcusbattle.com

How to Install/Use

  1. Install the plugin on both servers you want to synchronize
  2. On your target server, create a "PressSync Key" to allow the WordPress site to receive data
  3. On your push server, create a connection to the target server using the "PressSync Key"
  4. Select the type of content you want to push, click "Save"
  5. Press "Sync" to synchronize the data. Done!

Supports syncing the following data:

  • WP Users, WP Posts, WP Media and WP Comments
  • Custom Post Types
  • Featured Images
  • Categories, Tags and Custom Taxonomies
  • Post & User Meta
  • Posts 2 Posts Relationships

Changelog.

v0.9.2

  • [AOTECH-6795] Adds the ability to pass in any Press Sync options to the command line for syncing "All" and "Posts", see the CLI section "Setting Arbitrary Options" below.
  • Press Sync settings can be filtered using the press_sync_settings hook, which is executed immediately before the parsed settings are passed to the application.

v0.9.1

  • [AOTECH-6793] - Post dates of "0000-00-00 00:00:00" are discarded by wp_insert_post - these dates will now be set to the unix epoch before being inserted.
  • "Advanced" tab now shows for all users, however you get a blurb about how to enable the features if you don't have the advanced features filter set in your code.

v0.9.0

  • [AOTECH-6774] - Fix issue where incredibly large payloads were truncating data by adding ability to sync partial term data.
  • [AOTECH-6776] - Fix bad serialization of metadata for featured images.
  • [feature/dashboard-updates] - Cleans up the Dashboard to remove "Advanced Import/Export" and replace with a combined "Advanced" tab. Moving forward, settings should be defined on the source site and propagated to the remote site, regardless of whether the option applies on the sender or receiver.

v0.8.0

  • [AOTECH-6700] - Preserve proper taxonomy terms and meta when syncing posts.
  • [AOTECH-6704] - Fixes author lookups for multisite migrations.
  • [AOTECH-6705] - Adds the ability to migrate posts only after a certain date.

v0.7.3

  • [AOTECH-6678] - Fixes sycning "All" so that the browser doesn't lock.
  • [AOTECH-6691] - Pages weren't syncing due to incorrect handler being used.
  • [AOTECH-6691] - Addressed minor JS bug in single-type migrations.

v0.7.2

  • [AOTECH-6676] - Simplifies ID lookup when "Preserve IDs" is selected under Advanced Export.

v0.7.1

  • [AOTECH-6676] - Add ability to fix broken relationships.

v0.7.0

  • [AOTECH-6661] - Add Taxonomies and Terms as a Sync Object type.
  • [AOTECH-6662] - Add attachment media meta.
  • Better support for finding and syncing missing objects.

v0.6.1

  • Update README for updated Dashboard and installation.
  • Reintroduce SPL autoloader for non-composer setups.

v0.6.0

  • Merge in major functional updates for Bulk Sync.

v0.5.0

  • Overhauled the dashboard for better UX

v0.4.5

  • Update lookup for post parent to be able to ignore post_type.

  • v0.4.1 - Fixed the WP Coding Standards / PHPCS errors

  • v0.4.0 - Replaced CMB2 Support with native WP Options

  • v0.3.0 - Added WP Options migrations to the plugin.

  • v0.2.0 - Addition of CLI support.

  • v0.1.0 - The initial commit and development of Press Sync.

Installation

Using composer

If you have composer on your system, simply run composer dump-autoload to generate an autoloader classmap.

Non-composer

The plugin still works without composer and will register an autoloader using spl_autoload_register.

Usage

WordPress Admin

Press Sync can be found in the WordPress admin under Tools -> Press Sync. There are two tabs for configuring Press Sync - the Sync tab and the Settings tab.

Press Sync Dashboard

The Credentials tab is where you'll conifgure your Press Sync installation to connect to another WordPress site.

  • Press Sync Key - You should make this key unique and complex, and only share it with the other site that will be connecting to this site. It is strongly recommended that you connect to sites over SSL to avoid your key being transmitted in plaintext.
  • Remote Domain - The remote domain of the site you are connecting to. This site should have Press Sync installed and configured.
  • Remote URL Arguments - This is an advanced setting that you can use to supply additional arguments to the request URL. You should format this string as an HTTP GET query string, starting with a ? (question mark). See https://en.wikipedia.org/wiki/Query_string for more details.
  • Remote Press Sync Key - The Press Sync key configured in the Settings tab of the remote site's Press Sync configuration.

Once configured to connect to a remote Press Sync site, you can configure your sync job on the Bulk Sync tab. Options on that tab include.

  • Sync Method - Determine whether you're Pushing content to a remote site or Pulling content from a remote site. Currently the only method available here is "Push".
  • Objects to Sync - This list allows you to pick what type of content to Sync. WordPress built-ins like Post and Page are supported, as well as Custom Post Types.
  • WP Options to Sync - If your Objects to Sync is set to "Options", this field is used as a comma-separated whitelist of options to sync. Only the options specified in this field will be Pushed to the remote site.
  • Duplicate Action - Choose what action Press Sync should take when a duplicate record is found on the receiving side. When Sync is the selected action, non-synced duplicates will receive a Press Sync meta key to allow them to be synced in the future.
  • Force Update - By default, Press Sync only updates content that was modified more recently than it's synced counterpart. If this option is ste to "Yes", content will always be synced regardless of modified date.
  • Ignore Comments - Whether or not Comments should be synced with posts.

Command Line

Press Sync also includes the ability to sync content via the command line using WP-CLI. With Press Sync enabled and WP-CLI installed, you can see a list of basic commands:

$ wp press-sync
usage: wp press-sync media --remote_domain=<remote_domain> --remote_press_sync_key=<remote_press_sync_key> [--local_folder=<local_folder>]
   or: wp press-sync options --remote_domain=<remote_domain> --remote_press_sync_key=<remote_press_sync_key> [--options=<options>] [--local_folder=<local_folder>]
   or: wp press-sync pages --remote_domain=<remote_domain> --remote_press_sync_key=<remote_press_sync_key> [--local_folder=<local_folder>]
   or: wp press-sync posts --remote_domain=<remote_domain> --remote_press_sync_key=<remote_press_sync_key> [--local_folder=<local_folder>]
   or: wp press-sync users --remote_domain=<remote_domain> --remote_press_sync_key=<remote_press_sync_key> [--local_folder=<local_folder>]

Currently, Press Sync's CLI support includes Posts, Pages, Users, Options, and Media.

Common Arguments

All Press Sync CLI commands use the following required parameters:

  • --remote-domain - The remote site you are connecting to.
  • --remote_press_sync_key - The remote site's Press Sync Key, used to authenticate the connection.
  • --local_folder - This option allows you to use JSON files instead of local WordPress data to push to the remote site. More on this below.

Command-Specific Arguments

Some commands take optional parameters.

  • wp press-sync options
    • --options - A comma-separated list of option fields to sync.

Setting Arbitrary Options

For the All and Posts sync commands, any number of arbitrary options may be passed in that are valid Press Sync options. For example, the database option for preserving post-type object IDs can be set at runtime with --ps-preserve-ids=1, e.g.

wp press-sync posts --ps-date-delta="2017/06/23" --ps-fix-terms=false --ps-batch-size=8

This command would sync posts modified after June 23, 2017, would not attempt to fix term relationships (overriding any value set in the database), and specifies a batch size of 8. Note that advanced options are still usable here, even if the press_sync_show_advanced_options filter is not set to return true.

Importing Local JSON

The --local_folder folder option allows you to specify a folder with JSON data to push to the remote site instead of using the hosting WordPress site's data. This is useful for importing data from systems that aren't necessarily WordPress, but that can export their data in an easy-to-use form.

Structurally, your JSON files should be laid out like this:

+--local_folder
|
| ./posts/
| ./posts/YYYY
| ./posts/YYYY/slugged-title.json
| ./attachments.json
| ./users.json
| ./options.json

For Posts (and post-like objects such as Pages or CPTs), the JSON files should be located in a folder called posts/YYYY/, where YYYY is the four-digit year for the post.

All other types supported by CLI should be in the root of the folder specified in --local_folder as such:

  • Media - attachments.json
  • Users - users.json
  • Options - options.json

Releases

2 releases.

Tag
Published
Jan 29, 2018 8 years ago
Jan 29, 2018 8 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

Press Sync 0.7.3.1-no-release · audited by WP Registry

High-severity findings.

3 high
Audited release
0.7.3.1-no-release
Findings
3
Worst severity
high
Content hash
742fd923c9186a34cedbdf9d…

Findings

  • high Any logged-in user can trigger a full site sync via ungated AJAX

    missing_capability

    Dashboard registers wp_ajax_get_objects_to_sync_count, wp_ajax_sync_wp_data, and wp_ajax_get_order_to_sync_all with no capability check and no nonce. The Tools UI is correctly restricted with add_management_page(..., 'manage_options'), but the AJAX actions are not, and load_scripts() enqueues press-sync.js (with admin-ajax.php) on every admin screen including profile.php, which Subscribers can open. Dashboard::init() (runs from the constructor on plugins_loaded) copies $_REQUEST['objects_to_sync'] into $this->objects_to_sync. A Subscriber POSTing action=sync_wp_data&objects_to_sync=user&paged=1 causes sync_wp_data_via_ajax() to delete_option(ps_synced_post_session_user) and Press_Sync::sync_object('user') to enumerate all local users (login, email, role, every usermeta key including session_tokens and capabilities; password hash is nulled in prepare_user_args_to_sync) and wp_remote_post them to ps_remote_domain with ps_remote_key. The remote sync_user() will create those users and add_role() their original roles, so this is both off-site exfiltration of the entire identity store and a content-overwrite/DoS against the configured peer. objects_to_sync=post|option|all likewise pushes private/draft posts and the options whitelist. No nonce means any same-site request from a logged-in cookie also works.

    includes/class-dashboard.php:62-64

    Recommendation

    In every AJAX handler call check_ajax_referer() and current_user_can('manage_options') before any work. Restrict admin_enqueue_scripts to the press-sync tools page. Do not take objects_to_sync from $_REQUEST on every request; read it only after the capability check.

  • high Broken stripos comparison globally disables WordPress HTTP SSRF protections

    ssrf

    Press_Sync::hooks() adds approve_localhost_urls to http_request_host_is_external for the lifetime of the request. The intended check was 'host contains .local', but the predicate is 0 <= stripos($host, '.local'). stripos() returns false when the needle is absent; in PHP 0 <= false is true because false casts to 0. The function therefore always returns true, for every host. wp_http_validate_url() consults this filter specifically to reject loopback, RFC1918, link-local, and 169.254.0.0/16 (cloud metadata) before download_url() and wp_safe_remote_* run. With the filter stuck open, core pingbacks/oEmbed and this plugin's own maybe_upload_remote_attachment() → download_url($attachment_url) (attachment_url comes from the REST /sync JSON body) will fetch http://169.254.169.254/latest/meta-data/ and internal admin interfaces. allow_sync_external_host() additionally forces the same filter to true around featured-image downloads. This is site-wide, not limited to Press Sync requests.

    includes/class-press-sync.php:906-913

    Recommendation

    Use false !== stripos( $host, '.local' ) (or str_ends_with) and only return true for that host. Remove the global filter if localhost sync is not required; if it is, allow-list exact hostnames. Keep allow_sync_external_host() scoped to an allow-listed remote domain rather than returning true for every host. Validate attachment_url against a remote-domain allow-list before download_url().

  • high SQL injection in REST GET /sync object pull via concatenated WHERE clause

    sql_injection

    sync_objects() is reachable unauthenticated whenever press_sync_key matches ps_key (permission_callback validate_sync_key; secret is taken from $_REQUEST and is placed on the query string by get_remote_url()). For GET it builds $where_clause = "ID IN ('" . implode("''", $objects) . "')" from $request->get_param('objects') with no escaping and passes it to get_objects_to_sync() → get_posts_to_sync(), which concatenates AND {$where_clause} into the SELECT before $wpdb->prepare(). REST JSON bodies are parsed from php://input and are not run through wp_magic_quotes, so a GET/JSON request with objects=['1') UNION SELECT user_pass,user_login,... --'] breaks out of the quoted IN list. That is beyond what the pull API is meant to return (it already dumps matching posts) — UNION can read wp_users.user_pass and wp_options. The same receive path interpolates p2p_from/p2p_to in get_post_id_by_press_sync_id() (numeric, no prepare) when Posts 2 Posts is active. Combined with finding 1, the key can be stolen first and this pull used without knowing it a priori; the key is also routinely logged because every client request puts press_sync_key in the URL.

    includes/class-api.php:199-204

    Recommendation

    Never concatenate request data into SQL. Accept only an array of integers, map absint, and pass them as $wpdb->prepare() IN (%d,%d,...). Remove the GET pull-by-ID clause or require manage_options in addition to the sync key. Move press_sync_key out of the query string (Authorization header or POST body) and compare with hash_equals(). Prepare the p2p_from/p2p_to lookups in get_post_id_by_press_sync_id() with %d.

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.