# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true cs__scoped_require 'contrast/components/interface' cs__scoped_require 'contrast/extensions/ruby_core/assess/exec_trigger' # This module provides us with a way to invoke Kernel propagation for those # methods which are too complex to fit into one of the standard # Contrast::Agent::Assess::Policy::Propagator molds without cluttering up the # Kernel Module or exposing our methods there. module KernelPropagator class << self include Contrast::Components::Interface include Contrast::CoreExtensions::Assess::ExecTrigger access_component :logging # We're 'tracking' sprintf now, meaning if anything is tracked on the way # in, the entire result will be tracked out. We're going to take this # approach for now b/c it's fast and easy. I don't super love it, and by # that I mean I hate it. # # To actually track this, we'd have to find the index of the new things # being added, then remove the tags at the range of the format marker, # which is some arbitrary length thing, and add the new tags from the # inserted string, shifted down by the length of the aforementioned # marker. # # marker is in the format %[flags][width][.precision]type, type being a # single character. We could regexp this with %.+[bBdiouxXeEfgGaAcps%] # # also, b/c Ruby hates us, there are things called absolute markers, # (digit)$, that go in the flags section. These cannot be mixed w/ the # order assumed type # # oh, and there's also %type and %{name}... b/c of course there is # -HM def sprintf_tagger patcher, preshift, ret, _block format_string = preshift.args[0] args = preshift.args[1] track_sprintf(ret, format_string, args) ret.cs__properties.build_event( patcher, ret, preshift.object, ret, preshift.args, 1) ret end def track_sprintf result, format_string, args handle_sprintf_value(format_string, result) if args.is_a?(String) handle_sprintf_value(args, result) elsif args.is_a?(Hash) handle_sprintf_hash(args, result) elsif args.is_a?(Array) handle_sprintf_array args, result end result rescue StandardError => e logger.error( 'Unable to track dataflow through sprintf', e) result end private def handle_sprintf_value value, result return unless Contrast::Utils::DuckUtils.trackable?(value) && value.cs__tracked? value.cs__properties.events.each do |event| result.cs__properties.events << event end value.cs__splat_tags(result) end def handle_sprintf_array args, result args.each do |value| handle_sprintf_value(value, result) end end def handle_sprintf_hash args, result args.each_value do |value| handle_sprintf_value(value, result) end end end end cs__scoped_require 'cs__assess_kernel/cs__assess_kernel'