Five findings from the latest review on #758 (2 medium, 3 nit): Medium: - browser_oauth_routes.oauth_login_callback + oauth_routes.oauth_callback_nextcloud: fail closed with 400 when the oauth_session row is unknown/expired. Previously both callbacks fell through with code_verifier="" and expected_nonce=None, silently bypassing the PKCE + nonce protections introduced in earlier rounds. Symmetric unit tests pin both contracts. - token_utils.verify_id_token: use secrets.compare_digest for the nonce check instead of short-circuit !=. Mirrors the sibling PKCE verifier comparison; closes the last secret-equality timing-side-channel surface in the auth path. Nit: - Tighten the comment at all 4 mcp_authorization_code/code_verifier store + retrieve sites so a future refactor sees the field reuse immediately (renaming the column requires a schema migration). - _should_use_secure_cookies: explicit string normalisation instead of bool(settings.cookie_secure). Dynaconf normally coerces but tests / direct settings.set calls can leave the raw string in place — bool("false") is True. New parametrized unit tests cover the coercion matrix + http/https fallback. - oauth_routes.py:591 f-string log converted to lazy %s formatting (folded into the Flow 2 callback rewrite). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
74 lines
2.0 KiB
Python
74 lines
2.0 KiB
Python
"""Unit tests for ``browser_oauth_routes`` helpers.
|
|
|
|
Pins the round-6 review fix that ``_should_use_secure_cookies`` must not
|
|
trust ``bool(settings.cookie_secure)`` — Dynaconf normally coerces but
|
|
tests / direct ``settings.set`` calls can leave the raw string in place,
|
|
and ``bool("false")`` is ``True``.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from nextcloud_mcp_server.auth import browser_oauth_routes
|
|
|
|
pytestmark = pytest.mark.unit
|
|
|
|
|
|
def _fake_settings(*, cookie_secure, mcp_server_url=""):
|
|
return type(
|
|
"S",
|
|
(),
|
|
{
|
|
"cookie_secure": cookie_secure,
|
|
"nextcloud_mcp_server_url": mcp_server_url,
|
|
},
|
|
)()
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"value,expected",
|
|
[
|
|
(True, True),
|
|
(False, False),
|
|
("true", True),
|
|
("false", False),
|
|
("True", True),
|
|
("FALSE", False),
|
|
("0", False),
|
|
("1", True),
|
|
("no", False),
|
|
("yes", True),
|
|
("off", False),
|
|
("on", True),
|
|
("", False),
|
|
],
|
|
)
|
|
def test_should_use_secure_cookies_string_coercion(monkeypatch, value, expected):
|
|
monkeypatch.setattr(
|
|
browser_oauth_routes,
|
|
"get_settings",
|
|
lambda: _fake_settings(cookie_secure=value),
|
|
)
|
|
assert browser_oauth_routes._should_use_secure_cookies() is expected
|
|
|
|
|
|
def test_should_use_secure_cookies_falls_back_to_https_scheme(monkeypatch):
|
|
monkeypatch.setattr(
|
|
browser_oauth_routes,
|
|
"get_settings",
|
|
lambda: _fake_settings(
|
|
cookie_secure=None, mcp_server_url="https://mcp.example.com"
|
|
),
|
|
)
|
|
assert browser_oauth_routes._should_use_secure_cookies() is True
|
|
|
|
|
|
def test_should_use_secure_cookies_falls_back_to_http_scheme(monkeypatch):
|
|
monkeypatch.setattr(
|
|
browser_oauth_routes,
|
|
"get_settings",
|
|
lambda: _fake_settings(
|
|
cookie_secure=None, mcp_server_url="http://localhost:8000"
|
|
),
|
|
)
|
|
assert browser_oauth_routes._should_use_secure_cookies() is False
|