40 lines
1.4 KiB
Python
40 lines
1.4 KiB
Python
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
|
|
@pytest.fixture
|
|
def frontend(tmp_path, monkeypatch):
|
|
from backend.app import main
|
|
root = tmp_path / 'dist'
|
|
root.mkdir()
|
|
(root / 'index.html').write_text('<html>Public AppOps frontend</html>')
|
|
(root / 'icon.svg').write_text('public-icon')
|
|
(tmp_path / 'outside.txt').write_text('private-test-marker')
|
|
monkeypatch.setattr(main, 'static_root', root)
|
|
return TestClient(main.app), root
|
|
|
|
|
|
@pytest.mark.parametrize('path', ['/%2e%2e/outside.txt', '/%2e%2e%2foutside.txt', '/%2fetc/passwd'])
|
|
def test_public_frontend_rejects_encoded_path_escapes(frontend, path):
|
|
client, _ = frontend
|
|
response = client.get(path)
|
|
assert response.status_code == 404
|
|
assert 'private-test-marker' not in response.text
|
|
|
|
|
|
def test_public_frontend_serves_assets_and_spa_routes(frontend):
|
|
client, _ = frontend
|
|
assert client.get('/icon.svg').text == 'public-icon'
|
|
assert 'Public AppOps frontend' in client.get('/settings').text
|
|
assert client.get('/api/nonexistent').status_code in {401, 404}
|
|
|
|
|
|
def test_public_frontend_rejects_symlink_escape(frontend):
|
|
client, root = frontend
|
|
try:
|
|
(root / 'escaped.txt').symlink_to(root.parent / 'outside.txt')
|
|
except OSError:
|
|
pytest.skip('Symlinks unavailable on this host')
|
|
assert client.get('/escaped.txt').status_code == 404
|