WP7 hardening: thisismyurl-external-link-control (v1.6148.2110) - #34
Merged
Conversation
Register a read-only ability, thisismyurl-external-link-control/scan-external-links,
that exposes the broken-link checker's most recent scan to AI agents and REST
clients. The ability reads stored scan results only — it never triggers a new
scan, so it returns instantly and makes no outbound HTTP requests of its own.
- New includes/abilities.php registers the ability on wp_abilities_api_init,
guarded by function_exists( 'wp_register_ability' ) for pre-6.9 WordPress.
Category 'site'; annotations readonly/idempotent true, destructive false;
show_in_rest true. permission_callback requires manage_options, matching the
cap used by the checker's admin notice, dashboard widget, AJAX handlers, and
the plugin's existing REST inventory route.
- Input schema: optional post_id, status (broken|unverified|all), limit.
- Output schema: links[] of { url, post_id, post_ids, status_code, message,
is_broken, bucket } plus a summary with broken/unverified/total/returned/
urls_checked/checked_at/has_run counts.
- Extract ELC_Link_Checker::get_results() as the single shaping path for the
stored RESULTS_OPTION; the ability calls it. (Follow-up: the dashboard widget
should be migrated onto the same accessor so there is one read path.)
- readme.txt: changelog entry under 1.6149. Version header left untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…activation defaults Fixes the audited findings on thisismyurl-external-link-control: - SECURITY (authenticated SSRF in ajax_recheck_link): sanitize the URL with esc_url_raw; require it to be a member of the current broken/unverified result set before fetching; reject hosts that resolve to non-public addresses (loopback, 169.254/16 cloud-metadata, RFC-1918, ::1, fe80::/10, fc00::/7) before any wp_remote_*. manage_options + nonce gate preserved. - Updater: wire the hardened TIMU_GitHub_Release_Updater (guarded after_install, timeout + UA, 200-check, 6h transient cache) via timu_boot_github_release_updater with the repo slug set; delete the buggy FWO_GitHub_Updater (updater.php) and its dead .distignore line. - P2 activation default surprise: master switch now ships OFF so activating no longer silently rewrites every external link sitewide (opt-in); readme discloses the weekly outbound crawler cron. - P2 uninstall incomplete: also delete timu_elc_domain_rules, timu_elc_broken_link_results, timu_elc_broken_link_ignored, and clear the timu_elc_broken_link_check cron event. - P1/P2 promise under-disclosure: readme Description now discloses the weekly outbound link-crawler and the /timu-elc/v1/inventory REST endpoint. - P2 perf: collect_external_urls() now queries fields=ids with no_found_rows=true (and skips meta/term cache priming), fetching content per-ID instead of pulling full post objects while paging. - P3: changelog entry 1.6150; Version header left unchanged per contract. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
WP 7 hardening pass for the External Link Control plugin, including a new WP 7 Abilities API report, security hardening around broken-link rechecks, updater replacement, safer activation defaults, and uninstall cleanup.
Changes:
- Added WP 7 Abilities API registration to expose the last broken-link scan as a read-only, capability-gated report.
- Hardened and refactored the broken-link checker (SSRF guards for “Recheck”, result shaping helper, and a paging/query adjustment).
- Swapped the legacy GitHub updater integration to the hardened shared release updater and updated docs/version stamping.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
updater.php |
Removes the legacy GitHub updater implementation. |
thisismyurl-external-link-control.php |
Stamps release version, changes activation defaults, adds Abilities include, and wires the new GitHub release updater loader. |
includes/class-elc-link-checker.php |
Adjusts URL collection query strategy, adds get_results() shaping, and adds SSRF guards + request sanitization for recheck. |
includes/abilities.php |
Registers a WP 7 Ability that returns the last broken/unverified links report with optional filters. |
uninstall.php |
Expands uninstall cleanup to remove all plugin options and clear the weekly cron hook. |
readme.txt |
Updates stable tag, adds outbound-network-activity disclosure, and adds changelog entries for the hardening/features. |
.distignore |
Updates distribution ignores to match the new updater file and removal of the legacy one. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+329
to
333
| foreach ( $post_ids as $post_id ) { | ||
| $post_id = (int) $post_id; | ||
| $post_content = (string) get_post_field( 'post_content', $post_id ); | ||
| if ( '' === $post_content ) { | ||
| continue; |
Comment on lines
+157
to
+160
| // Single source of truth for the last scan's stored results. | ||
| $checker = new ELC_Link_Checker(); | ||
| $report = $checker->get_results(); | ||
|
|
Comment on lines
+114
to
+118
| = 1.6150 = | ||
| * Security: hardened the dashboard widget's "Recheck" action against server-side request forgery (SSRF). A recheck now only re-probes a URL the most recent scan already surfaced as broken or unverified, and refuses any host that resolves to a non-public address (loopback, link-local / cloud-metadata `169.254.169.254`, or RFC-1918 private space). The existing capability + nonce gate is unchanged. | ||
| * Security: swapped the GitHub update checker to the hardened release updater — the API request now carries a User-Agent and a timeout, checks for an HTTP 200 before trusting the response, caches results for six hours, and scopes its post-install folder move to this plugin only. | ||
| * Change: link filtering now ships OFF on activation. Activating the plugin no longer silently rewrites every external link sitewide; turn the master switch on from Tools > Link Control when you are ready. The new-tab / nofollow / UGC defaults are pre-set so the behaviour is in place the moment you enable filtering. | ||
| * Fix: uninstall now removes the per-domain rules, broken-link scan results, and ignore-list options, and clears the weekly broken-link cron event, instead of leaving them behind. |
Comment on lines
+329
to
333
| foreach ( $post_ids as $post_id ) { | ||
| $post_id = (int) $post_id; | ||
| $post_content = (string) get_post_field( 'post_content', $post_id ); | ||
| if ( '' === $post_content ) { | ||
| continue; |
Comment on lines
+157
to
+160
| // Single source of truth for the last scan's stored results. | ||
| $checker = new ELC_Link_Checker(); | ||
| $report = $checker->get_results(); | ||
|
|
Comment on lines
+114
to
+118
| = 1.6150 = | ||
| * Security: hardened the dashboard widget's "Recheck" action against server-side request forgery (SSRF). A recheck now only re-probes a URL the most recent scan already surfaced as broken or unverified, and refuses any host that resolves to a non-public address (loopback, link-local / cloud-metadata `169.254.169.254`, or RFC-1918 private space). The existing capability + nonce gate is unchanged. | ||
| * Security: swapped the GitHub update checker to the hardened release updater — the API request now carries a User-Agent and a timeout, checks for an HTTP 200 before trusting the response, caches results for six hours, and scopes its post-install folder move to this plugin only. | ||
| * Change: link filtering now ships OFF on activation. Activating the plugin no longer silently rewrites every external link sitewide; turn the master switch on from Tools > Link Control when you are ready. The new-tab / nofollow / UGC defaults are pre-set so the behaviour is in place the moment you enable filtering. | ||
| * Fix: uninstall now removes the per-domain rules, broken-link scan results, and ignore-list options, and clears the weekly broken-link cron event, instead of leaving them behind. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WP 7 hardening pass — thisismyurl-external-link-control.
Recommendation: TEST (net-new/mechanism change — activation + feature test before merge/tag).
Changes (4 commits):
Audit + scorecard: clients/thisismyurl/plugin-line-hardening/HARDENING-AUDIT.md
All files php -l clean. Version stamped X.6148.2110 (Toronto).