contrast-agent 3.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.clang-format +5 -0
- data/.dockerignore +10 -0
- data/.gitignore +58 -0
- data/.gitmodules +6 -0
- data/.rspec +6 -0
- data/.simplecov +4 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +12 -0
- data/Rakefile +15 -0
- data/exe/contrast_service +29 -0
- data/ext/build_funchook.rb +48 -0
- data/ext/cs__assess_active_record_named/cs__active_record_named.c +47 -0
- data/ext/cs__assess_active_record_named/cs__active_record_named.h +10 -0
- data/ext/cs__assess_active_record_named/extconf.rb +2 -0
- data/ext/cs__assess_array/cs__assess_array.c +38 -0
- data/ext/cs__assess_array/cs__assess_array.h +9 -0
- data/ext/cs__assess_array/extconf.rb +2 -0
- data/ext/cs__assess_basic_object/cs__assess_basic_object.c +50 -0
- data/ext/cs__assess_basic_object/cs__assess_basic_object.h +17 -0
- data/ext/cs__assess_basic_object/extconf.rb +2 -0
- data/ext/cs__assess_fiber_track/cs__assess_fiber_track.c +86 -0
- data/ext/cs__assess_fiber_track/cs__assess_fiber_track.h +34 -0
- data/ext/cs__assess_fiber_track/extconf.rb +2 -0
- data/ext/cs__assess_hash/cs__assess_hash.c +64 -0
- data/ext/cs__assess_hash/cs__assess_hash.h +24 -0
- data/ext/cs__assess_hash/extconf.rb +2 -0
- data/ext/cs__assess_kernel/cs__assess_kernel.c +36 -0
- data/ext/cs__assess_kernel/cs__assess_kernel.h +10 -0
- data/ext/cs__assess_kernel/extconf.rb +2 -0
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +47 -0
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +18 -0
- data/ext/cs__assess_marshal_module/extconf.rb +2 -0
- data/ext/cs__assess_module/cs__assess_module.c +78 -0
- data/ext/cs__assess_module/cs__assess_module.h +25 -0
- data/ext/cs__assess_module/extconf.rb +2 -0
- data/ext/cs__assess_regexp/cs__assess_regexp.c +48 -0
- data/ext/cs__assess_regexp/cs__assess_regexp.h +22 -0
- data/ext/cs__assess_regexp/extconf.rb +2 -0
- data/ext/cs__assess_regexp_track/cs__assess_regexp_track.c +63 -0
- data/ext/cs__assess_regexp_track/cs__assess_regexp_track.h +29 -0
- data/ext/cs__assess_regexp_track/extconf.rb +2 -0
- data/ext/cs__assess_string/cs__assess_string.c +38 -0
- data/ext/cs__assess_string/cs__assess_string.h +19 -0
- data/ext/cs__assess_string/extconf.rb +2 -0
- data/ext/cs__assess_string_interpolation26/cs__assess_string_interpolation26.c +31 -0
- data/ext/cs__assess_string_interpolation26/cs__assess_string_interpolation26.h +13 -0
- data/ext/cs__assess_string_interpolation26/extconf.rb +2 -0
- data/ext/cs__common/cs__common.c +60 -0
- data/ext/cs__common/cs__common.h +28 -0
- data/ext/cs__common/extconf.rb +20 -0
- data/ext/cs__contrast_patch/cs__contrast_patch.c +445 -0
- data/ext/cs__contrast_patch/cs__contrast_patch.h +196 -0
- data/ext/cs__contrast_patch/extconf.rb +2 -0
- data/ext/cs__protect_kernel/cs__protect_kernel.c +37 -0
- data/ext/cs__protect_kernel/cs__protect_kernel.h +11 -0
- data/ext/cs__protect_kernel/extconf.rb +2 -0
- data/ext/cs__scope/cs__scope.c +96 -0
- data/ext/cs__scope/cs__scope.h +33 -0
- data/ext/cs__scope/extconf.rb +2 -0
- data/ext/extconf_common.rb +49 -0
- data/funchook/LICENSE +360 -0
- data/funchook/Makefile +29 -0
- data/funchook/Makefile.in +29 -0
- data/funchook/README.md +121 -0
- data/funchook/appveyor.yml +42 -0
- data/funchook/autogen.sh +3 -0
- data/funchook/autom4te.cache/output.0 +4976 -0
- data/funchook/autom4te.cache/requests +78 -0
- data/funchook/autom4te.cache/traces.0 +364 -0
- data/funchook/config.guess +1530 -0
- data/funchook/config.log +490 -0
- data/funchook/config.status +1016 -0
- data/funchook/config.sub +1773 -0
- data/funchook/configure +4976 -0
- data/funchook/configure.ac +59 -0
- data/funchook/distorm/COPYING +26 -0
- data/funchook/distorm/MANIFEST +25 -0
- data/funchook/distorm/MANIFEST.in +4 -0
- data/funchook/distorm/README.md +12 -0
- data/funchook/distorm/disOps/disOps.py +795 -0
- data/funchook/distorm/disOps/x86db.py +404 -0
- data/funchook/distorm/disOps/x86header.py +247 -0
- data/funchook/distorm/disOps/x86sets.py +1664 -0
- data/funchook/distorm/examples/cs/TestdiStorm/Program.cs +79 -0
- data/funchook/distorm/examples/cs/TestdiStorm/Properties/AssemblyInfo.cs +36 -0
- data/funchook/distorm/examples/cs/TestdiStorm/TestdiStorm.csproj +69 -0
- data/funchook/distorm/examples/cs/distorm-net.sln +26 -0
- data/funchook/distorm/examples/cs/distorm-net/CodeInfo.cs +23 -0
- data/funchook/distorm/examples/cs/distorm-net/DecodedInst.cs +15 -0
- data/funchook/distorm/examples/cs/distorm-net/DecodedResult.cs +14 -0
- data/funchook/distorm/examples/cs/distorm-net/DecomposedInst.cs +36 -0
- data/funchook/distorm/examples/cs/distorm-net/DecomposedResult.cs +14 -0
- data/funchook/distorm/examples/cs/distorm-net/Opcodes.cs +1268 -0
- data/funchook/distorm/examples/cs/distorm-net/Opcodes.tt +37 -0
- data/funchook/distorm/examples/cs/distorm-net/Operand.cs +25 -0
- data/funchook/distorm/examples/cs/distorm-net/Properties/AssemblyInfo.cs +36 -0
- data/funchook/distorm/examples/cs/distorm-net/diStorm3.cs +411 -0
- data/funchook/distorm/examples/cs/distorm-net/distorm-net.csproj +80 -0
- data/funchook/distorm/examples/cs/readme +3 -0
- data/funchook/distorm/examples/ddk/README +48 -0
- data/funchook/distorm/examples/ddk/distorm.ini +11 -0
- data/funchook/distorm/examples/ddk/dummy.c +15 -0
- data/funchook/distorm/examples/ddk/main.c +91 -0
- data/funchook/distorm/examples/ddk/makefile +1 -0
- data/funchook/distorm/examples/ddk/sources +10 -0
- data/funchook/distorm/examples/java/Makefile +23 -0
- data/funchook/distorm/examples/java/distorm/src/Main.java +43 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/CodeInfo.java +27 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/DecodedInst.java +32 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/DecodedResult.java +11 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/DecomposedInst.java +89 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/DecomposedResult.java +11 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/OpcodeEnum.java +131 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/Opcodes.java +1123 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/Operand.java +24 -0
- data/funchook/distorm/examples/java/distorm/src/diStorm3/distorm3.java +41 -0
- data/funchook/distorm/examples/java/jdistorm.c +405 -0
- data/funchook/distorm/examples/java/jdistorm.h +40 -0
- data/funchook/distorm/examples/java/jdistorm.sln +20 -0
- data/funchook/distorm/examples/java/jdistorm.vcproj +208 -0
- data/funchook/distorm/examples/linux/Makefile +15 -0
- data/funchook/distorm/examples/linux/main.c +181 -0
- data/funchook/distorm/examples/tests/Makefile +15 -0
- data/funchook/distorm/examples/tests/main.cpp +42 -0
- data/funchook/distorm/examples/tests/main.py +66 -0
- data/funchook/distorm/examples/tests/test_distorm3.py +1672 -0
- data/funchook/distorm/examples/tests/tests.sln +20 -0
- data/funchook/distorm/examples/tests/tests.vcxproj +82 -0
- data/funchook/distorm/examples/tests/tests.vcxproj.filters +22 -0
- data/funchook/distorm/examples/win32/disasm.sln +25 -0
- data/funchook/distorm/examples/win32/disasm.vcxproj +201 -0
- data/funchook/distorm/examples/win32/disasm.vcxproj.filters +14 -0
- data/funchook/distorm/examples/win32/main.cpp +163 -0
- data/funchook/distorm/include/distorm.h +482 -0
- data/funchook/distorm/include/mnemonics.h +301 -0
- data/funchook/distorm/make/linux/Makefile +28 -0
- data/funchook/distorm/make/mac/Makefile +24 -0
- data/funchook/distorm/make/win32/cdistorm.vcxproj +239 -0
- data/funchook/distorm/make/win32/cdistorm.vcxproj.filters +80 -0
- data/funchook/distorm/make/win32/distorm.sln +25 -0
- data/funchook/distorm/make/win32/resource.h +14 -0
- data/funchook/distorm/make/win32/resource.rc +99 -0
- data/funchook/distorm/python/distorm3/__init__.py +957 -0
- data/funchook/distorm/python/distorm3/sample.py +51 -0
- data/funchook/distorm/setup.cfg +10 -0
- data/funchook/distorm/setup.py +266 -0
- data/funchook/distorm/src/config.h +169 -0
- data/funchook/distorm/src/decoder.c +641 -0
- data/funchook/distorm/src/decoder.h +33 -0
- data/funchook/distorm/src/distorm.c +413 -0
- data/funchook/distorm/src/instructions.c +597 -0
- data/funchook/distorm/src/instructions.h +463 -0
- data/funchook/distorm/src/insts.c +7939 -0
- data/funchook/distorm/src/insts.h +64 -0
- data/funchook/distorm/src/mnemonics.c +284 -0
- data/funchook/distorm/src/operands.c +1290 -0
- data/funchook/distorm/src/operands.h +28 -0
- data/funchook/distorm/src/prefix.c +368 -0
- data/funchook/distorm/src/prefix.h +64 -0
- data/funchook/distorm/src/textdefs.c +172 -0
- data/funchook/distorm/src/textdefs.h +57 -0
- data/funchook/distorm/src/wstring.c +47 -0
- data/funchook/distorm/src/wstring.h +35 -0
- data/funchook/distorm/src/x86defs.h +82 -0
- data/funchook/include/funchook.h +123 -0
- data/funchook/install-sh +527 -0
- data/funchook/src/Makefile +70 -0
- data/funchook/src/Makefile.in +70 -0
- data/funchook/src/__strerror.h +109 -0
- data/funchook/src/config.h +101 -0
- data/funchook/src/config.h.in +100 -0
- data/funchook/src/decoder.o +0 -0
- data/funchook/src/distorm.o +0 -0
- data/funchook/src/funchook.c +440 -0
- data/funchook/src/funchook.o +0 -0
- data/funchook/src/funchook_internal.h +155 -0
- data/funchook/src/funchook_io.c +182 -0
- data/funchook/src/funchook_io.h +64 -0
- data/funchook/src/funchook_io.o +0 -0
- data/funchook/src/funchook_syscall.S +134 -0
- data/funchook/src/funchook_syscall.o +0 -0
- data/funchook/src/funchook_unix.c +480 -0
- data/funchook/src/funchook_unix.o +0 -0
- data/funchook/src/funchook_windows.c +397 -0
- data/funchook/src/funchook_x86.c +622 -0
- data/funchook/src/funchook_x86.o +0 -0
- data/funchook/src/instructions.o +0 -0
- data/funchook/src/insts.o +0 -0
- data/funchook/src/libfunchook.so +0 -0
- data/funchook/src/mnemonics.o +0 -0
- data/funchook/src/operands.o +0 -0
- data/funchook/src/os_func.c +115 -0
- data/funchook/src/os_func.h +75 -0
- data/funchook/src/os_func.o +0 -0
- data/funchook/src/os_func_unix.c +94 -0
- data/funchook/src/os_func_unix.o +0 -0
- data/funchook/src/os_func_windows.c +32 -0
- data/funchook/src/prefix.o +0 -0
- data/funchook/src/printf_base.c +1688 -0
- data/funchook/src/printf_base.h +46 -0
- data/funchook/src/printf_base.o +0 -0
- data/funchook/src/textdefs.o +0 -0
- data/funchook/src/wstring.o +0 -0
- data/funchook/test/Makefile +43 -0
- data/funchook/test/Makefile.in +43 -0
- data/funchook/test/funchook_test +0 -0
- data/funchook/test/libfunchook_test.c +25 -0
- data/funchook/test/libfunchook_test.so +0 -0
- data/funchook/test/libfunchook_test2.c +18 -0
- data/funchook/test/suffix.list +600 -0
- data/funchook/test/test_main.c +430 -0
- data/funchook/test/test_main.o +0 -0
- data/funchook/test/x86_64_test.S +10 -0
- data/funchook/test/x86_64_test.o +0 -0
- data/funchook/test/x86_test.S +339 -0
- data/funchook/win32/config.h +1 -0
- data/funchook/win32/funchook.sln +52 -0
- data/funchook/win32/funchook.vcxproj +188 -0
- data/funchook/win32/funchook.vcxproj.filters +84 -0
- data/funchook/win32/funchook_test.vcxproj +170 -0
- data/funchook/win32/funchook_test.vcxproj.filters +22 -0
- data/funchook/win32/funchook_test_dll.vcxproj +184 -0
- data/funchook/win32/funchook_test_dll.vcxproj.filters +30 -0
- data/funchook/win32/funchook_test_exe.def +3 -0
- data/lib/contrast-agent.rb +8 -0
- data/lib/contrast.rb +57 -0
- data/lib/contrast/agent.rb +80 -0
- data/lib/contrast/agent/assess.rb +45 -0
- data/lib/contrast/agent/assess/adjusted_span.rb +25 -0
- data/lib/contrast/agent/assess/class_reverter.rb +82 -0
- data/lib/contrast/agent/assess/contrast_event.rb +398 -0
- data/lib/contrast/agent/assess/frozen_properties.rb +41 -0
- data/lib/contrast/agent/assess/insulator.rb +53 -0
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +78 -0
- data/lib/contrast/agent/assess/policy/patcher.rb +85 -0
- data/lib/contrast/agent/assess/policy/policy.rb +116 -0
- data/lib/contrast/agent/assess/policy/policy_node.rb +289 -0
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +44 -0
- data/lib/contrast/agent/assess/policy/preshift.rb +94 -0
- data/lib/contrast/agent/assess/policy/propagation_method.rb +260 -0
- data/lib/contrast/agent/assess/policy/propagation_node.rb +127 -0
- data/lib/contrast/agent/assess/policy/propagator.rb +35 -0
- data/lib/contrast/agent/assess/policy/propagator/append.rb +54 -0
- data/lib/contrast/agent/assess/policy/propagator/base.rb +37 -0
- data/lib/contrast/agent/assess/policy/propagator/center.rb +73 -0
- data/lib/contrast/agent/assess/policy/propagator/custom.rb +36 -0
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +62 -0
- data/lib/contrast/agent/assess/policy/propagator/insert.rb +55 -0
- data/lib/contrast/agent/assess/policy/propagator/keep.rb +26 -0
- data/lib/contrast/agent/assess/policy/propagator/next.rb +42 -0
- data/lib/contrast/agent/assess/policy/propagator/prepend.rb +50 -0
- data/lib/contrast/agent/assess/policy/propagator/remove.rb +76 -0
- data/lib/contrast/agent/assess/policy/propagator/replace.rb +27 -0
- data/lib/contrast/agent/assess/policy/propagator/reverse.rb +38 -0
- data/lib/contrast/agent/assess/policy/propagator/select.rb +86 -0
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +60 -0
- data/lib/contrast/agent/assess/policy/propagator/split.rb +49 -0
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +169 -0
- data/lib/contrast/agent/assess/policy/propagator/trim.rb +81 -0
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +79 -0
- data/lib/contrast/agent/assess/policy/source_method.rb +209 -0
- data/lib/contrast/agent/assess/policy/source_node.rb +62 -0
- data/lib/contrast/agent/assess/policy/trigger_method.rb +209 -0
- data/lib/contrast/agent/assess/policy/trigger_node.rb +198 -0
- data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +77 -0
- data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +31 -0
- data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +40 -0
- data/lib/contrast/agent/assess/properties.rb +392 -0
- data/lib/contrast/agent/assess/rule.rb +18 -0
- data/lib/contrast/agent/assess/rule/base.rb +72 -0
- data/lib/contrast/agent/assess/rule/csrf.rb +66 -0
- data/lib/contrast/agent/assess/rule/csrf/csrf_action.rb +28 -0
- data/lib/contrast/agent/assess/rule/csrf/csrf_applicator.rb +69 -0
- data/lib/contrast/agent/assess/rule/csrf/csrf_watcher.rb +132 -0
- data/lib/contrast/agent/assess/rule/provider.rb +21 -0
- data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +62 -0
- data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +73 -0
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +121 -0
- data/lib/contrast/agent/assess/rule/redos.rb +68 -0
- data/lib/contrast/agent/assess/rule/response_scanning_rule.rb +47 -0
- data/lib/contrast/agent/assess/rule/response_watcher.rb +36 -0
- data/lib/contrast/agent/assess/rule/watcher.rb +36 -0
- data/lib/contrast/agent/assess/tag.rb +151 -0
- data/lib/contrast/agent/at_exit_hook.rb +33 -0
- data/lib/contrast/agent/class_reopener.rb +195 -0
- data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +26 -0
- data/lib/contrast/agent/deadzone/policy/policy.rb +57 -0
- data/lib/contrast/agent/disable_reaction.rb +24 -0
- data/lib/contrast/agent/exclusion_matcher.rb +190 -0
- data/lib/contrast/agent/feature_state.rb +379 -0
- data/lib/contrast/agent/inventory/policy/policy.rb +32 -0
- data/lib/contrast/agent/inventory/policy/trigger_node.rb +22 -0
- data/lib/contrast/agent/logger_manager.rb +116 -0
- data/lib/contrast/agent/middleware.rb +352 -0
- data/lib/contrast/agent/module_data.rb +16 -0
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +37 -0
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +58 -0
- data/lib/contrast/agent/patching/policy/method_policy.rb +94 -0
- data/lib/contrast/agent/patching/policy/module_policy.rb +116 -0
- data/lib/contrast/agent/patching/policy/patch.rb +312 -0
- data/lib/contrast/agent/patching/policy/patch_status.rb +192 -0
- data/lib/contrast/agent/patching/policy/patcher.rb +310 -0
- data/lib/contrast/agent/patching/policy/policy.rb +138 -0
- data/lib/contrast/agent/patching/policy/policy_node.rb +80 -0
- data/lib/contrast/agent/patching/policy/policy_unpatcher.rb +28 -0
- data/lib/contrast/agent/patching/policy/trigger_node.rb +81 -0
- data/lib/contrast/agent/protect/policy/policy.rb +37 -0
- data/lib/contrast/agent/protect/policy/trigger_node.rb +23 -0
- data/lib/contrast/agent/protect/rule.rb +58 -0
- data/lib/contrast/agent/protect/rule/base.rb +300 -0
- data/lib/contrast/agent/protect/rule/base_service.rb +88 -0
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +156 -0
- data/lib/contrast/agent/protect/rule/csrf.rb +118 -0
- data/lib/contrast/agent/protect/rule/csrf/csrf_evaluator.rb +103 -0
- data/lib/contrast/agent/protect/rule/csrf/csrf_token_injector.rb +85 -0
- data/lib/contrast/agent/protect/rule/default_scanner.rb +300 -0
- data/lib/contrast/agent/protect/rule/deserialization.rb +193 -0
- data/lib/contrast/agent/protect/rule/http_method_tampering.rb +80 -0
- data/lib/contrast/agent/protect/rule/no_sqli.rb +101 -0
- data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +40 -0
- data/lib/contrast/agent/protect/rule/path_traversal.rb +143 -0
- data/lib/contrast/agent/protect/rule/sqli.rb +101 -0
- data/lib/contrast/agent/protect/rule/sqli/default_sql_scanner.rb +16 -0
- data/lib/contrast/agent/protect/rule/sqli/mysql_sql_scanner.rb +38 -0
- data/lib/contrast/agent/protect/rule/sqli/postgres_sql_scanner.rb +22 -0
- data/lib/contrast/agent/protect/rule/sqli/sqlite_sql_scanner.rb +19 -0
- data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +20 -0
- data/lib/contrast/agent/protect/rule/xss.rb +24 -0
- data/lib/contrast/agent/protect/rule/xxe.rb +120 -0
- data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +82 -0
- data/lib/contrast/agent/railtie.rb +30 -0
- data/lib/contrast/agent/reaction_processor.rb +47 -0
- data/lib/contrast/agent/request.rb +493 -0
- data/lib/contrast/agent/request_context.rb +225 -0
- data/lib/contrast/agent/require_state.rb +61 -0
- data/lib/contrast/agent/response.rb +215 -0
- data/lib/contrast/agent/rewriter.rb +244 -0
- data/lib/contrast/agent/scope.rb +28 -0
- data/lib/contrast/agent/service_heartbeat.rb +37 -0
- data/lib/contrast/agent/settings_state.rb +148 -0
- data/lib/contrast/agent/socket_client.rb +125 -0
- data/lib/contrast/agent/thread.rb +26 -0
- data/lib/contrast/agent/tracepoint_hook.rb +51 -0
- data/lib/contrast/agent/version.rb +8 -0
- data/lib/contrast/api.rb +17 -0
- data/lib/contrast/api/.gitkeep +0 -0
- data/lib/contrast/api/connection_status.rb +49 -0
- data/lib/contrast/api/socket.rb +43 -0
- data/lib/contrast/api/speedracer.rb +206 -0
- data/lib/contrast/api/tcp_socket.rb +31 -0
- data/lib/contrast/api/unix_socket.rb +25 -0
- data/lib/contrast/common_agent_configuration.rb +86 -0
- data/lib/contrast/components/agent.rb +85 -0
- data/lib/contrast/components/app_context.rb +188 -0
- data/lib/contrast/components/assess.rb +67 -0
- data/lib/contrast/components/config.rb +135 -0
- data/lib/contrast/components/contrast_service.rb +113 -0
- data/lib/contrast/components/heap_dump.rb +34 -0
- data/lib/contrast/components/interface.rb +178 -0
- data/lib/contrast/components/inventory.rb +23 -0
- data/lib/contrast/components/logger.rb +92 -0
- data/lib/contrast/components/protect.rb +38 -0
- data/lib/contrast/components/sampling.rb +41 -0
- data/lib/contrast/components/scope.rb +106 -0
- data/lib/contrast/components/settings.rb +140 -0
- data/lib/contrast/config.rb +33 -0
- data/lib/contrast/config/agent_configuration.rb +24 -0
- data/lib/contrast/config/application_configuration.rb +27 -0
- data/lib/contrast/config/assess_configuration.rb +22 -0
- data/lib/contrast/config/assess_rules_configuration.rb +18 -0
- data/lib/contrast/config/base_configuration.rb +105 -0
- data/lib/contrast/config/default_value.rb +16 -0
- data/lib/contrast/config/exception_configuration.rb +21 -0
- data/lib/contrast/config/heap_dump_configuration.rb +23 -0
- data/lib/contrast/config/inventory_configuration.rb +20 -0
- data/lib/contrast/config/logger_configuration.rb +20 -0
- data/lib/contrast/config/protect_configuration.rb +20 -0
- data/lib/contrast/config/protect_rule_configuration.rb +37 -0
- data/lib/contrast/config/protect_rules_configuration.rb +30 -0
- data/lib/contrast/config/root_configuration.rb +26 -0
- data/lib/contrast/config/ruby_configuration.rb +39 -0
- data/lib/contrast/config/sampling_configuration.rb +22 -0
- data/lib/contrast/config/server_configuration.rb +23 -0
- data/lib/contrast/config/service_configuration.rb +22 -0
- data/lib/contrast/configuration.rb +214 -0
- data/lib/contrast/core_extensions/assess.rb +51 -0
- data/lib/contrast/core_extensions/assess/array.rb +58 -0
- data/lib/contrast/core_extensions/assess/assess_extension.rb +145 -0
- data/lib/contrast/core_extensions/assess/basic_object.rb +15 -0
- data/lib/contrast/core_extensions/assess/erb.rb +42 -0
- data/lib/contrast/core_extensions/assess/exec_trigger.rb +48 -0
- data/lib/contrast/core_extensions/assess/fiber.rb +125 -0
- data/lib/contrast/core_extensions/assess/hash.rb +22 -0
- data/lib/contrast/core_extensions/assess/kernel.rb +95 -0
- data/lib/contrast/core_extensions/assess/module.rb +14 -0
- data/lib/contrast/core_extensions/assess/regexp.rb +206 -0
- data/lib/contrast/core_extensions/assess/string.rb +75 -0
- data/lib/contrast/core_extensions/assess/tilt_template_trigger.rb +73 -0
- data/lib/contrast/core_extensions/delegator.rb +14 -0
- data/lib/contrast/core_extensions/eval_trigger.rb +52 -0
- data/lib/contrast/core_extensions/inventory.rb +22 -0
- data/lib/contrast/core_extensions/inventory/datastores.rb +37 -0
- data/lib/contrast/core_extensions/module.rb +42 -0
- data/lib/contrast/core_extensions/object.rb +27 -0
- data/lib/contrast/core_extensions/protect.rb +20 -0
- data/lib/contrast/core_extensions/protect/applies_command_injection_rule.rb +70 -0
- data/lib/contrast/core_extensions/protect/applies_deserialization_rule.rb +58 -0
- data/lib/contrast/core_extensions/protect/applies_no_sqli_rule.rb +81 -0
- data/lib/contrast/core_extensions/protect/applies_path_traversal_rule.rb +119 -0
- data/lib/contrast/core_extensions/protect/applies_sqli_rule.rb +63 -0
- data/lib/contrast/core_extensions/protect/applies_xxe_rule.rb +141 -0
- data/lib/contrast/core_extensions/protect/kernel.rb +30 -0
- data/lib/contrast/core_extensions/protect/psych.rb +7 -0
- data/lib/contrast/core_extensions/thread.rb +31 -0
- data/lib/contrast/internal_exception.rb +8 -0
- data/lib/contrast/rails_extensions/assess/action_controller_inheritance.rb +48 -0
- data/lib/contrast/rails_extensions/assess/active_record.rb +32 -0
- data/lib/contrast/rails_extensions/assess/active_record_named.rb +61 -0
- data/lib/contrast/rails_extensions/assess/configuration.rb +26 -0
- data/lib/contrast/rails_extensions/buffer.rb +30 -0
- data/lib/contrast/rails_extensions/rack.rb +45 -0
- data/lib/contrast/security_exception.rb +14 -0
- data/lib/contrast/sinatra_extensions/assess/cookie.rb +26 -0
- data/lib/contrast/sinatra_extensions/inventory/sinatra_base.rb +59 -0
- data/lib/contrast/tasks/service.rb +95 -0
- data/lib/contrast/utils/assess/sampling_util.rb +96 -0
- data/lib/contrast/utils/assess/tracking_util.rb +39 -0
- data/lib/contrast/utils/boolean_util.rb +33 -0
- data/lib/contrast/utils/cache.rb +69 -0
- data/lib/contrast/utils/class_util.rb +58 -0
- data/lib/contrast/utils/comment_range.rb +19 -0
- data/lib/contrast/utils/data_store_util.rb +23 -0
- data/lib/contrast/utils/duck_utils.rb +58 -0
- data/lib/contrast/utils/env_configuration_item.rb +52 -0
- data/lib/contrast/utils/environment_util.rb +152 -0
- data/lib/contrast/utils/freeze_util.rb +36 -0
- data/lib/contrast/utils/gemfile_reader.rb +191 -0
- data/lib/contrast/utils/hash_digest.rb +148 -0
- data/lib/contrast/utils/heap_dump_util.rb +113 -0
- data/lib/contrast/utils/invalid_configuration_util.rb +88 -0
- data/lib/contrast/utils/inventory_util.rb +126 -0
- data/lib/contrast/utils/io_util.rb +61 -0
- data/lib/contrast/utils/object_share.rb +117 -0
- data/lib/contrast/utils/operating_environment.rb +38 -0
- data/lib/contrast/utils/os.rb +49 -0
- data/lib/contrast/utils/path_util.rb +151 -0
- data/lib/contrast/utils/performs_logging.rb +152 -0
- data/lib/contrast/utils/preflight_util.rb +13 -0
- data/lib/contrast/utils/prevent_serialization.rb +52 -0
- data/lib/contrast/utils/rack_assess_session_cookie.rb +104 -0
- data/lib/contrast/utils/rails_assess_configuration.rb +95 -0
- data/lib/contrast/utils/random_util.rb +22 -0
- data/lib/contrast/utils/resource_loader.rb +23 -0
- data/lib/contrast/utils/ruby_ast_rewriter.rb +74 -0
- data/lib/contrast/utils/scope_util.rb +99 -0
- data/lib/contrast/utils/service_response_util.rb +116 -0
- data/lib/contrast/utils/service_sender_util.rb +98 -0
- data/lib/contrast/utils/sha256_builder.rb +69 -0
- data/lib/contrast/utils/sinatra_helper.rb +49 -0
- data/lib/contrast/utils/stack_trace_utils.rb +209 -0
- data/lib/contrast/utils/string_utils.rb +72 -0
- data/lib/contrast/utils/tag_util.rb +139 -0
- data/lib/contrast/utils/thread_tracker.rb +54 -0
- data/lib/contrast/utils/timer.rb +78 -0
- data/resources/assess/policy.json +1673 -0
- data/resources/csrf/inject.js +44 -0
- data/resources/deadzone/policy.json +55 -0
- data/resources/factory-bot-spec/spec_helper.rb +30 -0
- data/resources/inventory/policy.json +110 -0
- data/resources/protect/policy.json +417 -0
- data/resources/rubocops/kernel/catch_cop.rb +37 -0
- data/resources/rubocops/kernel/require_cop.rb +37 -0
- data/resources/rubocops/kernel/require_relative_cop.rb +33 -0
- data/resources/rubocops/module/autoload_cop.rb +37 -0
- data/resources/rubocops/module/const_defined_cop.rb +37 -0
- data/resources/rubocops/module/const_get_cop.rb +37 -0
- data/resources/rubocops/module/const_set_cop.rb +37 -0
- data/resources/rubocops/module/constants_cop.rb +37 -0
- data/resources/rubocops/module/name_cop.rb +37 -0
- data/resources/rubocops/object/class_cop.rb +37 -0
- data/resources/rubocops/object/freeze_cop.rb +37 -0
- data/resources/rubocops/object/frozen_cop.rb +37 -0
- data/resources/rubocops/object/is_a_cop.rb +37 -0
- data/resources/rubocops/object/method_cop.rb +37 -0
- data/resources/rubocops/object/respond_to_cop.rb +37 -0
- data/resources/rubocops/object/singleton_class_cop.rb +37 -0
- data/resources/rubocops/regexp/spelling_cop.rb +44 -0
- data/resources/rubocops/thread/new_cop.rb +39 -0
- data/resources/ruby-spec/ancestors_spec.rb +70 -0
- data/resources/ruby-spec/modulo_spec.rb +831 -0
- data/resources/ruby-spec/parameters_spec.rb +261 -0
- data/resources/ruby-spec/ruby_spec_spec_helper.rb +35 -0
- data/resources/test_marker.txt +1 -0
- data/ruby-agent.gemspec +129 -0
- data/service_executables/.gitkeep +0 -0
- data/service_executables/VERSION +1 -0
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +945 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Contrast
|
|
5
|
+
module Agent
|
|
6
|
+
module Assess
|
|
7
|
+
module Policy
|
|
8
|
+
module Propagator
|
|
9
|
+
# This class is specifically for String#tr(_s) propagation
|
|
10
|
+
#
|
|
11
|
+
# Disclaimer: there may be a better way, but we're
|
|
12
|
+
# in a 'get it work' state. hopefully, we'll be in
|
|
13
|
+
# a 'get it right' state soon.
|
|
14
|
+
class Trim
|
|
15
|
+
class << self
|
|
16
|
+
def tr_tagger patcher, preshift, ret, _block
|
|
17
|
+
return ret unless ret && !ret.empty?
|
|
18
|
+
|
|
19
|
+
source = preshift.object
|
|
20
|
+
args = preshift.args
|
|
21
|
+
ret.cs__copy_from(source)
|
|
22
|
+
replace_string = args[1]
|
|
23
|
+
source_chars = source.chars
|
|
24
|
+
# if the replace string is empty, then there's a bunch of deletes. this
|
|
25
|
+
# functions the same as the Removal propagation.
|
|
26
|
+
if replace_string == Contrast::Utils::ObjectShare::EMPTY_STRING
|
|
27
|
+
Contrast::Agent::Assess::Policy::Propagator::Remove.handle_removal(source_chars, ret)
|
|
28
|
+
else
|
|
29
|
+
remove_ranges = []
|
|
30
|
+
ret_chars = ret.chars
|
|
31
|
+
curr_span = nil
|
|
32
|
+
source_chars.each_with_index do |char, idx|
|
|
33
|
+
if ret_chars[idx] == char
|
|
34
|
+
next unless curr_span
|
|
35
|
+
|
|
36
|
+
curr_span.stop = idx
|
|
37
|
+
remove_ranges << curr_span
|
|
38
|
+
curr_span = nil
|
|
39
|
+
else
|
|
40
|
+
curr_span ||= Contrast::Agent::Assess::AdjustedSpan.new(idx)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
# account for the last char being different
|
|
44
|
+
if curr_span
|
|
45
|
+
curr_span.stop = source_chars.length
|
|
46
|
+
remove_ranges << curr_span
|
|
47
|
+
end
|
|
48
|
+
ret.cs__properties.delete_tags_at_ranges(remove_ranges, false)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
ret.cs__properties.build_event(
|
|
52
|
+
patcher,
|
|
53
|
+
ret,
|
|
54
|
+
source,
|
|
55
|
+
ret,
|
|
56
|
+
args,
|
|
57
|
+
1)
|
|
58
|
+
ret
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def tr_s_tagger patcher, preshift, ret, _block
|
|
62
|
+
return unless ret && !ret.empty?
|
|
63
|
+
|
|
64
|
+
source = preshift.object
|
|
65
|
+
args = preshift.args
|
|
66
|
+
source.cs__splat_tags(ret)
|
|
67
|
+
ret.cs__properties.build_event(
|
|
68
|
+
patcher,
|
|
69
|
+
ret,
|
|
70
|
+
source,
|
|
71
|
+
ret,
|
|
72
|
+
args)
|
|
73
|
+
ret
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
cs__scoped_require 'contrast/utils/object_share'
|
|
5
|
+
cs__scoped_require 'contrast/agent/patching/policy/patch_status'
|
|
6
|
+
cs__scoped_require 'contrast/agent/module_data'
|
|
7
|
+
cs__scoped_require 'contrast/agent/rewriter'
|
|
8
|
+
cs__scoped_require 'contrast/components/interface'
|
|
9
|
+
|
|
10
|
+
module Contrast
|
|
11
|
+
module Agent
|
|
12
|
+
module Assess
|
|
13
|
+
module Policy
|
|
14
|
+
# This is our interface from the Patcher to the Rewriter
|
|
15
|
+
# functionality
|
|
16
|
+
#
|
|
17
|
+
# TODO: RUBY-534 remove w/ EOL of 2.5
|
|
18
|
+
module RewriterPatch
|
|
19
|
+
include Contrast::Components::Interface
|
|
20
|
+
access_component :agent, :analysis, :logging
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
def rewrite_interpolations
|
|
24
|
+
return unless ASSESS.enabled?
|
|
25
|
+
return unless AGENT.rewrite_interpolation?
|
|
26
|
+
|
|
27
|
+
logger.debug_with_time("\t\tRunning Assess interpolation rewrite") do
|
|
28
|
+
ObjectSpace.each_object(Module) do |mod|
|
|
29
|
+
rewrite_interpolation(mod)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Rails is being a jerk, again. It passes in a class before it is
|
|
35
|
+
# done being defined. There is a state where the files have been
|
|
36
|
+
# loaded, but the class definition is not complete, so the
|
|
37
|
+
# methods of the class are not defined despite the class existing
|
|
38
|
+
#
|
|
39
|
+
# To get around this, we have those methods tell us the class
|
|
40
|
+
# isn't ready
|
|
41
|
+
def mid_defining? mod
|
|
42
|
+
mod.instance_variable_defined?(:@cs__defining_class) &&
|
|
43
|
+
mod.instance_variable_get(:@cs__defining_class)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def rewrite_interpolation mod, redo_rewrite = false
|
|
47
|
+
return unless ASSESS.enabled?
|
|
48
|
+
return unless AGENT.rewrite_interpolation?
|
|
49
|
+
return unless AGENT.interpolation_enabled?
|
|
50
|
+
return if mod.cs__frozen?
|
|
51
|
+
return if mod.singleton_class?
|
|
52
|
+
return if mid_defining?(mod)
|
|
53
|
+
|
|
54
|
+
status = Contrast::Agent::Patching::Policy::PatchStatus.get_status(mod)
|
|
55
|
+
return if (status&.rewritten? || status&.rewriting?) && !redo_rewrite
|
|
56
|
+
|
|
57
|
+
module_name = mod.cs__name
|
|
58
|
+
return unless module_name
|
|
59
|
+
|
|
60
|
+
if module_name.start_with?(Contrast::Utils::ObjectShare::CONTRAST_MODULE_START, Contrast::Utils::ObjectShare::ANONYMOUS_CLASS_MARKER)
|
|
61
|
+
status.no_rewrite!
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
module_data = Contrast::Agent::ModuleData.new(mod, module_name)
|
|
66
|
+
logger.debug_with_time("\t\t\tRewriting #{ module_name }") do
|
|
67
|
+
Contrast::Agent::Rewriter.rewrite_class(module_data, redo_rewrite)
|
|
68
|
+
end
|
|
69
|
+
rescue StandardError => e
|
|
70
|
+
logger.error(
|
|
71
|
+
e,
|
|
72
|
+
"Unable to patch assess into the module #{ mod }")
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# This class is responsible for the origination of traces. A Source is any method
|
|
5
|
+
# that returns an untrusted value. In general, these sources are request methods
|
|
6
|
+
# as the request object is user controlled.
|
|
7
|
+
#
|
|
8
|
+
# Going forward, we may add in Dynamic sources (determined at runtime) based on
|
|
9
|
+
# database configs or other variables (used for Stored XSS or other persisted
|
|
10
|
+
# vulnerability detection rules)
|
|
11
|
+
cs__scoped_require 'set'
|
|
12
|
+
|
|
13
|
+
cs__scoped_require 'contrast/utils/object_share'
|
|
14
|
+
cs__scoped_require 'contrast/utils/sha256_builder'
|
|
15
|
+
cs__scoped_require 'contrast/agent/assess/adjusted_span'
|
|
16
|
+
|
|
17
|
+
cs__scoped_require 'contrast/components/interface'
|
|
18
|
+
|
|
19
|
+
module Contrast
|
|
20
|
+
module Agent
|
|
21
|
+
module Assess
|
|
22
|
+
module Policy
|
|
23
|
+
# This class controls the actions we take on Sources, as determined by
|
|
24
|
+
# our Assess policy. It indicates what actions we should take in order
|
|
25
|
+
# to mark data as User Input and treat it as untrusted, starting the
|
|
26
|
+
# dataflows used in Assess vulnerability detection.
|
|
27
|
+
module SourceMethod
|
|
28
|
+
include Contrast::Components::Interface
|
|
29
|
+
access_component :logging, :analysis
|
|
30
|
+
|
|
31
|
+
def self.determine_target source_node, object, ret, args
|
|
32
|
+
source_target = source_node.targets[0]
|
|
33
|
+
case source_target
|
|
34
|
+
when Contrast::Utils::ObjectShare::RETURN_KEY
|
|
35
|
+
ret
|
|
36
|
+
when Contrast::Utils::ObjectShare::OBJECT_KEY
|
|
37
|
+
object
|
|
38
|
+
else
|
|
39
|
+
if source_target.is_a?(Integer)
|
|
40
|
+
args[source_target]
|
|
41
|
+
# If this isn't an index param, it's a named one. R.I.P.
|
|
42
|
+
else
|
|
43
|
+
arg = nil
|
|
44
|
+
args.each do |search|
|
|
45
|
+
next unless search.is_a?(Hash)
|
|
46
|
+
|
|
47
|
+
arg = search[source_target]
|
|
48
|
+
break if arg
|
|
49
|
+
end
|
|
50
|
+
arg
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# This is called from within our woven proc. It will be called as if it
|
|
56
|
+
# were inline in the Rack application.
|
|
57
|
+
def self.source_patchers method_policy, object, ret, args
|
|
58
|
+
return if method_policy.source_node.nil?
|
|
59
|
+
|
|
60
|
+
current_context = Contrast::Agent::REQUEST_TRACKER.current
|
|
61
|
+
return unless current_context&.analyze_request? && ASSESS.enabled?
|
|
62
|
+
|
|
63
|
+
replaced_return = nil
|
|
64
|
+
source_node = method_policy.source_node
|
|
65
|
+
|
|
66
|
+
target = determine_target(source_node, object, ret, args)
|
|
67
|
+
|
|
68
|
+
# We don't propagate to frozen things that haven't been tracked
|
|
69
|
+
# before. By definition, something that is a source has not
|
|
70
|
+
# previously been tracked; therefore, we can break out early.
|
|
71
|
+
if target.cs__frozen?
|
|
72
|
+
# That being said, we don't have enough context to know if we
|
|
73
|
+
# can make this assumption and still function, so we'll allow for
|
|
74
|
+
# source tracking of frozen things by a common config setting.
|
|
75
|
+
#
|
|
76
|
+
# Rails' StrongParameters make a case for this to be default
|
|
77
|
+
# behavior
|
|
78
|
+
return replaced_return unless ASSESS.track_frozen_sources?
|
|
79
|
+
|
|
80
|
+
# If we're tracking the frozen target, we need to unfreeze
|
|
81
|
+
# (dup) it to track and then freeze that result. For
|
|
82
|
+
# simplicities sake, we ONLY do this if the return is the
|
|
83
|
+
# target (I don't want to have to deal with unfreezing self)
|
|
84
|
+
return replaced_return unless source_node.targets[0] == Contrast::Utils::ObjectShare::RETURN_KEY
|
|
85
|
+
|
|
86
|
+
restore_frozen_state = true
|
|
87
|
+
ret = Contrast::Utils::FreezeUtil.unfreeze_dup(ret)
|
|
88
|
+
target = ret
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
SourceMethod.cs__apply_source(current_context, source_node, target, object, ret, *args)
|
|
92
|
+
|
|
93
|
+
ret.cs__freeze if restore_frozen_state
|
|
94
|
+
ret
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# This is our method that actually taints the object our source_node
|
|
98
|
+
# targets.
|
|
99
|
+
def self.cs__apply_source context, source_node, target, object, ret, *args
|
|
100
|
+
return unless context
|
|
101
|
+
|
|
102
|
+
source_node_source = source_node.sources[0]
|
|
103
|
+
source_name = case source_node_source
|
|
104
|
+
when nil
|
|
105
|
+
nil
|
|
106
|
+
when Contrast::Utils::ObjectShare::RETURN_KEY
|
|
107
|
+
ret
|
|
108
|
+
when Contrast::Utils::ObjectShare::OBJECT_KEY
|
|
109
|
+
self
|
|
110
|
+
else
|
|
111
|
+
args[source_node_source]
|
|
112
|
+
end
|
|
113
|
+
_cs__apply_source context, source_node, target, object, ret, source_node.type, source_name, 0, *args
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# I lied above. We had to figure out what the target of the source was.
|
|
117
|
+
# Now that we know, we'll actually tag it.
|
|
118
|
+
def self._cs__apply_source context, source_node, target, object, ret, source_type, source_name = nil, invoked = 0, *args
|
|
119
|
+
return unless context && source_node && target
|
|
120
|
+
|
|
121
|
+
# We know we only work on certain things.
|
|
122
|
+
# Skip if this isn't one of them
|
|
123
|
+
if Contrast::Utils::DuckUtils.quacks_to?(target, :cs__properties)
|
|
124
|
+
|
|
125
|
+
# don't apply second source -- probably needs tuning later if we
|
|
126
|
+
# use more than 'UNTRUSTED' in our sources
|
|
127
|
+
return if target.cs__tracked? || target.cs__frozen?
|
|
128
|
+
|
|
129
|
+
# otherwise for each tag this source_node applies, create a tag range
|
|
130
|
+
# on the target object
|
|
131
|
+
# I realize this looping is counter-intuitive from the above
|
|
132
|
+
# message, that's why we're revisiting.
|
|
133
|
+
source_node.tags.each do |tag|
|
|
134
|
+
length = Contrast::Utils::StringUtils.ret_length(target)
|
|
135
|
+
target.cs__properties.add_tag(tag, Contrast::Agent::Assess::AdjustedSpan.new(0, length))
|
|
136
|
+
target.cs__properties.add_properties(source_node.properties)
|
|
137
|
+
logger.debug(nil, "Source #{ source_node.id } detected: #{ target.__id__ } tagged with #{ tag }")
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# make a representation of this method that TeamServer can render
|
|
141
|
+
target.cs__properties.build_event(source_node, target, object, ret, args, invoked, source_type, source_name)
|
|
142
|
+
|
|
143
|
+
# While we don't taint hashes themselves, we may taint the things
|
|
144
|
+
# they hold. Let's pass their keys and values back to ourselves and
|
|
145
|
+
# try again
|
|
146
|
+
elsif Contrast::Utils::DuckUtils.quacks_like_tracked_hash?(target)
|
|
147
|
+
source_key_type = invoked.zero? ? key_type(source_type) : source_type
|
|
148
|
+
invoked += 1
|
|
149
|
+
to_replace = []
|
|
150
|
+
target.each_pair do |key, value|
|
|
151
|
+
# We only do this for Strings b/c of the way Hash lookup works.
|
|
152
|
+
# To replace another object would break hash lookup and,
|
|
153
|
+
# therefore, the application
|
|
154
|
+
if ASSESS.track_frozen_sources? &&
|
|
155
|
+
key.is_a?(String) &&
|
|
156
|
+
Contrast::Utils::DuckUtils.quacks_to?(target, :delete)
|
|
157
|
+
key = Contrast::Utils::FreezeUtil.unfreeze_dup(key)
|
|
158
|
+
to_replace << key
|
|
159
|
+
end
|
|
160
|
+
_cs__apply_source(context, source_node, key, object, ret, source_key_type, key, invoked, *args)
|
|
161
|
+
_cs__apply_source(context, source_node, value, object, ret, source_type, key, invoked, *args)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Hash is designed to keep one instance of the string key in it.
|
|
165
|
+
# We need to remove the existing one and replace it with our new
|
|
166
|
+
# tracked one.
|
|
167
|
+
to_replace.each do |key|
|
|
168
|
+
key.cs__freeze
|
|
169
|
+
value = target[key]
|
|
170
|
+
target.delete(key)
|
|
171
|
+
target[key] = value
|
|
172
|
+
end
|
|
173
|
+
# While we don't taint arrays themselves, we may taint the things
|
|
174
|
+
# they hold. Let's pass their keys and values back to ourselves and
|
|
175
|
+
# try again
|
|
176
|
+
elsif Contrast::Utils::DuckUtils.quacks_like_tracked_enumerable?(target)
|
|
177
|
+
invoked += 1
|
|
178
|
+
target.each { |value| _cs__apply_source(context, source_node, value, object, ret, source_type, source_name, invoked, *args) }
|
|
179
|
+
end
|
|
180
|
+
rescue StandardError => e
|
|
181
|
+
logger.warn(e, "Unable to apply source for source_node #{ source_node.id }")
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Silly helper method so that TeamServer can properly mark up
|
|
185
|
+
# the source of this trace, if this source ends up in a trigger
|
|
186
|
+
PARAMETER_TYPE = 'PARAMETER'
|
|
187
|
+
PARAMETER_KEY_TYPE = 'PARAMETER_KEY'
|
|
188
|
+
HEADER_TYPE = 'HEADER'
|
|
189
|
+
HEADER_KEY_TYPE = 'HEADER_KEY'
|
|
190
|
+
COOKIE_TYPE = 'COOKIE'
|
|
191
|
+
COOKIE_KEY_TYPE = 'COOKIE_KEY'
|
|
192
|
+
|
|
193
|
+
def self.key_type source_type
|
|
194
|
+
case source_type
|
|
195
|
+
when PARAMETER_TYPE
|
|
196
|
+
PARAMETER_KEY_TYPE
|
|
197
|
+
when HEADER_TYPE
|
|
198
|
+
HEADER_KEY_TYPE
|
|
199
|
+
when COOKIE_TYPE
|
|
200
|
+
COOKIE_KEY_TYPE
|
|
201
|
+
else
|
|
202
|
+
source_type
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Contrast
|
|
5
|
+
module Agent
|
|
6
|
+
module Assess
|
|
7
|
+
module Policy
|
|
8
|
+
# This class functions to translate our policy.json into an actionable
|
|
9
|
+
# Ruby object, allowing for dynamic patching over hardcoded patching,
|
|
10
|
+
# specifically for those methods which result in the source of
|
|
11
|
+
# untrusted data (indicate points in the application where user
|
|
12
|
+
# controlled input is accessed).
|
|
13
|
+
class SourceNode < PolicyNode
|
|
14
|
+
attr_accessor :type
|
|
15
|
+
|
|
16
|
+
DB_SOURCE_TYPE = 'TAINTED_DATABASE'
|
|
17
|
+
def self.build_dynamic_source _id, dynamic_source
|
|
18
|
+
dynamic_source_hash = {
|
|
19
|
+
JSON_CLASS_NAME => dynamic_source.class_name,
|
|
20
|
+
JSON_METHOD_NAME => dynamic_source.method_name,
|
|
21
|
+
JSON_INSTANCE_METHOD => dynamic_source.instance_method,
|
|
22
|
+
JSON_TYPE => DB_SOURCE_TYPE,
|
|
23
|
+
JSON_METHOD_VISIBILITY => 'public',
|
|
24
|
+
JSON_TARGET => dynamic_source.target,
|
|
25
|
+
JSON_PROPERTIES => dynamic_source.properties
|
|
26
|
+
}
|
|
27
|
+
Contrast::Agent::Assess::Policy::SourceNode.new(dynamic_source_hash)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
JSON_TYPE = 'type'
|
|
31
|
+
JSON_SOURCE_NAME = 'source_name'
|
|
32
|
+
SOURCE_TAG = 'UNTRUSTED'
|
|
33
|
+
def initialize source_hash = {}
|
|
34
|
+
super(source_hash)
|
|
35
|
+
@type = source_hash[JSON_TYPE]
|
|
36
|
+
@tags << SOURCE_TAG
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
SOURCE = 'Source'
|
|
40
|
+
def node_class
|
|
41
|
+
SOURCE
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# This is confusing. Sources are Creation action but
|
|
45
|
+
# Propagation type. Oh and also Type refers to input type,
|
|
46
|
+
# like parameter, so we have to call this node_type. :-/
|
|
47
|
+
def node_type
|
|
48
|
+
:TYPE_PROPAGATION
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Standard validation + TS trace version two rules:
|
|
52
|
+
# Must have source and type
|
|
53
|
+
def validate
|
|
54
|
+
super
|
|
55
|
+
raise(ArgumentError, "Source #{ id } did not have a proper target. Unable to create.") unless targets&.any?
|
|
56
|
+
raise(ArgumentError, "Source #{ id } did not have a proper type. Unable to create.") unless type
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
cs__scoped_require 'contrast/utils/object_share'
|
|
5
|
+
cs__scoped_require 'contrast/utils/sha256_builder'
|
|
6
|
+
cs__scoped_require 'contrast/agent/assess/policy/trigger_validation/trigger_validation'
|
|
7
|
+
|
|
8
|
+
cs__scoped_require 'contrast/components/interface'
|
|
9
|
+
|
|
10
|
+
module Contrast
|
|
11
|
+
module Agent
|
|
12
|
+
module Assess
|
|
13
|
+
module Policy
|
|
14
|
+
# A trigger method is one which can perform a dangerous action, as
|
|
15
|
+
# described by the Contrast::Agent::Assess::Policy::TriggerNode class.
|
|
16
|
+
# Each such method will call to this module just after invocation in
|
|
17
|
+
# order to determine if the call was done safely. In those cases where
|
|
18
|
+
# it was not, a Finding report is issued to the Service
|
|
19
|
+
module TriggerMethod
|
|
20
|
+
include Contrast::Components::Interface
|
|
21
|
+
access_component :logging, :analysis
|
|
22
|
+
|
|
23
|
+
# The level of TeamServer compliance our traces meet
|
|
24
|
+
CURRENT_FINDING_VERSION = 2
|
|
25
|
+
|
|
26
|
+
def self.settings
|
|
27
|
+
Contrast::Agent::FeatureState.instance
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.apply_trigger_rule trigger_node, object, ret, args
|
|
31
|
+
return if trigger_node.nil?
|
|
32
|
+
|
|
33
|
+
current_context = Contrast::Agent::REQUEST_TRACKER.current
|
|
34
|
+
return unless current_context&.analyze_request? && ASSESS.enabled?
|
|
35
|
+
|
|
36
|
+
if trigger_node.sources&.any?
|
|
37
|
+
trigger_node.sources.each do |marker|
|
|
38
|
+
source = determine_source(marker, object, ret, args)
|
|
39
|
+
TriggerMethod.cs__apply_trigger(current_context,
|
|
40
|
+
trigger_node,
|
|
41
|
+
source,
|
|
42
|
+
object,
|
|
43
|
+
ret,
|
|
44
|
+
1,
|
|
45
|
+
*args)
|
|
46
|
+
end
|
|
47
|
+
else
|
|
48
|
+
TriggerMethod.cs__apply_trigger(current_context,
|
|
49
|
+
trigger_node,
|
|
50
|
+
nil,
|
|
51
|
+
object,
|
|
52
|
+
ret,
|
|
53
|
+
1,
|
|
54
|
+
*args)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.cs__apply_trigger context, trigger_node, source, object, ret, invoked, *args
|
|
59
|
+
return unless context && trigger_node
|
|
60
|
+
return if trigger_node.rule_disabled?
|
|
61
|
+
return if trigger_node.dataflow? && source.nil?
|
|
62
|
+
|
|
63
|
+
invoked += 1
|
|
64
|
+
if trigger_node.regexp_rule?
|
|
65
|
+
apply_regexp_rule(context, trigger_node, source, object, ret, invoked, *args)
|
|
66
|
+
elsif trigger_node.custom_trigger?
|
|
67
|
+
trigger_node.apply_custom_trigger(context, trigger_node, source, object, ret, invoked, *args)
|
|
68
|
+
elsif trigger_node.dataflow?
|
|
69
|
+
apply_dataflow_rule(context, trigger_node, source, object, ret, invoked, *args)
|
|
70
|
+
else # trigger rule - just calling the method is dangerous
|
|
71
|
+
build_finding(context, trigger_node, source, object, ret, invoked, *args)
|
|
72
|
+
end
|
|
73
|
+
rescue StandardError => e
|
|
74
|
+
logger.warn(e, 'Unable to apply trigger.')
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Given the marker from the trigger_node (the pointer indicating the entity
|
|
78
|
+
# from which the taint originated), return the entity on which this
|
|
79
|
+
# trigger needs to operate.
|
|
80
|
+
#
|
|
81
|
+
# In an effort to speed up this lookup, we've changed the marker for
|
|
82
|
+
# parameters to be implicit - if it is not a return or an object, it
|
|
83
|
+
# must be a parameter, which we can reference either by index or by
|
|
84
|
+
# name.
|
|
85
|
+
def self.determine_source marker, object, ret, args
|
|
86
|
+
case marker
|
|
87
|
+
when Contrast::Utils::ObjectShare::RETURN_KEY
|
|
88
|
+
ret
|
|
89
|
+
when Contrast::Utils::ObjectShare::OBJECT_KEY
|
|
90
|
+
object
|
|
91
|
+
else # 'P'
|
|
92
|
+
if marker.is_a?(Integer)
|
|
93
|
+
args[marker]
|
|
94
|
+
else
|
|
95
|
+
arg = nil
|
|
96
|
+
args.each do |search|
|
|
97
|
+
next unless search.is_a?(Hash)
|
|
98
|
+
|
|
99
|
+
arg = search[marker]
|
|
100
|
+
break if arg
|
|
101
|
+
end
|
|
102
|
+
arg
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def self.apply_regexp_rule context, trigger_node, source, object, ret, invoked, *args
|
|
108
|
+
return unless source.is_a?(String)
|
|
109
|
+
return if trigger_node.good_value && source.match?(trigger_node.good_value)
|
|
110
|
+
return if trigger_node.bad_value && source !~ trigger_node.bad_value
|
|
111
|
+
|
|
112
|
+
invoked += 1
|
|
113
|
+
build_finding(context, trigger_node, source, object, ret, invoked, *args)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def self.apply_dataflow_rule context, trigger_node, source, object, ret, invoked, *args
|
|
117
|
+
return unless source
|
|
118
|
+
|
|
119
|
+
invoked += 1
|
|
120
|
+
if Contrast::Utils::DuckUtils.quacks_to?(source, :cs__properties)
|
|
121
|
+
return unless source.cs__tracked?
|
|
122
|
+
return unless trigger_node.violated?(source)
|
|
123
|
+
|
|
124
|
+
build_finding(context, trigger_node, source, object, ret, invoked, *args)
|
|
125
|
+
elsif Contrast::Utils::DuckUtils.quacks_like_tracked_hash?(source)
|
|
126
|
+
invoked += 2 # the each & the block
|
|
127
|
+
source.each_pair do |key, value|
|
|
128
|
+
apply_dataflow_rule(context, trigger_node, key, object, ret, invoked, *args)
|
|
129
|
+
apply_dataflow_rule(context, trigger_node, value, object, ret, invoked, *args)
|
|
130
|
+
end
|
|
131
|
+
elsif Contrast::Utils::DuckUtils.quacks_like_tracked_enumerable?(source)
|
|
132
|
+
invoked += 2 # the each & the block
|
|
133
|
+
source.each do |value|
|
|
134
|
+
apply_dataflow_rule(context, trigger_node, value, object, ret, invoked, *args)
|
|
135
|
+
end
|
|
136
|
+
else
|
|
137
|
+
logger.warn(
|
|
138
|
+
nil,
|
|
139
|
+
"Target is a #{ source.cs__class.name } -- not sure how to inspect: #{ trigger_node.inspect }")
|
|
140
|
+
logger.debug(nil, source.to_s[0..99])
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def self.build_finding context, trigger_node, source, object, ret, invoked, *args
|
|
145
|
+
return unless Contrast::Agent::Assess::Policy::TriggerValidation.valid?(trigger_node, object, ret, args)
|
|
146
|
+
|
|
147
|
+
request = context.request
|
|
148
|
+
env = request.env
|
|
149
|
+
return if defined?(ActionController::Live) &&
|
|
150
|
+
env &&
|
|
151
|
+
env['action_controller.instance'].cs__class.included_modules.include?(ActionController::Live)
|
|
152
|
+
|
|
153
|
+
finding = Contrast::Api::Dtm::Finding.new
|
|
154
|
+
finding.rule_id = Contrast::Utils::StringUtils.protobuf_safe_string(trigger_node.rule_id)
|
|
155
|
+
finding.session_id = Contrast::Agent::FeatureState.instance.current_session_id
|
|
156
|
+
finding.version = CURRENT_FINDING_VERSION
|
|
157
|
+
|
|
158
|
+
build_from_source(finding, source)
|
|
159
|
+
trigger_event = Contrast::Agent::Assess::ContrastEvent.new(trigger_node, source, object, ret, args, invoked + 1).to_dtm_event
|
|
160
|
+
finding.events << trigger_event
|
|
161
|
+
build_hash(finding, source)
|
|
162
|
+
build_tags(context)
|
|
163
|
+
finding.routes << context.route if context.route
|
|
164
|
+
context.activity.findings << finding
|
|
165
|
+
logger.debug(nil, "Trigger #{ trigger_node.id } detected: #{ source.__id__ } triggered #{ trigger_node.rule_id }")
|
|
166
|
+
rescue StandardError => e
|
|
167
|
+
logger.error(e, "Unable to build a finding for #{ trigger_node.id }")
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def self.build_from_source finding, source
|
|
171
|
+
return unless source
|
|
172
|
+
return unless Contrast::Utils::DuckUtils.quacks_to?(
|
|
173
|
+
source,
|
|
174
|
+
:cs__properties)
|
|
175
|
+
return unless source.cs__properties
|
|
176
|
+
|
|
177
|
+
# events could technically be nil, but we would have failed
|
|
178
|
+
# the rule check before getting here. not worth the nil check
|
|
179
|
+
source.cs__properties.events.each do |event|
|
|
180
|
+
finding.events << event.to_dtm_event
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Google::Protobuf::Map doesn't support merge!, so we have to do this
|
|
184
|
+
# long form
|
|
185
|
+
source_props = source.cs__properties.properties
|
|
186
|
+
return unless source_props
|
|
187
|
+
|
|
188
|
+
source_props.each_pair do |key, value|
|
|
189
|
+
key = Contrast::Utils::StringUtils.force_utf8(key)
|
|
190
|
+
finding.properties[key] = Contrast::Utils::StringUtils.force_utf8(value)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def self.build_hash finding, source
|
|
195
|
+
hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source)
|
|
196
|
+
finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash_code)
|
|
197
|
+
finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def self.build_tags context
|
|
201
|
+
return unless ASSESS.tags
|
|
202
|
+
|
|
203
|
+
context.activity.finding_tags = Contrast::Utils::StringUtils.force_utf8(ASSESS.tags)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|