From e3b052f4ba97b04771c210c1c6b0c8aa96157765 Mon Sep 17 00:00:00 2001 From: Ryan Pratt Date: Fri, 17 Jan 2025 14:06:15 -0700 Subject: [PATCH 1/4] Check file path prefixes --- openc3/lib/openc3/utilities/local_mode.rb | 15 +++++++++++++-- openc3/python/openc3/utilities/local_mode.py | 6 +++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/openc3/lib/openc3/utilities/local_mode.rb b/openc3/lib/openc3/utilities/local_mode.rb index b0eed0118e..7e463efffe 100644 --- a/openc3/lib/openc3/utilities/local_mode.rb +++ b/openc3/lib/openc3/utilities/local_mode.rb @@ -380,6 +380,7 @@ def self.zip_target(target_name, zip, scope:) def self.put_target_file(path, io_or_string, scope:) full_folder_path = "#{OPENC3_LOCAL_MODE_PATH}/#{path}" + return unless File.expand_path(full_folder_path).start_with?(OPENC3_LOCAL_MODE_PATH) FileUtils.mkdir_p(File.dirname(full_folder_path)) File.open(full_folder_path, 'wb') do |file| if String === io_or_string @@ -393,7 +394,10 @@ def self.put_target_file(path, io_or_string, scope:) def self.open_local_file(path, scope:) full_path = "#{OPENC3_LOCAL_MODE_PATH}/#{scope}/targets_modified/#{path}" - return File.open(full_path, 'rb') + if File.expand_path(full_path).start_with?(OPENC3_LOCAL_MODE_PATH) + return File.open(full_path, 'rb') + end + nil rescue Errno::ENOENT nil end @@ -446,6 +450,7 @@ def self.sync_tool_config() def self.save_tool_config(scope, tool, name, data) json = JSON.parse(data, :allow_nan => true, :create_additions => true) config_path = "#{OPENC3_LOCAL_MODE_PATH}/#{scope}/tool_config/#{tool}/#{name}.json" + return unless File.expand_path(config_path).start_with?(OPENC3_LOCAL_MODE_PATH) FileUtils.mkdir_p(File.dirname(config_path)) File.open(config_path, 'w') do |file| file.write(JSON.pretty_generate(json, :allow_nan => true)) @@ -453,7 +458,9 @@ def self.save_tool_config(scope, tool, name, data) end def self.delete_tool_config(scope, tool, name) - FileUtils.rm_f("#{OPENC3_LOCAL_MODE_PATH}/#{scope}/tool_config/#{tool}/#{name}.json") + config_path = "#{OPENC3_LOCAL_MODE_PATH}/#{scope}/tool_config/#{tool}/#{name}.json" + return unless File.expand_path(config_path).start_with?(OPENC3_LOCAL_MODE_PATH) + FileUtils.rm_f(config_path) end def self.sync_settings() @@ -471,6 +478,7 @@ def self.sync_settings() def self.save_setting(scope, name, data) config_path = "#{OPENC3_LOCAL_MODE_PATH}/#{scope}/settings/#{name}.json" + return unless File.expand_path(config_path).start_with?(OPENC3_LOCAL_MODE_PATH) FileUtils.mkdir_p(File.dirname(config_path)) # Anything can be stored as a setting so write it out directly File.write(config_path, data) @@ -480,12 +488,14 @@ def self.save_setting(scope, name, data) def self.sync_remote_to_local(bucket, key) local_path = "#{OPENC3_LOCAL_MODE_PATH}/#{key}" + return unless File.expand_path(local_path).start_with?(OPENC3_LOCAL_MODE_PATH) FileUtils.mkdir_p(File.dirname(local_path)) bucket.get_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: key, path: local_path) end def self.sync_local_to_remote(bucket, key) local_path = "#{OPENC3_LOCAL_MODE_PATH}/#{key}" + return unless File.expand_path(local_path).start_with?(OPENC3_LOCAL_MODE_PATH) File.open(local_path, 'rb') do |read_file| bucket.put_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: key, body: read_file) end @@ -493,6 +503,7 @@ def self.sync_local_to_remote(bucket, key) def self.delete_local(key) local_path = "#{OPENC3_LOCAL_MODE_PATH}/#{key}" + return unless File.expand_path(local_path).start_with?(OPENC3_LOCAL_MODE_PATH) File.delete(local_path) if File.exist?(local_path) nil end diff --git a/openc3/python/openc3/utilities/local_mode.py b/openc3/python/openc3/utilities/local_mode.py index 569d0e8fb0..9edc028b1e 100644 --- a/openc3/python/openc3/utilities/local_mode.py +++ b/openc3/python/openc3/utilities/local_mode.py @@ -332,6 +332,8 @@ class LocalMode: @classmethod def put_target_file(cls, path, io_or_string, scope): full_folder_path = f"{cls.LOCAL_MODE_PATH}/{path}" + if not os.path.normpath(full_folder_path).startswith(cls.LOCAL_MODE_PATH): + return os.makedirs(os.path.dirname(full_folder_path), exist_ok=True) flags = "w" if isinstance(io_or_string, bytes): @@ -347,7 +349,9 @@ def put_target_file(cls, path, io_or_string, scope): def open_local_file(cls, path, scope): try: full_path = f"{cls.LOCAL_MODE_PATH}/{scope}/targets_modified/{path}" - return open(full_path, "rb") + if os.path.normpath(full_path).startswith(cls.LOCAL_MODE_PATH): + return open(full_path, "rb") + return None except OSError: return None From ab58a84642780877664f2d18ceb8bf5aa44a5f7a Mon Sep 17 00:00:00 2001 From: Ryan Pratt Date: Tue, 21 Jan 2025 16:56:30 -0700 Subject: [PATCH 2/4] Remove commented out code from local_mode.py --- openc3/python/openc3/utilities/local_mode.py | 422 ------------------- 1 file changed, 422 deletions(-) diff --git a/openc3/python/openc3/utilities/local_mode.py b/openc3/python/openc3/utilities/local_mode.py index 9edc028b1e..c9414306d9 100644 --- a/openc3/python/openc3/utilities/local_mode.py +++ b/openc3/python/openc3/utilities/local_mode.py @@ -47,288 +47,6 @@ class LocalMode: "openc3-tool-base", ] - # # Install plugins from local plugins folder - # # Can only be used from openc3cli because calls top_level load_plugin - # def self.local_init: - # if ENV['OPENC3_LOCAL_MODE'] and Dir.exist?(OPENC3_LOCAL_MODE_PATH): - # puts "Local init running: {OPENC3_LOCAL_MODE_PATH} exists" - # for scope_dir in Dir.each_child(OPENC3_LOCAL_MODE_PATH): - # next if not File.directory?("{OPENC3_LOCAL_MODE_PATH}/{scope_dir}") - # puts "Local init found scope: {scope_dir}" - # Dir.each_child("{OPENC3_LOCAL_MODE_PATH}/{scope_dir}") do |plugin_dir| - # full_folder_path = "{OPENC3_LOCAL_MODE_PATH}/{scope_dir}/{plugin_dir}" - # if plugin_dir == "targets_modified" or plugin_dir == "tool_config" ||: - # plugin_dir == "settings" or !File.directory?(full_folder_path) - # next - # puts "Local init found plugin_dir: {full_folder_path}" - # gems, plugin_instance = scan_plugin_dir(full_folder_path) - - # if len(gems) > 1: - # puts "Local plugin folder contains more than one gem - skipping: {full_folder_path}" - # next - - # if len(gems) == 1 and plugin_instance: - # # If one gem file and plugin_instance.json - Install instance - # load_plugin(gems[0], scope: scope_dir.upper(), plugin_hash_file: plugin_instance) - # elif: len(gems) == 1: - # # Else If just gem - Install with default settings - # load_plugin(gems[0], scope: scope_dir.upper()) - # else: - # puts "Local plugin folder contains no gem file - skipping: {full_folder_path}" - # sync_targets_modified() - # sync_tool_config() - # sync_settings() - # puts "Local init complete" - # else: - # puts "Local init canceled: Local mode not enabled or {OPENC3_LOCAL_MODE_PATH} does not exist" - - # @classmethod - # def scan_plugin_dir(cls, path): - # gems = [] - # plugin_instance = None - - # Dir.each_child(path) do |filename| - # full_path = "{path}/{filename}" - # if not File.directory?(full_path): - # if File.extname(filename) == '.gem': - # gems.append(full_path) - # elif: filename == 'plugin_instance.json': - # plugin_instance = full_path - - # return gems, plugin_instance - - # def self.scan_local_mode: - # if ENV['OPENC3_LOCAL_MODE'] and Dir.exist?(OPENC3_LOCAL_MODE_PATH): - # local_plugins = {} - - # Dir.each_child(OPENC3_LOCAL_MODE_PATH) do |scope_dir| - # full_scope_dir = "{OPENC3_LOCAL_MODE_PATH}/{scope_dir}" - # next if not File.directory?(full_scope_dir) - # local_plugins[scope_dir] ||= {} - # Dir.each_child("{OPENC3_LOCAL_MODE_PATH}/{scope_dir}") do |plugin_dir| - # full_folder_path = "{OPENC3_LOCAL_MODE_PATH}/{scope_dir}/{plugin_dir}" - # if plugin_dir == "targets_modified" or not File.directory?(full_folder_path): - # next - # gems, plugin_instance = scan_plugin_dir(full_folder_path) - # local_plugins[scope_dir][full_folder_path] = {gems: gems, plugin_instance: plugin_instance} - - # return local_plugins - # return {} - - # @classmethod - # def analyze_local_mode(cls, plugin_name:, scope:): - # if ENV['OPENC3_LOCAL_MODE'] and Dir.exist?(OPENC3_LOCAL_MODE_PATH): - # # We already know a plugin with this name doesn't exist in the models - # # Now need to determine if there is a highly likely candidate that has been - # # updated, so that we don't do an erroneous extra plugin install - - # gem_name = plugin_name.split("__")[0].split('-')[0..-2].join('-') - - # local_plugins = scan_local_mode() - # scope_plugins = local_plugins[scope] - # if scope_plugins: - # # Scan models for same gem - # found_models = {} - # models = OpenC3:'P'luginModel.all(scope: scope) - # for name, details in models: - # model_gem_name = name.split("__")[0].split('-')[0..-2].join('-') - # if gem_name == model_gem_name: - # found_models[name] = details - - # # Definitely new install if no found models - # if len(found_models) == 0: - # return None - - # # Scan local for same gem and try to match pairs - # found_local_plugins = {} - # for folder_path, details in scope_plugins: - # gems = details[:gems] - # plugin_instance = details[:plugin_instance] - # next if not plugin_instance - # if len(gems) != 1: - # next - - # local_gem_name = File.basename(gems[0]).split('-')[0..-2].join('-') - # if gem_name == local_gem_name: - # # Gems match - Do the names match? - # data = File.read(plugin_instance) - # json = json.loads(data) - - # found = False - # for name, model_details in found_models: - # if json["name"] == name: - # # Matched pair - # found = True - # break - - # if found # names match: - # # Remove from the list because we have a matched set - # # (local plugin_instance name and plugin model name) - # found_models.delete(json["name"]) - # else: - # # Found a local plugin with the right gem, but a different name - # found_local_plugins[folder_path] = details - - # # At this point we only have unmatched plugins in found_models - - # # Not a local mode install if no found local plugins - # if len(found_local_plugins) == 0: - # return None - - # # If we have any unmatched models, assume this should match the first - # # (found_models are existing installed plugins with the same gem but - # # a different name) - # for name, model_details in found_models: - # puts "Choosing {name} for update from local plugins" - # return model_details - # return None - - # # If old_plugin_name then this is an online upgrade - # @classmethod - # def update_local_plugin(cls, plugin_file_path, plugin_hash, old_plugin_name: None, scope:): - # if ENV['OPENC3_LOCAL_MODE'] and Dir.exist?(OPENC3_LOCAL_MODE_PATH): - # variables = plugin_hash['variables'] - # if variables: - # for name in PluginModel:'RESERVED_VARIABLE_NAMES': - # variables.delete(name) - # if plugin_file_path =~ Regexp("^{OPENC3_LOCAL_MODE_PATH}/{scope}/"): - # # From local init - Always just update the exact one - # File.open(File.join(File.dirname(plugin_file_path), 'plugin_instance.json'), 'wb') do |file| - # file.write(JSON.pretty_generate(plugin_hash)) - # else: - # # From online install / update - # # Or init install of container plugin - # # Try to find an existing local folder for this plugin - # found = False - - # gem_name = File.basename(plugin_file_path).split('-')[0..-2].join('-') - # FileUtils.mkdir_p("{OPENC3_LOCAL_MODE_PATH}/{scope}") - - # Dir.each_child("{OPENC3_LOCAL_MODE_PATH}/{scope}") do |plugin_dir| - # full_folder_path = "{OPENC3_LOCAL_MODE_PATH}/{scope}/{plugin_dir}" - # if plugin_dir == "targets_modified" or not File.directory?(full_folder_path): - # next - - # gems, plugin_instance = scan_plugin_dir(full_folder_path) - # if len(gems) > 1: - # next - - # if len(gems) == 1: - # found_gem_name = File.basename(gems[0]).split('-')[0..-2].join('-') - # if found_gem_name == gem_name: - # # Same gem at least - Now see if same instance - # if plugin_instance: - # if old_plugin_name: - # # And we're updating a plugin - # data = File.read(plugin_instance) - # json = json.loads(data) - # if json["name"] == old_plugin_name: - # # Found plugin to update - # found = True - # update_local_plugin_files(full_folder_path, plugin_file_path, plugin_hash, gem_name) - # else: - # # New install of same plugin - Leave it alone - # else: - # # No exiting instance.json, but we found the same gem - # # This shouldn't happen without users using this wrong - # # We will update - # found = True - # update_local_plugin_files(full_folder_path, plugin_file_path, plugin_hash, gem_name) - - # if not found - # # Then we will make a local version - # # Create a folder for this plugin and add gem and plugin_instance.json - # folder_name = gem_name - # count = 1 - # while File.exist?("{OPENC3_LOCAL_MODE_PATH}/{scope}/{folder_name}") - # folder_name = gem_name + "-" + str(count) - # count += 1 - # full_folder_path = "{OPENC3_LOCAL_MODE_PATH}/{scope}/{folder_name}" - # update_local_plugin_files(full_folder_path, plugin_file_path, plugin_hash, gem_name) - - # @classmethod - # def update_local_plugin_files(cls, full_folder_path, plugin_file_path, plugin_hash, gem_name): - # if DEFAULT_PLUGINS.include?(gem_name): - # return - # puts "Updating local plugin files: {full_folder_path}" - # FileUtils.mkdir_p(full_folder_path) - # gems, _ = scan_plugin_dir(full_folder_path) - # for gem in gems: - # File.delete(gem) - # temp_dir = Dir.mktmpdir - # try: - # if not File.exist?(plugin_file_path) - # plugin_file_path = OpenC3:'G'emModel.get(plugin_file_path) - # File.open(File.join(full_folder_path, File.basename(plugin_file_path)), 'wb') do |file| - # data = File.read(plugin_file_path) - # file.write(data) - # File.open(File.join(full_folder_path, 'plugin_instance.json'), 'wb') do |file| - # file.write(JSON.pretty_generate(plugin_hash)) - # ensure - # if temp_dir and File.exist?(temp_dir): - # FileUtils.remove_entry(temp_dir) - - # @classmethod - # def remove_local_plugin(cls, plugin_name, scope:): - # local_plugins = scan_local_mode() - # scope_local_plugins = local_plugins[scope] - # if scope_local_plugins: - # for full_folder_path, details in scope_local_plugins: - # gems = details[:gems] - # plugin_instance = details[:plugin_instance] - # if len(gems) == 1 and plugin_instance: - # data = File.read(plugin_instance) - # json = json.loads(data) - # instance_name = json['name'] - # if plugin_name == instance_name: - # puts "Removing local plugin files: {full_folder_path}" - # File.delete(gems[0]) - # File.delete(plugin_instance) - # if Dir.empty?(File.dirname(gems[0])): - # FileUtils.rm_rf(File.dirname(gems[0])) - # break - - # def self.sync_targets_modified: - # bucket = Bucket.getClient() - # scopes = ScopeModel.names() - # for scope in scopes: - # sync_with_bucket(bucket, scope: scope) - - # @classmethod - # def modified_targets(cls, scope:): - # targets = {} - # local_catalog = build_local_catalog(scope: scope) - # for key, size in local_catalog: - # split_key = key.split('/') # scope/targets_modified/target_name/* - # target_name = split_key[2] - # if target_name: - # targets[target_name] = True - # return targets.keys.sort - - # @classmethod - # def modified_files(cls, target_name, scope:): - # modified = [] - # local_catalog = build_local_catalog(scope: scope) - # for key, size in local_catalog: - # split_key = key.split('/') # scope/targets_modified/target_name/* - # local_target_name = split_key[2] - # if target_name == local_target_name: - # modified.append(split_key[3:].join('/')) - # # Paths do not include target name - # return modified.sort - - # @classmethod - # def delete_modified(cls, target_name, scope:): - # full_path = "{OPENC3_LOCAL_MODE_PATH}/{scope}/targets_modified/{target_name}" - # FileUtils.rm_rf(full_path) - - # @classmethod - # def zip_target(cls, target_name, zip, scope:): - # modified = modified_files(target_name, scope: scope) - # for file_path in modified: - # full_path = "{OPENC3_LOCAL_MODE_PATH}/{scope}/targets_modified/{target_name}/{file_path}" - # zip.add(file_path, full_path) - @classmethod def put_target_file(cls, path, io_or_string, scope): full_folder_path = f"{cls.LOCAL_MODE_PATH}/{path}" @@ -355,44 +73,6 @@ def open_local_file(cls, path, scope): except OSError: return None - # @classmethod - # def local_target_files(cls, scope:, path_matchers:, include_temp: False): - # files = [] - # local_catalog = build_local_catalog(scope: scope) - # for key, size in local_catalog: - # split_key = key.split('/') - # # DEFAULT/targets_modified/__TEMP__/YYYY_MM_DD_HH_MM_SS_mmm_temp.rb - # # See target_file.rb TEMP_FOLDER - # if split_key[2] === '__TEMP__': - # if include_temp: - # files.append(split_key[2:].join('/')) - # next - # if path_matchers: - # found = False - # for path in path_matchers: - # if split_key.include?(path): - # found = True - # break - # next if not found - # files.append(split_key[2:].join('/')) - # return files.sort - - # @classmethod - # def sync_tool_config(cls, ): - # scopes = ScopeModel.names() - # for scope in scopes: - # for config in Dir["{OPENC3_LOCAL_MODE_PATH}/{scope}/tool_config/**/*.json"]: - # parts = config.split('/') - # puts "Syncing tool_config {parts[-2]} {File.basename(config)}" - # data = File.read(config) - # try: - # # Parse just to ensure we have valid JSON - # json.loads(data) - # # Only save if the parse was successful - # ToolConfigModel.save_config(parts[-2], File.basename(config, '.json'), data, scope: scope, local_mode: False) - # except: JSON:'P'arserError : error - # puts "Unable to initialize tool config due to {repr(error)}" - @classmethod def save_tool_config(cls, scope, tool, name, data): json_data = json.loads(data) @@ -405,17 +85,6 @@ def save_tool_config(cls, scope, tool, name, data): def delete_tool_config(cls, scope, tool, name): os.remove(f"{cls.LOCAL_MODE_PATH}/{scope}/tool_config/{tool}/{name}.json") - # @classmethod - # def sync_settings(cls, ): - # scopes = ScopeModel.names() - # for scope in scopes: - # for config in Dir["{OPENC3_LOCAL_MODE_PATH}/{scope}/settings/*.json"]: - # name = File.basename(config, ".json") - # puts "Syncing setting {name}" - # # Anything can be stored in settings so read and set directly - # data = File.read(config) - # SettingModel.set({ name: name, data: data }, scope: scope) - @classmethod def save_setting(cls, scope, name, data): config_path = f"{cls.LOCAL_MODE_PATH}/{scope}/settings/{name}.json" @@ -423,94 +92,3 @@ def save_setting(cls, scope, name, data): # Anything can be stored as a setting so write it out directly with open(config_path, "w") as file: file.write(str(data)) - - # # Helper methods - - # @classmethod - # def sync_remote_to_local(cls, bucket, key): - # local_path = "{OPENC3_LOCAL_MODE_PATH}/{key}" - # FileUtils.mkdir_p(File.dirname(local_path)) - # bucket.get_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: key, path: local_path) - - # @classmethod - # def sync_local_to_remote(cls, bucket, key): - # local_path = "{OPENC3_LOCAL_MODE_PATH}/{key}" - # File.open(local_path, 'rb') do |read_file| - # bucket.put_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: key, body: read_file) - - # @classmethod - # def delete_local(cls, key): - # local_path = "{OPENC3_LOCAL_MODE_PATH}/{key}" - # if File.exist?(local_path): - # File.delete(local_path) - # None - - # @classmethod - # def delete_remote(cls, bucket, key): - # bucket.delete_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: key) - - # # Returns equivalent names and sizes to remote catalog - # # {"scope/targets_modified/target_name/file" => size} - # @classmethod - # def build_local_catalog(cls, scope:): - # local_catalog = {} - # local_folder_path = "{OPENC3_LOCAL_MODE_PATH}/{scope}/targets_modified" - # prefix_length = "{OPENC3_LOCAL_MODE_PATH}/".length - # FileUtils.mkdir_p(local_folder_path) - # Dir.glob(local_folder_path + "/**/*").each do |filename| - # if File.directory?(filename): - # next - # mod_filename = filename[prefix_length:] - # local_catalog[mod_filename] = File.size(filename) - # return local_catalog - - # # Returns keys and sizes from remote catalog - # # {"scope/targets_modified/target_name/file" => size} - # @classmethod - # def build_remote_catalog(cls, bucket, scope:): - # remote_catalog = {} - # prefix = "{scope}/targets_modified" - # resp = bucket.list_objects( - # bucket: ENV['OPENC3_CONFIG_BUCKET'], - # prefix: prefix, - # ) - # for item in resp: - # remote_catalog[item.key] = item.size - # return remote_catalog - - # @classmethod - # def sync_with_bucket(cls, bucket, scope:): - # # Build catalogs - # local_catalog = build_local_catalog(scope: scope) - # remote_catalog = build_remote_catalog(bucket, scope: scope) - - # # Find and Handle Differences - # for key, size in local_catalog: - # remote_size = remote_catalog[key] - # if remote_size: - # # Both files exist - # if ENV['OPENC3_LOCAL_MODE_SECONDARY']: - # if size != remote_size or ENV['OPENC3_LOCAL_MODE_FORCE_SYNC']: - # sync_remote_to_local(bucket, key) - # else: - # if size != remote_size or ENV['OPENC3_LOCAL_MODE_FORCE_SYNC']: - # sync_local_to_remote(bucket, key) - # else: - # # Remote is missing local file - # if ENV['OPENC3_LOCAL_MODE_SECONDARY'] and ENV['OPENC3_LOCAL_MODE_SYNC_REMOVE']: - # delete_local(key) - # else: - # # Go ahead and copy up to get in sync - # sync_local_to_remote(bucket, key) - - # for key, size in remote_catalog: - # local_size = local_catalog[key] - # if local_size: - # # Both files exist - Handled earlier - # else: - # # Local is missing remote file - # if not ENV['OPENC3_LOCAL_MODE_SECONDARY'] and ENV['OPENC3_LOCAL_MODE_SYNC_REMOVE']: - # delete_remote(bucket, key) - # else: - # # Go ahead and copy down to get in sync - # sync_remote_to_local(bucket, key) From 0e425c4712a86eb7106f3f9388f99dd3c6936ca0 Mon Sep 17 00:00:00 2001 From: Ryan Pratt Date: Tue, 21 Jan 2025 17:00:25 -0700 Subject: [PATCH 3/4] Add file path prefix check to rest of local_mode.py --- openc3/python/openc3/utilities/local_mode.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/openc3/python/openc3/utilities/local_mode.py b/openc3/python/openc3/utilities/local_mode.py index c9414306d9..97b5a3fecf 100644 --- a/openc3/python/openc3/utilities/local_mode.py +++ b/openc3/python/openc3/utilities/local_mode.py @@ -77,18 +77,22 @@ def open_local_file(cls, path, scope): def save_tool_config(cls, scope, tool, name, data): json_data = json.loads(data) config_path = f"{cls.LOCAL_MODE_PATH}/{scope}/tool_config/{tool}/{name}.json" - os.makedirs(os.path.dirname(config_path), exist_ok=True) - with open(config_path, "w") as file: - file.write(json.dumps(json_data, indent=2)) + if os.path.normpath(config_path).startswith(cls.LOCAL_MODE_PATH): + os.makedirs(os.path.dirname(config_path), exist_ok=True) + with open(config_path, "w") as file: + file.write(json.dumps(json_data, indent=2)) @classmethod def delete_tool_config(cls, scope, tool, name): - os.remove(f"{cls.LOCAL_MODE_PATH}/{scope}/tool_config/{tool}/{name}.json") + config_path = f"{cls.LOCAL_MODE_PATH}/{scope}/tool_config/{tool}/{name}.json" + if os.path.normpath(config_path).startswith(cls.LOCAL_MODE_PATH): + os.remove(config_path) @classmethod def save_setting(cls, scope, name, data): config_path = f"{cls.LOCAL_MODE_PATH}/{scope}/settings/{name}.json" - os.makedirs(os.path.dirname(config_path), exist_ok=True) - # Anything can be stored as a setting so write it out directly - with open(config_path, "w") as file: - file.write(str(data)) + if os.path.normpath(config_path).startswith(cls.LOCAL_MODE_PATH): + os.makedirs(os.path.dirname(config_path), exist_ok=True) + # Anything can be stored as a setting so write it out directly + with open(config_path, "w") as file: + file.write(str(data)) From 54353301720e4ba709797cc0b6ea1f0a420cd427 Mon Sep 17 00:00:00 2001 From: Ryan Pratt Date: Tue, 21 Jan 2025 17:03:46 -0700 Subject: [PATCH 4/4] fix spelling --- README.md | 2 +- .../src/tools/ScriptRunner/ScriptRunner.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f0cfe542f2..fc01b5af22 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ After configuring OpenC3 COSMOS to talk to your hardware, you immediately can us - Script Runner allows for running OpenC3 COSMOS test procedures or any other Ruby code from a graphical environment that highlights each line as it executes. At anytime during execution, the script can be paused or stopped. If a telemetry check fails or any other exception occurs, the script is immediately stopped and the user notified. - - Script Runner also allows you to break your operational or test procedures down into discret test cases that each complete with either SUCCESS or FAILURE. After running, a script report is automatically created for you. Convenient features such as the ability to loop testing help get the kinks out of your system before formal runs. + - Script Runner also allows you to break your operational or test procedures down into discrete test cases that each complete with either SUCCESS or FAILURE. After running, a script report is automatically created for you. Convenient features such as the ability to loop testing help get the kinks out of your system before formal runs. 1. **Packet Viewer** diff --git a/openc3-cosmos-init/plugins/packages/openc3-cosmos-tool-scriptrunner/src/tools/ScriptRunner/ScriptRunner.vue b/openc3-cosmos-init/plugins/packages/openc3-cosmos-tool-scriptrunner/src/tools/ScriptRunner/ScriptRunner.vue index b40821de85..f844c85253 100644 --- a/openc3-cosmos-init/plugins/packages/openc3-cosmos-tool-scriptrunner/src/tools/ScriptRunner/ScriptRunner.vue +++ b/openc3-cosmos-init/plugins/packages/openc3-cosmos-tool-scriptrunner/src/tools/ScriptRunner/ScriptRunner.vue @@ -1792,7 +1792,7 @@ export default { switch (data.method) { case 'ask': case 'ask_string': - // Reset values since this dialog can be re-used + // Reset values since this dialog can be reused this.ask.default = null this.ask.answerRequired = true this.ask.password = false