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,118 @@
|
|
|
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 'rack'
|
|
5
|
+
cs__scoped_require 'contrast/utils/object_share'
|
|
6
|
+
cs__scoped_require 'contrast/agent/request'
|
|
7
|
+
cs__scoped_require 'contrast/agent/response'
|
|
8
|
+
cs__scoped_require 'contrast/utils/stack_trace_utils'
|
|
9
|
+
cs__scoped_require 'contrast/utils/timer'
|
|
10
|
+
|
|
11
|
+
module Contrast
|
|
12
|
+
module Agent
|
|
13
|
+
module Protect
|
|
14
|
+
module Rule
|
|
15
|
+
# The Ruby implementation of the Protect Cross-Site Request Forgery
|
|
16
|
+
# rule.
|
|
17
|
+
class Csrf < Contrast::Agent::Protect::Rule::Base
|
|
18
|
+
NAME = 'csrf'
|
|
19
|
+
|
|
20
|
+
def name
|
|
21
|
+
NAME
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def stream_safe?
|
|
25
|
+
false
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def request_evaluator
|
|
29
|
+
@_request_evaluator ||= Contrast::Agent::Protect::Rule::Csrf::CsrfEvaluator.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def token_injector
|
|
33
|
+
@_token_injector ||= Contrast::Agent::Protect::Rule::Csrf::CsrfTokenInjector.new
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
BLOCK_MESSAGE = 'CSRF rule triggered. Request blocked.'
|
|
37
|
+
|
|
38
|
+
def prefilter context
|
|
39
|
+
return unless enabled? && mode != :NO_ACTION
|
|
40
|
+
|
|
41
|
+
request = context.request
|
|
42
|
+
return if request_evaluator.can_ignore_check?(request)
|
|
43
|
+
|
|
44
|
+
parameters = request.parameters
|
|
45
|
+
return unless parameters&.any?
|
|
46
|
+
|
|
47
|
+
tokens = parameters[token_injector.token_name]
|
|
48
|
+
expected_token = token_injector.get_expected_token(context)
|
|
49
|
+
return if valid_token?(expected_token, tokens)
|
|
50
|
+
|
|
51
|
+
# unexpected token is interpreted as an attack with a match
|
|
52
|
+
ia_result = Contrast::Api::Settings::InputAnalysisResult.new
|
|
53
|
+
ia_result.input_type = :BODY
|
|
54
|
+
ia_result.value = build_attack_string(context.request)
|
|
55
|
+
|
|
56
|
+
result = build_attack_with_match(
|
|
57
|
+
context,
|
|
58
|
+
ia_result,
|
|
59
|
+
nil,
|
|
60
|
+
expected_token,
|
|
61
|
+
details: build_details(expected_token, tokens))
|
|
62
|
+
|
|
63
|
+
append_to_activity(context, result)
|
|
64
|
+
|
|
65
|
+
raise Contrast::SecurityException.new(self, BLOCK_MESSAGE) if blocked?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def valid_token? expected, actual_tokens
|
|
69
|
+
actual_tokens&.include?(expected)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def postfilter context
|
|
73
|
+
return unless enabled? && POSTFILTER_MODES.include?(mode)
|
|
74
|
+
|
|
75
|
+
token_injector.do_injection(context)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def build_sample context, evaluation, _url, **kwargs
|
|
79
|
+
sample = build_base_sample(context, evaluation)
|
|
80
|
+
sample.csrf = kwargs[:details]
|
|
81
|
+
sample
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Build a subclass of the RaspRuleSample using the query string and the
|
|
85
|
+
# evaluation
|
|
86
|
+
def build_details expected, actual_tokens
|
|
87
|
+
details = Contrast::Api::Dtm::CsrfDetails.new
|
|
88
|
+
details.name = Contrast::Utils::StringUtils.protobuf_safe_string(token_injector.token_name)
|
|
89
|
+
details.expected = Contrast::Utils::StringUtils.protobuf_safe_string(expected)
|
|
90
|
+
presented = build_presented_token(actual_tokens)
|
|
91
|
+
details.presented = Contrast::Utils::StringUtils.protobuf_safe_string(presented)
|
|
92
|
+
details
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def build_presented_token actual_tokens
|
|
96
|
+
return Contrast::Utils::ObjectShare::EMPTY_STRING unless actual_tokens&.any?
|
|
97
|
+
|
|
98
|
+
actual_tokens.first
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Convert the request parameters into an attack string
|
|
102
|
+
def build_attack_string request
|
|
103
|
+
arr = []
|
|
104
|
+
request.dtm.normalized_request_params.each_with_index do |(name, value), index|
|
|
105
|
+
arr << Contrast::Utils::ObjectShare::AND unless index.zero?
|
|
106
|
+
arr += [name.to_s, Contrast::Utils::ObjectShare::EQUALS, value.values.to_s]
|
|
107
|
+
end
|
|
108
|
+
if arr.empty?
|
|
109
|
+
request.query_string
|
|
110
|
+
else
|
|
111
|
+
arr.join
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
cs__scoped_require 'contrast/utils/object_share'
|
|
5
|
+
cs__scoped_require 'contrast/components/interface'
|
|
6
|
+
cs__scoped_require 'uri'
|
|
7
|
+
|
|
8
|
+
# This class is used by the CSRF rule to determine if the given Request is
|
|
9
|
+
# susceptible to CSRF / if the rule applies to it.
|
|
10
|
+
class Contrast::Agent::Protect::Rule::Csrf::CsrfEvaluator # rubocop:disable Style/ClassAndModuleChildren
|
|
11
|
+
include Contrast::Components::Interface
|
|
12
|
+
access_component :logging
|
|
13
|
+
|
|
14
|
+
def can_ignore_check? request
|
|
15
|
+
if !form_submittable_method?(request)
|
|
16
|
+
logger.debug("Ignoring method #{ request.request_method } for URI #{ request.uri }")
|
|
17
|
+
true
|
|
18
|
+
elsif ajax_request?(request)
|
|
19
|
+
logger.debug("Ignoring Ajax request for URI #{ request.uri }")
|
|
20
|
+
true
|
|
21
|
+
elsif empty_post?(request)
|
|
22
|
+
logger.debug("Ignoring empty POST request for URI #{ request.uri }")
|
|
23
|
+
true
|
|
24
|
+
elsif !form_content_type?(request)
|
|
25
|
+
logger.debug("Ignoring POST request with Content-Type #{ request.content_type } for URI #{ request.uri }")
|
|
26
|
+
true
|
|
27
|
+
elsif request.static_request?
|
|
28
|
+
logger.debug("Ignoring static request for URI #{ request.uri }")
|
|
29
|
+
true
|
|
30
|
+
elsif origin_is_referer?(request)
|
|
31
|
+
logger.debug("Ignoring equivalent origin-referer for URI #{ request.uri }")
|
|
32
|
+
true
|
|
33
|
+
elsif login_page?(request)
|
|
34
|
+
logger.debug("Ignoring possible login page for URI #{ request.uri }")
|
|
35
|
+
true
|
|
36
|
+
else
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
rescue StandardError => e
|
|
40
|
+
logger.warn(e, 'Unable to determine if CSRF can be ignored')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
POST_METHOD = 'POST'
|
|
44
|
+
def form_submittable_method? request
|
|
45
|
+
POST_METHOD == request.request_method
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
REQUESTED_WITH = 'X-REQUESTED-WITH'
|
|
49
|
+
def ajax_request? request
|
|
50
|
+
requested_with = request.header_value(REQUESTED_WITH)
|
|
51
|
+
!requested_with.nil? && !requested_with.empty?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
USER_AGENT = 'USER-AGENT'
|
|
55
|
+
def empty_user_agent? request
|
|
56
|
+
agent = request.header_value(USER_AGENT)
|
|
57
|
+
agent.nil? || agent.empty?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def empty_post? request
|
|
61
|
+
request.request_body_str.empty? && (request.query_string.nil? || request.query_string.empty?)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
FORM_CONTENT_TYPES = %w[text/plain multipart/form-data application/x-www-form-urlencoded].cs__freeze
|
|
65
|
+
|
|
66
|
+
def form_content_type? request
|
|
67
|
+
content_type = request.content_type
|
|
68
|
+
return true if content_type.nil? || content_type.empty?
|
|
69
|
+
|
|
70
|
+
content_type.start_with?(*FORM_CONTENT_TYPES)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
ORIGIN = 'ORIGIN'
|
|
74
|
+
REFERER = 'REFERER'
|
|
75
|
+
def origin_is_referer? request
|
|
76
|
+
origin = request.header_value(ORIGIN)
|
|
77
|
+
referer = request.header_value(REFERER)
|
|
78
|
+
return false unless origin && referer
|
|
79
|
+
return false if origin.empty? || referer.empty?
|
|
80
|
+
|
|
81
|
+
origin = trim_origin(origin)
|
|
82
|
+
referer = URI(referer).host
|
|
83
|
+
origin == referer
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
HTTP = 'http://'
|
|
87
|
+
HTTPS = 'https://'
|
|
88
|
+
def trim_origin origin_header
|
|
89
|
+
origin_header = if origin_header.to_s.start_with?(HTTP, HTTPS)
|
|
90
|
+
URI(origin_header).host
|
|
91
|
+
else
|
|
92
|
+
idx = origin_header.index(Contrast::Utils::ObjectShare::COLON)
|
|
93
|
+
idx.nil? ? origin_header : origin_header[0, idx]
|
|
94
|
+
end
|
|
95
|
+
origin_header
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
LOGIN_MARKERS = %w[login auth j_security verify validate sessions].cs__freeze
|
|
99
|
+
def login_page? request
|
|
100
|
+
url = request.url.downcase
|
|
101
|
+
LOGIN_MARKERS.any? { |marker| url.index(marker) }
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
cs__scoped_require 'contrast/utils/object_share'
|
|
5
|
+
cs__scoped_require 'contrast/utils/random_util'
|
|
6
|
+
|
|
7
|
+
# This class is used by the CSRF rule to inject the Contrast CSRF token into
|
|
8
|
+
# the Response's body, allowing for the detection of CSRF attacks.
|
|
9
|
+
class Contrast::Agent::Protect::Rule::Csrf::CsrfTokenInjector # rubocop:disable Style/ClassAndModuleChildren
|
|
10
|
+
TKN_NAME = 'cs_csrf_tkn'
|
|
11
|
+
def token_name
|
|
12
|
+
TKN_NAME
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
SESSION_TOKEN_PREFIX = '__CONTRAST__'
|
|
16
|
+
def token_key
|
|
17
|
+
@_token_key ||= SESSION_TOKEN_PREFIX + token_name
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def javascript
|
|
21
|
+
@_javascript ||= build_javascript
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
SCRIPT_LOC = File.join('csrf', 'inject.js').cs__freeze
|
|
25
|
+
NAME_MARKER = '!TOKEN_NAME!'
|
|
26
|
+
VALUE_MARKER = '!TOKEN_VALUE!'
|
|
27
|
+
def build_javascript
|
|
28
|
+
script = Contrast::Utils::ResourceLoader.load(SCRIPT_LOC)
|
|
29
|
+
script&.sub!(NAME_MARKER, TKN_NAME)
|
|
30
|
+
script
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
CSRF_TOKEN_LENGTH = 8
|
|
34
|
+
def get_expected_token context
|
|
35
|
+
cookies = context.request.request_cookies
|
|
36
|
+
token = cookies[token_key]
|
|
37
|
+
return token if token
|
|
38
|
+
return unless context.response
|
|
39
|
+
|
|
40
|
+
build_token(context)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def build_token context
|
|
44
|
+
token = Contrast::Utils::RandomUtil.secure_random_string(CSRF_TOKEN_LENGTH)
|
|
45
|
+
context&.response&.set_header(token_key, token)
|
|
46
|
+
token
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
CONTENT_TYPE = 'CONTENT-TYPE'
|
|
50
|
+
|
|
51
|
+
def wedge_token? response
|
|
52
|
+
return true unless response
|
|
53
|
+
|
|
54
|
+
content_type = response.header(CONTENT_TYPE)
|
|
55
|
+
return true unless content_type
|
|
56
|
+
|
|
57
|
+
content_type = content_type.to_s
|
|
58
|
+
!content_type.start_with?(*DATA_CONTENT_TYPES)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
END_BODY_TAG = %r{</body>}i.cs__freeze
|
|
62
|
+
def do_injection context
|
|
63
|
+
return unless wedge_token?(context&.response)
|
|
64
|
+
|
|
65
|
+
body_string = context&.response&.body
|
|
66
|
+
return unless body_string
|
|
67
|
+
|
|
68
|
+
index = body_string.index(END_BODY_TAG)
|
|
69
|
+
return unless index
|
|
70
|
+
|
|
71
|
+
injection = get_expected_token(context)
|
|
72
|
+
return unless injection
|
|
73
|
+
|
|
74
|
+
injection = javascript.sub(VALUE_MARKER, injection)
|
|
75
|
+
body_string.insert(index, injection)
|
|
76
|
+
context&.response&.update_body(body_string)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
DATA_CONTENT_TYPES = [
|
|
80
|
+
'image/', 'audio/', 'video/',
|
|
81
|
+
'application/json', 'application/xml',
|
|
82
|
+
'application/octet', 'application/force',
|
|
83
|
+
'text/json', 'text/xml'
|
|
84
|
+
].cs__freeze
|
|
85
|
+
end
|
|
@@ -0,0 +1,300 @@
|
|
|
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
|
+
# The base class used to determine if a user input crosses a token boundary or
|
|
5
|
+
# state, indicating a successful attack using SQL or NoSQL Injection.
|
|
6
|
+
#
|
|
7
|
+
# @deprecated RUBY-356: this class and those that extend it are being phased out
|
|
8
|
+
# in favor of the more performant code in the Service.
|
|
9
|
+
class Contrast::Agent::Protect::Rule::DefaultScanner # rubocop:disable Style/ClassAndModuleChildren
|
|
10
|
+
# Potential states
|
|
11
|
+
# :STATE_INSIDE_TOKEN
|
|
12
|
+
# :STATE_INSIDE_NUMBER
|
|
13
|
+
# :STATE_EXPECTING_TOKEN
|
|
14
|
+
# :STATE_INSIDE_DOUBLEQUOTE
|
|
15
|
+
# :STATE_INSIDE_SINGLEQUOTE
|
|
16
|
+
# :STATE_INSIDE_STRING_ESCAPE_BLOCK
|
|
17
|
+
# :STATE_INSIDE_LINE_COMMENT # inside a comment that will continue to the end of the line
|
|
18
|
+
# :STATE_INSIDE_BLOCK_COMMENT # inside a commend that will end with a closing tag
|
|
19
|
+
# :STATE_SKIP_NEXT_CHARACTER
|
|
20
|
+
|
|
21
|
+
def crosses_boundary query, index, input
|
|
22
|
+
last_boundary = 0
|
|
23
|
+
token_boundaries(query).each do |boundary|
|
|
24
|
+
if boundary > index
|
|
25
|
+
return last_boundary, boundary if boundary < index + input.length
|
|
26
|
+
|
|
27
|
+
break
|
|
28
|
+
end
|
|
29
|
+
last_boundary = boundary
|
|
30
|
+
end
|
|
31
|
+
nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def token_boundaries query
|
|
35
|
+
@_token_boundaries ||= scan_token_boundaries(query)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
CANARY = "select * from tbl where bar='bar';#' and pwd='sec3r3t'; # CANARY"
|
|
39
|
+
def scan_token_boundaries query
|
|
40
|
+
boundaries = []
|
|
41
|
+
return boundaries unless query && !query.empty?
|
|
42
|
+
|
|
43
|
+
state = :STATE_EXPECTING_TOKEN
|
|
44
|
+
index = 0
|
|
45
|
+
|
|
46
|
+
while index < query.length
|
|
47
|
+
char = query[index]
|
|
48
|
+
|
|
49
|
+
previous_state = state
|
|
50
|
+
|
|
51
|
+
state = process_state(boundaries, state, char, index, query)
|
|
52
|
+
|
|
53
|
+
case state
|
|
54
|
+
when :STATE_SKIP_NEXT_CHARACTER
|
|
55
|
+
index += 1
|
|
56
|
+
state = previous_state
|
|
57
|
+
when :STATE_INSIDE_STRING_ESCAPE_BLOCK
|
|
58
|
+
index = find_escape_sequence_boundary(query, index + 1)
|
|
59
|
+
state = previous_state
|
|
60
|
+
when :STATE_INSIDE_BLOCK_COMMENT
|
|
61
|
+
index = find_block_comment_boundary(query, index + 2)
|
|
62
|
+
index += 1
|
|
63
|
+
state = previous_state
|
|
64
|
+
boundaries << index
|
|
65
|
+
when :STATE_INSIDE_LINE_COMMENT
|
|
66
|
+
index = find_new_line_boundary(query, index + 1)
|
|
67
|
+
state = previous_state
|
|
68
|
+
boundaries << index
|
|
69
|
+
end
|
|
70
|
+
index += 1
|
|
71
|
+
end
|
|
72
|
+
boundaries
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def process_state boundaries, current_state, char, index, query
|
|
76
|
+
case current_state
|
|
77
|
+
when :STATE_EXPECTING_TOKEN
|
|
78
|
+
process_expecting_token(boundaries, char, index, query)
|
|
79
|
+
when :STATE_INSIDE_NUMBER
|
|
80
|
+
process_number(boundaries, char, index, query)
|
|
81
|
+
when :STATE_INSIDE_TOKEN
|
|
82
|
+
process_inside_token(boundaries, char, index, query)
|
|
83
|
+
when :STATE_INSIDE_DOUBLEQUOTE
|
|
84
|
+
process_double_quote(boundaries, char, index, query)
|
|
85
|
+
when :STATE_INSIDE_SINGLEQUOTE
|
|
86
|
+
process_single_quote(boundaries, char, index, query)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def process_expecting_token boundaries, char, index, query
|
|
91
|
+
if char == Contrast::Utils::ObjectShare::SINGLE_QUOTE
|
|
92
|
+
boundaries << index
|
|
93
|
+
:STATE_INSIDE_SINGLEQUOTE
|
|
94
|
+
elsif char == Contrast::Utils::ObjectShare::DOUBLE_QUOTE
|
|
95
|
+
boundaries << index
|
|
96
|
+
:STATE_INSIDE_DOUBLEQUOTE
|
|
97
|
+
elsif char.match?(Contrast::Utils::ObjectShare::DIGIT_REGEXP)
|
|
98
|
+
boundaries << index
|
|
99
|
+
:STATE_INSIDE_NUMBER
|
|
100
|
+
elsif start_line_comment?(char, index, query)
|
|
101
|
+
boundaries << index
|
|
102
|
+
:STATE_INSIDE_LINE_COMMENT
|
|
103
|
+
elsif start_block_comment?(char, index, query)
|
|
104
|
+
boundaries << index
|
|
105
|
+
:STATE_INSIDE_BLOCK_COMMENT
|
|
106
|
+
elsif char.match?(Contrast::Utils::ObjectShare::NOT_WHITE_SPACE_REGEXP)
|
|
107
|
+
boundaries << index
|
|
108
|
+
:STATE_INSIDE_TOKEN
|
|
109
|
+
else
|
|
110
|
+
:STATE_EXPECTING_TOKEN
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def process_inside_token boundaries, char, index, query
|
|
115
|
+
if char == Contrast::Utils::ObjectShare::SINGLE_QUOTE
|
|
116
|
+
boundaries << index
|
|
117
|
+
:STATE_INSIDE_SINGLEQUOTE
|
|
118
|
+
elsif char == Contrast::Utils::ObjectShare::DOUBLE_QUOTE
|
|
119
|
+
boundaries << index
|
|
120
|
+
:STATE_INSIDE_DOUBLEQUOTE
|
|
121
|
+
elsif start_line_comment?(char, index, query)
|
|
122
|
+
boundaries << index
|
|
123
|
+
:STATE_INSIDE_LINE_COMMENT
|
|
124
|
+
elsif start_block_comment?(char, index, query)
|
|
125
|
+
boundaries << index
|
|
126
|
+
:STATE_INSIDE_BLOCK_COMMENT
|
|
127
|
+
elsif operator?(char)
|
|
128
|
+
boundaries << index
|
|
129
|
+
:STATE_EXPECTING_TOKEN
|
|
130
|
+
elsif char.match?(Contrast::Utils::ObjectShare::WHITE_SPACE_REGEXP)
|
|
131
|
+
boundaries << index
|
|
132
|
+
:STATE_EXPECTING_TOKEN
|
|
133
|
+
else
|
|
134
|
+
:STATE_INSIDE_TOKEN
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def process_number boundaries, char, index, _query
|
|
139
|
+
if char.match?(Contrast::Utils::ObjectShare::DIGIT_REGEXP) || char == Contrast::Utils::ObjectShare::PERIOD
|
|
140
|
+
:STATE_INSIDE_NUMBER
|
|
141
|
+
else
|
|
142
|
+
boundaries << index
|
|
143
|
+
:STATE_EXPECTING_TOKEN
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def process_double_quote boundaries, char, index, query
|
|
148
|
+
if escape_char?(char)
|
|
149
|
+
:STATE_SKIP_NEXT_CHARACTER
|
|
150
|
+
elsif escape_sequence_start?(char)
|
|
151
|
+
:STATE_INSIDE_STRING_ESCAPE_BLOCK
|
|
152
|
+
elsif char == Contrast::Utils::ObjectShare::DOUBLE_QUOTE
|
|
153
|
+
if double_quote_escape_in_double_quote? && double_quote?(query, index + 1)
|
|
154
|
+
:STATE_SKIP_NEXT_CHARACTER
|
|
155
|
+
else
|
|
156
|
+
boundaries << index
|
|
157
|
+
:STATE_EXPECTING_TOKEN
|
|
158
|
+
end
|
|
159
|
+
else
|
|
160
|
+
:STATE_INSIDE_DOUBLEQUOTE
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def process_single_quote boundaries, char, index, query
|
|
165
|
+
if escape_char?(char)
|
|
166
|
+
:STATE_SKIP_NEXT_CHARACTER
|
|
167
|
+
elsif escape_sequence_start?(char)
|
|
168
|
+
:STATE_INSIDE_STRING_ESCAPE_BLOCK
|
|
169
|
+
elsif char == Contrast::Utils::ObjectShare::SINGLE_QUOTE
|
|
170
|
+
if singe_quote_escape_in_singe_quote? && single_quote?(query, index + 1)
|
|
171
|
+
:STATE_SKIP_NEXT_CHARACTER
|
|
172
|
+
else
|
|
173
|
+
boundaries << index
|
|
174
|
+
:STATE_EXPECTING_TOKEN
|
|
175
|
+
end
|
|
176
|
+
else
|
|
177
|
+
:STATE_INSIDE_SINGLEQUOTE
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def double_quote? query, index
|
|
182
|
+
return false unless index >= 0 && index < query.length
|
|
183
|
+
|
|
184
|
+
query[index] == Contrast::Utils::ObjectShare::DOUBLE_QUOTE
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def single_quote? query, index
|
|
188
|
+
return false unless index >= 0 && index < query.length
|
|
189
|
+
|
|
190
|
+
query[index] == Contrast::Utils::ObjectShare::SINGLE_QUOTE
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def find_escape_sequence_boundary query, index
|
|
194
|
+
idx = index
|
|
195
|
+
while idx < query.length
|
|
196
|
+
char = query[idx]
|
|
197
|
+
break if escape_sequence_end?(char)
|
|
198
|
+
|
|
199
|
+
idx += 1
|
|
200
|
+
end
|
|
201
|
+
idx
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def find_block_comment_boundary query, index
|
|
205
|
+
idx = index
|
|
206
|
+
while idx < query.length
|
|
207
|
+
char = query[idx]
|
|
208
|
+
break if end_block_comment?(char, idx, query)
|
|
209
|
+
|
|
210
|
+
idx += 1
|
|
211
|
+
end
|
|
212
|
+
idx
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def find_new_line_boundary query, index
|
|
216
|
+
idx = index
|
|
217
|
+
while idx < query.length
|
|
218
|
+
char = query[idx]
|
|
219
|
+
break if char == Contrast::Utils::ObjectShare::NEW_LINE
|
|
220
|
+
break if char == Contrast::Utils::ObjectShare::RETURN
|
|
221
|
+
|
|
222
|
+
idx += 1
|
|
223
|
+
end
|
|
224
|
+
idx
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
OPERATOR_PATTERN = %r{[+=*^/%><!-]}.cs__freeze
|
|
228
|
+
def operator? char
|
|
229
|
+
char.match?(OPERATOR_PATTERN)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# @note: Any class extending this module should override these methods as needed
|
|
233
|
+
# Are the current and subsequent characters both '-' ?
|
|
234
|
+
def start_line_comment? char, index, query
|
|
235
|
+
return false unless char == Contrast::Utils::ObjectShare::DASH
|
|
236
|
+
return false unless (query.length - 2) >= index
|
|
237
|
+
|
|
238
|
+
query[index + 1] == Contrast::Utils::ObjectShare::DASH
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Is the current character / sequence of characters the start of a block comment
|
|
242
|
+
# We assume '/*' starts the comment by default
|
|
243
|
+
def start_block_comment? char, index, query
|
|
244
|
+
return false unless char == Contrast::Utils::ObjectShare::SLASH
|
|
245
|
+
return false unless (query.length - 2) >= index
|
|
246
|
+
|
|
247
|
+
query[index + 1] == Contrast::Utils::ObjectShare::ASTERISK
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Is the current character / sequence of characters the end of a block comment
|
|
251
|
+
# We assume '*/' ends the comment by default
|
|
252
|
+
def end_block_comment? char, index, query
|
|
253
|
+
return false unless char == Contrast::Utils::ObjectShare::ASTERISK
|
|
254
|
+
return false unless (query.length - 2) >= index
|
|
255
|
+
|
|
256
|
+
query[index + 1] == Contrast::Utils::ObjectShare::SLASH
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Indicates if '""' inside of double quotes is the equivalent of '\"'
|
|
260
|
+
# We assume no by default
|
|
261
|
+
def double_quote_escape_in_double_quote?
|
|
262
|
+
false
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Indicates if "''" inside of single quotes is the equivalent of "\'"
|
|
266
|
+
# We assume yes by default
|
|
267
|
+
def singe_quote_escape_in_singe_quote?
|
|
268
|
+
true
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Does this language let the user redefine the escape character
|
|
272
|
+
# We assume no by default
|
|
273
|
+
def redefines_escape_char?
|
|
274
|
+
false
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Is the character provided an escape character?
|
|
278
|
+
# By default, we'll assume
|
|
279
|
+
def escape_char? char
|
|
280
|
+
char == Contrast::Utils::ObjectShare::BACK_SLASH
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Does this language support string escape sequences?
|
|
284
|
+
# We assume no by default
|
|
285
|
+
def support_string_escape_sequence?
|
|
286
|
+
false
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Is this the start of a string escape sequence?
|
|
290
|
+
# Since escape sequences aren't supported, the answer is always false
|
|
291
|
+
def escape_sequence_start? _char
|
|
292
|
+
false
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Is this the end of a string escape sequence?
|
|
296
|
+
# Since escape sequences aren't supported, the answer is always false
|
|
297
|
+
def escape_sequence_end? _char
|
|
298
|
+
false
|
|
299
|
+
end
|
|
300
|
+
end
|