Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 3 additions & 26 deletions app/Helper/HtmlMeta.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Helper;

use Illuminate\Support\Facades\Log;
use Kovah\HtmlMeta\Exceptions\DisallowedIpException;
use Kovah\HtmlMeta\Exceptions\InvalidUrlException;
use Kovah\HtmlMeta\Exceptions\UnreachableUrlException;

Expand Down Expand Up @@ -32,10 +33,6 @@ public function getFromUrl(string $url, bool $flashAlerts = false): array
$this->url = $url;
$this->buildFallback();

if ($this->skipMetaForPrivateIP()) {
return $this->fallback;
}

try {
$this->meta = \Kovah\HtmlMeta\Facades\HtmlMeta::forUrl($url)->getMeta();
} catch (InvalidUrlException $e) {
Expand All @@ -44,7 +41,8 @@ public function getFromUrl(string $url, bool $flashAlerts = false): array
flash(trans('link.added_connection_error'), 'warning');
}
return $this->fallback;
} catch (UnreachableUrlException $e) {
} catch (DisallowedIpException|UnreachableUrlException $e) {
// DisallowedIpException catches all private and loopback IPs as well as hostnames resolving to those IPs
Log::warning($url . ': ' . $e->getMessage());
if ($flashAlerts) {
flash(trans('link.added_request_error'), 'warning');
Expand Down Expand Up @@ -119,25 +117,4 @@ protected function getThumbnail(): ?string

return $thumbnail;
}

protected function skipMetaForPrivateIP(): bool
{
$domain = parse_url($this->url, PHP_URL_HOST);

if (config('html-meta.allow_private_ip_ranges') !== false) {
return false;
}

if (filter_var($domain, FILTER_VALIDATE_IP) === false) {
// Hostname is not an IP address
return false;
}

if (filter_var($domain, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
// Hostname contains an IP address from the private or reserved ranges
return true;
}

return false;
}
}
55 changes: 37 additions & 18 deletions app/Http/Controllers/FetchController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
use App\Models\Link;
use App\Models\LinkList;
use App\Models\Tag;
use App\Rules\NoPrivateIpRule;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Masterminds\HTML5;
use Kovah\HtmlMeta\Exceptions\DisallowedIpException;
use Kovah\HtmlMeta\Exceptions\InvalidUrlException;
use Kovah\HtmlMeta\Exceptions\UnreachableUrlException;
use Kovah\HtmlMeta\Facades\HtmlMeta as HtmlMetaFacade;

class FetchController extends Controller
{
Expand Down Expand Up @@ -96,28 +99,44 @@ public static function checkForUpdates(): JsonResponse
public function htmlKeywordsFromUrl(Request $request): JsonResponse
{
$request->validate([
'url' => ['url', new NoPrivateIpRule],
'url' => ['url'],
]);

$url = $request->input('url');
$newRequest = setupHttpRequest(3);
$response = $newRequest->get($url);

if ($response->successful()) {
$html5 = new HTML5();
$dom = $html5->loadHTML($response->body());
$keywords = [];
/** @var \DOMElement $metaTag */
foreach ($dom->getElementsByTagName('meta') as $metaTag) {
if (strtolower($metaTag->getAttribute('name')) === 'keywords') {
$keywords = explode(',', $metaTag->getAttributeNode('content')?->value);
$keywords = array_map(fn($keyword) => trim(e($keyword)), $keywords);
array_push($keywords, ...$keywords);
}

try {
$response = HtmlMetaFacade::forUrl($url)->getResponse();
} catch (DisallowedIpException|InvalidUrlException|UnreachableUrlException) {
return response()->json(['keywords' => null]);
}

if (!$response?->successful()) {
return response()->json(['keywords' => null]);
}

return response()->json(['keywords' => $this->extractKeywords($response->body())]);
}

/**
* @return array<int, string>
*/
protected function extractKeywords(string $html): array
{
$html5 = new HTML5();
$dom = $html5->loadHTML($html);
$keywords = [];

/** @var \DOMElement $metaTag */
foreach ($dom->getElementsByTagName('meta') as $metaTag) {
if (strtolower($metaTag->getAttribute('name')) !== 'keywords') {
continue;
}
return response()->json(['keywords' => $keywords]);

$keywords = explode(',', $metaTag->getAttributeNode('content')?->value);
$keywords = array_map(fn($keyword) => trim(e($keyword)), $keywords);
array_push($keywords, ...$keywords);
}

return response()->json(['keywords' => null]);
return $keywords;
}
}
3 changes: 3 additions & 0 deletions app/Http/Controllers/Models/LinkController.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ public function show(Link $link): View
'tags' => function ($query) {
$query->visibleForUser();
},
'notes' => function ($query) {
$query->visibleForUser();
},
]);
return view('models.links.show', [
'pageTitle' => trans('link.link') . ': ' . $link->shortTitle(),
Expand Down
24 changes: 0 additions & 24 deletions app/Rules/NoPrivateIpRule.php

This file was deleted.

4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"composer/semver": "^3.3.1",
"doctrine/dbal": "^3.7",
"guzzlehttp/guzzle": "^7.0",
"kovah/laravel-html-meta": "^4.0",
"kovah/laravel-socialite-oidc": "^0.6",
"kovah/laravel-html-meta": "^4.3",
"kovah/laravel-socialite-oidc": "^0.7",
"laracasts/flash": "^3.1",
"laravel/fortify": "^1.7",
"laravel/framework": "^v11.48",
Expand Down
Loading
Loading