@@ -716,48 +716,47 @@ async def test_synchronize_projects_handles_case_sensitivity_bug(
716716 await project_service .repository .delete (db_project .id )
717717
718718
719- @pytest .mark .skipif (os .name == "nt" , reason = "Cloud mode only runs on POSIX systems" )
719+ @pytest .mark .skipif (os .name == "nt" , reason = "Project root constraints only tested on POSIX systems" )
720720@pytest .mark .asyncio
721- async def test_add_project_cloud_mode_sanitizes_paths (
721+ async def test_add_project_with_project_root_sanitizes_paths (
722722 project_service : ProjectService , config_manager : ConfigManager , tmp_path , monkeypatch
723723):
724- """Test that cloud mode sanitizes and validates project paths."""
725- # Set up cloud mode environment
726- cloud_home = tmp_path / "app" / "data" / "basic-memory "
727- cloud_home .mkdir (parents = True , exist_ok = True )
724+ """Test that BASIC_MEMORY_PROJECT_ROOT sanitizes and validates project paths."""
725+ # Set up project root environment
726+ project_root_path = tmp_path / "app" / "data"
727+ project_root_path .mkdir (parents = True , exist_ok = True )
728728
729- monkeypatch .setenv ("BASIC_MEMORY_HOME" , str (cloud_home ))
730- monkeypatch .setenv ("BASIC_MEMORY_CLOUD_MODE" , "true" )
729+ monkeypatch .setenv ("BASIC_MEMORY_PROJECT_ROOT" , str (project_root_path ))
731730
732- # Force reload config to pick up cloud mode
733- from basic_memory . services import project_service as ps_module
731+ # Invalidate config cache so it picks up the new env var
732+ from basic_memory import config as config_module
734733
735- monkeypatch . setattr ( ps_module , "config" , config_manager . load_config ())
734+ config_module . _CONFIG_CACHE = None
736735
737736 test_cases = [
738737 # (input_path, expected_result_path, should_succeed)
739- ("test" , str (cloud_home / "test" ), True ), # Simple relative path
740- ("~/Documents/test" , str (cloud_home / "Documents" / "test" ), True ), # Home directory
738+ ("test" , str (project_root_path / "test" ), True ), # Simple relative path
739+ ("~/Documents/test" , str (project_root_path / "Documents" / "test" ), True ), # Home directory
741740 (
742741 "/tmp/test" ,
743- str (cloud_home / "tmp" / "test" ),
742+ str (project_root_path / "tmp" / "test" ),
744743 True ,
745744 ), # Absolute path (sanitized to relative)
746745 (
747746 "../../../etc/passwd" ,
748- str (cloud_home ),
747+ str (project_root_path ),
749748 True ,
750- ), # Path traversal (all ../ removed, results in cloud_home )
751- ("folder/subfolder" , str (cloud_home / "folder" / "subfolder" ), True ), # Nested path
749+ ), # Path traversal (all ../ removed, results in project_root )
750+ ("folder/subfolder" , str (project_root_path / "folder" / "subfolder" ), True ), # Nested path
752751 (
753752 "~/folder/../test" ,
754- str (cloud_home / "test" ),
753+ str (project_root_path / "test" ),
755754 True ,
756755 ), # Mixed patterns (sanitized to just 'test')
757756 ]
758757
759758 for i , (input_path , expected_path , should_succeed ) in enumerate (test_cases ):
760- test_project_name = f"cloud -test-{ i } "
759+ test_project_name = f"project-root -test-{ i } "
761760
762761 try :
763762 # Add the project
@@ -768,9 +767,9 @@ async def test_add_project_cloud_mode_sanitizes_paths(
768767 assert test_project_name in project_service .projects
769768 actual_path = project_service .projects [test_project_name ]
770769
771- # The path should be under cloud_home
772- assert actual_path .startswith (str (cloud_home )), (
773- f"Path { actual_path } should start with { cloud_home } for input { input_path } "
770+ # The path should be under project_root
771+ assert actual_path .startswith (str (project_root_path )), (
772+ f"Path { actual_path } should start with { project_root_path } for input { input_path } "
774773 )
775774
776775 # Clean up
@@ -784,29 +783,28 @@ async def test_add_project_cloud_mode_sanitizes_paths(
784783 # Expected failure - continue to next test case
785784
786785
787- @pytest .mark .skipif (os .name == "nt" , reason = "Cloud mode only runs on POSIX systems" )
786+ @pytest .mark .skipif (os .name == "nt" , reason = "Project root constraints only tested on POSIX systems" )
788787@pytest .mark .asyncio
789- async def test_add_project_cloud_mode_rejects_escape_attempts (
788+ async def test_add_project_with_project_root_rejects_escape_attempts (
790789 project_service : ProjectService , config_manager : ConfigManager , tmp_path , monkeypatch
791790):
792- """Test that cloud mode rejects paths that try to escape cloud storage ."""
793- # Set up cloud mode environment
794- cloud_home = tmp_path / "app" / "data" / "basic-memory "
795- cloud_home .mkdir (parents = True , exist_ok = True )
791+ """Test that BASIC_MEMORY_PROJECT_ROOT rejects paths that try to escape the project root ."""
792+ # Set up project root environment
793+ project_root_path = tmp_path / "app" / "data"
794+ project_root_path .mkdir (parents = True , exist_ok = True )
796795
797- # Create a directory outside cloud_home to verify it's not accessible
796+ # Create a directory outside project_root to verify it's not accessible
798797 outside_dir = tmp_path / "outside"
799798 outside_dir .mkdir (parents = True , exist_ok = True )
800799
801- monkeypatch .setenv ("BASIC_MEMORY_HOME" , str (cloud_home ))
802- monkeypatch .setenv ("BASIC_MEMORY_CLOUD_MODE" , "true" )
800+ monkeypatch .setenv ("BASIC_MEMORY_PROJECT_ROOT" , str (project_root_path ))
803801
804- # Force reload config to pick up cloud mode
805- from basic_memory . services import project_service as ps_module
802+ # Invalidate config cache so it picks up the new env var
803+ from basic_memory import config as config_module
806804
807- monkeypatch . setattr ( ps_module , "config" , config_manager . load_config ())
805+ config_module . _CONFIG_CACHE = None
808806
809- # All of these should succeed by being sanitized to paths under cloud_home
807+ # All of these should succeed by being sanitized to paths under project_root
810808 # The sanitization removes dangerous patterns, so they don't escape
811809 safe_after_sanitization = [
812810 "../../../etc/passwd" ,
@@ -815,16 +813,16 @@ async def test_add_project_cloud_mode_rejects_escape_attempts(
815813 ]
816814
817815 for i , attack_path in enumerate (safe_after_sanitization ):
818- test_project_name = f"cloud -attack-test-{ i } "
816+ test_project_name = f"project-root -attack-test-{ i } "
819817
820818 try :
821819 # Add the project
822820 await project_service .add_project (test_project_name , attack_path )
823821
824- # Verify it was sanitized to be under cloud_home
822+ # Verify it was sanitized to be under project_root
825823 actual_path = project_service .projects [test_project_name ]
826- assert actual_path .startswith (str (cloud_home )), (
827- f"Sanitized path { actual_path } should be under { cloud_home } "
824+ assert actual_path .startswith (str (project_root_path )), (
825+ f"Sanitized path { actual_path } should be under { project_root_path } "
828826 )
829827
830828 # Clean up
@@ -835,16 +833,17 @@ async def test_add_project_cloud_mode_rejects_escape_attempts(
835833 pass
836834
837835
838- @pytest .mark .skipif (os .name == "nt" , reason = "Cloud mode only runs on POSIX systems" )
836+ @pytest .mark .skipif (os .name == "nt" , reason = "Project root constraints only tested on POSIX systems" )
839837@pytest .mark .asyncio
840- async def test_add_project_local_mode_allows_arbitrary_paths (
838+ async def test_add_project_without_project_root_allows_arbitrary_paths (
841839 project_service : ProjectService , config_manager : ConfigManager , tmp_path , monkeypatch
842840):
843- """Test that local mode (non-cloud) still allows arbitrary paths."""
844- # Ensure cloud mode is disabled
845- monkeypatch .setenv ("BASIC_MEMORY_CLOUD_MODE" , "false" )
841+ """Test that without BASIC_MEMORY_PROJECT_ROOT set, arbitrary paths are allowed."""
842+ # Ensure project_root is not set
843+ if "BASIC_MEMORY_PROJECT_ROOT" in os .environ :
844+ monkeypatch .delenv ("BASIC_MEMORY_PROJECT_ROOT" )
846845
847- # Force reload config to pick up local mode
846+ # Force reload config without project_root
848847 from basic_memory .services import project_service as ps_module
849848
850849 monkeypatch .setattr (ps_module , "config" , config_manager .load_config ())
@@ -853,10 +852,10 @@ async def test_add_project_local_mode_allows_arbitrary_paths(
853852 test_dir = tmp_path / "arbitrary-location"
854853 test_dir .mkdir (parents = True , exist_ok = True )
855854
856- test_project_name = "local-mode -test"
855+ test_project_name = "no-project-root -test"
857856
858857 try :
859- # In local mode , we should be able to use arbitrary absolute paths
858+ # Without project_root , we should be able to use arbitrary absolute paths
860859 await project_service .add_project (test_project_name , str (test_dir ))
861860
862861 # Verify the path was accepted as-is
0 commit comments