| T-08-01-01 |
Tampering |
Test fixture isolation |
mitigate |
Each CR test creates its own user via _register_user()/_login_session() helpers; revoke_client fixture provides isolated FakeRedis + db_session per test |
closed |
| T-08-01-02 |
Repudiation |
xfail strict mode |
mitigate |
@pytest.mark.xfail removed in plan 08-03; all three CR tests run strictly |
closed |
| T-08-01-SC |
Supply Chain |
pytest, pyotp |
accept |
Already pinned in requirements.txt (pytest==9.0.3, pyotp==2.9.0) |
closed |
| T-08-02-01 |
Information Disclosure |
CloudConnectionOut schema |
mitigate |
Field whitelist in api/schemas.py:14-42; credentials_enc confirmed absent |
closed |
| T-08-02-02 |
Tampering |
id coercion |
mitigate |
@field_validator("id", mode="before") coerce_id_to_str preserved verbatim at api/schemas.py:38-42 |
closed |
| T-08-02-03 |
Denial of Service |
Duplicate class definitions |
mitigate |
Old CloudConnectionOut in api/admin.py deleted in plan 08-04; api/cloud.py:35 imports only from api.schemas |
closed |
| T-08-02-SC |
Supply Chain |
No new packages |
accept |
Plan 08-02 installs zero new packages |
closed |
| T-08-03-01 |
Spoofing |
Toast message injection |
mitigate |
Toast message is a string literal in SettingsAccountTab.vue:204,240 and TotpEnrollment.vue:156; no user-controlled content |
closed |
| T-08-03-02 |
Repudiation |
Audit log for revoked sessions |
mitigate |
write_audit_log with sessions_revoked metadata preserved in api/auth/password.py:82-89, api/auth/totp.py:93-103, api/auth/totp.py:141-148 |
closed |
| T-08-03-03 |
Information Disclosure |
Toast leaks session count |
accept |
UI shows generic "Other sessions have been terminated."; exact count not disclosed |
closed |
| T-08-03-04 |
Tampering |
xfail strict=False masks regression |
mitigate |
Zero @pytest.mark.xfail decorators remain in backend/tests/test_auth.py |
closed |
| T-08-03-SC |
Supply Chain |
No new packages |
accept |
Plan 08-03 installs zero new packages |
closed |
| T-08-04-01 |
Elevation of Privilege |
Admin sub-router auth gate |
mitigate |
Every handler in users.py (7), quotas.py (2), ai.py (4) has _admin: User = Depends(get_current_admin) |
closed |
| T-08-04-02 |
Tampering |
Sub-router prefix doubling |
mitigate |
Sub-routers carry no prefix; sole prefix /api/admin in api/admin/__init__.py:20 |
closed |
| T-08-04-03 |
Information Disclosure |
_user_to_dict field set |
mitigate |
api/admin/shared.py:13-28 returns 10 whitelisted fields; password_hash, credentials_enc, totp_secret absent |
closed |
| T-08-04-04 |
Denial of Service |
Circular import via __init__.py |
mitigate |
api/admin/__init__.py — only APIRouter creation and three include_router calls |
closed |
| T-08-04-05 |
Tampering |
Validator duplication |
mitigate |
provider_must_be_known delegates entirely to services.ai_config.validate_provider_id (api/admin/ai.py:25,72-73,93-94) |
closed |
| T-08-04-06 |
Information Disclosure |
Old admin.py left in place |
mitigate |
backend/api/admin.py, backend/api/documents.py, backend/api/auth.py — all three old monolith files deleted |
closed |
| T-08-04-SC |
Supply Chain |
No new packages |
accept |
Plan 08-04 installs zero new packages |
closed |
| T-08-05-01 |
Elevation of Privilege |
IDOR on document endpoints |
mitigate |
Ownership assertion doc.user_id != current_user.id → 404 preserved on all handlers: crud.py:199-201,250-251,306-307,379-380, upload.py:289-291, content.py:89 |
closed |
| T-08-05-02 |
Tampering |
Sub-router prefix doubling |
mitigate |
Sub-routers carry no prefix; sole prefix /api/documents in api/documents/__init__.py:21 |
closed |
| T-08-05-03 |
Information Disclosure |
DocumentPatch.filename validator |
mitigate |
filename_no_path_separators raises ValueError on / or \ in api/documents/shared.py:40-45 |
closed |
| T-08-05-04 |
Tampering |
Atomic quota UPDATE invariant |
mitigate |
Atomic UPDATE quotas SET used_bytes = used_bytes + $delta WHERE … RETURNING preserved in upload.py:310-316; atomic decrement in services/storage.py:179-186 |
closed |
| T-08-05-05 |
Denial of Service |
Circular import via __init__.py |
mitigate |
api/documents/__init__.py — only aggregation |
closed |
| T-08-05-SC |
Supply Chain |
No new packages |
accept |
Plan 08-05 installs zero new packages |
closed |
| T-08-06-01 |
Spoofing |
limiter re-export |
mitigate |
api/auth/__init__.py:13 re-exports the same Limiter instance from shared.py:22; identity confirmed via app.state.limiter is api.auth.limiter |
closed |
| T-08-06-02 |
Repudiation |
CR-01/CR-02/CR-03 audit log entries |
mitigate |
write_audit_log with sessions_revoked metadata preserved on all three handlers in password.py and totp.py |
closed |
| T-08-06-03 |
Tampering |
Refresh-token rotation logic |
mitigate |
JTI, fgp, family-revocation on reuse all present in services/auth.py:114-115,233-239 |
closed |
| T-08-06-04 |
Information Disclosure |
Sub-router prefix doubling |
mitigate |
Sub-routers carry no prefix; sole prefix /api/auth in api/auth/__init__.py:15 |
closed |
| T-08-06-05 |
Denial of Service |
Circular import via __init__.py |
mitigate |
api/auth/__init__.py — only aggregation |
closed |
| T-08-06-06 |
Elevation of Privilege |
Session revocation skip |
mitigate |
skip_token_hash=skip_hash preserved in password.py:79-80, totp.py:89-90 (enable), totp.py:136-137 (disable) |
closed |
| T-08-06-SC |
Supply Chain |
No new packages |
accept |
Plan 08-06 installs zero new packages |
closed |
| T-08-07-01 |
Spoofing |
Bearer token injection |
mitigate |
utils.js:31-37 lazy-imports authStore and reads authStore.accessToken; pattern preserved in fetchWithRetry:97-102 |
closed |
| T-08-07-02 |
Tampering |
Circular import producing undefined exports |
mitigate |
request lives in utils.js; all domain modules (documents.js:8, auth.js:8, etc.) import from utils.js |
closed |
| T-08-07-03 |
Repudiation |
401-refresh-retry loop |
mitigate |
_retry flag preserved in request() (utils.js:45) and fetchWithRetry() (utils.js:107); recursion bounded |
closed |
| T-08-07-04 |
Information Disclosure |
Token stored in JS storage |
mitigate |
utils.js:35-36 reads authStore.accessToken (Pinia memory only); zero localStorage/sessionStorage references |
closed |
| T-08-07-05 |
Tampering |
export * name collision |
mitigate |
All 7 domain module export names verified unique; frontend build + test suite would fail on collision |
closed |
| T-08-07-06 |
Denial of Service |
Consumer files break |
mitigate |
client.js:13-20 — export * from each domain module + explicit re-exports from utils.js; 36 consumer files confirmed untouched |
closed |
| T-08-07-SC |
Supply Chain |
No new npm packages |
accept |
Plan 08-07 installs zero new packages |
closed |
| T-08-08-01 |
Supply Chain (Tampering) |
8 npm package installs |
mitigate |
Human checkpoint (task 1) required; packages verified on npmjs.com; package.json:12-19 confirms all 8 present |
closed |
| T-08-08-02 |
Tampering |
Vite 6 migration breaking behavior |
mitigate |
No breaking changes apply (no custom resolve.conditions, no Sass, no library mode); package.json:30 — "vite": "^6.4.3"; npm test + build pass |
closed |
| T-08-08-03 |
Information Disclosure |
requirements.txt leaks versions |
accept |
Version visibility in requirements.txt accepted; reproducibility benefit outweighs disclosure |
closed |
| T-08-08-04 |
Denial of Service |
alembic version discrepancy |
mitigate |
requirements.txt:18-19 — alembic==1.16.5 with inline comment documenting the >=1.18.4 → 1.16.5 discrepancy for future deliberate bump |
closed |
| T-08-08-05 |
Tampering |
@vitejs/plugin-vue peer-dep mismatch |
mitigate |
Both vite@^6 and @vitejs/plugin-vue@^6 installed together per RESEARCH.md §Pitfall 6; package.json:23,30 confirms major v6 alignment |
closed |
| T-08-08-SC |
Supply Chain |
npm packages |
mitigate |
Human checkpoint is the gate; packages verified; SUMMARY.md confirms pass |
closed |