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,225 @@
|
|
|
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/timer'
|
|
5
|
+
cs__scoped_require 'contrast/agent/request'
|
|
6
|
+
cs__scoped_require 'contrast/agent/response'
|
|
7
|
+
cs__scoped_require 'contrast/utils/comment_range'
|
|
8
|
+
cs__scoped_require 'contrast/utils/inventory_util'
|
|
9
|
+
cs__scoped_require 'contrast/components/interface'
|
|
10
|
+
|
|
11
|
+
module Contrast
|
|
12
|
+
module Agent
|
|
13
|
+
# This class acts to encapsulate information about the currently executed
|
|
14
|
+
# request, making it available to the Agent for the duration of the request
|
|
15
|
+
# in a standardized and normalized format which the Agent understands.
|
|
16
|
+
class RequestContext
|
|
17
|
+
include Contrast::Components::Interface
|
|
18
|
+
access_component :logging, :analysis, :scope, :contrast_service
|
|
19
|
+
|
|
20
|
+
EMPTY_INPUT_ANALYSIS_PB = Contrast::Api::Settings::InputAnalysis.new
|
|
21
|
+
|
|
22
|
+
attr_reader :timer,
|
|
23
|
+
:speedracer_input_analysis,
|
|
24
|
+
:request,
|
|
25
|
+
:response,
|
|
26
|
+
:activity,
|
|
27
|
+
:server_activity,
|
|
28
|
+
:route,
|
|
29
|
+
:observed_route
|
|
30
|
+
|
|
31
|
+
def initialize rack_request, app_loaded = true
|
|
32
|
+
with_contrast_scope do
|
|
33
|
+
# all requests get a timer
|
|
34
|
+
@timer = Contrast::Utils::Timer.new
|
|
35
|
+
|
|
36
|
+
# instantiate helper for request and response
|
|
37
|
+
@request = Contrast::Agent::Request.new(rack_request)
|
|
38
|
+
|
|
39
|
+
@activity = Contrast::Api::Dtm::Activity.new
|
|
40
|
+
@activity.http_request = request.dtm
|
|
41
|
+
|
|
42
|
+
@server_activity = Contrast::Api::Dtm::ServerActivity.new
|
|
43
|
+
|
|
44
|
+
@observed_route = Contrast::Api::Dtm::ObservedRoute.new
|
|
45
|
+
|
|
46
|
+
# build analyzer
|
|
47
|
+
@do_not_track = false
|
|
48
|
+
@speedracer_input_analysis = EMPTY_INPUT_ANALYSIS_PB
|
|
49
|
+
|
|
50
|
+
# flag to indicate whether the app is fully loaded
|
|
51
|
+
@app_loaded = !!app_loaded
|
|
52
|
+
|
|
53
|
+
# generic holder for properties that can be set throughout this request
|
|
54
|
+
@_properties = {}
|
|
55
|
+
|
|
56
|
+
@sample = true
|
|
57
|
+
|
|
58
|
+
@sample_request, @sample_response = Contrast::Utils::Assess::SamplingUtil.instance.sample?(@request) if ASSESS.enabled?
|
|
59
|
+
|
|
60
|
+
@sample_response &&= ASSESS.scan_response?
|
|
61
|
+
|
|
62
|
+
append_route_coverage(Contrast::Utils::PathUtil.get_route(@request))
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def app_loaded?
|
|
67
|
+
@app_loaded
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def analyze?
|
|
71
|
+
@sample_request || @sample_response
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def analyze_request?
|
|
75
|
+
@sample_request
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def analyze_response?
|
|
79
|
+
@sample_response
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Convert the discovered route for this request to appropriate forms and
|
|
83
|
+
# disseminate it to those locations where it is necessary for our route
|
|
84
|
+
# coverage and finding vulnerability discovery features to function.
|
|
85
|
+
def append_route_coverage route
|
|
86
|
+
return unless route
|
|
87
|
+
|
|
88
|
+
# For our findings
|
|
89
|
+
@route = route
|
|
90
|
+
|
|
91
|
+
# For SR findings
|
|
92
|
+
@activity.routes << route
|
|
93
|
+
|
|
94
|
+
# For TS routes
|
|
95
|
+
@observed_route.signature = route.route
|
|
96
|
+
@observed_route.verb = route.verb
|
|
97
|
+
@observed_route.session_id = Contrast::Agent::FeatureState.instance.current_session_id
|
|
98
|
+
|
|
99
|
+
# TODO: SPEED-273: deprecate when SR handles this. ContrastUI API currently does not allow empty url, so we have to provide a default
|
|
100
|
+
@observed_route.url = route.url.empty? ? Contrast::Utils::ObjectShare::SLASH : route.url
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Collect the results for the given rule with the given action
|
|
104
|
+
#
|
|
105
|
+
# @param rule [String] the id of the rule to which the results apply
|
|
106
|
+
# @param response_type [Symbol] the result of the response, matching a
|
|
107
|
+
# value of Contrast::Api::Dtm::AttackResult::ResponseType
|
|
108
|
+
# @return [Array<Contrast::Api::Dtm::AttackResult>]
|
|
109
|
+
def results_for rule, response_type = nil
|
|
110
|
+
if response_type.nil?
|
|
111
|
+
activity.results.select { |r| r.rule_id == rule }
|
|
112
|
+
else
|
|
113
|
+
activity.results.select { |r| r.rule_id == rule && r.response == response_type }
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def service_extract_request
|
|
118
|
+
return false if @do_not_track
|
|
119
|
+
|
|
120
|
+
service_response = CONTRAST_SERVICE.send_message(@activity.http_request)
|
|
121
|
+
return false unless service_response
|
|
122
|
+
|
|
123
|
+
handle_protect_state(service_response)
|
|
124
|
+
ia = service_response.input_analysis
|
|
125
|
+
if ia
|
|
126
|
+
logger.debug(nil, "Analysis from Contrast Service: evaluations=#{ ia.results.length }")
|
|
127
|
+
logger.debug(nil, "IA=#{ ia.inspect }")
|
|
128
|
+
@speedracer_input_analysis = ia
|
|
129
|
+
else
|
|
130
|
+
logger.debug(nil, 'Analysis from Contrast Service was empty.')
|
|
131
|
+
false
|
|
132
|
+
end
|
|
133
|
+
rescue Contrast::SecurityException
|
|
134
|
+
raise
|
|
135
|
+
rescue StandardError => e
|
|
136
|
+
logger.warn(e, 'Unable to extract Contrast Service information from request')
|
|
137
|
+
false
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# NOTE: this method is only used as a backstop if Speedracer sends Input Evaluations
|
|
141
|
+
# when the protect state indicates a security exception should be thrown. This method
|
|
142
|
+
# ensures that the attack reports are generated. Normally these should be generated on
|
|
143
|
+
# Speedracer for any attacks detected during prefilter.
|
|
144
|
+
def handle_protect_state agent_settings
|
|
145
|
+
return unless agent_settings
|
|
146
|
+
return unless agent_settings.protect_state
|
|
147
|
+
|
|
148
|
+
@uuid = agent_settings.protect_state.uuid
|
|
149
|
+
@do_not_track = true unless agent_settings.protect_state.track_request
|
|
150
|
+
|
|
151
|
+
return unless agent_settings.protect_state.security_exception
|
|
152
|
+
|
|
153
|
+
# If Contrast Service has NOT handled the input analysis, handle them here
|
|
154
|
+
build_attack_results(agent_settings)
|
|
155
|
+
|
|
156
|
+
msg = agent_settings.protect_state.security_message
|
|
157
|
+
logger.warn(nil, 'Contrast Service said to block this request')
|
|
158
|
+
raise Contrast::SecurityException.new(nil, (msg || 'Blocking suspicious behavior'))
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# append anything we've learned to the request seen message
|
|
162
|
+
# this is the sum-total of all inventory information that has
|
|
163
|
+
# been accumulated since the last request
|
|
164
|
+
def extract_after rack_response
|
|
165
|
+
@response = Contrast::Agent::Response.new(rack_response)
|
|
166
|
+
activity.http_response = @response.dtm if @sample_response
|
|
167
|
+
rescue StandardError => e
|
|
168
|
+
logger.error(e, 'Unable to extract information after request')
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def add_property key, value
|
|
172
|
+
@_properties[key] = value
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def get_property key
|
|
176
|
+
@_properties[key]
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def reset_activity
|
|
180
|
+
@activity = Contrast::Api::Dtm::Activity.new(http_request: request.dtm)
|
|
181
|
+
@server_activity = Contrast::Api::Dtm::ServerActivity.new # it doesn't look like this is ever actually used?
|
|
182
|
+
@observed_route = Contrast::Api::Dtm::ObservedRoute.new
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
private
|
|
186
|
+
|
|
187
|
+
# Generate attack results directly from any evaluations on the
|
|
188
|
+
# agent settings object.
|
|
189
|
+
def build_attack_results agent_settings
|
|
190
|
+
return unless agent_settings&.input_analysis&.results&.any?
|
|
191
|
+
|
|
192
|
+
attack_results_by_rule = {}
|
|
193
|
+
agent_settings.input_analysis.results.each do |ia_result|
|
|
194
|
+
rule_id = ia_result.rule_id
|
|
195
|
+
rule = PROTECT.rule(rule_id)
|
|
196
|
+
next unless rule
|
|
197
|
+
|
|
198
|
+
logger.debug(nil, "Building attack result from Contrast Service input analysis: result=#{ ia_result.inspect }")
|
|
199
|
+
|
|
200
|
+
attack_result = if rule.mode == :BLOCK
|
|
201
|
+
# special case for rules (like reflected xss)
|
|
202
|
+
# that used to have an infilter / block
|
|
203
|
+
# mode but now are just block at perimeter
|
|
204
|
+
rule.build_attack_with_match(
|
|
205
|
+
self,
|
|
206
|
+
ia_result,
|
|
207
|
+
attack_results_by_rule[rule_id],
|
|
208
|
+
ia_result.value)
|
|
209
|
+
else
|
|
210
|
+
rule.build_attack_without_match(
|
|
211
|
+
self,
|
|
212
|
+
ia_result,
|
|
213
|
+
attack_results_by_rule[rule_id])
|
|
214
|
+
end
|
|
215
|
+
attack_results_by_rule[rule_id] = attack_result
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
attack_results_by_rule.each_pair do |_, attack_result|
|
|
219
|
+
logger.debug(nil, "Blocking for #{ attack_result.rule_id }")
|
|
220
|
+
activity.results << attack_result
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
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
|
+
# Thread local way to access the current RequireState. Used as a convenient
|
|
7
|
+
# wrapper to ensure specific key is used and that we handle the case when
|
|
8
|
+
# we try to enter a previously unset require scope.
|
|
9
|
+
# (Note that 'require scopes' track nested requires. This is distinct
|
|
10
|
+
# from 'contrast scope', which tracks instrumentation.)
|
|
11
|
+
module RequireStates
|
|
12
|
+
class << self
|
|
13
|
+
KEY = :thread_local_contrast_require_scope
|
|
14
|
+
|
|
15
|
+
def enter
|
|
16
|
+
scope = current_scope
|
|
17
|
+
unless current_scope
|
|
18
|
+
scope = RequireState.new
|
|
19
|
+
Thread.current[KEY] = scope
|
|
20
|
+
end
|
|
21
|
+
scope.enter
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def exit
|
|
25
|
+
current_scope.exit
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def current_scope
|
|
29
|
+
Thread.current[KEY]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def status
|
|
33
|
+
current_scope&.scope.to_s
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def in_scope?
|
|
37
|
+
scope = current_scope
|
|
38
|
+
scope && scope.scope > 1
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Simple counter class for tracking how deep in nested requires / file
|
|
44
|
+
# load operations we currently are.
|
|
45
|
+
class RequireState
|
|
46
|
+
attr_reader :scope
|
|
47
|
+
|
|
48
|
+
def initialize
|
|
49
|
+
@scope = 0
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def enter
|
|
53
|
+
@scope += 1
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def exit
|
|
57
|
+
@scope -= 1
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,215 @@
|
|
|
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 'resolv'
|
|
5
|
+
cs__scoped_require 'timeout'
|
|
6
|
+
|
|
7
|
+
cs__scoped_require 'contrast/utils/object_share'
|
|
8
|
+
cs__scoped_require 'contrast/utils/string_utils'
|
|
9
|
+
cs__scoped_require 'contrast/utils/comment_range'
|
|
10
|
+
cs__scoped_require 'contrast/utils/hash_digest'
|
|
11
|
+
cs__scoped_require 'contrast/components/interface'
|
|
12
|
+
|
|
13
|
+
module Contrast
|
|
14
|
+
module Agent
|
|
15
|
+
# This class is the Contrast representation of the Rack::Response object. It
|
|
16
|
+
# provides access to the original Rack::Response object as well as extracts
|
|
17
|
+
# data in a format that the Agent expects, caching those transformations in
|
|
18
|
+
# order to avoid repeatedly creating Strings & thrashing GC.
|
|
19
|
+
class Response
|
|
20
|
+
include Contrast::Components::Interface
|
|
21
|
+
access_component :logging
|
|
22
|
+
|
|
23
|
+
extend Forwardable
|
|
24
|
+
|
|
25
|
+
attr_reader :rack_response
|
|
26
|
+
|
|
27
|
+
def initialize rack_response
|
|
28
|
+
@rack_response = rack_response
|
|
29
|
+
@is_array = !rack_response.is_a?(Rack::Response)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def document_type
|
|
33
|
+
case content_type
|
|
34
|
+
when /xml/i
|
|
35
|
+
:XML
|
|
36
|
+
when /json/i
|
|
37
|
+
:JSON
|
|
38
|
+
when /html/i
|
|
39
|
+
:NORMAL
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# B/c the response can change, we can't memoize this :(
|
|
44
|
+
def dtm
|
|
45
|
+
context_response = Contrast::Api::Dtm::HttpResponse.new
|
|
46
|
+
context_response.response_code = response_code.to_i
|
|
47
|
+
headers.each_pair do |key, value|
|
|
48
|
+
k = Contrast::Utils::StringUtils.force_utf8(key)
|
|
49
|
+
v = Contrast::Utils::StringUtils.force_utf8(value)
|
|
50
|
+
context_response.response_headers[k] = v
|
|
51
|
+
end
|
|
52
|
+
context_response.parsed_response_headers = true
|
|
53
|
+
|
|
54
|
+
context_response.response_body = Contrast::Utils::StringUtils.force_utf8(body)
|
|
55
|
+
context_response.parsed_response_body = true
|
|
56
|
+
|
|
57
|
+
doc_type = document_type
|
|
58
|
+
context_response.document_type = doc_type if doc_type
|
|
59
|
+
|
|
60
|
+
context_response
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def response_code
|
|
64
|
+
return unless @rack_response
|
|
65
|
+
|
|
66
|
+
@is_array ? @rack_response[0].to_i : @rack_response.status
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def headers
|
|
70
|
+
return unless @rack_response
|
|
71
|
+
|
|
72
|
+
if @is_array
|
|
73
|
+
@rack_response[1]
|
|
74
|
+
else
|
|
75
|
+
@rack_response.headers
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Header keys upcased and any underscores replaced with dashes
|
|
80
|
+
# We cannot memoize this, b/c response headers can change during the
|
|
81
|
+
# request lifecycle.
|
|
82
|
+
def normalized_headers
|
|
83
|
+
hash = {}
|
|
84
|
+
headers.each_pair do |header_name, header_value|
|
|
85
|
+
hash[Contrast::Utils::StringUtils.normalized_key(header_name)] = header_value
|
|
86
|
+
end
|
|
87
|
+
hash
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
CONTENT_TYPE_HEADER = 'CONTENT-TYPE'.cs__freeze
|
|
91
|
+
def content_type
|
|
92
|
+
return unless @rack_response
|
|
93
|
+
|
|
94
|
+
if @is_array
|
|
95
|
+
normalized_headers[CONTENT_TYPE_HEADER]
|
|
96
|
+
else
|
|
97
|
+
@rack_response.content_type
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def header key
|
|
102
|
+
return unless @rack_response
|
|
103
|
+
|
|
104
|
+
if @is_array
|
|
105
|
+
normalized_headers[Contrast::Utils::StringUtils.normalized_key(key)]
|
|
106
|
+
else
|
|
107
|
+
@rack_response.get_header(key)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def set_header key, value
|
|
112
|
+
return unless @rack_response
|
|
113
|
+
|
|
114
|
+
if @is_array
|
|
115
|
+
Rack::Utils.set_cookie_header!(@rack_response[1], key, value)
|
|
116
|
+
elsif @rack_response.is_a?(Rack::Response)
|
|
117
|
+
@rack_response.set_header(key, value)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# The response body can change during the request lifecycle
|
|
122
|
+
# We should not extract it out as a variable here, or we'll miss those
|
|
123
|
+
# changes. (headdesk)
|
|
124
|
+
def body
|
|
125
|
+
return unless @rack_response
|
|
126
|
+
|
|
127
|
+
if @is_array
|
|
128
|
+
extract_body(@rack_response[2])
|
|
129
|
+
elsif Contrast::Utils::DuckUtils.quacks_to?(@rack_response, :body)
|
|
130
|
+
extract_body(@rack_response.body)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def update_body body_string
|
|
135
|
+
return unless @rack_response
|
|
136
|
+
|
|
137
|
+
successfully_updated_body = true
|
|
138
|
+
if @is_array
|
|
139
|
+
if @rack_response[2].is_a?(Rack::BodyProxy)
|
|
140
|
+
successfully_updated_body = update_rack_body_proxy(body_string, true)
|
|
141
|
+
else
|
|
142
|
+
@rack_response[2] = [body_string]
|
|
143
|
+
end
|
|
144
|
+
elsif @rack_response.body.is_a?(Rack::BodyProxy)
|
|
145
|
+
successfully_updated_body = update_rack_body_proxy(body_string)
|
|
146
|
+
else
|
|
147
|
+
@rack_response.body = body_string
|
|
148
|
+
end
|
|
149
|
+
update_content_length(body_string.bytesize) if successfully_updated_body
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Set the length header for this response. This value should be set ot the
|
|
153
|
+
# bytesize, NOT THE LENGTH, of the response body. Otherwise, we may get
|
|
154
|
+
# got by the Lint thing.
|
|
155
|
+
CONTENT_LENGTH_HEADER = 'Content-Length'.cs__freeze
|
|
156
|
+
def update_content_length length
|
|
157
|
+
headers[CONTENT_LENGTH_HEADER] = length.to_s
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
private
|
|
161
|
+
|
|
162
|
+
HTTP_PREFIX = /^[Hh][Tt][Tt][Pp][_-]/i.cs__freeze
|
|
163
|
+
|
|
164
|
+
def update_rack_body_proxy body_string, is_array = false
|
|
165
|
+
top_body_proxy = is_array ? @rack_response[2] : @rack_response
|
|
166
|
+
parent_body_proxy = top_body_proxy
|
|
167
|
+
until (next_body = parent_body_proxy.instance_variable_get(:@body)).cs__class != Rack::BodyProxy
|
|
168
|
+
parent_body_proxy = next_body
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
if next_body.cs__class.to_s == 'ActionDispatch::Response::RackBody'
|
|
172
|
+
modified_response = next_body.instance_variable_defined?(:@response) ? next_body.instance_variable_get(:@response) : nil
|
|
173
|
+
if modified_response
|
|
174
|
+
modified_response.body = body_string
|
|
175
|
+
next_body.instance_variable_set(:@response, modified_response)
|
|
176
|
+
end
|
|
177
|
+
elsif next_body.is_a?(Array) && next_body[0].cs__class.to_s == 'ActionView::OutputBuffer'
|
|
178
|
+
new_body = ActionView::OutputBuffer.new(body_string)
|
|
179
|
+
next_body[0] = new_body
|
|
180
|
+
else
|
|
181
|
+
logger.warn("Detected unsupported Rack::BodyProxy internal response class #{ next_body.cs__class }")
|
|
182
|
+
return false
|
|
183
|
+
end
|
|
184
|
+
true
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def extract_body body
|
|
188
|
+
return nil unless body
|
|
189
|
+
|
|
190
|
+
if defined?(Rack::File) && body.is_a?(Rack::File)
|
|
191
|
+
# not sure what to do in this situation, so don't do anything.
|
|
192
|
+
nil
|
|
193
|
+
elsif Contrast::Utils::DuckUtils.quacks_to?(body, :each)
|
|
194
|
+
acc = []
|
|
195
|
+
body.each { |tmp| acc << read_or_string(tmp) }
|
|
196
|
+
acc.compact.join(Contrast::Utils::ObjectShare::NEW_LINE)
|
|
197
|
+
else
|
|
198
|
+
read_or_string(body)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def read_or_string obj
|
|
203
|
+
return nil unless obj
|
|
204
|
+
|
|
205
|
+
if Contrast::Utils::DuckUtils.quacks_to?(obj, :read)
|
|
206
|
+
tmp = obj.read
|
|
207
|
+
obj.rewind
|
|
208
|
+
tmp
|
|
209
|
+
else
|
|
210
|
+
obj.to_s
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|