Skip to content

Fix SSRF on TOCTOU#4344

Merged
ildyria merged 3 commits into
masterfrom
ssrf-toctou
May 12, 2026
Merged

Fix SSRF on TOCTOU#4344
ildyria merged 3 commits into
masterfrom
ssrf-toctou

Conversation

@ildyria
Copy link
Copy Markdown
Member

@ildyria ildyria commented May 11, 2026

Summary by CodeRabbit

  • New Features

    • Option to use fopen for URL imports (configurable).
  • Improvements

    • Centralized URL validation that returns structured validation results.
    • Stronger network/security checks and clearer import error reporting.
    • Download handling now preserves detected MIME and better resolves file extensions.
  • Bug Fixes

    • Validation/form handling updated to consistently pass validated URL objects through import flow.
  • Tests

    • New and updated unit tests covering URL validation and URL-based import validation.

Review Change Stack

@ildyria ildyria requested a review from a team as a code owner May 11, 2026 18:22
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

📝 Walkthrough

Walkthrough

New UrlValidatedDTO and UrlValidation service centralize URL validation; requests are preprocessed into DTOs, PhotoUrlRule and FromUrl action consume DTOs, DownloadedFile accepts DTOs and supports fopen/cURL paths; feature flag and unit tests added.

Changes

URL Validation & Download Refactor

Layer / File(s) Summary
URL Validation DTO
app/DTO/UrlValidatedDTO.php
New final DTO holds url, resolved_ip, and error properties; includes fromError() factory method.
URL Validation Service
app/Services/UrlValidation.php
New service validates mixed input, enforces scheme/port/HTTPS policies, resolves host to IPs, detects private/reserved/localhost, and returns UrlValidatedDTO.
Request Input Validation
app/Http/Requests/Photo/FromUrlRequest.php
prepareForValidation() maps submitted URLs through UrlValidation producing UrlValidatedDTO[]; rules() updated and urls() now returns DTOs.
Validation Rule Refactor
app/Rules/PhotoUrlRule.php
Rule requires UrlValidatedDTO, throws on non-DTO, fails on DTO.error or missing resolved_ip; removed constructor and DNS/IP helpers.
File Download with DTO & Dual Paths
app/Image/Files/DownloadedFile.php
Class now final, constructor accepts UrlValidatedDTO; selects downloadWithFopen() or downloadWithCurl() via feature flag; MIME resolution precedence and extractMimeTypeFromHeader() added.
Action Integration
app/Actions/Import/FromUrl.php
PHPDoc and internals updated to read URL string from $url->url on UrlValidatedDTO inputs.
Feature Configuration
config/features.php
Added use_fopen_for_url_imports flag to toggle fopen vs cURL for URL imports.
Dockerfile
Dockerfile
Updated production FrankenPHP base image digest.
Tests — PhotoUrlRule
tests/Unit/Rules/PhotoUrlRuleTest.php
Tests rewritten to exercise DTO-driven rule behavior (type checking, error propagation, resolved_ip presence, passing case).
Tests — UrlValidation
tests/Unit/Services/UrlValidationTest.php
New comprehensive tests for UrlValidation service behavior with DNS stubbing and config toggles.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through URLs, sniffed each byte,

Errors and IPs set to right,
DTOs in hand, I guard the gate,
MIME resolved, no tangled state,
fopens or curls — imports feel light.

🚥 Pre-merge checks | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.74% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (1)
app/Services/UrlValidation.php (1)

107-111: ⚖️ Poor tradeoff

Consider exposing all resolved IPs, not just the first.

resolved_ip retains only $resolved_ips[0]. Since DownloadedFile::downloadWithCurl() pins that single IP via CURLOPT_RESOLVE, multi-homed hosts lose all fallback IPs and a transient outage on the first record fails the import even though sibling records were validated. Storing the full list (e.g. resolved_ips: string[]) and joining them with commas into the CURLOPT_RESOLVE entry would preserve resiliency while keeping the SSRF guarantee intact (all IPs in the list have already been vetted).


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f85e6c1-e85a-4693-b272-3bd8262a8ab7

📥 Commits

Reviewing files that changed from the base of the PR and between e3221b7 and 3d4cdee.

📒 Files selected for processing (7)
  • app/Actions/Import/FromUrl.php
  • app/DTO/UrlValidatedDTO.php
  • app/Http/Requests/Photo/FromUrlRequest.php
  • app/Image/Files/DownloadedFile.php
  • app/Rules/PhotoUrlRule.php
  • app/Services/UrlValidation.php
  • config/features.php

Comment thread app/DTO/UrlValidatedDTO.php
Comment thread app/Http/Requests/Photo/FromUrlRequest.php
Comment thread app/Image/Files/DownloadedFile.php
Comment thread app/Image/Files/DownloadedFile.php
Comment thread app/Image/Files/DownloadedFile.php Outdated
Comment thread app/Rules/PhotoUrlRule.php Outdated
Comment thread app/Services/UrlValidation.php
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/Unit/Services/UrlValidationTest.php (1)

30-49: ⚡ Quick win

Make the config fixture self-restoring and build UrlValidation after seeding it.

Line 33 constructs the service before Lines 35-38 set the flags, and Lines 43-46 restore fixed '1' values instead of the pre-test state. That makes this suite depend on UrlValidation reading config lazily and on those defaults never changing. Capturing the original values, setting the test values, then constructing the service would make these tests much less order-dependent.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0ff4c6a4-7a05-464b-a048-bc37a7c484cd

📥 Commits

Reviewing files that changed from the base of the PR and between 3d4cdee and e5d5c43.

📒 Files selected for processing (7)
  • Dockerfile
  • app/DTO/UrlValidatedDTO.php
  • app/Image/Files/DownloadedFile.php
  • app/Rules/PhotoUrlRule.php
  • app/Services/UrlValidation.php
  • tests/Unit/Rules/PhotoUrlRuleTest.php
  • tests/Unit/Services/UrlValidationTest.php
✅ Files skipped from review due to trivial changes (1)
  • Dockerfile
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/DTO/UrlValidatedDTO.php
  • app/Rules/PhotoUrlRule.php
  • app/Services/UrlValidation.php
  • app/Image/Files/DownloadedFile.php

@codecov
Copy link
Copy Markdown

codecov Bot commented May 11, 2026

Codecov Report

❌ Patch coverage is 87.22222% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.25%. Comparing base (e3221b7) to head (e5d5c43).

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ildyria ildyria merged commit a5f23e3 into master May 12, 2026
46 checks passed
@ildyria ildyria deleted the ssrf-toctou branch May 12, 2026 07:12
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