superlocalmemory 3.6.23 → 3.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -0
- package/README.md +271 -71
- package/bin/slm-npm +43 -89
- package/ide/configs/antigravity-mcp.json +2 -2
- package/ide/configs/chatgpt-desktop-mcp.json +1 -1
- package/ide/configs/claude-desktop-mcp.json +2 -2
- package/ide/configs/windsurf-mcp.json +2 -2
- package/ide/hooks/context-hook.js +6 -2
- package/ide/hooks/post-recall-hook.js +7 -3
- package/ide/hooks/tool-event-hook.sh +2 -1
- package/package.json +17 -9
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/_GENERATED.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin-src/agents/slm-memory-advisor.md +1 -1
- package/plugin-src/manifest.json +1 -1
- package/plugin-src/requirements.txt +1 -1
- package/plugin-src/rules/AGENTS.md +1 -1
- package/plugin-src/skills/slm-session/SKILL.md +1 -1
- package/pyproject.toml +40 -8
- package/scripts/postinstall-interactive.js +17 -94
- package/scripts/postinstall.js +185 -258
- package/scripts/preuninstall.js +9 -50
- package/src/superlocalmemory/__init__.py +2 -2
- package/src/superlocalmemory/attribution/mathematical_dna.py +1 -1
- package/src/superlocalmemory/attribution/signer.py +34 -19
- package/src/superlocalmemory/attribution/watermark.py +1 -1
- package/src/superlocalmemory/cli/_lazy_init.py +7 -7
- package/src/superlocalmemory/cli/commands.py +453 -191
- package/src/superlocalmemory/cli/context_commands.py +5 -4
- package/src/superlocalmemory/cli/daemon.py +282 -187
- package/src/superlocalmemory/cli/db_migrate.py +3 -1
- package/src/superlocalmemory/cli/diagnostics_cmd.py +28 -0
- package/src/superlocalmemory/cli/evidence_cmd.py +103 -0
- package/src/superlocalmemory/cli/ingest_cmd.py +7 -3
- package/src/superlocalmemory/cli/main.py +128 -31
- package/src/superlocalmemory/cli/pending_store.py +54 -38
- package/src/superlocalmemory/cli/scale_engine_cmd.py +37 -0
- package/src/superlocalmemory/cli/service_installer.py +57 -52
- package/src/superlocalmemory/cli/setup_wizard.py +142 -88
- package/src/superlocalmemory/cli/version_banner.py +2 -1
- package/src/superlocalmemory/code_graph/config.py +3 -1
- package/src/superlocalmemory/core/backend_orchestrator.py +81 -21
- package/src/superlocalmemory/core/config.py +65 -20
- package/src/superlocalmemory/core/consolidation_engine.py +9 -7
- package/src/superlocalmemory/core/context_cache.py +56 -8
- package/src/superlocalmemory/core/derivation_lineage.py +246 -0
- package/src/superlocalmemory/core/embedding_worker.py +32 -20
- package/src/superlocalmemory/core/embeddings.py +54 -18
- package/src/superlocalmemory/core/engine.py +150 -104
- package/src/superlocalmemory/core/engine_ingestion.py +513 -0
- package/src/superlocalmemory/core/engine_wiring.py +32 -23
- package/src/superlocalmemory/core/evidence_bundle.py +526 -0
- package/src/superlocalmemory/core/fact_consolidator.py +5 -11
- package/src/superlocalmemory/core/graph_analyzer.py +2 -2
- package/src/superlocalmemory/core/health_monitor.py +4 -2
- package/src/superlocalmemory/core/ingestion_command.py +636 -0
- package/src/superlocalmemory/core/injection.py +69 -18
- package/src/superlocalmemory/core/lifecycle_state.py +153 -0
- package/src/superlocalmemory/core/maintenance.py +1 -1
- package/src/superlocalmemory/core/maintenance_scheduler.py +51 -35
- package/src/superlocalmemory/core/mutations.py +143 -0
- package/src/superlocalmemory/core/platform_utils.py +7 -4
- package/src/superlocalmemory/core/ram_lock.py +16 -5
- package/src/superlocalmemory/core/rate_limit.py +1 -1
- package/src/superlocalmemory/core/recall_pipeline.py +60 -101
- package/src/superlocalmemory/core/recall_worker.py +76 -59
- package/src/superlocalmemory/core/registry.py +1 -1
- package/src/superlocalmemory/core/scale_engine.py +293 -0
- package/src/superlocalmemory/core/score_contract.py +62 -0
- package/src/superlocalmemory/core/security_primitives.py +3 -1
- package/src/superlocalmemory/core/slm_disabled.py +3 -5
- package/src/superlocalmemory/core/store_pipeline.py +172 -40
- package/src/superlocalmemory/core/tier_manager.py +32 -20
- package/src/superlocalmemory/core/worker_pool.py +13 -4
- package/src/superlocalmemory/dynamics/activation_guided_quantization.py +1 -1
- package/src/superlocalmemory/dynamics/eap_scheduler.py +10 -3
- package/src/superlocalmemory/dynamics/ebbinghaus_langevin_coupling.py +1 -1
- package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +1 -1
- package/src/superlocalmemory/encoding/auto_linker.py +1 -1
- package/src/superlocalmemory/encoding/cognitive_consolidator.py +7 -16
- package/src/superlocalmemory/encoding/consolidator.py +22 -5
- package/src/superlocalmemory/encoding/fact_extractor.py +1 -1
- package/src/superlocalmemory/encoding/foresight.py +2 -0
- package/src/superlocalmemory/encoding/graph_builder.py +1 -1
- package/src/superlocalmemory/encoding/temporal_parser.py +2 -0
- package/src/superlocalmemory/evaluation/__init__.py +13 -0
- package/src/superlocalmemory/evaluation/calibration.py +308 -0
- package/src/superlocalmemory/evolution/skill_evolver.py +2 -1
- package/src/superlocalmemory/graph/cozo_backend.py +256 -23
- package/src/superlocalmemory/hooks/_outcome_common.py +21 -11
- package/src/superlocalmemory/hooks/antigravity_adapter.py +10 -31
- package/src/superlocalmemory/hooks/auto_invoker.py +25 -27
- package/src/superlocalmemory/hooks/auto_recall.py +31 -6
- package/src/superlocalmemory/hooks/auto_recall_hook.py +13 -33
- package/src/superlocalmemory/hooks/before_web_hook.py +9 -7
- package/src/superlocalmemory/hooks/claude_code_hooks.py +123 -35
- package/src/superlocalmemory/hooks/codex_assets.py +59 -0
- package/src/superlocalmemory/hooks/codex_hooks.py +186 -0
- package/src/superlocalmemory/hooks/context_payload.py +1 -1
- package/src/superlocalmemory/hooks/copilot_adapter.py +9 -24
- package/src/superlocalmemory/hooks/cursor_adapter.py +10 -32
- package/src/superlocalmemory/hooks/hook_daemon.py +4 -2
- package/src/superlocalmemory/hooks/hook_handlers.py +219 -32
- package/src/superlocalmemory/hooks/memory_protocol.py +5 -3
- package/src/superlocalmemory/hooks/post_tool_async_hook.py +4 -2
- package/src/superlocalmemory/hooks/session_registry.py +15 -8
- package/src/superlocalmemory/hooks/stop_outcome_hook.py +10 -6
- package/src/superlocalmemory/hooks/topic_shift_hook.py +42 -12
- package/src/superlocalmemory/hooks/user_prompt_hook.py +9 -14
- package/src/superlocalmemory/hooks/user_prompt_rehash_hook.py +19 -11
- package/src/superlocalmemory/infra/auth_middleware.py +38 -5
- package/src/superlocalmemory/infra/backup.py +7 -5
- package/src/superlocalmemory/infra/cloud_backup.py +18 -8
- package/src/superlocalmemory/infra/daemon_identity.py +248 -0
- package/src/superlocalmemory/infra/data_root.py +199 -0
- package/src/superlocalmemory/infra/event_bus.py +3 -1
- package/src/superlocalmemory/infra/local_diagnostics.py +327 -0
- package/src/superlocalmemory/infra/process_reaper.py +23 -0
- package/src/superlocalmemory/ingestion/adapter_manager.py +27 -9
- package/src/superlocalmemory/ingestion/base_adapter.py +25 -31
- package/src/superlocalmemory/ingestion/calendar_adapter.py +13 -4
- package/src/superlocalmemory/ingestion/credentials.py +14 -7
- package/src/superlocalmemory/ingestion/gmail_adapter.py +13 -4
- package/src/superlocalmemory/ingestion/transcript_adapter.py +7 -2
- package/src/superlocalmemory/learning/consolidation_quantization_worker.py +1 -1
- package/src/superlocalmemory/learning/ensemble.py +11 -0
- package/src/superlocalmemory/learning/entity_compiler.py +1 -1
- package/src/superlocalmemory/learning/feedback.py +1 -1
- package/src/superlocalmemory/learning/forgetting_scheduler.py +12 -7
- package/src/superlocalmemory/learning/quantization_scheduler.py +1 -1
- package/src/superlocalmemory/learning/ranker.py +4 -1
- package/src/superlocalmemory/learning/source_quality.py +1 -1
- package/src/superlocalmemory/learning/trigram_index.py +3 -2
- package/src/superlocalmemory/llm/backbone.py +1 -1
- package/src/superlocalmemory/math/ebbinghaus.py +1 -1
- package/src/superlocalmemory/math/fisher.py +1 -1
- package/src/superlocalmemory/math/fisher_quantized.py +1 -1
- package/src/superlocalmemory/math/hopfield.py +1 -1
- package/src/superlocalmemory/math/langevin.py +1 -1
- package/src/superlocalmemory/math/polar_quant.py +3 -4
- package/src/superlocalmemory/math/qjl.py +1 -1
- package/src/superlocalmemory/math/sheaf.py +1 -1
- package/src/superlocalmemory/math/turbo_quant.py +3 -2
- package/src/superlocalmemory/mcp/_daemon_proxy.py +12 -11
- package/src/superlocalmemory/mcp/_pool_adapter.py +27 -0
- package/src/superlocalmemory/mcp/http_transport.py +53 -0
- package/src/superlocalmemory/mcp/server.py +39 -13
- package/src/superlocalmemory/mcp/shared.py +69 -3
- package/src/superlocalmemory/mcp/tools_active.py +141 -31
- package/src/superlocalmemory/mcp/tools_core.py +130 -31
- package/src/superlocalmemory/mcp/tools_evolution.py +5 -7
- package/src/superlocalmemory/mcp/tools_learning.py +42 -2
- package/src/superlocalmemory/mcp/tools_mesh.py +7 -23
- package/src/superlocalmemory/mcp/tools_optimize.py +8 -1
- package/src/superlocalmemory/mcp/tools_v28.py +23 -2
- package/src/superlocalmemory/mcp/tools_v3.py +26 -1
- package/src/superlocalmemory/mcp/tools_v33.py +56 -17
- package/src/superlocalmemory/mesh/broker.py +2 -0
- package/src/superlocalmemory/mesh/remote_sync.py +50 -12
- package/src/superlocalmemory/optimize/cache/manager.py +77 -1
- package/src/superlocalmemory/optimize/cache/semantic.py +23 -3
- package/src/superlocalmemory/optimize/compress/ccr.py +4 -0
- package/src/superlocalmemory/optimize/compress/router.py +6 -1
- package/src/superlocalmemory/optimize/config/__init__.py +5 -0
- package/src/superlocalmemory/optimize/config/store.py +6 -4
- package/src/superlocalmemory/optimize/proxy/_helpers.py +15 -5
- package/src/superlocalmemory/optimize/proxy/capture.py +3 -2
- package/src/superlocalmemory/optimize/proxy/server.py +2 -2
- package/src/superlocalmemory/optimize/storage/db.py +12 -12
- package/src/superlocalmemory/retrieval/agentic.py +1 -1
- package/src/superlocalmemory/retrieval/ann_index.py +1 -1
- package/src/superlocalmemory/retrieval/bm25_channel.py +35 -11
- package/src/superlocalmemory/retrieval/bridge_discovery.py +73 -8
- package/src/superlocalmemory/retrieval/engine.py +169 -79
- package/src/superlocalmemory/retrieval/entity_channel.py +289 -67
- package/src/superlocalmemory/retrieval/forgetting_filter.py +1 -1
- package/src/superlocalmemory/retrieval/fusion.py +1 -1
- package/src/superlocalmemory/retrieval/hopfield_channel.py +118 -30
- package/src/superlocalmemory/retrieval/profile_channel.py +1 -1
- package/src/superlocalmemory/retrieval/quantization_aware_search.py +16 -10
- package/src/superlocalmemory/retrieval/reranker.py +56 -20
- package/src/superlocalmemory/retrieval/scope_policy.py +85 -0
- package/src/superlocalmemory/retrieval/semantic_channel.py +122 -14
- package/src/superlocalmemory/retrieval/spreading_activation.py +141 -25
- package/src/superlocalmemory/retrieval/strategy.py +1 -1
- package/src/superlocalmemory/retrieval/temporal_channel.py +30 -15
- package/src/superlocalmemory/retrieval/vector_store.py +1 -1
- package/src/superlocalmemory/server/api.py +10 -7
- package/src/superlocalmemory/server/bandit_loops.py +4 -2
- package/src/superlocalmemory/server/recall_serializer.py +24 -0
- package/src/superlocalmemory/server/route_mutations.py +84 -0
- package/src/superlocalmemory/server/routes/agents.py +8 -6
- package/src/superlocalmemory/server/routes/brain.py +14 -12
- package/src/superlocalmemory/server/routes/chat.py +29 -12
- package/src/superlocalmemory/server/routes/data_io.py +55 -24
- package/src/superlocalmemory/server/routes/helpers.py +8 -63
- package/src/superlocalmemory/server/routes/ingest.py +53 -36
- package/src/superlocalmemory/server/routes/memories.py +104 -43
- package/src/superlocalmemory/server/routes/mesh.py +31 -0
- package/src/superlocalmemory/server/routes/profiles.py +26 -4
- package/src/superlocalmemory/server/routes/tiers.py +43 -11
- package/src/superlocalmemory/server/routes/timeline.py +5 -1
- package/src/superlocalmemory/server/routes/v3_api.py +76 -21
- package/src/superlocalmemory/server/security_middleware.py +1 -1
- package/src/superlocalmemory/server/unified_daemon.py +680 -293
- package/src/superlocalmemory/server/write_identity.py +147 -0
- package/src/superlocalmemory/storage/access_log.py +4 -3
- package/src/superlocalmemory/storage/database.py +118 -25
- package/src/superlocalmemory/storage/migration_runner.py +84 -1
- package/src/superlocalmemory/storage/migration_v33.py +1 -1
- package/src/superlocalmemory/storage/migrations/M002_model_state_history.py +6 -60
- package/src/superlocalmemory/storage/migrations/M018_ingestion_operations.py +120 -0
- package/src/superlocalmemory/storage/migrations/M019_derivation_lineage.py +54 -0
- package/src/superlocalmemory/storage/migrations/M020_model_state_integrity.py +52 -0
- package/src/superlocalmemory/storage/migrations/__init__.py +5 -0
- package/src/superlocalmemory/storage/models.py +16 -0
- package/src/superlocalmemory/storage/quantized_store.py +20 -3
- package/src/superlocalmemory/storage/v2_migrator.py +5 -3
- package/src/superlocalmemory/ui/favicon.svg +5 -0
- package/src/superlocalmemory/ui/index.html +1 -0
- package/src/superlocalmemory/ui/js/compliance.js +1 -1
- package/src/superlocalmemory/ui/js/core.js +49 -8
- package/src/superlocalmemory/ui/js/dashboard.js +23 -2
- package/src/superlocalmemory/ui/js/feedback.js +1 -1
- package/src/superlocalmemory/ui/js/graph-filters.js +1 -1
- package/src/superlocalmemory/ui/js/graph-ui.js +1 -1
- package/src/superlocalmemory/ui/js/lifecycle.js +1 -1
- package/src/superlocalmemory/ui/js/ng-mesh.js +15 -49
- package/src/superlocalmemory/ui/js/settings.js +4 -2
- package/src/superlocalmemory/vector/lancedb_backend.py +57 -9
- package/bin/slm +0 -59
- package/bin/slm.bat +0 -77
- package/bin/slm.cmd +0 -5
- package/ide/integrations/langchain/README.md +0 -106
- package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +0 -9
- package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +0 -201
- package/ide/integrations/langchain/pyproject.toml +0 -38
- package/ide/integrations/langchain/tests/__init__.py +0 -3
- package/ide/integrations/langchain/tests/test_chat_message_history.py +0 -215
- package/ide/integrations/langchain/tests/test_security.py +0 -117
- package/ide/integrations/llamaindex/README.md +0 -81
- package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +0 -9
- package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +0 -316
- package/ide/integrations/llamaindex/pyproject.toml +0 -43
- package/ide/integrations/llamaindex/tests/__init__.py +0 -3
- package/ide/integrations/llamaindex/tests/test_chat_store.py +0 -294
- package/ide/integrations/llamaindex/tests/test_security.py +0 -241
- package/scripts/__tests__/build-plugin.test.mjs +0 -613
- package/scripts/_savings_math.py +0 -270
- package/scripts/build-dmg.sh +0 -417
- package/scripts/build-plugin.js +0 -742
- package/scripts/build-slm-hook.ps1 +0 -40
- package/scripts/build-slm-hook.sh +0 -45
- package/scripts/build_entry.py +0 -452
- package/scripts/ci/stage5b_gate.sh +0 -50
- package/scripts/dogfood_savings.py +0 -490
- package/scripts/generate-thumbnails.py +0 -218
- package/scripts/install-skills.ps1 +0 -4
- package/scripts/install-skills.sh +0 -5
- package/scripts/install.ps1 +0 -701
- package/scripts/install.sh +0 -1015
- package/scripts/postinstall_binary.js +0 -287
- package/scripts/prepack.js +0 -33
- package/scripts/release_manifest.py +0 -273
- package/scripts/slm-hook.spec +0 -56
- package/scripts/start-dashboard.ps1 +0 -52
- package/scripts/start-dashboard.sh +0 -41
- package/scripts/sync-wiki.ps1 +0 -127
- package/scripts/sync-wiki.sh +0 -82
- package/scripts/test-dmg.sh +0 -161
- package/scripts/test-npm-package.ps1 +0 -252
- package/scripts/test-npm-package.sh +0 -207
- package/scripts/verify-install.ps1 +0 -294
- package/scripts/verify-install.sh +0 -266
- package/scripts/verify-v27.ps1 +0 -301
- package/scripts/verify-v27.sh +0 -233
- package/src/superlocalmemory.egg-info/PKG-INFO +0 -516
- package/src/superlocalmemory.egg-info/SOURCES.txt +0 -529
- package/src/superlocalmemory.egg-info/dependency_links.txt +0 -1
- package/src/superlocalmemory.egg-info/entry_points.txt +0 -2
- package/src/superlocalmemory.egg-info/requires.txt +0 -71
- package/src/superlocalmemory.egg-info/top_level.txt +0 -1
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
-
# Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
|
|
4
|
-
"""SuperLocalMemory V2 - LlamaIndex Chat Store Security Tests
|
|
5
|
-
|
|
6
|
-
Security-focused tests: SQL injection, XSS, oversized payloads, null bytes,
|
|
7
|
-
and other adversarial inputs that a production chat store must handle safely.
|
|
8
|
-
"""
|
|
9
|
-
import os
|
|
10
|
-
import sys
|
|
11
|
-
import tempfile
|
|
12
|
-
|
|
13
|
-
import pytest
|
|
14
|
-
|
|
15
|
-
# Ensure the SLM source tree is importable for tests
|
|
16
|
-
_SLM_SRC = os.path.join(
|
|
17
|
-
os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, "src"
|
|
18
|
-
)
|
|
19
|
-
_SLM_SRC = os.path.abspath(_SLM_SRC)
|
|
20
|
-
if _SLM_SRC not in sys.path:
|
|
21
|
-
sys.path.insert(0, _SLM_SRC)
|
|
22
|
-
|
|
23
|
-
from llama_index.core.base.llms.types import ChatMessage, MessageRole
|
|
24
|
-
from llama_index.storage.chat_store.superlocalmemory import SuperLocalMemoryChatStore
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# ---------------------------------------------------------------------------
|
|
28
|
-
# Fixtures
|
|
29
|
-
# ---------------------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
@pytest.fixture
|
|
33
|
-
def tmp_db():
|
|
34
|
-
with tempfile.TemporaryDirectory() as d:
|
|
35
|
-
yield os.path.join(d, "test_security.db")
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@pytest.fixture
|
|
39
|
-
def store(tmp_db):
|
|
40
|
-
return SuperLocalMemoryChatStore(db_path=tmp_db)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# ---------------------------------------------------------------------------
|
|
44
|
-
# SQL Injection Tests
|
|
45
|
-
# ---------------------------------------------------------------------------
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class TestSQLInjection:
|
|
49
|
-
"""Verify that SQL injection attempts in keys and content are harmless."""
|
|
50
|
-
|
|
51
|
-
SQL_PAYLOADS = [
|
|
52
|
-
"'; DROP TABLE memories; --",
|
|
53
|
-
"' OR '1'='1",
|
|
54
|
-
"'; DELETE FROM memories WHERE '1'='1",
|
|
55
|
-
"1; SELECT * FROM memories --",
|
|
56
|
-
"Robert'); DROP TABLE memories;--",
|
|
57
|
-
"' UNION SELECT * FROM memories --",
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
def test_sql_injection_in_key(self, store):
|
|
61
|
-
"""SQL injection attempts in the session key must not corrupt the store."""
|
|
62
|
-
for payload in self.SQL_PAYLOADS:
|
|
63
|
-
msg = ChatMessage(role=MessageRole.USER, content="safe content")
|
|
64
|
-
store.add_message(payload, msg)
|
|
65
|
-
|
|
66
|
-
# The store should still function — retrieve the message back
|
|
67
|
-
msgs = store.get_messages(payload)
|
|
68
|
-
assert len(msgs) == 1
|
|
69
|
-
assert msgs[0].content == "safe content"
|
|
70
|
-
|
|
71
|
-
# Clean up
|
|
72
|
-
store.delete_messages(payload)
|
|
73
|
-
|
|
74
|
-
def test_sql_injection_in_content(self, store):
|
|
75
|
-
"""SQL injection attempts in message content must be stored literally."""
|
|
76
|
-
for payload in self.SQL_PAYLOADS:
|
|
77
|
-
msg = ChatMessage(role=MessageRole.USER, content=payload)
|
|
78
|
-
store.add_message("safe-key", msg)
|
|
79
|
-
|
|
80
|
-
msgs = store.get_messages("safe-key")
|
|
81
|
-
assert len(msgs) == len(self.SQL_PAYLOADS)
|
|
82
|
-
|
|
83
|
-
# Verify content is stored literally, not executed
|
|
84
|
-
for i, payload in enumerate(self.SQL_PAYLOADS):
|
|
85
|
-
assert msgs[i].content == payload
|
|
86
|
-
|
|
87
|
-
# Clean up
|
|
88
|
-
store.delete_messages("safe-key")
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
# ---------------------------------------------------------------------------
|
|
92
|
-
# XSS Tests
|
|
93
|
-
# ---------------------------------------------------------------------------
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
class TestXSS:
|
|
97
|
-
"""Verify that XSS payloads in content are stored literally (not executed)."""
|
|
98
|
-
|
|
99
|
-
XSS_PAYLOADS = [
|
|
100
|
-
"<script>alert('xss')</script>",
|
|
101
|
-
'<img src=x onerror=alert(1)>',
|
|
102
|
-
"javascript:alert(document.cookie)",
|
|
103
|
-
"<svg/onload=alert('xss')>",
|
|
104
|
-
'"><script>alert(String.fromCharCode(88,83,83))</script>',
|
|
105
|
-
]
|
|
106
|
-
|
|
107
|
-
def test_xss_in_content(self, store):
|
|
108
|
-
"""XSS payloads must be stored and retrieved as literal strings."""
|
|
109
|
-
for payload in self.XSS_PAYLOADS:
|
|
110
|
-
msg = ChatMessage(role=MessageRole.USER, content=payload)
|
|
111
|
-
store.add_message("xss-key", msg)
|
|
112
|
-
|
|
113
|
-
msgs = store.get_messages("xss-key")
|
|
114
|
-
assert len(msgs) == len(self.XSS_PAYLOADS)
|
|
115
|
-
|
|
116
|
-
for i, payload in enumerate(self.XSS_PAYLOADS):
|
|
117
|
-
assert msgs[i].content == payload
|
|
118
|
-
|
|
119
|
-
store.delete_messages("xss-key")
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
# ---------------------------------------------------------------------------
|
|
123
|
-
# Oversized Content Tests
|
|
124
|
-
# ---------------------------------------------------------------------------
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
class TestOversizedContent:
|
|
128
|
-
"""Verify that oversized payloads are handled without crashing."""
|
|
129
|
-
|
|
130
|
-
def test_oversized_content_1mb(self, store):
|
|
131
|
-
"""A 1MB message should be rejected by MemoryStoreV2's validation."""
|
|
132
|
-
huge = "A" * 1_000_001 # Just over the 1MB limit
|
|
133
|
-
msg = ChatMessage(role=MessageRole.USER, content=huge)
|
|
134
|
-
|
|
135
|
-
# MemoryStoreV2 raises ValueError for content > 1MB
|
|
136
|
-
with pytest.raises((ValueError, Exception)):
|
|
137
|
-
store.add_message("huge-key", msg)
|
|
138
|
-
|
|
139
|
-
def test_large_but_valid_content(self, store):
|
|
140
|
-
"""A 500KB message should be stored successfully (under 1MB limit)."""
|
|
141
|
-
large = "B" * 500_000
|
|
142
|
-
msg = ChatMessage(role=MessageRole.USER, content=large)
|
|
143
|
-
store.add_message("large-key", msg)
|
|
144
|
-
|
|
145
|
-
msgs = store.get_messages("large-key")
|
|
146
|
-
assert len(msgs) == 1
|
|
147
|
-
assert len(msgs[0].content) == 500_000
|
|
148
|
-
|
|
149
|
-
store.delete_messages("large-key")
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
# ---------------------------------------------------------------------------
|
|
153
|
-
# Edge Case Key Tests
|
|
154
|
-
# ---------------------------------------------------------------------------
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
class TestEdgeCaseKeys:
|
|
158
|
-
"""Verify safe handling of unusual session key values."""
|
|
159
|
-
|
|
160
|
-
def test_empty_key(self, store):
|
|
161
|
-
"""An empty string key should still work (tags allow empty strings)."""
|
|
162
|
-
msg = ChatMessage(role=MessageRole.USER, content="empty key test")
|
|
163
|
-
store.add_message("", msg)
|
|
164
|
-
|
|
165
|
-
msgs = store.get_messages("")
|
|
166
|
-
assert len(msgs) == 1
|
|
167
|
-
assert msgs[0].content == "empty key test"
|
|
168
|
-
|
|
169
|
-
store.delete_messages("")
|
|
170
|
-
|
|
171
|
-
def test_null_bytes_in_key(self, store):
|
|
172
|
-
"""Null bytes in a key should be handled without crash."""
|
|
173
|
-
key_with_null = "key\x00with\x00nulls"
|
|
174
|
-
msg = ChatMessage(role=MessageRole.USER, content="null byte key")
|
|
175
|
-
store.add_message(key_with_null, msg)
|
|
176
|
-
|
|
177
|
-
msgs = store.get_messages(key_with_null)
|
|
178
|
-
assert len(msgs) == 1
|
|
179
|
-
assert msgs[0].content == "null byte key"
|
|
180
|
-
|
|
181
|
-
store.delete_messages(key_with_null)
|
|
182
|
-
|
|
183
|
-
def test_very_long_key(self, store):
|
|
184
|
-
"""A very long key (1000 chars) should work."""
|
|
185
|
-
long_key = "k" * 1000
|
|
186
|
-
msg = ChatMessage(role=MessageRole.USER, content="long key test")
|
|
187
|
-
store.add_message(long_key, msg)
|
|
188
|
-
|
|
189
|
-
msgs = store.get_messages(long_key)
|
|
190
|
-
assert len(msgs) == 1
|
|
191
|
-
|
|
192
|
-
store.delete_messages(long_key)
|
|
193
|
-
|
|
194
|
-
def test_special_characters_in_key(self, store):
|
|
195
|
-
"""Keys with special characters should work."""
|
|
196
|
-
special_keys = [
|
|
197
|
-
"user@example.com",
|
|
198
|
-
"path/to/session",
|
|
199
|
-
"key with spaces",
|
|
200
|
-
"key-with-dashes_and_underscores",
|
|
201
|
-
"unicode-key",
|
|
202
|
-
]
|
|
203
|
-
for key in special_keys:
|
|
204
|
-
store.add_message(
|
|
205
|
-
key, ChatMessage(role=MessageRole.USER, content=f"test {key}")
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
for key in special_keys:
|
|
209
|
-
msgs = store.get_messages(key)
|
|
210
|
-
assert len(msgs) == 1, f"Failed for key: {key}"
|
|
211
|
-
|
|
212
|
-
# Verify all keys are discoverable
|
|
213
|
-
all_keys = store.get_keys()
|
|
214
|
-
for key in special_keys:
|
|
215
|
-
assert key in all_keys, f"Key '{key}' not found in get_keys()"
|
|
216
|
-
|
|
217
|
-
for key in special_keys:
|
|
218
|
-
store.delete_messages(key)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
# ---------------------------------------------------------------------------
|
|
222
|
-
# Null / None Content Tests
|
|
223
|
-
# ---------------------------------------------------------------------------
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
class TestNullContent:
|
|
227
|
-
"""Verify that null bytes and None-like content are handled safely."""
|
|
228
|
-
|
|
229
|
-
def test_null_bytes_in_content(self, store):
|
|
230
|
-
"""Null bytes in content should be stored and retrieved literally."""
|
|
231
|
-
content_with_null = "before\x00after"
|
|
232
|
-
msg = ChatMessage(role=MessageRole.USER, content=content_with_null)
|
|
233
|
-
store.add_message("null-content-key", msg)
|
|
234
|
-
|
|
235
|
-
msgs = store.get_messages("null-content-key")
|
|
236
|
-
assert len(msgs) == 1
|
|
237
|
-
# The content may or may not preserve null bytes depending on SQLite/JSON
|
|
238
|
-
# The key requirement is that it doesn't crash
|
|
239
|
-
assert msgs[0].content is not None
|
|
240
|
-
|
|
241
|
-
store.delete_messages("null-content-key")
|