queue) ? $scripts->queue : array(); foreach ($scripts->registered as $handle => $dep) { if (empty($dep->src)) { continue; } $src = self::absolute_url($dep->src); $origin = ASH_CSP_Normalizer::origin_from_url($src); if ($origin === '') { continue; } $from = self::detected_from_src($src); if ($from === '' && in_array($handle, $queued, true)) { $from = __('Plugin script enqueue', 'abdal-security-headers'); } $findings[] = array( 'origin' => $origin, 'directive' => 'script-src', 'resource_type' => 'script', 'method' => 'static', 'page' => $page, 'detected_from' => $from, ); } return $findings; } /** * @param string $page Page path. * @return array */ private static function scan_styles($page) { $findings = array(); $styles = wp_styles(); if (!($styles instanceof WP_Styles)) { return $findings; } foreach ($styles->registered as $dep) { if (empty($dep->src)) { continue; } $src = self::absolute_url($dep->src); $origin = ASH_CSP_Normalizer::origin_from_url($src); if ($origin === '') { continue; } $findings[] = array( 'origin' => $origin, 'directive' => 'style-src', 'resource_type' => 'stylesheet', 'method' => 'static', 'page' => $page, 'detected_from' => self::detected_from_src($src), ); } return $findings; } /** * @param string $page Page path. * @return array */ private static function scan_core_endpoints($page) { $findings = array(); $targets = array( array(rest_url(), 'connect-src', 'fetch', __('WordPress REST API', 'abdal-security-headers')), array(admin_url('admin-ajax.php'), 'connect-src', 'fetch', __('WordPress AJAX', 'abdal-security-headers')), array(home_url('/'), 'form-action', 'form', __('WordPress site URL', 'abdal-security-headers')), ); foreach ($targets as $target) { $origin = ASH_CSP_Normalizer::origin_from_url($target[0]); if ($origin === '') { continue; } $findings[] = array( 'origin' => $origin, 'directive' => $target[1], 'resource_type' => $target[2], 'method' => 'static', 'page' => $page, 'detected_from' => $target[3], ); } return $findings; } /** * @param string $page Page path. * @return array */ private static function scan_media($page) { $findings = array(); $urls = array(); $icon = get_site_icon_url(); if (is_string($icon) && $icon !== '') { $urls[] = $icon; } $logo_id = get_theme_mod('custom_logo'); if ($logo_id) { $logo = wp_get_attachment_url($logo_id); if (is_string($logo) && $logo !== '') { $urls[] = $logo; } } $attachments = get_posts(array( 'post_type' => 'attachment', 'post_status' => 'inherit', 'posts_per_page' => 30, 'fields' => 'ids', 'orderby' => 'date', 'order' => 'DESC', 'no_found_rows' => true, )); foreach ($attachments as $attachment_id) { $url = wp_get_attachment_url($attachment_id); if (is_string($url) && $url !== '') { $urls[] = $url; } } foreach (array_unique($urls) as $url) { $origin = ASH_CSP_Normalizer::origin_from_url($url); if ($origin === '') { continue; } $findings[] = array( 'origin' => $origin, 'directive' => 'img-src', 'resource_type' => 'image', 'method' => 'static', 'page' => $page, 'detected_from' => self::detected_from_src($url), ); } return $findings; } /** * @param string $src Script or style source. * @return string */ private static function absolute_url($src) { $src = (string) $src; if ($src === '') { return ''; } if (strpos($src, '//') === 0) { return (is_ssl() ? 'https:' : 'http:') . $src; } if (strpos($src, 'http://') !== 0 && strpos($src, 'https://') !== 0) { return site_url($src); } return $src; } /** * Identify a plugin or theme from a local URL. Never guess remote vendors. * * @param string $src Absolute URL. * @return string */ private static function detected_from_src($src) { $path = (string) wp_parse_url($src, PHP_URL_PATH); if ($path === '') { return ''; } if (preg_match('#/wp-content/plugins/([^/]+)/#', $path, $match)) { return $match[1]; } if (preg_match('#/wp-content/themes/([^/]+)/#', $path, $match)) { return $match[1]; } if (strpos($path, '/wp-includes/') !== false || strpos($path, '/wp-admin/') !== false) { return __('WordPress core', 'abdal-security-headers'); } return ''; } }