/* Copyright (c) 2023 Contrast Security, Inc. See * https://www.contrastsecurity.com/enduser-terms-0317a for more details. */ #include "cs__assess_string.h" #include "../cs__common/cs__common.h" #include /* * The -@ method source: * * static VALUE * str_uminus(VALUE str) * { * if (!BARE_STRING_P(str) && !rb_obj_frozen_p(str)) { * str = rb_str_dup(str); * } * return rb_fstring(str); * } */ /* * This patch won't do the Prepend. We would call to the String instance' * uminus directly and skip other propagation from prepended modules. * We could come back to this one and rethink it's prepend patching. */ static VALUE contrast_assess_string_freeze(const int argc, VALUE *argv, const VALUE obj) { if (!OBJ_FROZEN(obj)) { rb_funcall(properties_hash, rb_sym_pre_freeze, 1, obj); } return rb_funcall(obj, rb_sym_assess_string_freeze, 0); } static VALUE contrast_assess_string_uminus(const int argc, VALUE *argv, const VALUE obj) { if (!OBJ_FROZEN(obj)) { /* We're doing something intentionally different here. Ruby, for -@, * attempts to de-duplicate the String and use an "interned" copy of * the String. We cannot allow that to happen for a couple reasons: * - prior to Ruby 2.7, this would cause us to track ALL instances of * that interned copy. * - 2.7 and later, this action is actually missed because of a change * to the str_uminus method in Ruby, which dups the String in a way * that we cannot see. * B/c we cannot track this in 2.7, rather than having a version check * and two approaches, we'll instead directly call the #freeze method, * so the end result is that this String itself is frozen and never * deduplicated. */ return contrast_assess_string_freeze(argc, argv, obj); } /* in all other cases, preserve monkey patching and c call */ return rb_funcall(obj, rb_sym_assess_string_uminus, 0); } void Init_cs__assess_string(void) { rb_sym_dup = rb_intern("dup"); rb_sym_freeze = rb_intern("freeze"); rb_sym_pre_freeze = rb_intern("pre_freeze"); /* Contrast::Agent::Assess::Tracker::PROPERTIES_HASH */ VALUE tracker = rb_define_class_under(assess, "Tracker", rb_cObject); properties_hash = rb_const_get(tracker, rb_intern("PROPERTIES_HASH")); /* We only do alias for this one */ rb_sym_assess_string_uminus = contrast_register_patch("String", "-@", &contrast_assess_string_uminus); rb_sym_assess_string_freeze = contrast_register_patch( "String", "freeze", &contrast_assess_string_freeze); }