Every CVE Search/ Get API key

CVE-2026-50552

Koel: Server-Side Request Forgery (SSRF) in radio station creation due to missing validation bail

6.3 MediumFix available

Koel is a free, open-source music streaming solution. Prior to version 9.7.1, Koel contains a Server-Side Request Forgery (SSRF) vulnerability in the radio station creation endpoint (POST /api/radio/stations). The url field validation rules are declared without the bail keyword, so the HasAudioContentType rule — which issues HTTP requests to the supplied URL — still executes even after the SafeUrl rule has rejected the URL as pointing to a private/reserved address. Any authenticated, non-admin user can therefore coerce the server into making HEAD/GET requests to arbitrary internal hosts. This issue has been patched in version 9.7.1.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
Low
Integrity
Low
Availability
Low

Exploit likelihood

0.17%Up 0.1 points since Jun 12, 2026
90%50%10%1%

What each source reports

CVE List Score used 6.3

Koel: Server-Side Request Forgery (SSRF) in radio station creation due to missing validation bail

Koel is a free, open-source music streaming solution. Prior to version 9.7.1, Koel contains a Server-Side Request Forgery (SSRF) vulnerability in the radio station creation endpoint (POST /api/radio/stations). The url field validation rules are declared without the bail keyword, so the HasAudioContentType rule — which issues HTTP requests to the supplied URL — still executes even after the SafeUrl rule has rejected the URL as pointing to a private/reserved address. Any authenticated, non-admin user can therefore coerce the server into making HEAD/GET requests to arbitrary internal hosts. This issue has been patched in version 9.7.1.

Record
CVE-2026-50552
CVSS 3.1
6.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L
Weakness
CWE-918
Updated
2026-11-02
EUVDEUVD-2026-36546 6.3

Koel is a free, open-source music streaming solution. Prior to version 9.7.1, Koel contains a Server-Side Request Forgery (SSRF) vulnerability in the radio station creation endpoint (POST /api/radio/stations). The url field validation rules are declared without the bail keyword, so the HasAudioContentType rule — which issues HTTP requests to the supplied URL — still executes even after the SafeUrl rule has rejected the URL as pointing to a private/reserved address. Any authenticated, non-admin user can therefore coerce the server into making HEAD/GET requests to arbitrary internal hosts. This issue has been patched in version 9.7.1.

Record
EUVD-2026-36546
CVSS 3.1
6.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L
Updated
2026-11-02
GitHub AdvisoriesGHSA-jr4p-4xjh-fwvw 6.3

Koel: Server-Side Request Forgery (SSRF) in radio station creation due to missing validation bail

Summary

Koel v9.5.0 contains a Server-Side Request Forgery (SSRF) vulnerability in the radio station creation endpoint (POST /api/radio/stations). The url field validation rules are declared without the bail keyword, so the HasAudioContentType rule — which issues HTTP requests to the supplied URL — still executes even after the SafeUrl rule has rejected the URL as pointing to a private/reserved address. Any authenticated, non-admin user can therefore coerce the server into making HEAD/GET requests to arbitrary internal hosts.

This is a blind SSRF: the response body is never returned to the client, but the two distinct validation error messages form a reliable internal-network reachability oracle.

Severity

Medium — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N (5.4)

Authenticated (low-privilege) actor; blind SSRF (no response-body exfiltration). Confidentiality (L) reflects the internal reachability oracle; Integrity (L) reflects state-changing internal endpoints that act on GET/HEAD.

Affected Component

  • Version: Koel v9.5.0 (latest release, HEAD b2e9a34, verified 2026-05-30)
  • Endpoint: POST /api/radio/stations
  • Files:
    • app/Http/Requests/API/Radio/RadioStationStoreRequest.php (missing bail)
    • app/Rules/HasAudioContentType.php (unguarded HTTP fetch)

Root Cause

app/Http/Requests/API/Radio/RadioStationStoreRequest.php (lines 25-34):

'url' => [
    'required',
    'url',
    Rule::unique('radio_stations')->where(function ($query) {
        return $query->where('user_id', $this->user()->id);
    }),
    new SafeUrl(),
    new HasAudioContentType(),
],

In Laravel, validation rules within a single attribute run in sequence and do not stop on the first failure unless bail is present or FormRequest::$stopOnFirstFailure is set. Neither is the case here (App\Http\Requests\Request does not override stopOnFirstFailure, and authorize() simply returns true).

For a directly-supplied private/reserved address (e.g. http://169.254.169.254, http://127.0.0.1:6379, http://192.168.0.1):

  1. SafeUrl (app/Rules/SafeUrl.php lines 45-49) calls isPublicHost($uri->host()), fails it, calls $fail(...) and returns. SafeUrl itself does not make a request in this path — good.

  2. Because there is no bail, validation continues to HasAudioContentType.

  3. HasAudioContentType::resolveContentType() (app/Rules/HasAudioContentType.php lines 41-57) issues the request with no IP/host validation of its own:

    private function resolveContentType(string $url): string { try { $response = Http::head($url); // <-- SSRF if ($response->successful()) { return $response->header('Content-Type'); } } catch (Throwable) { }

     // Falls back to a streaming GET
     $response = Http::withHeaders(['Icy-MetaData' => '1'])
         ->withOptions(['stream' => true])
         ->get($url);                               // <-- SSRF
     return $response->header('Content-Type');
    

    }

The rule's own docblock (line 13-14) states "Should be used after SafeUrl to ensure the URL is safe to reach." — that assumption is silently violated by the missing bail.

Authorization Context

The endpoint is reachable by any authenticated user, not just admins:

  • routes/api.base.php line 108 wraps the route group in Route::middleware('auth') only.
  • routes/api.base.php line 268: Route::apiResource('stations', RadioStationController::class).
  • RadioStationController::store() (lines 31-37) carries only #[DisabledInDemo] — no $this->authorize(...), no #[RequiresPlus], no permission check.

Reachability Oracle (Information Disclosure)

HasAudioContentType::validate() returns two distinct messages:

  • Host unreachable / request throws → "The url couldn't be reached." (line 26)
  • Host reachable but Content-Type is not audio/* → "The url doesn't look like a valid radio station URL." (line 32)

By diffing these responses an attacker can…

Record
GHSA-jr4p-4xjh-fwvw
CVSS 3.1
6.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L
Weakness
CWE-918
Updated
2026-11-02
NVD 6.3

Koel is a free, open-source music streaming solution. Prior to version 9.7.1, Koel contains a Server-Side Request Forgery (SSRF) vulnerability in the radio station creation endpoint (POST /api/radio/stations). The url field validation rules are declared without the bail keyword, so the HasAudioContentType rule — which issues HTTP requests to the supplied URL — still executes even after the SafeUrl rule has rejected the URL as pointing to a private/reserved address. Any authenticated, non-admin user can therefore coerce the server into making HEAD/GET requests to arbitrary internal hosts. This issue has been patched in version 9.7.1.

Record
CVE-2026-50552
CVSS 3.1
6.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L
Weakness
CWE-918
Updated
2026-11-02
OSV 6.3

Koel: Server-Side Request Forgery (SSRF) in radio station creation due to missing validation bail

Koel is a free, open-source music streaming solution. Prior to version 9.7.1, Koel contains a Server-Side Request Forgery (SSRF) vulnerability in the radio station creation endpoint (POST /api/radio/stations). The url field validation rules are declared without the bail keyword, so the HasAudioContentType rule — which issues HTTP requests to the supplied URL — still executes even after the SafeUrl rule has rejected the URL as pointing to a private/reserved address. Any authenticated, non-admin user can therefore coerce the server into making HEAD/GET requests to arbitrary internal hosts. This issue has been patched in version 9.7.1.

Record
CVE-2026-50552
CVSS 3.1
6.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L
Weakness
CWE-918
Updated
2026-11-02
OSVGHSA-jr4p-4xjh-fwvw 6.3

Koel: Server-Side Request Forgery (SSRF) in radio station creation due to missing validation bail

Summary

Koel v9.5.0 contains a Server-Side Request Forgery (SSRF) vulnerability in the radio station creation endpoint (POST /api/radio/stations). The url field validation rules are declared without the bail keyword, so the HasAudioContentType rule — which issues HTTP requests to the supplied URL — still executes even after the SafeUrl rule has rejected the URL as pointing to a private/reserved address. Any authenticated, non-admin user can therefore coerce the server into making HEAD/GET requests to arbitrary internal hosts.

This is a blind SSRF: the response body is never returned to the client, but the two distinct validation error messages form a reliable internal-network reachability oracle.

Severity

Medium — CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N (5.4)

Authenticated (low-privilege) actor; blind SSRF (no response-body exfiltration). Confidentiality (L) reflects the internal reachability oracle; Integrity (L) reflects state-changing internal endpoints that act on GET/HEAD.

Affected Component

  • Version: Koel v9.5.0 (latest release, HEAD b2e9a34, verified 2026-05-30)
  • Endpoint: POST /api/radio/stations
  • Files:
    • app/Http/Requests/API/Radio/RadioStationStoreRequest.php (missing bail)
    • app/Rules/HasAudioContentType.php (unguarded HTTP fetch)

Root Cause

app/Http/Requests/API/Radio/RadioStationStoreRequest.php (lines 25-34):

'url' => [
    'required',
    'url',
    Rule::unique('radio_stations')->where(function ($query) {
        return $query->where('user_id', $this->user()->id);
    }),
    new SafeUrl(),
    new HasAudioContentType(),
],

In Laravel, validation rules within a single attribute run in sequence and do not stop on the first failure unless bail is present or FormRequest::$stopOnFirstFailure is set. Neither is the case here (App\Http\Requests\Request does not override stopOnFirstFailure, and authorize() simply returns true).

For a directly-supplied private/reserved address (e.g. http://169.254.169.254, http://127.0.0.1:6379, http://192.168.0.1):

  1. SafeUrl (app/Rules/SafeUrl.php lines 45-49) calls isPublicHost($uri->host()), fails it, calls $fail(...) and returns. SafeUrl itself does not make a request in this path — good.

  2. Because there is no bail, validation continues to HasAudioContentType.

  3. HasAudioContentType::resolveContentType() (app/Rules/HasAudioContentType.php lines 41-57) issues the request with no IP/host validation of its own:

    private function resolveContentType(string $url): string { try { $response = Http::head($url); // <-- SSRF if ($response->successful()) { return $response->header('Content-Type'); } } catch (Throwable) { }

     // Falls back to a streaming GET
     $response = Http::withHeaders(['Icy-MetaData' => '1'])
         ->withOptions(['stream' => true])
         ->get($url);                               // <-- SSRF
     return $response->header('Content-Type');
    

    }

The rule's own docblock (line 13-14) states "Should be used after SafeUrl to ensure the URL is safe to reach." — that assumption is silently violated by the missing bail.

Authorization Context

The endpoint is reachable by any authenticated user, not just admins:

  • routes/api.base.php line 108 wraps the route group in Route::middleware('auth') only.
  • routes/api.base.php line 268: Route::apiResource('stations', RadioStationController::class).
  • RadioStationController::store() (lines 31-37) carries only #[DisabledInDemo] — no $this->authorize(...), no #[RequiresPlus], no permission check.

Reachability Oracle (Information Disclosure)

HasAudioContentType::validate() returns two distinct messages:

  • Host unreachable / request throws → "The url couldn't be reached." (line 26)
  • Host reachable but Content-Type is not audio/* → "The url doesn't look like a valid radio station URL." (line 32)

By diffing these responses an attacker can…

Record
GHSA-jr4p-4xjh-fwvw
CVSS 3.1
6.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L
Weakness
CWE-918
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
phanan/koel
Packagist
through 9.7.0
9.7.1
phanan/koel
Packagist
before 9.7.1, plus listed versions
1.0.0-betav0.0.0-betav1.1.1v1.1.2v2.0.0
Show 45 moreShow less
v2.0.1v2.0.2v2.1.0v2.2.0v2.2.1v3.0.0v3.0.1v3.1.0v3.1.1v3.2.0v3.3.0v3.3.1v3.4.0v3.4.1v3.5.0v3.5.1v3.5.2v3.5.3v3.5.4v3.5.5v3.6.0v3.6.1v3.6.2v3.7.0v3.7.1v3.7.2v4.0.0v4.1.0v4.1.1v4.2.0v4.2.1v4.2.2v4.3.0v4.3.1v4.4.0v5.0.0v5.0.1v5.0.2v5.1.0v5.1.1v5.1.10v5.1.11v5.1.12v5.1.13v5.1.14
9.7.1

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-50552 is a medium vulnerability (CVSS 6.3) in Koel, published June 12, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.17%, the 6th percentile. Fixed in phanan/koel 9.7.1.

References

This page doesn't list everything

Left out here: 120 versions and the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open