Skip to content

Security: fix SSRF in POST /api/imports — validate VideoUrl against YouTube allowlist#178

Open
davidortinau wants to merge 1 commit into
mainfrom
op-fix-ssrf-video-url-validation
Open

Security: fix SSRF in POST /api/imports — validate VideoUrl against YouTube allowlist#178
davidortinau wants to merge 1 commit into
mainfrom
op-fix-ssrf-video-url-validation

Conversation

@davidortinau

Copy link
Copy Markdown
Owner

What

An authenticated user could submit any URL to POST /api/imports and the server would make an outbound HTTP request to it before performing any host or scheme validation. This includes Azure IMDS (http://169.254.169.254/metadata/instance), RFC-1918 addresses, and any other internal network resource — potentially exposing managed-identity credentials or internal services.

Fix

Added a YouTube allowlist guard in StartImport (before the VideoImport record is created or the pipeline is started). Any VideoUrl that does not satisfy all three conditions is rejected immediately with 400 Bad Request:

  • Parses as an absolute URI
  • Scheme is https
  • Host is exactly www.youtube.com, youtube.com, or youtu.be

The fix is API-boundary-only — no changes to VideoImportPipelineService or downstream services were needed.

Changes

File Change
src/SentenceStudio.Api/ImportEndpoints.cs 6-line allowlist guard after the null/whitespace check
tests/SentenceStudio.Api.Tests/ImportEndpointsSsrfTests.cs New test class (13 cases)
tests/SentenceStudio.Api.Tests/Infrastructure/TestJwtGenerator.cs Optional userProfileId param added to GenerateToken

Tests

ImportEndpointsSsrfTests covers:

Rejection (400) cases:

  • Azure IMDS: http://169.254.169.254/metadata/instance, http://169.254.169.254/
  • RFC-1918: http://10.0.0.1/internal, http://192.168.1.1/admin
  • Loopback: http://localhost/evil
  • Valid YouTube host, wrong scheme: http://www.youtube.com/...
  • Non-YouTube HTTPS host: https://evil.com/...
  • Wrong scheme altogether: ftp://www.youtube.com/...
  • Unparseable input: not-a-url-at-all
  • Empty string

Acceptance (non-400) cases:

  • https://www.youtube.com/watch?v=...
  • https://youtube.com/watch?v=...
  • https://youtu.be/...

Verification

  • Build passes
  • 120 unit/classification tests pass
  • 32 integration tests fail with a pre-existing IChatClient/AiService DI error that exists on main before this branch — not caused by this change

…n POST /api/imports

Add a YouTube URL allowlist guard in StartImport before the import record
is created or the pipeline is started. Any URL that is not an https:// URL
on www.youtube.com, youtube.com, or youtu.be is rejected with 400 Bad Request.

- ImportEndpoints.cs: insert 6-line guard after the null check (lines 67-72)
- TestJwtGenerator.cs: add optional userProfileId parameter so integration
  tests can supply the claim required by the /api/imports endpoint
- ImportEndpointsSsrfTests.cs: 13 new tests covering Azure IMDS, RFC-1918
  addresses, loopback, non-https YouTube, non-YouTube HTTPS hosts, unparseable
  input, and three valid YouTube URLs that must pass the guard

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant