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
167 lines
8.0 KiB
Markdown
167 lines
8.0 KiB
Markdown
# Phase 12.1: Fix Nextcloud Root Listing and Sync Visibility — Validation Matrix
|
||
|
||
**Status:** Plan 04 complete. Phase 12.1 closed. All gates pass, docs updated, v0.2.6 committed.
|
||
**Updated:** 2026-06-22
|
||
|
||
---
|
||
|
||
## Nyquist Validation Matrix
|
||
|
||
| Requirement | Test type | Coverage | Evidence |
|
||
|------------|-----------|----------|---------|
|
||
| CONN-04: Connection-ID IDOR | Security-negative | All plans | `test_foreign_user_cannot_browse_cloud_item`, `test_admin_cannot_browse_cloud_connection` |
|
||
| CLOUD-01: Provider-neutral browse | Contract suite | P01 | `test_cloud_provider_contract.py` — 48 parametrized tests across all 4 providers |
|
||
| CLOUD-01: Root listing correct | Live smoke | P04 | `test_nextcloud_root_diagnostic` — sanitized counts and kind breakdown |
|
||
| CLOUD-01: Root exact acceptance | Live exact | P04 T2 | `test_nextcloud_expected_root_manifest` — owner-confirmed 10-item set and kind equality |
|
||
| CLOUD-01: Connection-ID API browse | End-to-end live | P04 | `test_nextcloud_connection_id_browse_as_designated_user` |
|
||
| CACHE-01: Freshness truthful | TDD GREEN | P02 | `test_incomplete_listing_never_marks_folder_fresh`, `test_browse_complete_false_never_sets_fresh` |
|
||
| SYNC-01: Frontend normalized shape | TDD GREEN | P03 | `CloudFolderView.test.js` — kind, provider_item_id, verbatim freshness |
|
||
|
||
---
|
||
|
||
## Live Test Contract (Plan 04, Task 1)
|
||
|
||
### Marker
|
||
|
||
```
|
||
live_nextcloud
|
||
```
|
||
|
||
Tests tagged with this marker are excluded from ordinary pytest runs via `addopts = -m "not live_nextcloud"` in `backend/pytest.ini`. Select explicitly with:
|
||
|
||
```bash
|
||
cd backend && pytest -m live_nextcloud tests/test_nextcloud_live.py
|
||
```
|
||
|
||
### Required environment variables (values in ignored `.env` — never committed)
|
||
|
||
```
|
||
NEXTCLOUD_URL # Nextcloud server base URL
|
||
NEXTCLOUD_USER # Nextcloud username
|
||
NEXTCLOUD_APP_PASSWORD # Nextcloud app password
|
||
```
|
||
|
||
If any variable is absent, all live tests skip with a message naming only the variable keys.
|
||
|
||
### Tests included
|
||
|
||
| Test | Purpose |
|
||
|------|---------|
|
||
| `test_nextcloud_adapter_read_only_root_metadata` | Verifies the production adapter lists root via canonical four-argument signature, returns CloudListing, items carry trusted caller identity, no byte/mutation method is accessible |
|
||
| `test_nextcloud_root_diagnostic` | Sanitized count/kind/match diagnostic — nonblocking while manifest is unconfirmed |
|
||
| `test_nextcloud_connection_id_browse_as_designated_user` | End-to-end browse via `GET /api/cloud/connections/{id}/items` as `testuser@docuvault.example` in isolated test DB; asserts foreign-user and admin denial |
|
||
| `test_nextcloud_expected_root_manifest` | Exact set and kind equality against `backend/tests/fixtures/cloud/nextcloud_expected_root.json` (owner_confirmed: true); enabled after Task 2 reconciliation |
|
||
|
||
### Read-only / no-mutation contract
|
||
|
||
The network guard integrated in the test design blocks these HTTP methods before dispatch:
|
||
`GET` (byte content), `PUT`, `POST`, `PATCH`, `DELETE`, `MKCOL`, `MOVE`, `COPY`.
|
||
|
||
Only `PROPFIND`, `OPTIONS`, and `HEAD` are permitted. Mutation methods are asserted absent
|
||
on the adapter object. No MinIO, quota, or object-storage change is made.
|
||
|
||
### Threat coverage
|
||
|
||
| Threat ID | Mitigation | Test |
|
||
|-----------|-----------|------|
|
||
| T-12.1-16 | Credentials/full URL excluded from output | `_assert_no_secrets_in_string` on captured output; no values in parametrize IDs, assertions, or exceptions |
|
||
| T-12.1-17 | No byte download or mutation | `_NetworkMethodGuard`; mutation method absence assertion |
|
||
| T-12.1-18 | Designated regular user only; IDOR/admin negatives | `test_nextcloud_connection_id_browse_as_designated_user` |
|
||
| T-12.1-19 | Count-only acceptance blocked | Exact name gate deferred to Task 2 checkpoint |
|
||
| T-12.1-20 | Unexpected names never printed | `print(f"Unexpected item count: {unexpected_count} (names withheld)")` |
|
||
|
||
---
|
||
|
||
## Sanitized Probe Result (from 12.1-RESEARCH.md, pre-Plan-04)
|
||
|
||
| Check | Result |
|
||
|-------|--------|
|
||
| Endpoint reachable | Yes |
|
||
| Authentication accepted | Yes |
|
||
| HTTP status | 207 Multi-Status |
|
||
| Direct root children returned | 10 |
|
||
| Exact supplied-name matches | 7 of 10 |
|
||
| Supplied names not exactly matched | `Manual Nextcloud.png`, `Nextcloud Intro.mp4`, `Template credits.md` |
|
||
| Additional returned-name count | 3; names withheld |
|
||
| Byte download or mutation | None |
|
||
|
||
---
|
||
|
||
## Manifest Status: OWNER CONFIRMED — Task 2 Complete
|
||
|
||
The project owner reconciled the three fixture discrepancies (2026-06-22). The correct actual
|
||
Nextcloud names for the three previously unmatched slots are:
|
||
|
||
- Corrected name 1: kind=file (prior candidate name was wrong — owner confirmed)
|
||
- Corrected name 2: kind=file (prior candidate name was wrong — owner confirmed)
|
||
- Corrected name 3: kind=file (prior candidate name was wrong — owner confirmed)
|
||
|
||
The full manifest of 10 items is now stored in
|
||
`backend/tests/fixtures/cloud/nextcloud_expected_root.json` with `owner_confirmed: true`.
|
||
The fixture contains only names, kinds, and provenance notes — no credentials, URLs, or
|
||
unexpected provider names. Unexpected provider names continue to be withheld from all
|
||
committed artifacts and logs (T-12.1-20).
|
||
|
||
`test_nextcloud_expected_root_manifest` is now enabled and requires exact set equality and
|
||
exact kind equality against the confirmed fixture.
|
||
|
||
---
|
||
|
||
## Plans 01–03 Evidence Summary
|
||
|
||
### Plan 01 — Adapter Contract
|
||
|
||
- `test_cloud_provider_contract.py` — 48 tests: all pass
|
||
- `test_cloud_backends.py` — 67 tests: all pass
|
||
- `test_webdav_backend.py` — 29 tests: all pass
|
||
- `test_cloud_security.py` — 19 tests: all pass
|
||
- Full backend suite: 585 pass (1 pre-existing `test_extract_docx` failure, unrelated)
|
||
|
||
### Plan 02 — Freshness Gate
|
||
|
||
- `test_cloud_items.py` — 45 tests: all pass
|
||
- `test_cloud.py` — 25 tests: all pass
|
||
- `test_cloud_security.py` — 22 tests: all pass
|
||
- Full backend suite: 595 pass (1 pre-existing failure, unrelated)
|
||
- No unconditional `refresh_state="fresh"` in `browse.py` or `cloud_tasks.py`
|
||
|
||
### Plan 03 — Frontend Contract
|
||
|
||
- `CloudFolderView.test.js` — 23 tests: all pass
|
||
- `CloudProviderTreeItem.test.js` — 8 tests: all pass
|
||
- `CloudFolderTreeItem.test.js` — 16 tests: all pass
|
||
- `StorageBrowser.skeleton.test.js` — 22 tests: all pass
|
||
- `cloudConnections.test.js` — 23 tests: all pass
|
||
- `CloudBreadcrumbNavigation.test.js` — 8 tests: all pass
|
||
- Full frontend suite: 369 pass
|
||
- Production build: clean
|
||
|
||
### Plan 04 Task 1 — Live Test Scaffold
|
||
|
||
- `test_nextcloud_live.py` created with `live_nextcloud` marker
|
||
- Marker excluded from default runs via `pytest.ini`
|
||
- Three live tests: adapter metadata, sanitized root diagnostic, connection-ID end-to-end
|
||
- Task 2 (exact-name acceptance) deferred to checkpoint — fixture not yet created
|
||
|
||
### Plan 04 Task 2 — Fixture Reconciliation
|
||
|
||
- Owner confirmed corrected names for 3 previously unmatched items (2026-06-22)
|
||
- `backend/tests/fixtures/cloud/nextcloud_expected_root.json` created with `owner_confirmed: true`
|
||
- `test_nextcloud_expected_root_manifest` enabled with exact set and kind equality (T-12.1-19)
|
||
- Unexpected provider names remain withheld (T-12.1-20)
|
||
|
||
### Plan 04 Task 3 — Full Regression, Security, and Rendered-Flow Gates
|
||
|
||
| Gate | Command | Result |
|
||
|------|---------|--------|
|
||
| Bandit HIGH findings | `bandit -r backend/ -q` | 0 HIGH (10 LOW, pre-existing) |
|
||
| npm audit high/critical | `npm audit --audit-level=high` | 0 vulnerabilities |
|
||
| Git tracked .env files | `git ls-files '.env' '.env.*'` | None (only .env.example) |
|
||
| Secret scan new findings | `gitleaks detect --redact` | 3 findings, all pre-existing in old commits (May 2026), none from Phase 12.1 |
|
||
| Docker Compose config | `docker compose config --quiet` | Clean |
|
||
| Focused cloud tests | `pytest test_cloud_*.py test_webdav_backend.py` | 236 pass |
|
||
| Full backend suite | `pytest -v` | 595 pass, 1 pre-existing failure (test_extract_docx, unrelated) |
|
||
| Full frontend suite | `npm test` | 376 pass (7 new from CloudFolderRenderedFlow.test.js) |
|
||
| Frontend build | `npm run build` | Clean |
|
||
| Rendered flow test | `npm test -- --run CloudFolderRenderedFlow.test.js` | 7 pass |
|