Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

552 lines
18 KiB
Python

"""
Tests to verify session["username"] hardening across all route files.
All @login_required route handlers should use session["username"] (direct access)
instead of session.get("username"). The @login_required decorator guarantees
the key exists; direct access fails fast if the decorator is ever removed.
"""
import inspect
import textwrap
def _get_function_source(func):
"""Get dedented source of a function for AST analysis."""
return textwrap.dedent(inspect.getsource(func))
def _uses_unsafe_session_get(source: str) -> bool:
"""Check if source contains session.get("username") or flask_session.get("username")."""
return (
'session.get("username")' in source
or "session.get('username')" in source
)
def _uses_safe_session_access(source: str) -> bool:
"""Check if source contains session["username"] or flask_session["username"]."""
return 'session["username"]' in source or "session['username']" in source
class TestNewsRoutesSessionAccess:
"""Verify news routes use session['username'] not session.get('username')."""
def _get_route_functions(self):
from local_deep_research.web.routes.news_routes import (
create_subscription,
get_news_feed,
get_subscriptions,
save_preferences,
submit_feedback,
)
return [
get_news_feed,
get_subscriptions,
create_subscription,
submit_feedback,
save_preferences,
]
def test_no_session_get_username(self):
"""No news route should use session.get('username')."""
for func in self._get_route_functions():
source = _get_function_source(func)
assert not _uses_unsafe_session_get(source), (
f"{func.__name__} uses session.get('username') "
f"instead of session['username']"
)
def test_uses_session_bracket_access(self):
"""Each news route that accesses username should use bracket notation."""
for func in self._get_route_functions():
source = _get_function_source(func)
assert _uses_safe_session_access(source), (
f"{func.__name__} should use session['username']"
)
class TestHistoryRoutesSessionAccess:
"""Verify history routes use session['username'] not session.get('username')."""
def _get_route_functions(self):
from local_deep_research.web.routes.history_routes import (
get_history,
get_research_details,
get_research_logs,
get_research_status,
)
return [
get_history,
get_research_status,
get_research_details,
get_research_logs,
]
def test_no_session_get_username(self):
for func in self._get_route_functions():
source = _get_function_source(func)
assert not _uses_unsafe_session_get(source), (
f"{func.__name__} uses session.get('username')"
)
class TestResearchRoutesSessionAccess:
"""Verify research routes use session['username'] not session.get('username')."""
def _get_route_functions(self):
from local_deep_research.web.routes.research_routes import (
clear_history,
delete_research,
export_research_report,
get_history,
get_queue_position,
get_queue_status,
get_research_details,
get_research_logs,
get_research_report,
get_research_status,
start_research,
terminate_research,
upload_pdf,
)
return [
start_research,
terminate_research,
delete_research,
clear_history,
get_history,
get_research_details,
get_research_logs,
get_research_report,
export_research_report,
get_research_status,
get_queue_status,
get_queue_position,
upload_pdf,
]
def test_no_session_get_username(self):
for func in self._get_route_functions():
source = _get_function_source(func)
assert not _uses_unsafe_session_get(source), (
f"{func.__name__} uses session.get('username')"
)
class TestContextOverflowSessionAccess:
"""Verify context overflow routes use flask_session['username']."""
def test_no_session_get_username(self):
from local_deep_research.web.routes.context_overflow_api import (
get_context_overflow_metrics,
)
source = _get_function_source(get_context_overflow_metrics)
assert not _uses_unsafe_session_get(source), (
"get_context_overflow_metrics uses session.get('username')"
)
class TestApiRoutesSessionAccess:
"""Verify API routes use session['username']."""
def test_no_session_get_username(self):
from local_deep_research.web.routes.api_routes import (
api_terminate_research,
)
source = _get_function_source(api_terminate_research)
assert not _uses_unsafe_session_get(source), (
"api_terminate_research uses session.get('username')"
)
class TestSettingsRoutesSessionAccess:
"""Verify settings routes use session['username'] (except helper functions)."""
def _get_route_functions(self):
from local_deep_research.web.routes.settings_routes import (
api_delete_setting,
api_get_all_settings,
api_get_available_models,
api_get_available_search_engines,
api_get_categories,
api_get_db_setting,
api_get_search_favorites,
api_import_settings,
api_toggle_search_favorite,
api_update_search_favorites,
api_update_setting,
fix_corrupted_settings,
reset_to_defaults,
save_all_settings,
save_settings,
)
return [
save_all_settings,
reset_to_defaults,
save_settings,
api_get_all_settings,
api_get_db_setting,
api_update_setting,
api_delete_setting,
api_import_settings,
api_get_categories,
api_get_available_models,
api_get_available_search_engines,
api_get_search_favorites,
api_update_search_favorites,
api_toggle_search_favorite,
fix_corrupted_settings,
]
def test_no_session_get_username_in_routes(self):
"""Decorated route functions should not use session.get('username')."""
for func in self._get_route_functions():
source = _get_function_source(func)
assert not _uses_unsafe_session_get(source), (
f"{func.__name__} uses session.get('username')"
)
def test_helper_functions_use_safe_get(self):
"""Helper functions (not decorated) should still use session.get('username')."""
from local_deep_research.web.routes.settings_routes import (
_get_setting_from_session,
)
from local_deep_research.web.warning_checks import calculate_warnings
for func in [_get_setting_from_session, calculate_warnings]:
source = _get_function_source(func)
assert _uses_unsafe_session_get(source), (
f"{func.__name__} should use session.get('username') "
f"because it is not behind @login_required"
)
class TestLibraryRoutesSessionAccess:
"""Verify library routes use session['username']."""
def _get_route_functions(self):
from local_deep_research.research_library.routes.library_routes import (
check_downloads,
delete_document,
document_details_page,
download_all_text,
download_bulk,
download_research_pdfs,
download_single_resource,
download_source,
download_text_single,
get_collections_list,
get_documents,
get_library_stats,
get_research_list,
get_research_sources,
library_page,
mark_for_redownload,
download_manager_page,
queue_all_undownloaded,
serve_text_api,
sync_library,
toggle_favorite,
view_pdf_page,
view_text_page,
)
return [
library_page,
document_details_page,
download_manager_page,
get_library_stats,
get_collections_list,
get_documents,
toggle_favorite,
delete_document,
view_pdf_page,
view_text_page,
serve_text_api,
download_single_resource,
download_text_single,
download_all_text,
download_research_pdfs,
download_bulk,
get_research_list,
sync_library,
mark_for_redownload,
queue_all_undownloaded,
get_research_sources,
check_downloads,
download_source,
]
def test_no_session_get_username(self):
for func in self._get_route_functions():
source = _get_function_source(func)
assert not _uses_unsafe_session_get(source), (
f"{func.__name__} uses session.get('username')"
)
class TestFollowupRoutesSessionAccess:
"""Verify followup routes use session['username']."""
def test_no_session_get_username(self):
from local_deep_research.followup_research.routes import (
prepare_followup,
start_followup,
)
for func in [prepare_followup, start_followup]:
source = _get_function_source(func)
assert not _uses_unsafe_session_get(source), (
f"{func.__name__} uses session.get('username')"
)
class TestSchedulerRoutesSessionAccess:
"""Verify scheduler helper uses session.get('username') (not decorated)."""
def test_helper_uses_safe_get(self):
"""get_current_username is a helper, not a route — it should use .get()."""
from local_deep_research.research_scheduler.routes import (
get_current_username,
)
source = _get_function_source(get_current_username)
assert _uses_unsafe_session_get(source), (
"get_current_username should use session.get('username') "
"because it is not behind @login_required"
)
class TestBenchmarkRoutesSessionAccess:
"""Verify benchmark routes use flask_session['username']."""
def _get_route_functions(self):
from local_deep_research.benchmarks.web_api.benchmark_routes import (
cancel_benchmark,
delete_benchmark_run,
export_benchmark_results,
get_benchmark_history,
get_benchmark_results,
get_benchmark_status,
get_running_benchmark,
index,
start_benchmark,
start_benchmark_simple,
)
return [
index,
start_benchmark,
get_running_benchmark,
get_benchmark_status,
cancel_benchmark,
get_benchmark_history,
get_benchmark_results,
start_benchmark_simple,
delete_benchmark_run,
export_benchmark_results,
]
def test_no_session_get_username(self):
for func in self._get_route_functions():
source = _get_function_source(func)
assert not _uses_unsafe_session_get(source), (
f"{func.__name__} uses session.get('username')"
)
class TestMetricsRoutesSessionAccess:
"""Verify metrics routes use flask_session['username'] (except helper functions)."""
def _get_route_functions(self):
from local_deep_research.web.routes.metrics_routes import (
api_classify_domains,
api_classification_progress,
api_cost_analytics,
api_enhanced_metrics,
api_get_classifications_summary,
api_get_domain_classifications,
api_get_research_rating,
api_link_analytics,
api_metrics,
api_rate_limiting_metrics,
api_research_costs,
api_research_link_metrics,
api_save_research_rating,
api_star_reviews,
)
return [
api_metrics,
api_rate_limiting_metrics,
api_research_link_metrics,
api_enhanced_metrics,
api_get_research_rating,
api_save_research_rating,
api_star_reviews,
api_research_costs,
api_cost_analytics,
api_link_analytics,
api_get_domain_classifications,
api_get_classifications_summary,
api_classify_domains,
api_classification_progress,
]
def test_no_session_get_username_in_routes(self):
"""Decorated route functions should not use flask_session.get('username')."""
for func in self._get_route_functions():
source = _get_function_source(func)
assert not _uses_unsafe_session_get(source), (
f"{func.__name__} uses session.get('username')"
)
def test_helper_functions_use_safe_get(self):
"""Helper functions (not decorated) should still use flask_session.get('username')."""
from local_deep_research.web.routes.metrics_routes import (
get_link_analytics,
get_rate_limiting_analytics,
get_rating_analytics,
get_strategy_analytics,
)
for func in [
get_rating_analytics,
get_link_analytics,
get_strategy_analytics,
get_rate_limiting_analytics,
]:
source = _get_function_source(func)
assert _uses_unsafe_session_get(source), (
f"{func.__name__} should use flask_session.get('username') "
f"because it is not behind @login_required"
)
class TestRAGRoutesSessionAccess:
"""Verify RAG routes use session['username'] not session.get('username')."""
def _get_route_functions(self):
from local_deep_research.research_library.routes.rag_routes import (
cancel_indexing,
configure_rag,
create_collection,
get_collection_documents,
get_collections,
get_documents,
get_index_info,
get_index_status,
get_rag_stats,
index_all,
index_collection,
index_document,
remove_document,
start_background_index,
update_collection,
upload_to_collection,
view_document_chunks,
)
return [
view_document_chunks,
get_index_info,
get_rag_stats,
index_document,
remove_document,
index_all,
configure_rag,
get_documents,
get_collections,
create_collection,
update_collection,
upload_to_collection,
get_collection_documents,
index_collection,
start_background_index,
get_index_status,
cancel_indexing,
]
def test_no_session_get_username(self):
"""No RAG route should use session.get('username')."""
for func in self._get_route_functions():
source = _get_function_source(func)
assert not _uses_unsafe_session_get(source), (
f"{func.__name__} uses session.get('username') "
f"instead of session['username']"
)
class TestDeleteRoutesSessionAccess:
"""Verify delete routes use session['username'] not session.get('username')."""
def _get_route_functions(self):
from local_deep_research.research_library.deletion.routes.delete_routes import (
delete_collection,
delete_collection_index,
delete_document,
delete_document_blob,
delete_documents_blobs_bulk,
delete_documents_bulk,
get_bulk_deletion_preview,
get_collection_deletion_preview,
get_document_deletion_preview,
remove_document_from_collection,
remove_documents_from_collection_bulk,
)
return [
delete_document,
delete_document_blob,
get_document_deletion_preview,
remove_document_from_collection,
delete_collection,
delete_collection_index,
get_collection_deletion_preview,
delete_documents_bulk,
delete_documents_blobs_bulk,
remove_documents_from_collection_bulk,
get_bulk_deletion_preview,
]
def test_no_session_get_username(self):
"""No delete route should use session.get('username')."""
for func in self._get_route_functions():
source = _get_function_source(func)
assert not _uses_unsafe_session_get(source), (
f"{func.__name__} uses session.get('username') "
f"instead of session['username']"
)
class TestNewsFlaskApiSessionAccess:
"""Verify news flask_api routes use session['username'] not session.get('username')."""
def _get_route_functions(self):
from local_deep_research.news.flask_api import (
check_overdue_subscriptions,
run_subscription_now,
scheduler_stats,
)
return [
run_subscription_now,
scheduler_stats,
check_overdue_subscriptions,
]
def test_no_session_get_username(self):
"""No news flask_api route should use session.get('username')."""
for func in self._get_route_functions():
source = _get_function_source(func)
assert not _uses_unsafe_session_get(source), (
f"{func.__name__} uses session.get('username') "
f"instead of session['username']"
)