mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-07-25 09:00:37 -04:00
fix: harden server-initiated HTTP against SSRF and DNS rebinding
Route all server-initiated outbound HTTP through one SSRF-protected layer: - Resolve each host once, validate every resolved address (private, loopback, link-local, CGNAT 100.64/10, multicast, reserved, unspecified, IPv4-mapped IPv6), and reject if any is unsafe. - Pin the connection to the validated address via curl's RESOLVE option so curl cannot re-resolve to a rebound IP (closes the validate-then-connect TOCTOU). Uses getaddrinfo so IPv6 records are checked too. - Add a sync SafeTransport and safehttp.post, and move the webhook and recipe-action paths off raw requests.post so they are guarded like the scraper. - Add HTTP_ALLOW_LIST / HTTP_DISALLOW_LIST settings (deny > allow > default) so operators can reach an internal server or block extra hosts. Apprise notifications egress through the third-party library and are not covered here.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from mealie.pkgs import safehttp
|
||||
from mealie.schema.household.group_recipe_action import (
|
||||
CreateGroupRecipeAction,
|
||||
GroupRecipeActionOut,
|
||||
@@ -17,8 +17,8 @@ from tests.utils.fixture_schemas import TestUser
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_requests_post(monkeypatch: pytest.MonkeyPatch):
|
||||
monkeypatch.setattr(requests, "post", lambda *args, **kwargs: None)
|
||||
def mock_outbound_post(monkeypatch: pytest.MonkeyPatch):
|
||||
monkeypatch.setattr(safehttp, "post", lambda *args, **kwargs: None)
|
||||
|
||||
|
||||
def create_action(action_type: GroupRecipeActionType = GroupRecipeActionType.link) -> CreateGroupRecipeAction:
|
||||
|
||||
@@ -91,7 +91,7 @@ def test_delete_webhook(api_client: TestClient, webhook_data, unique_user: TestU
|
||||
def test_post_test_webhook(
|
||||
monkeypatch: pytest.MonkeyPatch, api_client: TestClient, unique_user: TestUser, webhook_data
|
||||
):
|
||||
# Mock the requests.post to avoid actual HTTP calls
|
||||
# Mock the outbound post to avoid actual HTTP calls
|
||||
class MockResponse:
|
||||
status_code = 200
|
||||
|
||||
@@ -101,7 +101,7 @@ def test_post_test_webhook(
|
||||
mock_calls.append((args, kwargs))
|
||||
return MockResponse()
|
||||
|
||||
monkeypatch.setattr("mealie.services.event_bus_service.publisher.requests.post", mock_post)
|
||||
monkeypatch.setattr("mealie.services.event_bus_service.publisher.safehttp.post", mock_post)
|
||||
|
||||
# Create a webhook and post it
|
||||
response = api_client.post(
|
||||
@@ -124,7 +124,7 @@ def test_post_test_webhook(
|
||||
test_message = "This is a test webhook message"
|
||||
post_test_webhook(webhook, test_message)
|
||||
|
||||
# Verify that requests.post was called with the correct parameters
|
||||
# Verify that the outbound post was called with the correct parameters
|
||||
assert len(mock_calls) == 1
|
||||
args, kwargs = mock_calls[0]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user