Skip to content

Instantly share code, notes, and snippets.

@thomasjslone
Last active January 21, 2025 18:53
Show Gist options
  • Save thomasjslone/2de58f3db794454406cd1375625baaaa to your computer and use it in GitHub Desktop.
Save thomasjslone/2de58f3db794454406cd1375625baaaa to your computer and use it in GitHub Desktop.
Parse Array/Hash This was created out of the need to read arrays and hashes from files with out the worry of evaling injected script.
def parse_array *args
if args.length > 0 ; str = args[0] ; else ; str = self ; end
if str.to_s == "[]"; return []; end
str = str.strip.gsub(/^\[|\]$/, '')
elements = []; current_element = ''; nested_level = 0
str.each_char do |c|
if c == ',' && nested_level == 0
elements << current_element.strip
current_element = ''
else
current_element += c
if c == '['; nested_level += 1
elsif c == ']'; nested_level -= 1
end
end
end
elements << current_element.strip
elements.map do |element|
if element.start_with?('"') && element.end_with?('"')
element.gsub(/^"|"$/, '')
elsif element.start_with?("'") && element.end_with?("'")
element.gsub(/^'|'$/, '')
elsif element =~ /\A\d+\z/
element.to_i
elsif element =~ /\A\d+\.\d+\z/
element.to_f
elsif element == 'true' || element == 'false'
element == 'true'
elsif element.start_with?('[') && element.end_with?(']')
self.parse_array(element)
elsif element.start_with?('{') && element.end_with?('}')
self.parse_hash(element)
else
element
end
end
end
def parse_hash *args
if args.length == 0; str = self
else; str = args[0]
end
if str.to_s == "{}"; return {}; end
str = str.strip.gsub(/^\{|\}$/, '')
pairs = []; current_key = ''; current_value = ''; nested_level = 0
str.each_char do |c|
if c == '>' && nested_level == 0
current_key = current_value.gsub(/['":]\s*(\w+)\s*['":]?/, '\1'); current_key = current_key.delete(" =")
current_value = ''
elsif c == ',' && nested_level == 0
pairs << [current_key, current_value.strip]
current_key = ''
current_value = ''
else
current_value += c
if c == '{' || c == '['
nested_level += 1
elsif c == '}' || c == ']'
nested_level -= 1
end
end
end
pairs << [current_key, current_value.strip]
hash = {}
pairs.each do |pair|
key = pair[0]
value = pair[1]
if value.start_with?('{') && value.end_with?('}')
hash[key] = value.parse_hash
elsif value.start_with?('[') && value.end_with?(']')
hash[key] = value.parse_array
elsif value == 'true'
hash[key] = true
elsif value == 'false'
hash[key] = false
elsif value =~ /\A\d+\z/
hash[key] = value.to_i
elsif value =~ /\A\d+\.\d+\z/
hash[key] = value.to_f
else
hash[key] = value.gsub(/^\"|\"$/, '')
end
end
return hash
end
}
@thomasjslone
Copy link
Author

thomasjslone commented Jan 21, 2025

So the whole deal was that for programs where i save config like [1,2,3].to_s to a file and just eval it when loading, you could always just insert
script in the file and make sure the last line is still the array and the script would run with out interupting the config load
this code processes the string characters of arrays and hashes to get objects back with out using eval, if code is injected the parsing will just fail.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment