From f214f8b17ee6f22c8ae57bb981a057f76466bb15 Mon Sep 17 00:00:00 2001 From: spelech <28486500+spelech@users.noreply.github.com> Date: Tue, 27 Oct 2026 23:18:28 +0000 Subject: [PATCH 1/3] fix(credentials): surface host-list query failures instead of returning empty success Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- REQUIREMENTS.md | 5 +++-- app/mcp/handlers/repo_handlers.py | 6 +++++- app/services/database/credentials.py | 2 +- pytest.ini | 1 + tests/backend/test_multi_git_providers.py | 26 +++++++++++++++++++++++ 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md index 59d9c13..1add4d8 100644 --- a/REQUIREMENTS.md +++ b/REQUIREMENTS.md @@ -2,7 +2,7 @@ > **Note:** This document is automatically generated and verified against the live test suite by `scripts/generate_requirements.py` and `tests/backend/test_requirements_sync.py`. -**Test Verification Baseline:** **1052 Automated Tests** (699 Pytest Backend + 303 Vitest Frontend + 50 Playwright E2E). +**Test Verification Baseline:** **1053 Automated Tests** (700 Pytest Backend + 303 Vitest Frontend + 50 Playwright E2E). --- @@ -647,13 +647,14 @@ classDiagram - `test_search_code_empty_and_missing_ast_boundaries` - `test_search_docs_with_dense_weight_and_score_breakdown` -#### `tests/backend/test_multi_git_providers.py` (9 tests) +#### `tests/backend/test_multi_git_providers.py` (10 tests) - `test_detect_git_provider` - `test_build_authenticated_url_multi_provider` - `test_sanitize_url_for_logging_multi_scheme` - `test_git_host_credentials_vault_crud` - `test_effective_git_token_hierarchy` - `test_host_credentials_api_endpoints` +- `test_host_credentials_api_query_failure_and_empty_success` - `test_multi_token_settings_api` - `test_process_file_content_with_custom_provider` - `test_sync_single_git_repo_triggers_notification` diff --git a/app/mcp/handlers/repo_handlers.py b/app/mcp/handlers/repo_handlers.py index 0bd2a7c..2c9470b 100644 --- a/app/mcp/handlers/repo_handlers.py +++ b/app/mcp/handlers/repo_handlers.py @@ -100,7 +100,11 @@ async def handle_index_status() -> str: gh_tok, _, gh_src = get_effective_git_token("https://github.com", provider="github") gl_tok, _, gl_src = get_effective_git_token("https://gitlab.com", provider="gitlab") gt_tok, _, gt_src = get_effective_git_token("https://gitea.com", provider="gitea") - vault_creds = list_git_host_credentials() + try: + vault_creds = list_git_host_credentials() + except Exception as e: + logger.error(f"Failed to list vault credentials for status: {e}") + vault_creds = [] rate_info = check_github_rate_limit(gh_tok) lines = [ diff --git a/app/services/database/credentials.py b/app/services/database/credentials.py index cba4f0a..1393e60 100644 --- a/app/services/database/credentials.py +++ b/app/services/database/credentials.py @@ -13,7 +13,7 @@ def list_git_host_credentials() -> List[Dict[str, Any]]: return [dict(r) for r in rows] except Exception as e: logger.error(f"Failed to list git host credentials: {e}") - return [] + raise def get_git_host_credential(host: str) -> Optional[Dict[str, Any]]: if not host: diff --git a/pytest.ini b/pytest.ini index 00b9ce4..4bc8c42 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,5 +1,6 @@ [pytest] pythonpath = . +asyncio_default_fixture_loop_scope = function filterwarnings = error ignore:.*Field 'lifespan' has an incomplete definition.*:UserWarning diff --git a/tests/backend/test_multi_git_providers.py b/tests/backend/test_multi_git_providers.py index cf087e2..613257a 100644 --- a/tests/backend/test_multi_git_providers.py +++ b/tests/backend/test_multi_git_providers.py @@ -143,6 +143,32 @@ def test_host_credentials_api_endpoints(temp_db): assert res_del.status_code == 200 assert res_del.json()["status"] == "success" +def test_host_credentials_api_query_failure_and_empty_success(temp_db): + # 1. Empty credential store returns HTTP 200 with [] + res_empty = client.get("/admin/api/settings/hosts") + assert res_empty.status_code == 200 + assert res_empty.json() == [] + + # 2. Query failure returns HTTP 500 sanitized error response instead of [] + with patch("app.services.database.credentials.get_db_connection") as mock_conn: + mock_conn.side_effect = Exception("no such table: git_host_credentials (database connection or table error)") + res_fail = client.get("/admin/api/settings/hosts") + assert res_fail.status_code == 500 + fail_json = res_fail.json() + assert fail_json == {"error": "Failed to list host credentials."} + # Ensure error response does not expose internal exception text, connection string, or secret tokens + assert "no such table" not in json.dumps(fail_json) + + # 3. Non-empty success response retains existing credential masking + save_git_host_credential("github.company.com", "github", "ghp_secret_token_12345", auth_user="git") + res_non_empty = client.get("/admin/api/settings/hosts") + assert res_non_empty.status_code == 200 + data = res_non_empty.json() + assert len(data) == 1 + assert data[0]["host"] == "github.company.com" + assert "ghp_secret_token_12345" not in data[0]["masked_token"] + assert "..." in data[0]["masked_token"] or "***" in data[0]["masked_token"] or len(data[0]["masked_token"]) < 20 + def test_multi_token_settings_api(temp_db): res = client.post("/admin/api/settings/token", json={ "github_token": "ghp_updated123", From 58a7061b898c1e3d66e82637361f2a096a4cdc65 Mon Sep 17 00:00:00 2001 From: spelech <28486500+spelech@users.noreply.github.com> Date: Wed, 28 Oct 2026 01:55:14 +0000 Subject: [PATCH 2/3] fix(credentials): surface host-list query failures instead of returning empty success Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> From cd461db6eacff2e4ef60b9359b6768645bf0e04e Mon Sep 17 00:00:00 2001 From: Antigravity Agent Date: Tue, 27 Oct 2026 21:29:13 -0500 Subject: [PATCH 3/3] fix(credentials): report query failure explicitly when querying git credentials --- app/mcp/handlers/repo_handlers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/mcp/handlers/repo_handlers.py b/app/mcp/handlers/repo_handlers.py index 2c9470b..e0924e9 100644 --- a/app/mcp/handlers/repo_handlers.py +++ b/app/mcp/handlers/repo_handlers.py @@ -100,13 +100,16 @@ async def handle_index_status() -> str: gh_tok, _, gh_src = get_effective_git_token("https://github.com", provider="github") gl_tok, _, gl_src = get_effective_git_token("https://gitlab.com", provider="gitlab") gt_tok, _, gt_src = get_effective_git_token("https://gitea.com", provider="gitea") + vault_failed = False try: vault_creds = list_git_host_credentials() except Exception as e: logger.error(f"Failed to list vault credentials for status: {e}") vault_creds = [] + vault_failed = True rate_info = check_github_rate_limit(gh_tok) + vault_str = "[Unavailable - Query Failed]" if vault_failed else f"{len(vault_creds)}" lines = [ f"Vector Store Provider: {provider}", f"Storage Mode: {mode_str} ({mode_val})", @@ -117,7 +120,7 @@ async def handle_index_status() -> str: f"Total Indexed Files: {files_count}", f"Total AST Symbols: {symbols_count}", f"Registered Git Repos: {git_count}", - f"Git Auth Sources: GitHub ({gh_src}: {mask_token(gh_tok)}), GitLab ({gl_src}), Gitea ({gt_src}), Host Vaults ({len(vault_creds)})", + f"Git Auth Sources: GitHub ({gh_src}: {mask_token(gh_tok)}), GitLab ({gl_src}), Gitea ({gt_src}), Host Vaults ({vault_str})", ] if gh_tok: lines.append(f"GitHub API Rate Limit: {rate_info.get('remaining', 0)} / {rate_info.get('limit', 60)}")