PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.14.1
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.14.1
2.14.2 2.14.1 2.14.0 2.13.0 2.12.0 2.11.0 2.10.0 2.9.0 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 All 57 releases
← All changes | includes/ai/class-content-brief-generator.php +4 -29 2.14.2 → 2.14.1 View file →
@@ -959,32 +959,8 @@
959 959 }
960 960
961 961 return $normalized;
962 962 }
963 - /**
964 - * The URL to check and fetch for a competitor URL the user entered, or
965 - * null when it is not a valid URL.
966 - *
967 - * A competitor page on an internationalised domain, or with a Bengali or
968 - * Arabic slug, is a valid URL, so the syntax check is Url_Validator's.
969 - * What comes back is the ASCII form (punycode host, percent-encoded path),
970 - * and both the SSRF guard and the fetch use it: the guard cannot resolve
971 - * a Unicode host name, and it must check exactly the URL that is then
972 - * requested.
973 - *
974 - * @since 2.14.2
975 - *
976 - * @param string $url Trimmed URL as entered.
977 - * @return string|null ASCII URL, or null when invalid.
978 - */
979 - private function competitor_fetch_url(string $url): ?string {
980 - if ('' === $url || !\ThinkRank\Core\Url_Validator::is_valid($url)) {
981 - return null;
982 - }
983 -
984 - return \ThinkRank\Core\Url_Validator::to_ascii($url);
985 - }
986 -
987 963 private function analyze_competitor_urls(array $urls): string {
988 964 $analysis_results = [];
989 965 $failed_urls = [];
990 966
@@ -992,10 +968,9 @@
992 968 $urls = array_slice($urls, 0, 3);
993 969
994 970 foreach ($urls as $url) {
995 971 $url = trim($url);
996 - $fetch_url = $this->competitor_fetch_url($url);
997 - if (null === $fetch_url) {
972 + if (empty($url) || !filter_var($url, FILTER_VALIDATE_URL)) {
998 973 $failed_urls[] = $url . " (invalid URL)";
999 974 continue;
1000 975 }
1001 976
@@ -1001,14 +976,14 @@
1001 976
1002 977 // SSRF guard: only fetch public http/https hosts. Blocks loopback,
1003 978 // link-local (cloud metadata), private and reserved ranges before any
1004 979 // request is made.
1005 - if (!$this->is_safe_public_url($fetch_url)) {
980 + if (!$this->is_safe_public_url($url)) {
1006 981 $failed_urls[] = $url . " (blocked: non-public host)";
1007 982 continue;
1008 983 }
1009 984
1010 - $content_data = $this->scrape_competitor_content($fetch_url);
985 + $content_data = $this->scrape_competitor_content($url);
1011 986 if ($content_data) {
1012 987 $analysis_results[] = $this->format_competitor_analysis($url, $content_data);
1013 988 } else {
1014 989 $failed_urls[] = $url . " (failed to scrape)";
@@ -1254,9 +1229,9 @@
1254 1229 // Add heading structure
1255 1230 if (!empty($content_data['headings'])) {
1256 1231 $analysis .= "\nCONTENT STRUCTURE:\n";
1257 1232 foreach ($content_data['headings'] as $level => $headings) {
1258 - $analysis .= "- " . strtoupper($level) . " (" . count($headings) . "): " . implode(', ', array_slice($headings, 0, 3));
1233 + $analysis .= "- " . strtoupper($level) . " ({count}): " . implode(', ', array_slice($headings, 0, 3));
1259 1234 if (count($headings) > 3) {
1260 1235 $analysis .= "... (+" . (count($headings) - 3) . " more)";
1261 1236 }
1262 1237 $analysis .= "\n";