• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

plank / laravel-mediable / 29219995787

13 Jul 2026 02:40AM UTC coverage: 93.691% (-0.6%) from 94.27%
29219995787

Pull #391

github

frasmage
add 7.x upgrade instructions
Pull Request #391: 7.x

187 of 211 new or added lines in 8 files covered. (88.63%)

1 existing line in 1 file now uncovered.

1589 of 1696 relevant lines covered (93.69%)

162.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.16
/src/SourceAdapters/RemoteUrlAdapter.php
1
<?php
2
declare(strict_types=1);
3

4
namespace Plank\Mediable\SourceAdapters;
5

6
use GuzzleHttp\Psr7\Utils;
7
use Plank\Mediable\Exceptions\MediaUpload\ConfigurationException;
8

9
/**
10
 * URL Adapter.
11
 *
12
 * Adapts a string representing a URL
13
 */
14
class RemoteUrlAdapter extends StreamAdapter
15
{
16
    protected string $url;
17

18
    public function __construct(string $source)
19
    {
20
        $this->validateUrl($source);
84✔
21
        $this->url = $source;
42✔
22
        try {
23
            $resource = Utils::tryFopen($source, 'rb');
42✔
24
            $stream = Utils::streamFor($resource);
36✔
25
        } catch (\RuntimeException $e) {
6✔
26
            throw ConfigurationException::invalidSource(
6✔
27
                "Failed to connect to URL: {$e->getMessage()}",
6✔
28
                $e
6✔
29
            );
6✔
30
        }
31
        parent::__construct(
36✔
32
            $stream
36✔
33
        );
36✔
34
    }
35

36
    private function validateUrl(string $url): void
37
    {
38
        $allowedSchemes = (array) config('mediable.allowed_remote_schemes', ['https']);
84✔
39
        $parsed = parse_url($url);
84✔
40
        $scheme = $parsed['scheme'] ?? '';
84✔
41
        $host = $parsed['host'] ?? '';
84✔
42
        if (empty($host) || empty($scheme)) {
84✔
NEW
43
            throw ConfigurationException::invalidSource(
×
NEW
44
                'Remote URL must include a valid scheme and host.'
×
NEW
45
            );
×
46
        }
47

48
        if (!empty($allowedSchemes) && !in_array($scheme, $allowedSchemes, true)) {
84✔
49
            throw ConfigurationException::invalidSource(
12✔
50
                "Remote URL scheme '{$parsed['scheme']}' is not allowed."
12✔
51
            );
12✔
52
        }
53

54
        $allowedHosts = (array) config('mediable.allowed_remote_hosts', []);
78✔
55
        if (!empty($allowedHosts)) {
78✔
56
            foreach ($allowedHosts as $allowedHost) {
12✔
57
                if (fnmatch(strtolower($allowedHost), strtolower($host))) {
12✔
58
                    return; // Host is allowed, exit validation
12✔
59
                }
60
            }
61
            throw ConfigurationException::invalidSource(
12✔
62
                'Remote URL host is not in the allowlist.'
12✔
63
            );
12✔
64
        } else {
65
            // If no allowed hosts are specified, automatically prevent private IPs
66
            $ip  = gethostbyname($host);
66✔
67
            $isPublic = filter_var(
66✔
68
                $ip,
66✔
69
                FILTER_VALIDATE_IP,
66✔
70
                FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
66✔
71
            );
66✔
72
            if (!$isPublic) {
66✔
73
                throw ConfigurationException::invalidSource(
36✔
74
                    'Private IP ranges are not permitted for remote URLs.'
36✔
75
                );
36✔
76
            }
77
        }
78
    }
79

80
    /**
81
     * {@inheritdoc}
82
     */
83
    public function path(): ?string
84
    {
85
        return $this->url;
×
86
    }
87

88
    /**
89
     * {@inheritdoc}
90
     */
91
    public function filename(): ?string
92
    {
93
        return pathinfo(
6✔
94
            parse_url($this->url, PHP_URL_PATH),
6✔
95
            PATHINFO_FILENAME
6✔
96
        ) ?: null;
6✔
97
    }
98

99
    /**
100
     * {@inheritdoc}
101
     */
102
    public function extension(): ?string
103
    {
104
        return pathinfo(
6✔
105
            parse_url($this->url, PHP_URL_PATH),
6✔
106
            PATHINFO_EXTENSION
6✔
107
        ) ?: null;
6✔
108
    }
109
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc