parser.rb 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. module TriggerWebhookJob::CustomPayload::Parser
  3. # This module is used to scan, collect all replacment variables within a
  4. # custom payload, parse them for validity and replace, escape certain
  5. # characters in the final payload.
  6. private
  7. STRING_LIKE_CLASSES = %w[
  8. String
  9. ActiveSupport::TimeWithZone
  10. ActiveSupport::Duration
  11. ].freeze
  12. # This module validates the scanned replacement variables.
  13. def parse(variables, tracks)
  14. mappings = {}
  15. variables.each do |variable|
  16. methods = variable.split('.')
  17. object = methods.shift
  18. mappings[variable] = validate_object!(object, tracks)
  19. next if !mappings[variable].nil?
  20. reference = tracks[object.to_sym]
  21. mappings[variable] = validate_methods!(methods, reference, object)
  22. end
  23. mappings
  24. end
  25. # Replace, escape double quotes and whitespace characters to ensure the
  26. # payload is valid JSON.
  27. def replace(record, mappings)
  28. mappings.each do |variable, value|
  29. escaped_variable = Regexp.escape(variable)
  30. pattern = %r{("\#\{#{escaped_variable}\}"|\#\{#{escaped_variable}\})}
  31. is_string_like = value.class.to_s.in?(STRING_LIKE_CLASSES)
  32. record.gsub!(pattern) do |match|
  33. if match.start_with?('"')
  34. escaped_value = escape_replace_value(value, is_string_like:)
  35. is_string_like ? "\"#{escaped_value}\"" : escaped_value
  36. else
  37. escape_replace_value(value, is_string_like: true)
  38. end
  39. end
  40. end
  41. record
  42. end
  43. def escape_replace_value(value, is_string_like: false)
  44. if is_string_like
  45. value.to_s
  46. .gsub(%r{"}, '\"')
  47. .gsub(%r{\n}, '\n')
  48. .gsub(%r{\r}, '\r')
  49. .gsub(%r{\t}, '\t')
  50. .gsub(%r{\f}, '\f')
  51. .gsub(%r{\v}, '\v')
  52. else
  53. value.to_json
  54. end
  55. end
  56. # Scan the custom payload for replacement variables.
  57. def scan(record)
  58. placeholders = record.scan(%r{(#\{[a-z0-9_.?!]+\})}).flatten.uniq
  59. return [] if placeholders.blank?
  60. variables = []
  61. placeholders.each do |placeholder|
  62. next if !placeholder.match?(%r{^#\{(.+)\}$})
  63. placeholder.gsub!(%r{^#\{(.+)\}$}, '\1')
  64. variables.push(placeholder)
  65. end
  66. variables
  67. end
  68. end