type !== 'email') { throw new RunnerException('Gravatar only supports "email" targets.'); } if (! filter_var($target->value, FILTER_VALIDATE_EMAIL)) { throw new RunnerException('Value is not a valid email address.'); } // Gravatar's public profile JSON endpoint now requires API auth // (returns 404 even for real profiles without a key), but the // plain avatar-existence check is still open and reliable. $hash = md5(strtolower(trim($target->value))); $avatarUrl = "https://www.gravatar.com/avatar/{$hash}"; $response = Http::timeout((int) config('osint.run_timeout')) ->get($avatarUrl, ['d' => '404']); if ($response->status() !== 200 && $response->status() !== 404) { throw new RunnerException('Gravatar request failed with status '.$response->status().'.'); } $hasAvatar = $response->status() === 200; return new RunnerResult( raw: [ 'has_avatar' => $hasAvatar, 'avatar_url' => $hasAvatar ? $avatarUrl : null, 'profile_url' => "https://gravatar.com/{$hash}", ], summary: $hasAvatar ? 'Gravatar image found — this email is registered with Gravatar.' : 'No Gravatar found for this email.', ); } }