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

daycry / jobs / 24850441053

23 Apr 2026 05:54PM UTC coverage: 52.404% (-1.5%) from 53.938%
24850441053

push

github

daycry
Fixes

104 of 219 new or added lines in 42 files covered. (47.49%)

14 existing lines in 9 files now uncovered.

1210 of 2309 relevant lines covered (52.4%)

4.37 hits per line

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

0.0
/src/Jobs/UrlJob.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of Daycry Queues.
7
 *
8
 * (c) Daycry <daycry9@proton.me>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace Daycry\Jobs\Jobs;
15

16
use Daycry\Jobs\Exceptions\JobException;
17
use Daycry\Jobs\Interfaces\JobInterface;
18
use Daycry\Jobs\Job;
19
use Daycry\Jobs\Traits\InteractsWithCurrentJob;
20

21
/**
22
 * Performs an HTTP request using CodeIgniter curlrequest service.
23
 * Payload: ['method' => 'GET'|'POST'|..., 'url' => string, 'options' => array(optional)].
24
 * Returns raw response body string.
25
 */
26
class UrlJob extends Job implements JobInterface
27
{
28
    use InteractsWithCurrentJob;
29

30
    private const ALLOWED_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];
31

32
    public function handle(mixed $payload): mixed
33
    {
NEW
34
        $this->validatePayload($payload);
×
35

NEW
36
        $options = $payload['options'] ?? [];
×
37
        // Prevent disabling SSL verification via options
NEW
38
        unset($options['verify'], $options[CURLOPT_SSL_VERIFYPEER], $options[CURLOPT_SSL_VERIFYHOST]);
×
39

NEW
40
        return service('curlrequest')->request($payload['method'], $payload['url'], $options)->getBody();
×
41
    }
42

43
    private function validatePayload(mixed $payload): void
44
    {
NEW
45
        if (! is_array($payload)) {
×
NEW
46
            throw JobException::validationError('UrlJob payload must be an array with method and url keys.');
×
47
        }
48

NEW
49
        if (empty($payload['url']) || ! is_string($payload['url'])) {
×
NEW
50
            throw JobException::validationError('UrlJob payload must contain a valid url string.');
×
51
        }
52

NEW
53
        if (empty($payload['method']) || ! is_string($payload['method'])) {
×
NEW
54
            throw JobException::validationError('UrlJob payload must contain a valid method string.');
×
55
        }
56

NEW
57
        $method = strtoupper($payload['method']);
×
NEW
58
        if (! in_array($method, self::ALLOWED_METHODS, true)) {
×
NEW
59
            throw JobException::forInvalidMethod($method);
×
60
        }
61

NEW
62
        $url = $payload['url'];
×
NEW
63
        if (filter_var($url, FILTER_VALIDATE_URL) === false) {
×
NEW
64
            throw JobException::validationError('UrlJob payload contains an invalid URL.');
×
65
        }
66

NEW
67
        $this->blockInternalUrls($url);
×
68
    }
69

70
    /**
71
     * Prevent Server-Side Request Forgery (SSRF) by blocking requests to internal/private IPs.
72
     */
73
    private function blockInternalUrls(string $url): void
74
    {
NEW
75
        $host = parse_url($url, PHP_URL_HOST);
×
NEW
76
        if ($host === null || $host === false) {
×
NEW
77
            throw JobException::validationError('UrlJob could not parse host from URL.');
×
78
        }
79

80
        // Resolve hostname to IP (catches DNS rebinding for common cases)
NEW
81
        $ip = gethostbyname($host);
×
82

NEW
83
        if (! filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
×
NEW
84
            throw JobException::validationError('UrlJob does not allow requests to private or reserved IP ranges.');
×
85
        }
86
    }
87
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc