test: write failure paths, expired OAuth token, non-admin admin-tool refusals and webhook credential leak checks
This commit is contained in:
1 parent
84fa29e380
commit
d1b9a4845e
3 files changed
+139
-7
No files matched your search
@@ -47,6 +47,7 @@ class OAuthStack:
|
||||
bearer: str
|
||||
user_login: str
|
||||
user_access_token: str
|
||||
expired_access_token: str
|
||||
secrets: list[str]
|
||||
|
||||
|
||||
@@ -96,6 +97,15 @@ async def build_stack(
|
||||
scopes=[MCP_SCOPE],
|
||||
expires_at=time.time() + ACCESS_TOKEN_TTL,
|
||||
)
|
||||
# A token of the same valid session whose lifetime ended a minute ago.
|
||||
expired = TOKEN_PREFIX + secrets.token_urlsafe(32)
|
||||
store.tokens[token_hash(expired)] = TokenRecord(
|
||||
kind="access",
|
||||
session_id=session_id,
|
||||
client_id=OAUTH_CLIENT_ID,
|
||||
scopes=[MCP_SCOPE],
|
||||
expires_at=time.time() - 60,
|
||||
)
|
||||
await store.save()
|
||||
|
||||
audit = AuditLogger(settings.resolved_audit_log_path)
|
||||
@@ -117,9 +127,11 @@ async def build_stack(
|
||||
bearer=bearer,
|
||||
user_login=user.login,
|
||||
user_access_token=access,
|
||||
expired_access_token=expired,
|
||||
secrets=[
|
||||
bearer,
|
||||
access,
|
||||
expired,
|
||||
user.token,
|
||||
gitea.admin_token,
|
||||
oauth_client_secret,
|
||||
@@ -192,11 +204,32 @@ async def test_calls_run_as_the_signed_in_user(
|
||||
)
|
||||
).status in (403, 404)
|
||||
|
||||
# Admin tools and sudo are refused by Gitea with 403, not by the server.
|
||||
admin_only = await mcp.call("gitea_admin_cron_list")
|
||||
assert admin_only.is_error and admin_only.status == 403
|
||||
assert admin_only.error_kind == "forbidden"
|
||||
assert admin_only.identity["mode"] == "oauth"
|
||||
# Admin tools and sudo are refused by Gitea with 403, not by the server: every
|
||||
# admin list endpoint and an admin create, all for this non-admin user.
|
||||
for tool in (
|
||||
"gitea_admin_cron_list",
|
||||
"gitea_admin_search_users",
|
||||
"gitea_admin_get_all_orgs",
|
||||
"gitea_admin_get_all_emails",
|
||||
"gitea_admin_list_hooks",
|
||||
"gitea_admin_list_packages",
|
||||
"gitea_admin_get_runners",
|
||||
"gitea_admin_unadopted_list",
|
||||
):
|
||||
refused = await mcp.call(tool)
|
||||
assert refused.is_error and refused.status == 403, tool
|
||||
assert refused.error_kind == "forbidden", tool
|
||||
assert refused.identity["mode"] == "oauth"
|
||||
created = await mcp.call(
|
||||
"gitea_admin_create_user",
|
||||
body={
|
||||
"username": gitea.unique("it-nope"),
|
||||
"email": f"{gitea.unique('nope')}@example.test",
|
||||
"password": secrets.token_urlsafe(24),
|
||||
},
|
||||
)
|
||||
assert created.is_error and created.status == 403
|
||||
assert created.error_kind == "forbidden"
|
||||
sudo = await mcp.call("gitea_user_get_current", sudo=gitea.admin_login)
|
||||
assert sudo.is_error and sudo.status == 403
|
||||
|
||||
@@ -206,6 +239,7 @@ async def test_calls_run_as_the_signed_in_user(
|
||||
assert {e["tool_name"] for e in entries} >= {
|
||||
"gitea_user_get_current",
|
||||
"gitea_admin_cron_list",
|
||||
"gitea_admin_create_user",
|
||||
}
|
||||
gitea.api("DELETE", f"/repos/{regular_user.login}/{own}")
|
||||
|
||||
@@ -254,6 +288,7 @@ async def test_unknown_and_expired_tokens_never_reach_gitea(
|
||||
):
|
||||
for token in (
|
||||
TOKEN_PREFIX + secrets.token_urlsafe(32), # looks like ours, unknown
|
||||
stack.expired_access_token, # ours and known, but past its expiry
|
||||
regular_user.token, # the user's own Gitea token is not an MCP token
|
||||
secrets.token_urlsafe(36),
|
||||
):
|
||||
@@ -264,3 +299,10 @@ async def test_unknown_and_expired_tokens_never_reach_gitea(
|
||||
assert token not in response.text
|
||||
anonymous = await http_client.post("/mcp", json=body, headers=headers)
|
||||
assert anonymous.status_code == 401
|
||||
# The expiry is what excluded the token above: the live one of the same session works.
|
||||
live = await http_client.post(
|
||||
"/mcp",
|
||||
json=body,
|
||||
headers={**headers, "Authorization": f"Bearer {stack.user_access_token}"},
|
||||
)
|
||||
assert live.status_code == 200
|
||||
Reference in new issue
Block a user