# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true # This module acts a trigger & propagator for Tilt::Template evaluation module TiltTemplateTrigger class << self NODE_HASH = { 'class_name' => 'Tilt::Template', 'instance_method' => true, 'method_name' => 'render', 'method_visibility' => 'public', 'action' => 'CUSTOM', 'source' => 'O,P0', 'target' => 'R', 'patch_class' => 'NOOP', 'patch_method' => 'track_template_evaluation' }.cs__freeze TEMPLATE_PROPAGATION_NODE = Contrast::Agent::Assess::Policy::PropagationNode.new(NODE_HASH) def render_trigger_check context, trigger_node, _source, object, ret, *args scope = args[0] erb_template_prerender = object.instance_variable_get(:@data) interpolated_inputs = [] handle_binding_variables(scope, erb_template_prerender, ret, interpolated_inputs) handle_local_variables(args, erb_template_prerender, ret, interpolated_inputs) unless interpolated_inputs.empty? interpolated_inputs.each do |input| input.cs__properties.events.each do |event| ret.cs__properties.events << event end end ret.cs__properties.build_event(TEMPLATE_PROPAGATION_NODE, ret, erb_template_prerender, ret, interpolated_inputs) end if ret.cs__tracked? Contrast::Agent::Assess::Policy::TriggerMethod.build_finding(context, trigger_node, ret, erb_template_prerender, ret, interpolated_inputs) end ret end private def handle_binding_variables scope, erb_template_prerender, ret, interpolated_inputs binding_variables = scope.instance_variables binding_variables.each do |bound_variable_sym| bound_variable_value = scope.instance_variable_get(bound_variable_sym) next unless bound_variable_value.cs__respond_to?(:cs__tracked?) && bound_variable_value.cs__tracked? next unless erb_template_prerender.include?(bound_variable_sym.to_s) start_index = ret.index(bound_variable_value) next if start_index.nil? ret.cs__copy_from(bound_variable_value, start_index) interpolated_inputs << bound_variable_sym end end def handle_local_variables args, erb_template_prerender, ret, interpolated_inputs locals = args[1] locals.each do |local_name, local_value| next unless local_value.cs__respond_to?(:cs__tracked?) && local_value.cs__tracked? next unless erb_template_prerender.include?(local_name.to_s) start_index = ret.index(local_value) next if start_index.nil? ret.cs__copy_from(local_value, start_index) interpolated_inputs << local_name end end end end