SA-4 SSRF protections implemented.

This commit is contained in:
2026-08-26 16:52:59 +02:00
parent 3e61302bf6
commit c70850b44d
9 changed files with 151 additions and 35 deletions
+15 -13
View File
@@ -648,19 +648,20 @@ def test_link_submission_posts_to_enabled_mastodon_plugin():
try:
headers = login_headers()
base_url = f'http://127.0.0.1:{server.server_port}'
assert client.put('/api/user/plugins/mastodon', headers=headers, json={
'instance': base_url,
'access_token': 'test-token',
'post_prefix': 'From my #LinkLog: ',
}).status_code == 200
assert client.put('/api/admin/plugins/mastodon', headers=headers, json={'enabled': True}).status_code == 200
with patch('backend.app.services.plugin_manager.validate_public_instance', return_value=base_url):
assert client.put('/api/user/plugins/mastodon', headers=headers, json={
'instance': base_url,
'access_token': 'test-token',
'post_prefix': 'From my #LinkLog: ',
}).status_code == 200
assert client.put('/api/admin/plugins/mastodon', headers=headers, json={'enabled': True}).status_code == 200
response = client.post('/api/links', headers=headers, json={
'title': 'A useful page',
'url': 'https://example.com/useful',
'comment': 'Worth sharing',
'tags': ['#python', '#web'],
})
response = client.post('/api/links', headers=headers, json={
'title': 'A useful page',
'url': 'https://example.com/useful',
'comment': 'Worth sharing',
'tags': ['#python', '#web'],
})
assert response.status_code == 201
assert received['path'] == '/api/v1/statuses'
@@ -683,7 +684,8 @@ def test_mastodon_post_without_title_omits_source_line():
plugin = MastodonPlugin()
plugin.initialize({'instance': 'https://mastodon.example', 'access_token': 'test-token'})
with patch('backend.app.services.plugin_manager.urlopen', return_value=response) as open_url:
with patch('backend.app.services.plugin_manager.open_no_redirect', return_value=response) as open_url, \
patch('backend.app.services.plugin_manager.validate_public_instance', return_value='https://mastodon.example'):
result = plugin.handle_event({
'url': 'https://example.com/useful',
'title': '',
+34
View File
@@ -0,0 +1,34 @@
## Copyright © 2026 Olaf Kolkman
## SPDX-License-Identifier: GPL-3.0-or-later
from unittest.mock import patch
from urllib.error import HTTPError
from urllib.request import Request
import pytest
from backend.app.services.mastodon_security import open_no_redirect, validate_public_instance
@pytest.mark.parametrize('instance', [
'http://mastodon.example',
'https://127.0.0.1',
'https://[::ffff:127.0.0.1]',
'https://user:password@mastodon.example',
])
def test_mastodon_instance_rejects_unsafe_urls(instance):
with pytest.raises(ValueError):
validate_public_instance(instance)
def test_mastodon_instance_rejects_private_dns_result():
with patch('backend.app.services.mastodon_security.socket.getaddrinfo', return_value=[(2, 1, 6, '', ('10.0.0.5', 443))]):
with pytest.raises(ValueError, match='public IP'):
validate_public_instance('https://mastodon.example')
def test_mastodon_outbound_redirects_are_rejected():
request = Request('https://mastodon.example/api/v1/statuses')
with patch('backend.app.services.mastodon_security.NO_REDIRECT_OPENER.open', side_effect=HTTPError(request.full_url, 302, 'Redirects are not allowed', {}, None)):
with pytest.raises(HTTPError, match='Redirects are not allowed'):
open_no_redirect(request)