7a0da7932b
Backwards Compatibility / Verify Encryption Constants (push) Waiting to run
Backwards Compatibility / PyPI Version Compatibility (push) Waiting to run
Backwards Compatibility / Database Migration Tests (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Blocked by required conditions
Docker Tests (Consolidated) / detect-changes (push) Waiting to run
Docker Tests (Consolidated) / Build Test Image (push) Waiting to run
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Blocked by required conditions
Docker Tests (Consolidated) / Accessibility Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Unit Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Example Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / Production Image Smoke Test (push) Blocked by required conditions
Docker Tests (Consolidated) / Infrastructure Tests (push) Blocked by required conditions
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Waiting to run
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
227 lines
7.6 KiB
Python
227 lines
7.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Test in-memory storage after encryption removal (issue #593)."""
|
|
|
|
from freezegun import freeze_time
|
|
|
|
from local_deep_research.database.session_passwords import (
|
|
session_password_store,
|
|
)
|
|
from local_deep_research.database.temp_auth import temp_auth_store
|
|
|
|
|
|
class TestSessionPasswordStore:
|
|
"""Test session password storage without encryption."""
|
|
|
|
def test_store_and_retrieve_password(self):
|
|
"""Test storing and retrieving a password."""
|
|
username = "test_user"
|
|
session_id = "test_session_123"
|
|
password = "test_password_456"
|
|
|
|
# Store password
|
|
session_password_store.store_session_password(
|
|
username, session_id, password
|
|
)
|
|
|
|
# Retrieve password
|
|
retrieved = session_password_store.get_session_password(
|
|
username, session_id
|
|
)
|
|
assert retrieved == password
|
|
|
|
def test_wrong_session_returns_none(self):
|
|
"""Test that wrong session ID returns None."""
|
|
username = "test_user"
|
|
session_id = "test_session_123"
|
|
password = "test_password_456"
|
|
|
|
session_password_store.store_session_password(
|
|
username, session_id, password
|
|
)
|
|
|
|
# Try with wrong session
|
|
wrong_retrieved = session_password_store.get_session_password(
|
|
username, "wrong_session"
|
|
)
|
|
assert wrong_retrieved is None
|
|
|
|
def test_clear_session(self):
|
|
"""Test clearing a session."""
|
|
username = "test_user"
|
|
session_id = "test_session_123"
|
|
password = "test_password_456"
|
|
|
|
session_password_store.store_session_password(
|
|
username, session_id, password
|
|
)
|
|
|
|
# Clear session
|
|
session_password_store.clear_session(username, session_id)
|
|
|
|
# Should return None after clearing
|
|
cleared_retrieved = session_password_store.get_session_password(
|
|
username, session_id
|
|
)
|
|
assert cleared_retrieved is None
|
|
|
|
def test_plain_text_storage(self):
|
|
"""Test that passwords are stored in plain text internally."""
|
|
username = "internal_test"
|
|
session_id = "internal_session"
|
|
password = "plain_text_password"
|
|
|
|
key = f"{username}:{session_id}"
|
|
session_password_store._store_credentials(
|
|
key, {"username": username, "password": password}
|
|
)
|
|
|
|
# Check internal storage directly
|
|
with session_password_store._lock:
|
|
stored_entry = session_password_store._store.get(key)
|
|
assert stored_entry is not None
|
|
assert "password" in stored_entry
|
|
assert stored_entry["password"] == password
|
|
assert "encrypted_password" not in stored_entry
|
|
|
|
# Clean up
|
|
session_password_store.clear_entry(key)
|
|
|
|
|
|
class TestTemporaryAuthStore:
|
|
"""Test temporary auth storage without encryption."""
|
|
|
|
def test_store_and_retrieve_auth(self):
|
|
"""Test storing and retrieving authentication."""
|
|
username = "test_user"
|
|
password = "test_password_789"
|
|
|
|
# Store auth
|
|
token = temp_auth_store.store_auth(username, password)
|
|
assert token is not None
|
|
|
|
# Retrieve auth (removes it)
|
|
retrieved = temp_auth_store.retrieve_auth(token)
|
|
assert retrieved == (username, password)
|
|
|
|
# Should be None after retrieval
|
|
retrieved_again = temp_auth_store.retrieve_auth(token)
|
|
assert retrieved_again is None
|
|
|
|
def test_peek_auth(self):
|
|
"""Test peeking at auth without removing it."""
|
|
username = "test_user"
|
|
password = "test_password_789"
|
|
|
|
token = temp_auth_store.store_auth(username, password)
|
|
|
|
# Peek at auth (doesn't remove)
|
|
peeked = temp_auth_store.peek_auth(token)
|
|
assert peeked == (username, password)
|
|
|
|
# Should still be there
|
|
peeked_again = temp_auth_store.peek_auth(token)
|
|
assert peeked_again == (username, password)
|
|
|
|
# Clean up
|
|
temp_auth_store.retrieve_auth(token)
|
|
|
|
def test_expiration(self):
|
|
"""Test that auth expires after TTL."""
|
|
# audit: PUNCHLIST reviewed 2026-05 — issue resolved by prior PR (recommendation: replace with freezegun or monkeypatch on the store's clock).
|
|
username = "test_user"
|
|
password = "test_password_789"
|
|
|
|
# Store original TTL
|
|
original_ttl = temp_auth_store.ttl
|
|
|
|
try:
|
|
# Set very short TTL
|
|
temp_auth_store.ttl = 1 # 1 second
|
|
|
|
# SUT (credential_store_base) uses time.time() for TTL
|
|
# comparison, so freezegun fully mocks the clock —
|
|
# no real sleep required.
|
|
with freeze_time("2026-01-01 00:00:00") as frozen:
|
|
token = temp_auth_store.store_auth(username, password)
|
|
# Advance past TTL
|
|
frozen.tick(2)
|
|
# Should be expired
|
|
expired = temp_auth_store.peek_auth(token)
|
|
assert expired is None
|
|
|
|
finally:
|
|
# Restore original TTL
|
|
temp_auth_store.ttl = original_ttl
|
|
|
|
def test_plain_text_storage(self):
|
|
"""Test that auth is stored in plain text internally."""
|
|
username = "internal_test"
|
|
password = "plain_text_password"
|
|
|
|
token = "test_token_123"
|
|
temp_auth_store._store_credentials(
|
|
token, {"username": username, "password": password}
|
|
)
|
|
|
|
# Check internal storage directly
|
|
with temp_auth_store._lock:
|
|
stored_entry = temp_auth_store._store.get(token)
|
|
assert stored_entry is not None
|
|
assert "password" in stored_entry
|
|
assert stored_entry["password"] == password
|
|
assert "encrypted_password" not in stored_entry
|
|
|
|
# Clean up
|
|
temp_auth_store.clear_entry(token)
|
|
|
|
|
|
class TestEncryptionRemoval:
|
|
"""Test that encryption has been properly removed."""
|
|
|
|
def test_no_cryptography_import(self):
|
|
"""Test that cryptography is not imported in the base class."""
|
|
import local_deep_research.database.credential_store_base as store_base
|
|
|
|
# Check that Fernet is not in the module
|
|
assert not hasattr(store_base, "Fernet")
|
|
|
|
# Check that cipher attributes don't exist
|
|
test_store = session_password_store
|
|
assert not hasattr(test_store, "_cipher")
|
|
assert not hasattr(test_store, "_master_key")
|
|
|
|
def test_storage_format_consistency(self):
|
|
"""Test that all stores use the same plain text format."""
|
|
# Test data
|
|
username = "consistency_test"
|
|
password = "test_password"
|
|
|
|
# Session password store
|
|
session_key = f"{username}:session_123"
|
|
session_password_store._store_credentials(
|
|
session_key, {"username": username, "password": password}
|
|
)
|
|
|
|
# Temp auth store
|
|
auth_token = "auth_token_123"
|
|
temp_auth_store._store_credentials(
|
|
auth_token, {"username": username, "password": password}
|
|
)
|
|
|
|
# Check both have same structure
|
|
with session_password_store._lock:
|
|
session_entry = session_password_store._store.get(session_key)
|
|
assert session_entry["password"] == password
|
|
assert "username" in session_entry
|
|
assert "expires_at" in session_entry
|
|
|
|
with temp_auth_store._lock:
|
|
auth_entry = temp_auth_store._store.get(auth_token)
|
|
assert auth_entry["password"] == password
|
|
assert "username" in auth_entry
|
|
assert "expires_at" in auth_entry
|
|
|
|
# Clean up
|
|
session_password_store.clear_entry(session_key)
|
|
temp_auth_store.clear_entry(auth_token)
|