7a0da7932b
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
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
489 lines
18 KiB
Python
489 lines
18 KiB
Python
"""
|
|
Behavioral tests for data_sanitizer module.
|
|
|
|
Tests the DataSanitizer class which removes or redacts sensitive
|
|
information from data structures.
|
|
"""
|
|
|
|
|
|
class TestSanitizeBasicDictionary:
|
|
"""Tests for sanitizing basic dictionaries."""
|
|
|
|
def test_removes_api_key(self):
|
|
"""api_key is removed from dictionary."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"username": "user", "api_key": "secret123"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "api_key" not in result
|
|
assert result["username"] == "user"
|
|
|
|
def test_removes_password(self):
|
|
"""password is removed from dictionary."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"username": "user", "password": "secret123"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "password" not in result
|
|
assert result["username"] == "user"
|
|
|
|
def test_removes_secret(self):
|
|
"""secret is removed from dictionary."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"name": "service", "secret": "abc123"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "secret" not in result
|
|
assert result["name"] == "service"
|
|
|
|
def test_removes_access_token(self):
|
|
"""access_token is removed from dictionary."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"user_id": 1, "access_token": "token123"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "access_token" not in result
|
|
assert result["user_id"] == 1
|
|
|
|
def test_removes_refresh_token(self):
|
|
"""refresh_token is removed from dictionary."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"user_id": 1, "refresh_token": "refresh123"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "refresh_token" not in result
|
|
|
|
def test_removes_private_key(self):
|
|
"""private_key is removed from dictionary."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"key_id": "k1", "private_key": "-----BEGIN..."}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "private_key" not in result
|
|
|
|
def test_removes_auth_token(self):
|
|
"""auth_token is removed from dictionary."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"session": "s1", "auth_token": "auth123"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "auth_token" not in result
|
|
|
|
def test_removes_session_token(self):
|
|
"""session_token is removed from dictionary."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"user": "u1", "session_token": "sess123"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "session_token" not in result
|
|
|
|
def test_removes_csrf_token(self):
|
|
"""csrf_token is removed from dictionary."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"form": "f1", "csrf_token": "csrf123"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "csrf_token" not in result
|
|
|
|
def test_removes_apikey_no_underscore(self):
|
|
"""apikey (without underscore) is removed."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"service": "s1", "apikey": "key123"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "apikey" not in result
|
|
|
|
|
|
class TestSanitizeCaseInsensitive:
|
|
"""Tests for case-insensitive key matching."""
|
|
|
|
def test_removes_API_KEY_uppercase(self):
|
|
"""API_KEY in uppercase is removed."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"name": "test", "API_KEY": "secret"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "API_KEY" not in result
|
|
|
|
def test_removes_Api_Key_mixed_case(self):
|
|
"""Api_Key in mixed case is removed."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"name": "test", "Api_Key": "secret"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "Api_Key" not in result
|
|
|
|
def test_removes_PASSWORD_uppercase(self):
|
|
"""PASSWORD in uppercase is removed."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"user": "u1", "PASSWORD": "secret"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "PASSWORD" not in result
|
|
|
|
def test_removes_Secret_capitalized(self):
|
|
"""Secret capitalized is removed."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"id": 1, "Secret": "mysecret"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "Secret" not in result
|
|
|
|
|
|
class TestSanitizeNestedStructures:
|
|
"""Tests for nested dictionaries and lists."""
|
|
|
|
def test_removes_key_in_nested_dict(self):
|
|
"""api_key in nested dictionary is removed."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"user": {"name": "test", "api_key": "secret"}}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "api_key" not in result["user"]
|
|
assert result["user"]["name"] == "test"
|
|
|
|
def test_removes_key_in_deeply_nested_dict(self):
|
|
"""api_key in deeply nested dictionary is removed."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"a": {"b": {"c": {"password": "secret", "value": 1}}}}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert "password" not in result["a"]["b"]["c"]
|
|
assert result["a"]["b"]["c"]["value"] == 1
|
|
|
|
def test_removes_key_in_list_of_dicts(self):
|
|
"""api_key in list of dictionaries is removed."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = [{"name": "a", "api_key": "k1"}, {"name": "b", "api_key": "k2"}]
|
|
result = DataSanitizer.sanitize(data)
|
|
assert all("api_key" not in item for item in result)
|
|
assert result[0]["name"] == "a"
|
|
assert result[1]["name"] == "b"
|
|
|
|
def test_removes_key_in_mixed_nested_structure(self):
|
|
"""api_key in mixed nested structure is removed."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {
|
|
"users": [{"id": 1, "password": "p1"}, {"id": 2, "password": "p2"}],
|
|
"config": {"api_key": "main_key"},
|
|
}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert all("password" not in user for user in result["users"])
|
|
assert "api_key" not in result["config"]
|
|
|
|
|
|
class TestSanitizePrimitives:
|
|
"""Tests for primitive values."""
|
|
|
|
def test_returns_string_unchanged(self):
|
|
"""String primitives are returned unchanged."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
assert DataSanitizer.sanitize("hello") == "hello"
|
|
|
|
def test_returns_number_unchanged(self):
|
|
"""Number primitives are returned unchanged."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
assert DataSanitizer.sanitize(42) == 42
|
|
assert DataSanitizer.sanitize(3.14) == 3.14
|
|
|
|
def test_returns_boolean_unchanged(self):
|
|
"""Boolean primitives are returned unchanged."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
assert DataSanitizer.sanitize(True) is True
|
|
assert DataSanitizer.sanitize(False) is False
|
|
|
|
def test_returns_none_unchanged(self):
|
|
"""None is returned unchanged."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
assert DataSanitizer.sanitize(None) is None
|
|
|
|
|
|
class TestSanitizeCustomKeys:
|
|
"""Tests for custom sensitive key sets."""
|
|
|
|
def test_custom_keys_are_removed(self):
|
|
"""Custom sensitive keys are removed."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"custom_secret": "value", "name": "test"}
|
|
result = DataSanitizer.sanitize(data, sensitive_keys={"custom_secret"})
|
|
assert "custom_secret" not in result
|
|
assert result["name"] == "test"
|
|
|
|
def test_default_keys_not_removed_with_custom(self):
|
|
"""Default keys are not removed when custom set is provided."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"api_key": "value", "custom_key": "secret"}
|
|
result = DataSanitizer.sanitize(data, sensitive_keys={"custom_key"})
|
|
# With custom keys, default keys are not removed
|
|
assert "api_key" in result
|
|
assert "custom_key" not in result
|
|
|
|
def test_empty_custom_keys_removes_nothing(self):
|
|
"""Empty custom key set removes nothing."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"api_key": "value", "password": "secret"}
|
|
result = DataSanitizer.sanitize(data, sensitive_keys=set())
|
|
assert "api_key" in result
|
|
assert "password" in result
|
|
|
|
|
|
class TestSanitizeEmptyInputs:
|
|
"""Tests for empty inputs."""
|
|
|
|
def test_empty_dict_returns_empty(self):
|
|
"""Empty dictionary returns empty dictionary."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
assert DataSanitizer.sanitize({}) == {}
|
|
|
|
def test_empty_list_returns_empty(self):
|
|
"""Empty list returns empty list."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
assert DataSanitizer.sanitize([]) == []
|
|
|
|
|
|
class TestSanitizeOriginalUnmodified:
|
|
"""Tests that original data is not modified."""
|
|
|
|
def test_original_dict_unchanged(self):
|
|
"""Original dictionary is not modified."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
original = {"name": "test", "api_key": "secret"}
|
|
DataSanitizer.sanitize(original)
|
|
assert "api_key" in original
|
|
|
|
def test_original_nested_dict_unchanged(self):
|
|
"""Original nested dictionary is not modified."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
original = {"user": {"name": "test", "password": "secret"}}
|
|
DataSanitizer.sanitize(original)
|
|
assert "password" in original["user"]
|
|
|
|
|
|
class TestRedactBasicDictionary:
|
|
"""Tests for redacting basic dictionaries."""
|
|
|
|
def test_redacts_api_key(self):
|
|
"""api_key value is redacted."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"username": "user", "api_key": "secret123"}
|
|
result = DataSanitizer.redact(data)
|
|
assert result["api_key"] == "[REDACTED]"
|
|
assert result["username"] == "user"
|
|
|
|
def test_redacts_password(self):
|
|
"""password value is redacted."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"username": "user", "password": "secret123"}
|
|
result = DataSanitizer.redact(data)
|
|
assert result["password"] == "[REDACTED]"
|
|
assert result["username"] == "user"
|
|
|
|
def test_redacts_multiple_keys(self):
|
|
"""Multiple sensitive keys are redacted."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {
|
|
"api_key": "k1",
|
|
"password": "p1",
|
|
"secret": "s1",
|
|
"name": "test",
|
|
}
|
|
result = DataSanitizer.redact(data)
|
|
assert result["api_key"] == "[REDACTED]"
|
|
assert result["password"] == "[REDACTED]"
|
|
assert result["secret"] == "[REDACTED]"
|
|
assert result["name"] == "test"
|
|
|
|
|
|
class TestRedactCustomText:
|
|
"""Tests for custom redaction text."""
|
|
|
|
def test_custom_redaction_text(self):
|
|
"""Custom redaction text is used."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"api_key": "secret"}
|
|
result = DataSanitizer.redact(data, redaction_text="***HIDDEN***")
|
|
assert result["api_key"] == "***HIDDEN***"
|
|
|
|
def test_empty_redaction_text(self):
|
|
"""Empty redaction text is used."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"api_key": "secret"}
|
|
result = DataSanitizer.redact(data, redaction_text="")
|
|
assert result["api_key"] == ""
|
|
|
|
|
|
class TestRedactNestedStructures:
|
|
"""Tests for redacting nested structures."""
|
|
|
|
def test_redacts_nested_dict(self):
|
|
"""Nested dictionary values are redacted."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"config": {"api_key": "secret", "url": "http://example.com"}}
|
|
result = DataSanitizer.redact(data)
|
|
assert result["config"]["api_key"] == "[REDACTED]"
|
|
assert result["config"]["url"] == "http://example.com"
|
|
|
|
def test_redacts_list_of_dicts(self):
|
|
"""List of dictionaries values are redacted."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = [
|
|
{"name": "a", "password": "p1"},
|
|
{"name": "b", "password": "p2"},
|
|
]
|
|
result = DataSanitizer.redact(data)
|
|
assert result[0]["password"] == "[REDACTED]"
|
|
assert result[1]["password"] == "[REDACTED]"
|
|
assert result[0]["name"] == "a"
|
|
|
|
|
|
class TestRedactPreservesStructure:
|
|
"""Tests that redact preserves data structure."""
|
|
|
|
def test_preserves_key_in_result(self):
|
|
"""Redacted key is still present in result."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"api_key": "secret"}
|
|
result = DataSanitizer.redact(data)
|
|
assert "api_key" in result
|
|
|
|
def test_preserves_nested_structure(self):
|
|
"""Nested structure is preserved."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"a": {"b": {"password": "secret"}}}
|
|
result = DataSanitizer.redact(data)
|
|
assert "a" in result
|
|
assert "b" in result["a"]
|
|
assert "password" in result["a"]["b"]
|
|
|
|
|
|
class TestConvenienceFunctions:
|
|
"""Tests for convenience functions."""
|
|
|
|
def test_sanitize_data_function(self):
|
|
"""sanitize_data convenience function works."""
|
|
from local_deep_research.security.data_sanitizer import sanitize_data
|
|
|
|
data = {"api_key": "secret", "name": "test"}
|
|
result = sanitize_data(data)
|
|
assert "api_key" not in result
|
|
assert result["name"] == "test"
|
|
|
|
def test_redact_data_function(self):
|
|
"""redact_data convenience function works."""
|
|
from local_deep_research.security.data_sanitizer import redact_data
|
|
|
|
data = {"api_key": "secret", "name": "test"}
|
|
result = redact_data(data)
|
|
assert result["api_key"] == "[REDACTED]"
|
|
assert result["name"] == "test"
|
|
|
|
def test_sanitize_data_with_custom_keys(self):
|
|
"""sanitize_data works with custom keys."""
|
|
from local_deep_research.security.data_sanitizer import sanitize_data
|
|
|
|
data = {"custom": "value", "other": "data"}
|
|
result = sanitize_data(data, sensitive_keys={"custom"})
|
|
assert "custom" not in result
|
|
assert result["other"] == "data"
|
|
|
|
def test_redact_data_with_custom_text(self):
|
|
"""redact_data works with custom redaction text."""
|
|
from local_deep_research.security.data_sanitizer import redact_data
|
|
|
|
data = {"password": "secret"}
|
|
result = redact_data(data, redaction_text="<removed>")
|
|
assert result["password"] == "<removed>"
|
|
|
|
|
|
class TestDefaultSensitiveKeys:
|
|
"""Tests for the default sensitive keys constant."""
|
|
|
|
def test_default_keys_include_api_key(self):
|
|
"""Default keys include api_key."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
assert "api_key" in DataSanitizer.DEFAULT_SENSITIVE_KEYS
|
|
|
|
def test_default_keys_include_password(self):
|
|
"""Default keys include password."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
assert "password" in DataSanitizer.DEFAULT_SENSITIVE_KEYS
|
|
|
|
def test_default_keys_include_secret(self):
|
|
"""Default keys include secret."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
assert "secret" in DataSanitizer.DEFAULT_SENSITIVE_KEYS
|
|
|
|
def test_default_keys_is_set(self):
|
|
"""Default keys is a set for O(1) lookup."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
assert isinstance(DataSanitizer.DEFAULT_SENSITIVE_KEYS, set)
|
|
|
|
|
|
class TestEdgeCases:
|
|
"""Tests for edge cases."""
|
|
|
|
def test_handles_none_values_in_dict(self):
|
|
"""Handles None values in dictionary."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"name": None, "api_key": "secret"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert result["name"] is None
|
|
assert "api_key" not in result
|
|
|
|
def test_handles_numeric_values(self):
|
|
"""Handles numeric values in dictionary."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"count": 42, "api_key": "secret"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert result["count"] == 42
|
|
assert "api_key" not in result
|
|
|
|
def test_handles_boolean_values(self):
|
|
"""Handles boolean values in dictionary."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = {"enabled": True, "password": "secret"}
|
|
result = DataSanitizer.sanitize(data)
|
|
assert result["enabled"] is True
|
|
assert "password" not in result
|
|
|
|
def test_handles_mixed_types_in_list(self):
|
|
"""Handles mixed types in list."""
|
|
from local_deep_research.security.data_sanitizer import DataSanitizer
|
|
|
|
data = [1, "string", {"api_key": "secret"}, None, True]
|
|
result = DataSanitizer.sanitize(data)
|
|
assert result[0] == 1
|
|
assert result[1] == "string"
|
|
assert "api_key" not in result[2]
|
|
assert result[3] is None
|
|
assert result[4] is True
|