Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check file path prefixes #1844

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions openc3/lib/openc3/utilities/local_mode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@

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
Expand All @@ -393,7 +394,10 @@

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')

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
This path depends on a
user-provided value
.
This path depends on a
user-provided value
.
This path depends on a
user-provided value
.
This path depends on a
user-provided value
.

Copilot Autofix AI 3 days ago

To fix the problem, we need to ensure that the full_path is properly sanitized and validated before being used in file operations. This can be achieved by implementing stricter validation rules and using a whitelist of allowed patterns for the scope and path parameters.

  1. Implement a method to validate the scope and path parameters against a whitelist of allowed patterns.
  2. Use this validation method in the open_local_file method to ensure that the full_path is safe to use.
  3. Update the sanitize_params method to include additional validation rules if necessary.
Suggested changeset 1
openc3/lib/openc3/utilities/local_mode.rb

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/openc3/lib/openc3/utilities/local_mode.rb b/openc3/lib/openc3/utilities/local_mode.rb
--- a/openc3/lib/openc3/utilities/local_mode.rb
+++ b/openc3/lib/openc3/utilities/local_mode.rb
@@ -395,2 +395,3 @@
     def self.open_local_file(path, scope:)
+      return nil unless valid_scope?(scope) && valid_path?(path)
       full_path = "#{OPENC3_LOCAL_MODE_PATH}/#{scope}/targets_modified/#{path}"
@@ -585,2 +586,15 @@
     end
+    private
+
+    def self.valid_scope?(scope)
+      # Add validation logic for scope
+      # Example: only allow alphanumeric characters and underscores
+      /^[a-zA-Z0-9_]+$/.match?(scope)
+    end
+
+    def self.valid_path?(path)
+      # Add validation logic for path
+      # Example: only allow alphanumeric characters, underscores, and forward slashes
+      /^[a-zA-Z0-9_\/]+$/.match?(path) && !path.include?('..')
+    end
   end
EOF
@@ -395,2 +395,3 @@
def self.open_local_file(path, scope:)
return nil unless valid_scope?(scope) && valid_path?(path)
full_path = "#{OPENC3_LOCAL_MODE_PATH}/#{scope}/targets_modified/#{path}"
@@ -585,2 +586,15 @@
end
private

def self.valid_scope?(scope)
# Add validation logic for scope
# Example: only allow alphanumeric characters and underscores
/^[a-zA-Z0-9_]+$/.match?(scope)
end

def self.valid_path?(path)
# Add validation logic for path
# Example: only allow alphanumeric characters, underscores, and forward slashes
/^[a-zA-Z0-9_\/]+$/.match?(path) && !path.include?('..')
end
end
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
end
nil
rescue Errno::ENOENT
nil
end
Expand Down Expand Up @@ -446,14 +450,17 @@
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))
end
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()
Expand All @@ -471,6 +478,7 @@

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)
Expand All @@ -480,19 +488,22 @@

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
end

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
Expand Down
6 changes: 5 additions & 1 deletion openc3/python/openc3/utilities/local_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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

Expand Down
Loading