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,30 @@
|
|
|
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/operating_environment'
|
|
5
|
+
|
|
6
|
+
module Contrast
|
|
7
|
+
module Agent
|
|
8
|
+
# A Railtie to allow for the automatic hooking of the Agent into a Rails
|
|
9
|
+
# application.
|
|
10
|
+
class Railtie < Rails::Railtie
|
|
11
|
+
initializer 'Contrast Ruby Agent Initializer' do |app|
|
|
12
|
+
if defined?(Rails) && defined?(Rails.logger)
|
|
13
|
+
Rails.logger.debug('In railtie ::')
|
|
14
|
+
Rails.logger.debug(app.middleware.inspect)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if Contrast::Utils::OperatingEnvironment.unsupported?
|
|
18
|
+
Rails.logger.debug('Detected a non-webserver context, skipping Contrast middleware insertion.')
|
|
19
|
+
else
|
|
20
|
+
# Keep our middleware at the outermost layer of the onion
|
|
21
|
+
app.middleware.insert_before 0, Contrast::Agent::Middleware
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
rake_tasks do
|
|
26
|
+
load 'contrast/tasks/service.rb'
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
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/agent/disable_reaction'
|
|
5
|
+
cs__scoped_require 'contrast/components/interface'
|
|
6
|
+
|
|
7
|
+
module Contrast
|
|
8
|
+
module Agent
|
|
9
|
+
# Because communication between the Agent/Service and TeamServer can only
|
|
10
|
+
# be initiated by outbound connections from the Agent/Service, we must
|
|
11
|
+
# provide a mechanism for the TeamServer to direct the Agent to take a
|
|
12
|
+
# specific action. This action is referred to as a Reaction. This class is
|
|
13
|
+
# how we handle those Reaction messages.
|
|
14
|
+
class ReactionProcessor
|
|
15
|
+
include Contrast::Components::Interface
|
|
16
|
+
access_component :logging
|
|
17
|
+
|
|
18
|
+
# Process the given Reactions from the application settings based on what
|
|
19
|
+
# TeamServer has indicated. Each Reaction will result in a log message
|
|
20
|
+
# and, optionally, an action.
|
|
21
|
+
#
|
|
22
|
+
# @param application_settings [Contrast::Api::Settings::ApplicationSettings]
|
|
23
|
+
# those settings which the Service has relayed from TeamServer.
|
|
24
|
+
def self.process application_settings
|
|
25
|
+
return nil unless application_settings&.reactions&.any?
|
|
26
|
+
|
|
27
|
+
application_settings.reactions.each do |reaction|
|
|
28
|
+
logger.debug(nil, "Received the following reaction: #{ reaction.operation }")
|
|
29
|
+
|
|
30
|
+
# the enums are all uppercase, we need to downcase them before attempting to log
|
|
31
|
+
level = reaction.log_level.nil? ? :error : reaction.log_level.downcase
|
|
32
|
+
|
|
33
|
+
logger.with_level(nil, reaction.message, level) if reaction.message
|
|
34
|
+
|
|
35
|
+
case reaction.operation
|
|
36
|
+
when :DISABLE
|
|
37
|
+
Contrast::Agent::DisableReaction.run reaction, level
|
|
38
|
+
when :NOOP # rubocop:disable Lint/EmptyWhen
|
|
39
|
+
# NOOP
|
|
40
|
+
else
|
|
41
|
+
logger.warn(nil, "ReactionProcessor received a reaction with an unknown operation: #{ reaction.operation }")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,493 @@
|
|
|
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::Request object. It
|
|
16
|
+
# provides access to the original Rack::Request 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 Request
|
|
20
|
+
include Contrast::Components::Interface
|
|
21
|
+
access_component :logging, :scope
|
|
22
|
+
|
|
23
|
+
extend Forwardable
|
|
24
|
+
|
|
25
|
+
INNER_REST_TOKEN = %r{/[\d]+/}.cs__freeze
|
|
26
|
+
LAST_REST_TOKEN = %r{/[\d]+$}.cs__freeze
|
|
27
|
+
INNER_NUMBER_MARKER = '/{n}/'
|
|
28
|
+
LAST_NUMBER_MARKER = '/{n}'
|
|
29
|
+
OMITTED_BODY = '{{body-omitted-by-contrast}}'
|
|
30
|
+
|
|
31
|
+
attr_reader :rack_request
|
|
32
|
+
|
|
33
|
+
def_delegators :@rack_request,
|
|
34
|
+
:env,
|
|
35
|
+
:query_string,
|
|
36
|
+
:user_agent,
|
|
37
|
+
:path,
|
|
38
|
+
:base_url
|
|
39
|
+
|
|
40
|
+
# receiver is memoized because it is the address/host/port of the server, once we
|
|
41
|
+
# resolve this for the first time, it shouldn't change
|
|
42
|
+
def self.receiver
|
|
43
|
+
@_receiver ||= build_receiver
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def initialize rack_request
|
|
47
|
+
@rack_request = rack_request
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
ACCEPT = 'ACCEPT'
|
|
51
|
+
def accept_headers
|
|
52
|
+
accepts = Array(normalized_request_headers[ACCEPT])
|
|
53
|
+
accepts.any? ? accepts : nil
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
STATIC_SUFFIXES = /\.(?:js|css|jpeg|jpg|gif|png|ico|woff|svg|pdf|eot|ttf|jar)$/i.cs__freeze
|
|
57
|
+
WILDCARD = '*/*'
|
|
58
|
+
# Utility method for checking if a request is for a static resource.
|
|
59
|
+
# @return [Boolean] true, if the request is for a well-known static
|
|
60
|
+
# type, like the following, and false otherwise: .js, .css, .jpg,
|
|
61
|
+
# .gif, .png, .ico
|
|
62
|
+
def static_request?
|
|
63
|
+
return true if trimmed_uri&.match?(STATIC_SUFFIXES)
|
|
64
|
+
|
|
65
|
+
accepts = accept_headers
|
|
66
|
+
if accepts
|
|
67
|
+
return false if accepts[0].to_s.start_with?(WILDCARD)
|
|
68
|
+
return true if media_content_type?(accepts[0])
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
false
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
MEDIA_TYPE_MARKERS = %w[image/ text/css text/javascript].cs__freeze
|
|
75
|
+
def media_content_type? str
|
|
76
|
+
str = str.to_s
|
|
77
|
+
str.start_with?(*MEDIA_TYPE_MARKERS)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def trimmed_uri
|
|
81
|
+
@_trimmed_uri ||= begin
|
|
82
|
+
raise ArgumentError, 'url was nil when attempting to trim' unless uri
|
|
83
|
+
|
|
84
|
+
trimmed = uri.split(Contrast::Utils::ObjectShare::SEMICOLON).first # remove ;jsessionid
|
|
85
|
+
trimmed.split(Contrast::Utils::ObjectShare::QUESTION_MARK).first # remove ?query_string=
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Returns a normalized form of the URI. In "normal" URIs
|
|
90
|
+
# this will return an unchanged String, but in REST-y
|
|
91
|
+
# URIs this will normalize the digit path tokens, e.g.:
|
|
92
|
+
#
|
|
93
|
+
# /accounts/5/view
|
|
94
|
+
# ...becomes:
|
|
95
|
+
# /accounts/{n}/view
|
|
96
|
+
#
|
|
97
|
+
# Should also handle the ;jsessionid.
|
|
98
|
+
def normalized_uri
|
|
99
|
+
@_normalized_uri ||= begin
|
|
100
|
+
uri = trimmed_uri
|
|
101
|
+
uri = uri.gsub(INNER_REST_TOKEN, INNER_NUMBER_MARKER) # replace interior tokens
|
|
102
|
+
uri.gsub(LAST_REST_TOKEN, LAST_NUMBER_MARKER) # replace last token
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
UNKNOWN_REQUEST_METHOD = 'UNKNOWN'
|
|
107
|
+
|
|
108
|
+
def request_method
|
|
109
|
+
rack_request.get_header(Rack::REQUEST_METHOD)
|
|
110
|
+
rescue StandardError => e
|
|
111
|
+
logger.warn("Unable to extract request method: #{ e }")
|
|
112
|
+
UNKNOWN_REQUEST_METHOD
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def document_type_from_header
|
|
116
|
+
case content_type
|
|
117
|
+
when /xml/i
|
|
118
|
+
:XML
|
|
119
|
+
when /json/i
|
|
120
|
+
:JSON
|
|
121
|
+
when /html/i
|
|
122
|
+
:NORMAL
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def document_type
|
|
127
|
+
@_document_type ||= begin
|
|
128
|
+
type = document_type_from_header
|
|
129
|
+
if type
|
|
130
|
+
type
|
|
131
|
+
elsif request_body_str.start_with?('<?xml')
|
|
132
|
+
:XML
|
|
133
|
+
elsif request_body_str.match?(/\s*[{\[]/)
|
|
134
|
+
:JSON
|
|
135
|
+
else
|
|
136
|
+
:NORMAL
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def request_headers
|
|
142
|
+
@_request_headers ||= begin
|
|
143
|
+
with_contrast_scope do
|
|
144
|
+
headers = header_pairs(env).each_with_object({}) do |pair, h|
|
|
145
|
+
h[pair[0]] = pair[1]
|
|
146
|
+
end
|
|
147
|
+
headers
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Header keys upcased and any underscores replaced with dashes
|
|
153
|
+
def normalized_request_headers
|
|
154
|
+
@_normalized_request_headers ||= begin
|
|
155
|
+
hash = {}
|
|
156
|
+
request_headers.each_pair do |header_name, header_value|
|
|
157
|
+
hash[Contrast::Utils::StringUtils.normalized_key(header_name)] = header_value
|
|
158
|
+
end
|
|
159
|
+
hash
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def header_value key
|
|
164
|
+
normalized_request_headers[Contrast::Utils::StringUtils.normalized_key(key)]
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Return a flattened hash of params with realized paths for keys, in
|
|
168
|
+
# addition to a separate, valueless, entry for each nest key.
|
|
169
|
+
# See RUBY-621 for more details.
|
|
170
|
+
# { key : { nested_key : ['x','y','z' ] } }
|
|
171
|
+
# becomes
|
|
172
|
+
# {
|
|
173
|
+
# key[nested_key][0] : 'x'
|
|
174
|
+
# key[nested_key][1] : 'y'
|
|
175
|
+
# key[nested_key][2] : 'z'
|
|
176
|
+
# key : ''
|
|
177
|
+
# nested_key : ''
|
|
178
|
+
# }
|
|
179
|
+
def normalize_params val, prefix: nil
|
|
180
|
+
# In non-recursive invocations, val should always be a Hash
|
|
181
|
+
# (rather than breaking this out into two methods)
|
|
182
|
+
case val
|
|
183
|
+
when Tempfile
|
|
184
|
+
{ prefix => Contrast::Utils::StringUtils.force_utf8(val.path) }
|
|
185
|
+
when Hash
|
|
186
|
+
res = val.each_with_object({}) do |(k, v), hash|
|
|
187
|
+
k = Contrast::Utils::StringUtils.force_utf8(k)
|
|
188
|
+
nested_prefix = prefix.nil? ? k : "#{ prefix }[#{ k }]"
|
|
189
|
+
hash[k] = Contrast::Utils::ObjectShare::EMPTY_STRING
|
|
190
|
+
hash.merge! normalize_params(v, prefix: nested_prefix)
|
|
191
|
+
end
|
|
192
|
+
res[prefix] = Contrast::Utils::ObjectShare::EMPTY_STRING if prefix
|
|
193
|
+
res
|
|
194
|
+
when Enumerable
|
|
195
|
+
res = val.each_with_index.each_with_object({}) do |(v, i), hash|
|
|
196
|
+
hash.merge! normalize_params(v, prefix: "#{ prefix }[#{ i }]")
|
|
197
|
+
end
|
|
198
|
+
res[prefix] = Contrast::Utils::ObjectShare::EMPTY_STRING if prefix
|
|
199
|
+
res
|
|
200
|
+
else
|
|
201
|
+
{ prefix => Contrast::Utils::StringUtils.force_utf8(val) }
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def request_body
|
|
206
|
+
# Memoize a flag indicating whether we've tried to read the body or not
|
|
207
|
+
# (can't use body because it might be nil)
|
|
208
|
+
@_request_body_read ||= begin
|
|
209
|
+
body = @rack_request.body
|
|
210
|
+
if defined?(Rack::Multipart)
|
|
211
|
+
if defined?(Rack::Multipart::UploadedFile) && body.is_a?(Rack::Multipart::UploadedFile)
|
|
212
|
+
logger.debug("not parsing uploaded file body :: #{ body.original_filename }::#{ body.content_type }")
|
|
213
|
+
@_request_body = nil
|
|
214
|
+
else
|
|
215
|
+
logger.debug("parsing body from request :: #{ body.cs__class.cs__name }")
|
|
216
|
+
@_request_body = Contrast::Utils::StringUtils.force_utf8(read_body(body), logger)
|
|
217
|
+
end
|
|
218
|
+
else
|
|
219
|
+
logger.debug('Rack before 1.3.x does not support Rack::Multipart')
|
|
220
|
+
@_request_body = Contrast::Utils::StringUtils.force_utf8(read_body(body), logger)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
true
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Return memoized body (which might be nil)
|
|
227
|
+
@_request_body
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def request_body_str
|
|
231
|
+
request_body.to_s || Contrast::Utils::ObjectShare::EMPTY_STRING
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# This will become to_protobuf
|
|
235
|
+
# Expectation is that all data from a previous phase will be populated
|
|
236
|
+
# before the subsequent one begins
|
|
237
|
+
def dtm
|
|
238
|
+
@_dtm ||= begin
|
|
239
|
+
with_contrast_scope do
|
|
240
|
+
http_request = Contrast::Api::Dtm::HttpRequest.new
|
|
241
|
+
http_request.uuid = Contrast::Utils::StringUtils.force_utf8(__id__)
|
|
242
|
+
http_request.timestamp_ms = Contrast::Utils::Timer.now_ms.to_i
|
|
243
|
+
|
|
244
|
+
append_receiver(http_request)
|
|
245
|
+
append_connection(http_request)
|
|
246
|
+
append_params(http_request)
|
|
247
|
+
append_headers(http_request)
|
|
248
|
+
append_body(http_request)
|
|
249
|
+
|
|
250
|
+
http_request
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def append_receiver http_request
|
|
256
|
+
r = cs__class.receiver
|
|
257
|
+
r.port = port.to_i if port
|
|
258
|
+
http_request.receiver = r unless r.nil?
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def append_connection http_request
|
|
262
|
+
http_request.sender = Contrast::Api::Dtm::Address.new
|
|
263
|
+
http_request.sender.ip = Contrast::Utils::StringUtils.force_utf8(ip)
|
|
264
|
+
http_request.protocol = Contrast::Utils::StringUtils.force_utf8(scheme)
|
|
265
|
+
http_request.version = '1.1' # currently not in rack request; hard-coding
|
|
266
|
+
http_request.method = Contrast::Utils::StringUtils.force_utf8(request_method)
|
|
267
|
+
http_request.raw = Contrast::Utils::StringUtils.force_utf8(@rack_request.path_info)
|
|
268
|
+
http_request.parsed_connection = true
|
|
269
|
+
http_request.uri = Contrast::Utils::StringUtils.force_utf8(path)
|
|
270
|
+
http_request.normalized_uri = Contrast::Utils::StringUtils.force_utf8(normalized_uri)
|
|
271
|
+
http_context = if http_request.uri == Contrast::Utils::ObjectShare::SLASH
|
|
272
|
+
Contrast::Utils::ObjectShare::SLASH
|
|
273
|
+
else
|
|
274
|
+
http_request.uri.split(Contrast::Utils::ObjectShare::SLASH).first
|
|
275
|
+
end
|
|
276
|
+
http_request.context = Contrast::Utils::StringUtils.force_utf8(http_context)
|
|
277
|
+
http_request.path = Contrast::Utils::StringUtils.force_utf8(http_request.uri)
|
|
278
|
+
http_request.query_string = Contrast::Utils::StringUtils.force_utf8(query_string)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def append_params http_request
|
|
282
|
+
parameters.each do |k, v|
|
|
283
|
+
next unless k && v
|
|
284
|
+
next if v.is_a?(Hash)
|
|
285
|
+
|
|
286
|
+
key = Contrast::Utils::StringUtils.force_utf8(k)
|
|
287
|
+
val = Contrast::Utils::StringUtils.force_utf8(v)
|
|
288
|
+
params = http_request.normalized_request_params
|
|
289
|
+
params[key] = Contrast::Api::Dtm::Pair.new unless params[key].is_a?(Contrast::Api::Dtm::Pair)
|
|
290
|
+
params[key].key = key
|
|
291
|
+
params[key].values << val
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def append_headers http_request
|
|
296
|
+
request_headers.each do |k, v|
|
|
297
|
+
next unless k && v
|
|
298
|
+
next if v.is_a?(Hash)
|
|
299
|
+
|
|
300
|
+
key = Contrast::Utils::StringUtils.force_utf8(k)
|
|
301
|
+
val = Contrast::Utils::StringUtils.force_utf8(v)
|
|
302
|
+
http_request.request_headers[key] = val
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
http_request.parsed_request_headers = true
|
|
306
|
+
|
|
307
|
+
normalized_request_headers.each do |k, v|
|
|
308
|
+
next unless k && v
|
|
309
|
+
next if v.is_a?(Hash)
|
|
310
|
+
|
|
311
|
+
key = Contrast::Utils::StringUtils.force_utf8(k)
|
|
312
|
+
val = Contrast::Utils::StringUtils.force_utf8(v)
|
|
313
|
+
http_request.normalized_request_headers[key] ||= Contrast::Api::Dtm::Pair.new
|
|
314
|
+
http_request.normalized_request_headers[key].key = key
|
|
315
|
+
http_request.normalized_request_headers[key].values << val
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
request_cookies.each do |k, v|
|
|
319
|
+
next unless k && v
|
|
320
|
+
next if v.is_a?(Hash)
|
|
321
|
+
|
|
322
|
+
key = Contrast::Utils::StringUtils.force_utf8(k)
|
|
323
|
+
val = Contrast::Utils::StringUtils.force_utf8(v)
|
|
324
|
+
http_request.normalized_cookies[key] ||= Contrast::Api::Dtm::Pair.new
|
|
325
|
+
http_request.normalized_cookies[key].key = key
|
|
326
|
+
http_request.normalized_cookies[key].values << val
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def append_body http_request
|
|
331
|
+
http_request.document_type = Contrast::Utils::StringUtils.force_utf8(document_type)
|
|
332
|
+
|
|
333
|
+
http_request.request_body = if omit_body?
|
|
334
|
+
OMITTED_BODY
|
|
335
|
+
else
|
|
336
|
+
Contrast::Utils::StringUtils.force_utf8(request_body)
|
|
337
|
+
end
|
|
338
|
+
return if file_names.empty?
|
|
339
|
+
|
|
340
|
+
file_names.each do |name, filename|
|
|
341
|
+
pair = Contrast::Api::Dtm::SimplePair.new
|
|
342
|
+
pair.key = Contrast::Utils::StringUtils.force_utf8(name)
|
|
343
|
+
pair.value = Contrast::Utils::StringUtils.force_utf8(filename)
|
|
344
|
+
http_request.multipart_headers << pair
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def omit_body?
|
|
349
|
+
return true if Contrast::Agent::FeatureState.instance.omit_body?
|
|
350
|
+
return false if document_type == :XML
|
|
351
|
+
return false if document_type == :JSON
|
|
352
|
+
|
|
353
|
+
content_type&.include?('multipart/form-data')
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def self.build_receiver
|
|
357
|
+
address = Contrast::Api::Dtm::Address.new
|
|
358
|
+
address.host = 'localhost'
|
|
359
|
+
address.ip = '127.0.0.1'
|
|
360
|
+
begin
|
|
361
|
+
Timeout.timeout(1) do
|
|
362
|
+
address.host = Contrast::Utils::StringUtils.force_utf8(Socket.gethostname)
|
|
363
|
+
address.ip = Contrast::Utils::StringUtils.force_utf8(Resolv.getaddress(address.host))
|
|
364
|
+
end
|
|
365
|
+
rescue Timeout::Error
|
|
366
|
+
logger.warn(nil, "Timeout resolving host or ip in #{ address }")
|
|
367
|
+
rescue StandardError => e
|
|
368
|
+
logger.warn(e, "Error resolving address for #{ address }")
|
|
369
|
+
end
|
|
370
|
+
address
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# Memoized Rack Request Values
|
|
374
|
+
def ip
|
|
375
|
+
@_ip ||= @rack_request.ip
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def scheme
|
|
379
|
+
@_scheme ||= @rack_request.scheme
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def port
|
|
383
|
+
@_port ||= @rack_request.port
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
def uri
|
|
387
|
+
@_uri ||= @rack_request.path
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def url
|
|
391
|
+
@_url ||= @rack_request.url
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def content_type
|
|
395
|
+
@_content_type ||= @rack_request.content_type
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def path
|
|
399
|
+
@_path ||= @rack_request.path
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def request_cookies
|
|
403
|
+
@_request_cookies ||= @rack_request.cookies
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def parameters
|
|
407
|
+
@_parameters ||= with_contrast_scope { normalize_params(@rack_request.params) }
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def base_url
|
|
411
|
+
@_base_url ||= @rack_request.base_url
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# End of Rack Request memoized values
|
|
415
|
+
|
|
416
|
+
def file_names
|
|
417
|
+
@_file_names ||= begin
|
|
418
|
+
names = {}
|
|
419
|
+
parsed_data = Rack::Multipart.parse_multipart(@rack_request.env)
|
|
420
|
+
traverse_parsed_multipart(parsed_data, names)
|
|
421
|
+
rescue StandardError => _e
|
|
422
|
+
logger.warn('Unable to parse multipart request!')
|
|
423
|
+
{}
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def hash_id
|
|
428
|
+
@_hash_id ||= Contrast::Utils::HashDigest.generate_request_hash(self)
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
# we just need to map length to a repeatable value
|
|
432
|
+
# unlike Java, we hash with strings, so we'll use single character
|
|
433
|
+
# strings for our purposes.
|
|
434
|
+
CHARS = %w[a b c d e f g].cs__freeze
|
|
435
|
+
def normalized_length_header chr
|
|
436
|
+
chr = chr.to_s
|
|
437
|
+
tmp = CHARS[Math.log10(chr.length).to_i] if chr
|
|
438
|
+
tmp ||= CHARS[6]
|
|
439
|
+
tmp
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
private
|
|
443
|
+
|
|
444
|
+
HTTP_PREFIX = /^[Hh][Tt][Tt][Pp][_-]/i.cs__freeze
|
|
445
|
+
|
|
446
|
+
def header_pairs env
|
|
447
|
+
selected = env.select do |k, _v|
|
|
448
|
+
k.to_s.start_with?(Contrast::Utils::ObjectShare::HTTP_SCORE)
|
|
449
|
+
end
|
|
450
|
+
selected.map do |k, v|
|
|
451
|
+
name = k.to_s.sub(HTTP_PREFIX, Contrast::Utils::ObjectShare::EMPTY_STRING)
|
|
452
|
+
[name, v]
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def read_body body
|
|
457
|
+
return body if body.is_a?(String)
|
|
458
|
+
|
|
459
|
+
begin
|
|
460
|
+
can_rewind = Contrast::Utils::DuckUtils.quacks_to?(body, :rewind)
|
|
461
|
+
# if we are after a middleware that failed to rewind
|
|
462
|
+
body.rewind if can_rewind
|
|
463
|
+
body.read
|
|
464
|
+
rescue StandardError => e
|
|
465
|
+
logger.error("Error in attempt to read body :: #{ e.message }")
|
|
466
|
+
logger.debug(e.backtrace.join(Contrast::Utils::ObjectShare::NEW_LINE))
|
|
467
|
+
body.to_s
|
|
468
|
+
ensure
|
|
469
|
+
# be a good citizen and rewind
|
|
470
|
+
body.rewind if can_rewind
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
def traverse_parsed_multipart multipart_data, current_names
|
|
475
|
+
return current_names unless multipart_data
|
|
476
|
+
|
|
477
|
+
multipart_data.each_value do |data_value|
|
|
478
|
+
next unless data_value.is_a?(Hash)
|
|
479
|
+
|
|
480
|
+
tempfile = data_value[:tempfile]
|
|
481
|
+
if tempfile.nil?
|
|
482
|
+
traverse_parsed_multipart(data_value, current_names)
|
|
483
|
+
else
|
|
484
|
+
name = data_value[:name].to_s
|
|
485
|
+
file_name = data_value[:filename].to_s
|
|
486
|
+
current_names[name] = file_name
|
|
487
|
+
end
|
|
488
|
+
end
|
|
489
|
+
current_names
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
end
|