Commit Graph
250 Commits
Author SHA1 Message Date
curo1305andClaude Sonnet 4.6 ed046752e4 test(13): add Nyquist validation — promote 3 xfail audit tests to green
Implemented write_audit_log calls for open_cloud_file, create_cloud_folder,
and rename_cloud_item in api/cloud/operations.py. Removed xfail markers from
test_cloud_audit.py. All 11 audit tests pass; 360 cloud tests pass total.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 08:17:24 +02:00
curo1305 60df8552b5 fix(13): CR-05 commit audit log after write_audit_log in reconnect endpoint so event is not silently lost 2026-06-23 00:29:34 +02:00
curo1305 af0de30e93 fix(13): CR-04 soft-delete CloudItem row on successful cloud delete to prevent orphaned metadata 2026-06-23 00:29:11 +02:00
curo1305 405c7a6610 fix(13): CR-03 wrap post-upload DB block in try/except to handle provider-success/DB-fail divergence 2026-06-23 00:28:34 +02:00
curo1305 a1d1c3ba2e fix(13): CR-02 apply PurePosixPath.name guard in WebDAV upload_file and rename to prevent path traversal 2026-06-23 00:27:52 +02:00
curo1305 d4b2697109 fix(13): CR-01 use RFC 6266 filename*=UTF-8'' encoding in Content-Disposition to prevent header injection 2026-06-23 00:26:59 +02:00
curo1305 ebc74f4abe fix(13): CR-06 re-raise HTTPException in preview_cloud_file instead of masking as unsupported_preview 2026-06-23 00:26:23 +02:00
curo1305 e68faf3051 chore(13-11): bump to v0.3.0, update docs, roadmap, and Phase 13 security gate evidence
- Version bump: 0.2.6 → 0.3.0 (Phase 13 complete — full cloud mutation surface shipped)
- CLAUDE.md: update current-state, shared module map, and Phase 13 non-negotiable rules
- README.md: add cloud file management, connection health, and authorized preview features; add Phase 13 mutation API table; mark Phase 13 complete
- ROADMAP.md: mark Phase 13 11/11 plans complete
- SECURITY.md: add Phase 13 threat register (T-13-01 through T-13-34), gate evidence (766 backend + 429 frontend tests pass, bandit 0 HIGH, npm audit 0 high/critical, gitleaks 3 pre-existing)
2026-06-23 00:14:57 +02:00
curo1305 508d27643b feat(13-09): move stale-guard, descendant safety, reconciliation, delete disclosure, and metadata-only auditing
Move endpoint (D-07, D-08, D-09, T-13-27, T-13-28, T-13-29):
- Descendant destination detection via cloud_items ancestor-chain walk (D-09)
- Stale provider result marks source folder non-fresh, returns typed stale body (D-07)
- Success: upsert_cloud_item with new parent_ref before returning
- Success: update_folder_state for both source and destination folders
- Success: write_audit_log 'cloud.item_moved' with metadata-only payload

Delete endpoint (D-10, D-11, T-13-29):
- Pre-fetch item kind for D-10 folder-vs-file disclosure
- Success: update_folder_state for parent folder before returning
- Success: write_audit_log 'cloud.item_deleted' with delete_kind metadata
- Response carries is_folder/item_kind for frontend disclosure
- Failed deletes never emit audit rows

Promote xfail markers in test_cloud_audit.py for move and delete audit rows
2026-06-22 20:02:09 +02:00
curo1305 af7d45f3c4 test(13-09): add failing RED tests for move stale-guard, reconciliation, and delete disclosure/audit
- Move: stale etag returns 409 + refreshes source folder state
- Move: descendant destination rejected (D-09)
- Move: success upserts cloud_item with new parent_ref (reconcile-before-return)
- Move: success marks source + destination folders non-fresh
- Move: success writes metadata-only 'cloud.item_moved' audit row
- Delete: success marks parent folder non-fresh
- Delete: folder disclosure stronger than file (is_folder=True, D-10)
- Delete: success writes metadata-only 'cloud.item_deleted' audit row
- Delete: failed delete must not write false audit event
2026-06-22 19:58:00 +02:00
curo1305 aaa63c19e4 feat(13-08): implement create-folder and rename collision retry, stale guard, and reconciliation
- create_cloud_folder: bounded collision retry (up to 5 attempts) with keep_both_name counter suffix (D-05, D-06)
- create_cloud_folder: stale precondition marks parent folder non-fresh and returns typed stale result (D-07)
- rename_cloud_item: stale precondition marks parent folder non-fresh via folder state update (D-07)
- create_cloud_folder: reconcile-before-return upserts new folder in cloud_items and invalidates parent (T-13-26)
- rename_cloud_item: reconcile-before-return upserts renamed item and invalidates parent folder (T-13-26)
- Import upsert_cloud_item, update_folder_state, keep_both_name, CloudResource from service modules
2026-06-22 19:51:57 +02:00
curo1305 9ad99461bf test(13-08): add RED tests for create-folder and rename collision retry, stale guard, and reconciliation
- Test 1: collision auto-retry with bounded window (D-05, D-06)
- Test 2: stale guard updates folder state and returns typed stale kind (D-07)
- Test 3: create-folder and rename success upserts CloudItem and invalidates parent folder
- Test 4: failed rename does not mutate CloudItem rows
2026-06-22 19:49:41 +02:00
curo1305 d959e0cf42 feat(13-06): emit metadata-only audit rows on authoritative upload success
- Write cloud.file_uploaded audit row in same transaction as reconciliation
- Audit payload is metadata-only: filename, size_bytes, connection_id, provider_item_id, parent_ref
- No provider URLs, tokens, bytes, or document text in audit metadata (T-13-02)
- Non-success paths (conflict, offline, reauth_required) bypass audit write (T-13-21)
- Import get_client_ip from deps.utils and write_audit_log from services.audit
2026-06-22 19:29:46 +02:00
curo1305 33f0498503 test(13-06): add failing upload audit test (RED) — promote from xfail
- Remove xfail marker from test_upload_success_writes_metadata_only_audit_row
- Add mock adapter so test isolates audit write logic without real provider I/O
- Test expects cloud.file_uploaded audit row with metadata-only payload (T-13-20)
2026-06-22 19:28:11 +02:00
curo1305 7ecbec7df9 feat(13-06): route upload success through centralized reconciliation before returning
- Upsert uploaded item into cloud_items for stable row identity and navigation
- Invalidate parent folder state (warning/upload_mutated) so next browse re-fetches
- Reconcile-before-return: session committed before response returned
- Failed/conflict uploads bypass reconciliation path (T-13-19)
2026-06-22 19:26:41 +02:00
curo1305 4cd6499a96 test(13-06): add failing upload reconciliation tests (RED)
- test_upload_success_upserts_cloud_item_before_returning — verifies CloudItem row exists after success
- test_upload_success_marks_folder_freshness_stale — verifies folder state updated on success
- test_upload_failed_does_not_mutate_cloud_items — verifies failed uploads don't create phantom items
2026-06-22 19:25:47 +02:00
curo1305 3ddf4da6a8 feat(13-05): add typed upload route mechanics tests (Task 2)
- test_upload_success_returns_typed_uploaded_body: kind/provider_item_id/name/size
- test_upload_provider_retryable_error_returns_offline_kind: typed offline body
- test_upload_provider_conflict_returns_conflict_kind: provider-detected collision
- test_upload_reauth_result_is_typed_and_not_500: CONN-02 credential failure path
- test_upload_queue_control_semantics_are_backend_authored: all 4 queue fields
- test_upload_keep_both_name_format: route can use keep_both_name() from service
- test_upload_foreign_user_blocked: IDOR protection on upload route
- 7 new tests pass; full suite 741 passed
2026-06-22 19:20:18 +02:00
curo1305 e1ce4cbe14 feat(13-05): implement keep_both_name() for D-03/D-05 collision naming
- keep_both_name(filename, counter) inserts counter before first extension
- Handles compound extensions (archive.tar.gz → archive (1).tar.gz)
- Handles extensionless files (README → README (1))
- Lives in services/cloud_operations as shared upload-queue helper
- All 23 upload contract + keep-both naming tests pass
2026-06-22 19:17:59 +02:00
curo1305 f47e36d93d test(13-05): add failing RED tests for upload conflict semantics and keep-both naming
- TestUploadConflictSemantics: typed upload results across all 4 providers
- TestKeepBothNaming: keep_both_name() helper with counter before extension
- Tests verify credentials never appear in upload results (T-13-17)
- Upload kind values must be MUT_KINDS constants
- 16/23 pass (providers already have upload_file); 7 fail (keep_both_name missing)
2026-06-22 19:17:13 +02:00
curo1305 94a0617b9b feat(13-04): add authorized cloud content and mutation routes with typed bodies
- backend/api/cloud/operations.py: owner-scoped open, preview, download, create-folder,
  rename, move, delete, upload routes with typed kind/reason response bodies (D-02, D-03,
  D-05, D-07, D-08, D-09, D-10, D-11, D-18, T-13-01, T-13-14)
- backend/api/cloud/__init__.py: register operations_router on /api/cloud
- backend/api/cloud/connections.py: Google Drive OAuth scope broadened to 'drive' (D-17)
- backend/api/cloud/schemas.py: ConnectionHealthOut, ReconnectOut, ContentResultOut,
  MutationResultOut, CreateFolderRequest, RenameItemRequest, MoveItemRequest typed schemas
- frontend/src/api/cloud.js: centralized helpers for all Phase 13 routes
- backend/tests/test_cloud_mutations.py: mock adapter + settings key fixture; 21 tests pass
- backend/tests/test_cloud_audit.py: mark 6 RED audit tests as xfail (audit writes are
  T-13-05 scope for a later plan); update credential fixture to use settings key
2026-06-22 19:09:38 +02:00
curo1305 5c0bc2a3b4 feat(13-04): upgrade Google Drive scope to drive, add health/reconnect/content API helpers
- connections.py: Update Google Drive OAuth to broader `drive` scope (D-17)
  instead of `drive.file` so authorized users can operate on all existing Drive
  items, not just files created by this app. Both initiation and callback flows
  updated to keep scopes consistent.
- schemas.py: Add Phase 13 typed response schemas — ConnectionHealthOut, ReconnectOut,
  ContentResultOut, MutationResultOut, and request schemas CreateFolderRequest,
  RenameItemRequest, MoveItemRequest. All credential-free (T-13-14, CONN-03).
- api/cloud.js: Add centralized frontend helpers for getConnectionHealth,
  reconnectCloudConnection, testCloudConnection (D-12, D-13), plus authorized
  cloud content and mutation helpers: openCloudFile, previewCloudFile,
  createCloudFolder, renameCloudItem, moveCloudItem, deleteCloudItem, uploadCloudFile.

All reconnect/health/test tests pass (14/14). Security suite passes (19/19).
2026-06-22 18:49:47 +02:00
curo1305 6784d3bdb7 feat(13-03): add cloud operations service and reconnect/health/test routes
- Create backend/services/cloud_operations.py as the single Phase 13 orchestration seam:
  get_connection_health (D-12), test_connection (D-13), reconnect_connection (CONN-01/02/03,
  D-14), disconnect_connection (D-16, explicit CloudItem cascade for SQLite compatibility)
- Add POST /connections/{id}/reconnect route (CONN-01/02/03, D-14)
- Add GET /connections/{id}/health route (D-12, T-13-02)
- Add POST /connections/{id}/test route (D-13, T-13-02)
- Update DELETE /connections/{id} to use service-layer disconnect with explicit
  CloudItem deletion (D-16; covers FK-cascade-less environments like SQLite tests)
- Fix (Rule 2 - T-13-02): add _scrub_audit_metadata() to admin audit log API to
  remove credential fields before returning audit rows to admin callers
- All 14 reconnect tests pass + 110 provider-contract tests pass
- Pre-existing failures in test_cloud_mutations and test_extractor are unrelated
2026-06-22 18:43:50 +02:00
curo1305 88a62da4c4 feat(13-03): extend cloud contract with mutable adapter and provider implementations
- Add MutableCloudResourceAdapter abstract class to cloud_base.py with Phase 13
  mutable-operation vocabulary (MUT_KIND_* / MUT_REASON_* constants, PreviewSupport)
- Add abstract methods: create_folder, rename, move, delete, upload_file + _normalize_error
- Update GoogleDriveBackend to implement MutableCloudResourceAdapter with Drive-scope
  methods; prefer trash() for delete (D-11); update SCOPES to drive (D-17)
- Update OneDriveBackend to implement MutableCloudResourceAdapter; use
  PublicClientApplication for refresh_token flow; Graph DELETE is always permanent (D-11)
- Update WebDAVBackend to implement MutableCloudResourceAdapter; SSRF guard via __init__
  (not per-call) for mutable methods; _validate_destination for MOVE SSRF (T-13-04)
- NextcloudBackend inherits all mutable methods from WebDAVBackend (no changes needed)
- Add build_mutable_cloud_adapter() to cloud_backend_factory.py
- All 184 tests pass (test_cloud_backends.py + test_cloud_provider_contract.py)
2026-06-22 18:31:53 +02:00
curo1305 fd6b561899 test(13-01): extend provider contract suites for four-provider mutable-operation parity
- Add Phase 13 mutable-operation RED tests to test_cloud_backends.py:
  TestGoogleDriveMutableContract (D-17 scope, create/rename/move/delete/upload),
  TestOneDriveMutableContract (CONN-02 token handoff, permanent-delete disclosure,
  nextLink SSRF guard), TestNextcloudMutableContract (create-folder SSRF, delete
  normalization), TestWebDAVMutableContract (permanent-delete, move SSRF, no
  cloud_items imports)
- Add Phase 13 mutable-operation RED tests to test_cloud_provider_contract.py:
  TestMutableAdapterContract (method existence, async contract, canonical signatures
  for all four providers), TestMutableAdapterResultNormalization (normalized kind/reason
  return types, conflict normalization documentation, unsupported-capability disclosure)
- All new tests fail against the current codebase — mutable adapter methods do not
  exist yet (expected RED); all prior Phase 12 tests remain green
2026-06-22 18:01:15 +02:00
curo1305 efb596433c test(13-01): add red API and audit contracts for reconnect, content, and mutation flows
- Create test_cloud_mutations.py: IDOR, admin block, credential secrecy, and typed
  kind/reason body coverage for open, preview, upload, create-folder, rename, move,
  and delete endpoint contracts (D-02 through D-11, D-18)
- Create test_cloud_reconnect.py: reconnect patch-in-place (CONN-01), encrypted credential
  persistence (CONN-02), response secrecy (CONN-03), health endpoint, test action,
  cache-preservation on reconnect (D-14), transient-outage data preservation (D-15),
  and disconnect metadata cleanup (D-16)
- Create test_cloud_audit.py: metadata-only audit rows for every successful cloud operation,
  false-overwrite prevention (T-13-05), admin audit log credential exclusion (T-13-02)
- All tests fail against current codebase — Phase 13 routes do not exist yet (expected RED)
2026-06-22 17:57:55 +02:00
curo1305 b00218e5c5 test(phase-12.1): repair live browse validation guard 2026-06-22 12:03:06 +02:00
curo1305 46b7ea6c12 test(12.1): verify live cloud browse integration
Phase 12.1 close — docs, version, and security evidence:
- Bump version 0.2.5 → 0.2.6 in backend/main.py, frontend/package.json
- CLAUDE.md: update current state to Phase 12.1 complete v0.2.6
- README.md: add Phase 12.1 corrections, live smoke variable names, Phase 13/14 exclusions
- RUNBOOK.md: add live smoke invocation, warning semantics table, fixture reconciliation procedure
- SECURITY.md: add Phase 12.1 Plan 04 threat table (T-12.1-16..20, T-12.1-SC) and gate evidence
- 12.1-VALIDATION.md: mark Plan 04 complete with final gate evidence
2026-06-22 09:46:25 +02:00
curo1305 1ec5158c65 test(12.1): owner-confirmed expected root manifest
- Create backend/tests/fixtures/cloud/nextcloud_expected_root.json with
  owner_confirmed: true and corrected 10-item name/kind manifest
- Add test_nextcloud_expected_root_manifest: exact set and kind equality
  against fixture (T-12.1-19 — count-only acceptance blocked)
- Update 12.1-VALIDATION.md: manifest status OWNER CONFIRMED, add
  exact-acceptance row to Nyquist matrix
2026-06-22 09:35:36 +02:00
curo1305 779b05086b test(12.1-04): add opt-in live Nextcloud smoke test suite
- backend/tests/test_nextcloud_live.py: three read-only live tests behind
  live_nextcloud marker (adapter root metadata, sanitized diagnostic, connection-ID
  browse as testuser@docuvault.example with IDOR/admin negatives)
- backend/pytest.ini: register live_nextcloud marker; addopts excludes it from
  ordinary runs so CI never contacts Nextcloud without explicit selection
- 12.1-VALIDATION.md: live test contract, commands, skip behaviour, sanitized
  7-of-10 probe result, and threat references T-12.1-16 through T-12.1-20
- Manifest status: unconfirmed — Task 2 exact-name gate pending owner decision
2026-06-22 09:02:42 +02:00
curo1305 7bb046ac41 fix(12.1): align cloud browser contract — kind, provider_item_id, server freshness
Normalized cloud frontend for DocuVault v0.2.5:
- Cloud UI classifies items by kind=folder/file (is_dir removed from all active paths)
- Folder navigation uses provider_item_id (opaque provider ref) — DocuVault id kept for row identity
- Server freshness (refresh_state, last_refreshed_at) mapped verbatim — no browser-clock evidence
- Breadcrumb lineage built from explicit visited nodes — never split(provider_item_id)
- CloudFolderTreeItem: expandable/icon/navigate/loadChildren all use kind and provider_item_id
- Version bump 0.2.4 → 0.2.5
- SECURITY.md: T-12.1-11 through T-12.1-15 closed
- 369 tests pass, npm audit clean, production build clean
2026-06-22 08:51:14 +02:00
curo1305 70a543a8c1 fix(12.1): make cloud freshness authoritative — plan 02 documentation and version
- Version bump 0.2.3 → 0.2.4 (user-visible sync status corrected)
- CLAUDE.md: update current state; add apply_listing_and_finalize to module map; add no-independent-fresh rule
- README.md: bump version
- RUNBOOK.md: update freshness state docs; explain complete=True gate and last_refreshed_at behavior
- SECURITY.md: add T-12.1-06..10 threat closure evidence for Plan 02
2026-06-22 08:34:51 +02:00
curo1305 bfa4dc502a feat(12.1-02): centralize listing freshness gate — apply_listing_and_finalize
- Add ListingResult dataclass to cloud_items.py
- Add apply_listing_and_finalize() — single shared gate for complete/incomplete semantics
- complete=True: upserts, soft-deletes unseen children, marks fresh, advances last_refreshed_at
- complete=False: upserts seen items only, never deletes unseen, preserves prior timestamp, warns
- browse.py: replace unconditional update_folder_state('fresh') with apply_listing_and_finalize
- cloud_tasks.py: replace unconditional fresh transition with apply_listing_and_finalize
- Celery task returns structured warning status on incomplete (no credentials/provider item names)
- Fix timezone naive/aware comparison in test (SQLite strips tzinfo)
2026-06-22 08:29:41 +02:00
curo1305 fe08afd740 test(12.1-02): add failing tests for truthful freshness gate (RED)
- test_incomplete_listing_never_marks_folder_fresh
- test_incomplete_listing_retains_cached_rows_and_last_success
- test_complete_empty_listing_is_authoritative_and_fresh
- test_partial_items_upsert_without_deleting_unseen_children
- test_apply_listing_returns_warning_for_complete_false
- test_sync_browse_returns_warning_for_complete_false
- test_worker_returns_warning_for_complete_false
- security: browse_complete_false_never_sets_fresh, no_raw_provider_error, owner_scoped_on_incomplete
2026-06-22 08:26:35 +02:00
curo1305 62128730e8 fix(12.1-01): restore asyncio import, bump version to 0.2.3, update docs
- Restore asyncio import removed when legacy list_folder was deleted (health_check
  still uses asyncio.to_thread); fixes test_nextcloud_connect_persists regression
- Bump backend/main.py and frontend/package.json version to 0.2.3
- Update CLAUDE.md: note Phase 12.1 Plan 01 complete, add normalize_nextcloud_url
  to shared module map, add NextcloudBackend no-override rule
- Update README.md: version 0.2.3, note canonical Nextcloud URL normalization and
  OneDrive nextLink origin guard
- Full backend suite: 585 passed, 1 pre-existing failure (missing python-docx module)
2026-06-22 08:18:23 +02:00
curo1305 805fe44bfb feat(12.1-01): add cross-origin nextLink guard and Drive/OneDrive contract tests
- Validate @odata.nextLink hostname against graph.microsoft.com before following
  (T-12.1-03); cross-origin nextLink returns complete=False with prior items retained
- Add TestOneDriveCrossOriginNextLink: cross-origin rejection, same-origin follows,
  page failure retains prior items
- Add TestGoogleDriveAuthFailureControl: 401 returns complete=False, page1+page2
  error retains page1 items
- All 163 four-provider contract tests pass
2026-06-22 08:12:39 +02:00
curo1305 2b46f74329 feat(12.1-01): repair Nextcloud adapter contract and add URL normalization
- Remove incompatible NextcloudBackend.list_folder(folder_path="") override
  so Nextcloud inherits canonical WebDAVBackend.list_folder(connection_id,
  user_id, parent_ref=None, page_token=None) -> CloudListing
- Add normalize_nextcloud_url() to cloud_utils.py: idempotent canonical DAV
  root derivation, username percent-encoding, https-only, rejects userinfo/
  query/fragment, validates via SSRF guard before return
- Use normalize_nextcloud_url in cloud_backend_factory for nextcloud provider
- Add TestNextcloudBackendNoListFolderOverride and TestNextcloudUrlNormalization
  to test_webdav_backend.py
- All 9 Nextcloud contract failures now pass; full focused suite: 158 passed
2026-06-22 08:11:06 +02:00
curo1305 eb68facd6c test(12.1-01): add failing four-provider contract suite and fixtures
- Create test_cloud_provider_contract.py with parametrized suite for
  Nextcloud, WebDAV, Google Drive, OneDrive
- Assert canonical (connection_id, user_id, parent_ref=None, page_token=None)
  signature, CloudListing return type, trusted caller identity propagation,
  metadata normalization, pagination completeness, and forbidden-operation spies
- Add synthetic credential-free fixtures: nextcloud_root.xml, webdav_root.xml,
  google_drive_pages.json, onedrive_pages.json
- 9 tests fail for nextcloud (expected RED state — incompatible list_folder override)
- All webdav, google_drive, onedrive contract tests pass
2026-06-22 08:09:00 +02:00
curo1305 c85e4abd91 chore(12-06): bump version 0.2.1 → 0.2.2 2026-06-21 22:30:11 +02:00
curo1305 731b65ecdd feat(12-06): connection-ID native lifecycle — always INSERT, PUT credentials, connectionsFor multi-account
- Replace _upsert_cloud_connection with _insert_cloud_connection (always inserts new UUID row)
- Add PUT /connections/{id}/credentials endpoint (owner-scoped, SSRF + health-check, password-preserve)
- SettingsCloudTab: connectionsFor() renders all same-provider connections with Add account row
- CloudCredentialModal.submit: calls updateWebDavCredentials on edit, connectWebDav on create
- utils.js: FastAPI validation arrays normalised to concise field messages (Rule 2)
- Backend tests: same-provider independence + IDOR negative test for credential update
2026-06-21 22:29:59 +02:00
curo1305andClaude Sonnet 4.6 206f564248 test(12-05): fix test_nextcloud_connect_persists + compose migration test path
- test_nextcloud_connect_persists: provider=nextcloud routes to NextcloudBackend
  (not WebDAVBackend) — add storage.nextcloud_backend.validate_cloud_url patch
  and use AsyncMock for asyncio.to_thread (plain return_value=True is not
  awaitable, swallowed by except → False). Test now passes HTTP 201.
- test_compose_migrations: skip module when docker-compose.yml is not found at
  the expected repo-root path (file is not mounted inside the backend container).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 12:35:35 +02:00
curo1305andClaude Sonnet 4.6 043a7817e2 fix(12-uat): add display_name_override schema regression + Compose hardening
- test_list_connections_reads_display_name_override_column: proves GET /api/cloud/connections
  does not raise UndefinedColumn (direct Phase 12 UAT blocker regression)
- docker-compose: promtail socket :ro, Grafana password fail-fast (:?)
- test_migration_0006: DDL privilege test fails not skips when role absent
- README: version 0.2.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 11:07:30 +02:00
curo1305andClaude Sonnet 4.6 760a8d4bcd fix(12-uat): resolve critical code review findings in gap closure (CR-01 through CR-03, IN-01)
- CR-01: promtail Docker socket mounted :ro (was read-write)
- CR-02: Grafana admin password uses :? fail-fast (was :-changeme default)
- CR-03: DDL privilege test fails not skips when docuvault_app role absent
- IN-01: README version synced to 0.2.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 10:55:23 +02:00
curo1305 3ca57dcd0c fix(12-05): bump version to 0.2.1 and update docs for migration-gated startup
- backend/main.py, frontend/package.json: version 0.2.0 → 0.2.1
- CLAUDE.md, AGENTS.md: current state updated to reflect gap-closure
- README.md: startup instructions note migrate runs automatically; update migration commands
- RUNBOOK.md: startup diagram includes migrate service; migration gate explanation
- SECURITY.md: Phase 12 gap-closure threat register and security gate evidence
2026-06-20 10:49:21 +02:00
curo1305 de2efd1664 test(12-05): add migration 0005→0006 integration regression and RUNBOOK migration ops
- test_migration_0006.py: proves display_name_override, cloud_items, cloud_item_topics,
  cloud_folder_states, unique constraints, indexes, and DDL privilege boundary
- Tests skip cleanly without INTEGRATION=1/INTEGRATION_DATABASE_URL (no dev DB mutation)
- RUNBOOK: add Database Migrations section with revision check, upgrade, recovery, and
  emergency schema-drift remediation commands
2026-06-20 10:47:11 +02:00
curo1305 1b3084ddfa feat(12-05): add migration-gated Compose startup path
- Add one-shot migrate service using DATABASE_MIGRATE_URL and alembic upgrade head
- Add migrate dependency (service_completed_successfully) to backend, celery-worker, celery-beat
- migrate service has same read_only/cap_drop/no-new-privileges hardening as other services
- Add test_compose_migrations.py: 7 static assertions on Compose configuration
2026-06-20 10:45:22 +02:00
curo1305andClaude Sonnet 4.6 b6911fb4ed fix(12): resolve critical code review findings (CR-01 through CR-04)
- CR-01: fix browse URL from /folders/{id} to /items?parent_ref= (broken feature)
- CR-02: remove raw provider exception text from _TerminalProviderError messages
- CR-03: add user_id predicate to get_or_create_folder_state query (ownership boundary)
- CR-04: add max_length=255 to ConnectionRenameRequest.display_name

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 02:00:05 +02:00
curo1305 c441fc63e5 chore(12-04): bump version to 0.2.0 and finalize Phase 12 documentation
- backend/main.py, frontend/package.json: 0.1.6 → 0.2.0 (phase-complete minor bump)
- AGENTS.md: current state updated to Phase 12 complete; cloud-resource-foundation
  summary with browse contract, security suite, and Phase 13/14 boundary note
- README.md: version updated to 0.2.0
- All existing shared module map rules and frontend architecture constraints unchanged
2026-06-19 01:54:24 +02:00
curo1305 3b24058e15 test(12-04): add dedicated cloud security-negative integration suite
- test_foreign_user_cannot_browse_cloud_item (IDOR T-12-01, CONN-04)
- test_admin_cannot_browse_cloud_connection (admin block T-12-01, D-03)
- test_browse_response_excludes_credentials_and_raw_fields (T-12-03, D-06)
- test_ssrf_url_validation_invariants (T-12-04, D-14)
- test_browse_no_quota_mutation (T-12-09, D-07)
- test_browse_no_minio_calls (T-12-09, D-08)
- test_disabled_connection_browse_blocked (D-17)
- test_same_provider_items_scoped_to_connection (D-01, D-05 PostgreSQL)
- test_no_byte_download_during_browse (D-18)
- 16 tests pass; full suite 509 passed
2026-06-19 01:51:13 +02:00
curo1305 c6c0742267 feat(12-03): breadcrumb freshness, formatRelativeTime, v0.1.6, docs
- BreadcrumbBar: refreshing spinner, fresh checkmark (fades 3s), stale warning banner
- Accessible labels and role=status for all freshness states
- formatters.js: add formatRelativeTime (shared, no duplication)
- Version bump 0.1.5 → 0.1.6 in backend/main.py and frontend/package.json
- AGENTS.md: update state, shared module map (formatRelativeTime)
- README.md: unified connection-root browsing and capability explanations feature text
- 17 BreadcrumbBar tests pass; 323 total; production build clean
2026-06-18 23:31:50 +02:00
curo1305 c6237ca57f feat(12-02): add refresh_cloud_folder Celery task, staleness trigger, version 0.1.5
- Add tasks/cloud_tasks.py: durable refresh_cloud_folder task with 3-retry
  bounded backoff (30s/90s/270s +jitter), credential decryption in worker only
- Register tasks.cloud_tasks.* on documents queue in celery_app.py
- Add stale-while-revalidate staleness trigger in browse.py (5-min threshold)
- Add 4 Task 3 tests: idempotency, cached-row retention on failure, task structure,
  no-byte-download contract; add background-refresh scheduling integration test
- Bump backend version 0.1.4 → 0.1.5, frontend package.json 0.1.4 → 0.1.5
- Update AGENTS.md with Phase 12 Plan 02 state and new shared module map entries
- Update README with connection-ID browse API table and v0.1.5
2026-06-18 23:17:34 +02:00