From 7c1572f54c533fe371a3aa9a3b1faed70af296a3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 14 Aug 2025 22:41:00 +0000 Subject: [PATCH 1/2] Initial plan From 657f0b4762c0a0a2fd1dd6894eb1c6dd10720128 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 14 Aug 2025 22:55:59 +0000 Subject: [PATCH 2/2] Fix /follows.json POST handler to validate publisher exists before writing to DB Co-authored-by: mekarpeles <978325+mekarpeles@users.noreply.github.com> --- openlibrary/plugins/openlibrary/api.py | 5 +++ .../openlibrary/tests/test_followsapi.py | 41 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 openlibrary/plugins/openlibrary/tests/test_followsapi.py diff --git a/openlibrary/plugins/openlibrary/api.py b/openlibrary/plugins/openlibrary/api.py index b96fe65aa29..b4ce992621b 100644 --- a/openlibrary/plugins/openlibrary/api.py +++ b/openlibrary/plugins/openlibrary/api.py @@ -528,6 +528,11 @@ def POST(self, key): if not user or user.key != key: raise web.seeother(f'/account/login?redir_url={i.redir_url}') + # Validate that the publisher account exists + publisher_account = accounts.find(username=i.publisher) + if not publisher_account: + raise web.notfound("Publisher not found") + username = user.key.split('/')[2] action = PubSub.subscribe if i.state == '0' else PubSub.unsubscribe action(username, i.publisher) diff --git a/openlibrary/plugins/openlibrary/tests/test_followsapi.py b/openlibrary/plugins/openlibrary/tests/test_followsapi.py new file mode 100644 index 00000000000..97cb9862fc6 --- /dev/null +++ b/openlibrary/plugins/openlibrary/tests/test_followsapi.py @@ -0,0 +1,41 @@ +"""Tests for the /follows.json API endpoint.""" +import pytest +import web + +from openlibrary import accounts +from openlibrary.plugins.openlibrary.api import patrons_follows_json + + +class TestPatronFollowsAPI: + """Test the /follows.json API endpoint for following/unfollowing patrons.""" + + def test_post_follows_nonexistent_publisher_returns_404(self, mock_site, monkeypatch): + """Test that following a non-existent publisher returns 404. + + This test validates that the fix prevents following non-existent publishers. + """ + # Setup mock user and input + mock_user = web.storage() + mock_user.key = '/people/test_user' + + mock_input = web.storage( + publisher='nonexistent_user', + redir_url='/', + state='0' # subscribe + ) + + monkeypatch.setattr(web, 'input', lambda: mock_input) + monkeypatch.setattr(accounts, 'get_current_user', lambda: mock_user) + + # Mock accounts.find to return None for nonexistent user + monkeypatch.setattr(accounts, 'find', lambda **kwargs: None) + + # Create the API handler instance + handler = patrons_follows_json() + + # This should now return a 404 after the fix + with pytest.raises(Exception) as exc_info: + handler.POST('/people/test_user') + + # Check that it's a 404 error (web.notfound) + assert 'notfound' in str(exc_info.type).lower() or '404' in str(exc_info.value) \ No newline at end of file