fix: restore all code deleted/truncated by d2217531 Jules SSRF commit

Commit d2217531 (google-labs-jules SSRF fix) catastrophically deleted
11,500+ lines across 100+ files while fixing an unrelated IMAP issue.

Restored from d2217531^ (pre-bad-commit state):

Deleted files (fully restored):
- app/api/{automation,classification_rules,comments,sharing}.py
- app/middleware/upload_rate_limit.py
- app/tasks/{automation_tasks,classify_document}.py
- app/utils/{automation_hooks,classification_rules}.py
- docs/AppleAppStoreCompliance.md
- frontend/input.css, package.json, package-lock.json, tailwind.config.js
- frontend/static/js/{annotations,claim,comments,sharing}.js
- frontend/templates/{admin_connections,file_annotations,file_summary}.html
- tests/{test_api_files_comprehensive,test_auth_extended,test_sharing,
         test_comments,test_connections,test_imap_profiles,test_api_sessions,
         test_automation,test_classification_rules,test_api_advanced_filters,
         test_api_classification_rules,test_upload_rate_limit,test_api_dropbox,
         test_classify_document,test_comments_ui,test_upload_to_icloud,
         test_api_onedrive_comprehensive,test_frontend_build,test_sentry,
         test_diagnostic,test_database,test_views_dropbox,test_local_auth}.py

Truncated files (content restored):
- app/{auth,config,main,models,celery_worker,database}.py
- app/api/{__init__,api_tokens,diagnostic,dropbox,files,google_drive,
           integrations,local_auth,mobile,onedrive,pipelines,qr_auth,
           settings,url_upload}.py
- app/middleware/upload_rate_limit.py
- app/tasks/upload_to_nextcloud.py
- app/utils/{allowed_types,settings_service,settings_sync,user_scope,webhook}.py
- app/views/{base,dropbox,files,google_drive,onedrive,settings}.py
- docs/{API,AuthenticationSetup,ConfigurationGuide,DatabaseConfiguration,
        DeploymentGuide,DropboxSetup,GoogleDriveSetup,KubernetesDeployment,
        MobileApp,OneDriveSetup,ProductionReadiness,SentrySetup,
        SocialLoginSetup,UserGuide}.md
- frontend/static/{js/upload.js,styles.css}
- frontend/templates/{api_tokens,base,devices,dropbox,dropbox_callback,
                      file_view,files,google_drive,onedrive,onedrive_callback,
                      signup}.html
- frontend/translations/en.json
- migrations/env.py
- tests/{conftest,test_api_integrations,test_api_mobile,test_api_settings,
         test_api_tokens,test_audit_logs,test_duplicates,test_imap_tasks,
         test_setup_wizard,test_views_files_comprehensive}.py

Security fixes kept from post-d2217531 commits:
- app/utils/network.py: DNS SSRF fail-secure fix (06b0fced)
- app/utils/file_operations.py: path traversal fix (1018ea17)
- tests/test_imap_tasks.py: re-applied 4 is_private_ip mock patches

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Agent-Logs-Url: https://github.com/christianlouis/DocuElevate/sessions/51133dd8-9bec-41ab-aa10-3de753634187
This commit is contained in:
copilot-swe-agent[bot]
2026-03-23 23:52:39 +00:00
parent 11a49eb7fd
commit c7d3ec57c3
115 changed files with 23532 additions and 1043 deletions
+96 -1
View File
@@ -5,12 +5,15 @@ Covers the audit service (recording, querying, SIEM forwarding),
the REST API endpoints, and the admin viewer page.
"""
import base64
import json
import socket
from datetime import datetime, timezone
from unittest.mock import MagicMock, patch
from unittest.mock import MagicMock, Mock, PropertyMock, patch
import pytest
from fastapi import HTTPException
from itsdangerous import TimestampSigner
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool
@@ -645,6 +648,16 @@ class TestAuditLogAPI:
# View tests
# ---------------------------------------------------------------------------
_TEST_SESSION_SECRET = "test_secret_key_for_testing_must_be_at_least_32_characters_long"
def _make_admin_session_cookie() -> str:
"""Create a signed session cookie with admin user data for integration tests."""
session_data = {"user": {"id": "admin", "is_admin": True}}
signer = TimestampSigner(_TEST_SESSION_SECRET)
data = base64.b64encode(json.dumps(session_data).encode()).decode("utf-8")
return signer.sign(data).decode("utf-8")
@pytest.mark.integration
class TestAuditLogView:
@@ -655,3 +668,85 @@ class TestAuditLogView:
resp = client.get("/admin/audit-logs")
assert resp.status_code == 200
assert "Audit Logs" in resp.text
def test_audit_logs_page_accessible_with_admin_session(self, client):
"""GET /admin/audit-logs with admin session cookie returns 200."""
client.cookies.set("session", _make_admin_session_cookie())
resp = client.get("/admin/audit-logs", follow_redirects=False)
assert resp.status_code == 200
assert "Audit Logs" in resp.text
def test_audit_logs_page_redirects_non_admin(self, client):
"""GET /admin/audit-logs without admin session redirects to home."""
resp = client.get("/admin/audit-logs", follow_redirects=False)
assert resp.status_code == 302
@pytest.mark.unit
class TestAuditLogsPageUnit:
"""Unit tests for the audit_logs_page view function (lines 29-43)."""
@patch("app.views.audit_logs.templates")
@patch("app.views.audit_logs.settings")
@pytest.mark.asyncio
async def test_audit_logs_page_siem_disabled(self, mock_settings, mock_templates):
"""Renders the template with siem_transport=None when SIEM is disabled."""
from app.views.audit_logs import audit_logs_page
mock_settings.audit_siem_enabled = False
mock_settings.version = "2.0.0"
mock_request = Mock()
mock_request.session = {"user": {"id": "admin", "is_admin": True}}
mock_db = Mock()
await audit_logs_page(mock_request, mock_db)
mock_templates.TemplateResponse.assert_called_once()
call_args = mock_templates.TemplateResponse.call_args
assert call_args[0][0] == "audit_logs.html"
context = call_args[0][1]
assert context["siem_enabled"] is False
assert context["siem_transport"] is None
assert context["app_version"] == "2.0.0"
@patch("app.views.audit_logs.templates")
@patch("app.views.audit_logs.settings")
@pytest.mark.asyncio
async def test_audit_logs_page_siem_enabled(self, mock_settings, mock_templates):
"""Renders the template with siem_transport set when SIEM is enabled."""
from app.views.audit_logs import audit_logs_page
mock_settings.audit_siem_enabled = True
mock_settings.audit_siem_transport = "syslog"
mock_settings.version = "2.0.0"
mock_request = Mock()
mock_request.session = {"user": {"id": "admin", "is_admin": True}}
mock_db = Mock()
await audit_logs_page(mock_request, mock_db)
mock_templates.TemplateResponse.assert_called_once()
call_args = mock_templates.TemplateResponse.call_args
context = call_args[0][1]
assert context["siem_enabled"] is True
assert context["siem_transport"] == "syslog"
@patch("app.views.audit_logs.settings")
@pytest.mark.asyncio
async def test_audit_logs_page_raises_500_on_error(self, mock_settings):
"""Raises HTTP 500 when an unexpected error occurs while loading the page."""
from app.views.audit_logs import audit_logs_page
# Make accessing audit_siem_enabled raise an exception to trigger the except branch
type(mock_settings).audit_siem_enabled = PropertyMock(side_effect=RuntimeError("settings unavailable"))
mock_request = Mock()
mock_request.session = {"user": {"id": "admin", "is_admin": True}}
mock_db = Mock()
with pytest.raises(HTTPException) as exc_info:
await audit_logs_page(mock_request, mock_db)
assert exc_info.value.status_code == 500
assert "Failed to load audit logs page" in exc_info.value.detail