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.
Files changed (500) hide show
  1. checksums.yaml +7 -0
  2. data/.clang-format +5 -0
  3. data/.dockerignore +10 -0
  4. data/.gitignore +58 -0
  5. data/.gitmodules +6 -0
  6. data/.rspec +6 -0
  7. data/.simplecov +4 -0
  8. data/Gemfile +7 -0
  9. data/LICENSE.txt +12 -0
  10. data/Rakefile +15 -0
  11. data/exe/contrast_service +29 -0
  12. data/ext/build_funchook.rb +48 -0
  13. data/ext/cs__assess_active_record_named/cs__active_record_named.c +47 -0
  14. data/ext/cs__assess_active_record_named/cs__active_record_named.h +10 -0
  15. data/ext/cs__assess_active_record_named/extconf.rb +2 -0
  16. data/ext/cs__assess_array/cs__assess_array.c +38 -0
  17. data/ext/cs__assess_array/cs__assess_array.h +9 -0
  18. data/ext/cs__assess_array/extconf.rb +2 -0
  19. data/ext/cs__assess_basic_object/cs__assess_basic_object.c +50 -0
  20. data/ext/cs__assess_basic_object/cs__assess_basic_object.h +17 -0
  21. data/ext/cs__assess_basic_object/extconf.rb +2 -0
  22. data/ext/cs__assess_fiber_track/cs__assess_fiber_track.c +86 -0
  23. data/ext/cs__assess_fiber_track/cs__assess_fiber_track.h +34 -0
  24. data/ext/cs__assess_fiber_track/extconf.rb +2 -0
  25. data/ext/cs__assess_hash/cs__assess_hash.c +64 -0
  26. data/ext/cs__assess_hash/cs__assess_hash.h +24 -0
  27. data/ext/cs__assess_hash/extconf.rb +2 -0
  28. data/ext/cs__assess_kernel/cs__assess_kernel.c +36 -0
  29. data/ext/cs__assess_kernel/cs__assess_kernel.h +10 -0
  30. data/ext/cs__assess_kernel/extconf.rb +2 -0
  31. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +47 -0
  32. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +18 -0
  33. data/ext/cs__assess_marshal_module/extconf.rb +2 -0
  34. data/ext/cs__assess_module/cs__assess_module.c +78 -0
  35. data/ext/cs__assess_module/cs__assess_module.h +25 -0
  36. data/ext/cs__assess_module/extconf.rb +2 -0
  37. data/ext/cs__assess_regexp/cs__assess_regexp.c +48 -0
  38. data/ext/cs__assess_regexp/cs__assess_regexp.h +22 -0
  39. data/ext/cs__assess_regexp/extconf.rb +2 -0
  40. data/ext/cs__assess_regexp_track/cs__assess_regexp_track.c +63 -0
  41. data/ext/cs__assess_regexp_track/cs__assess_regexp_track.h +29 -0
  42. data/ext/cs__assess_regexp_track/extconf.rb +2 -0
  43. data/ext/cs__assess_string/cs__assess_string.c +38 -0
  44. data/ext/cs__assess_string/cs__assess_string.h +19 -0
  45. data/ext/cs__assess_string/extconf.rb +2 -0
  46. data/ext/cs__assess_string_interpolation26/cs__assess_string_interpolation26.c +31 -0
  47. data/ext/cs__assess_string_interpolation26/cs__assess_string_interpolation26.h +13 -0
  48. data/ext/cs__assess_string_interpolation26/extconf.rb +2 -0
  49. data/ext/cs__common/cs__common.c +60 -0
  50. data/ext/cs__common/cs__common.h +28 -0
  51. data/ext/cs__common/extconf.rb +20 -0
  52. data/ext/cs__contrast_patch/cs__contrast_patch.c +445 -0
  53. data/ext/cs__contrast_patch/cs__contrast_patch.h +196 -0
  54. data/ext/cs__contrast_patch/extconf.rb +2 -0
  55. data/ext/cs__protect_kernel/cs__protect_kernel.c +37 -0
  56. data/ext/cs__protect_kernel/cs__protect_kernel.h +11 -0
  57. data/ext/cs__protect_kernel/extconf.rb +2 -0
  58. data/ext/cs__scope/cs__scope.c +96 -0
  59. data/ext/cs__scope/cs__scope.h +33 -0
  60. data/ext/cs__scope/extconf.rb +2 -0
  61. data/ext/extconf_common.rb +49 -0
  62. data/funchook/LICENSE +360 -0
  63. data/funchook/Makefile +29 -0
  64. data/funchook/Makefile.in +29 -0
  65. data/funchook/README.md +121 -0
  66. data/funchook/appveyor.yml +42 -0
  67. data/funchook/autogen.sh +3 -0
  68. data/funchook/autom4te.cache/output.0 +4976 -0
  69. data/funchook/autom4te.cache/requests +78 -0
  70. data/funchook/autom4te.cache/traces.0 +364 -0
  71. data/funchook/config.guess +1530 -0
  72. data/funchook/config.log +490 -0
  73. data/funchook/config.status +1016 -0
  74. data/funchook/config.sub +1773 -0
  75. data/funchook/configure +4976 -0
  76. data/funchook/configure.ac +59 -0
  77. data/funchook/distorm/COPYING +26 -0
  78. data/funchook/distorm/MANIFEST +25 -0
  79. data/funchook/distorm/MANIFEST.in +4 -0
  80. data/funchook/distorm/README.md +12 -0
  81. data/funchook/distorm/disOps/disOps.py +795 -0
  82. data/funchook/distorm/disOps/x86db.py +404 -0
  83. data/funchook/distorm/disOps/x86header.py +247 -0
  84. data/funchook/distorm/disOps/x86sets.py +1664 -0
  85. data/funchook/distorm/examples/cs/TestdiStorm/Program.cs +79 -0
  86. data/funchook/distorm/examples/cs/TestdiStorm/Properties/AssemblyInfo.cs +36 -0
  87. data/funchook/distorm/examples/cs/TestdiStorm/TestdiStorm.csproj +69 -0
  88. data/funchook/distorm/examples/cs/distorm-net.sln +26 -0
  89. data/funchook/distorm/examples/cs/distorm-net/CodeInfo.cs +23 -0
  90. data/funchook/distorm/examples/cs/distorm-net/DecodedInst.cs +15 -0
  91. data/funchook/distorm/examples/cs/distorm-net/DecodedResult.cs +14 -0
  92. data/funchook/distorm/examples/cs/distorm-net/DecomposedInst.cs +36 -0
  93. data/funchook/distorm/examples/cs/distorm-net/DecomposedResult.cs +14 -0
  94. data/funchook/distorm/examples/cs/distorm-net/Opcodes.cs +1268 -0
  95. data/funchook/distorm/examples/cs/distorm-net/Opcodes.tt +37 -0
  96. data/funchook/distorm/examples/cs/distorm-net/Operand.cs +25 -0
  97. data/funchook/distorm/examples/cs/distorm-net/Properties/AssemblyInfo.cs +36 -0
  98. data/funchook/distorm/examples/cs/distorm-net/diStorm3.cs +411 -0
  99. data/funchook/distorm/examples/cs/distorm-net/distorm-net.csproj +80 -0
  100. data/funchook/distorm/examples/cs/readme +3 -0
  101. data/funchook/distorm/examples/ddk/README +48 -0
  102. data/funchook/distorm/examples/ddk/distorm.ini +11 -0
  103. data/funchook/distorm/examples/ddk/dummy.c +15 -0
  104. data/funchook/distorm/examples/ddk/main.c +91 -0
  105. data/funchook/distorm/examples/ddk/makefile +1 -0
  106. data/funchook/distorm/examples/ddk/sources +10 -0
  107. data/funchook/distorm/examples/java/Makefile +23 -0
  108. data/funchook/distorm/examples/java/distorm/src/Main.java +43 -0
  109. data/funchook/distorm/examples/java/distorm/src/diStorm3/CodeInfo.java +27 -0
  110. data/funchook/distorm/examples/java/distorm/src/diStorm3/DecodedInst.java +32 -0
  111. data/funchook/distorm/examples/java/distorm/src/diStorm3/DecodedResult.java +11 -0
  112. data/funchook/distorm/examples/java/distorm/src/diStorm3/DecomposedInst.java +89 -0
  113. data/funchook/distorm/examples/java/distorm/src/diStorm3/DecomposedResult.java +11 -0
  114. data/funchook/distorm/examples/java/distorm/src/diStorm3/OpcodeEnum.java +131 -0
  115. data/funchook/distorm/examples/java/distorm/src/diStorm3/Opcodes.java +1123 -0
  116. data/funchook/distorm/examples/java/distorm/src/diStorm3/Operand.java +24 -0
  117. data/funchook/distorm/examples/java/distorm/src/diStorm3/distorm3.java +41 -0
  118. data/funchook/distorm/examples/java/jdistorm.c +405 -0
  119. data/funchook/distorm/examples/java/jdistorm.h +40 -0
  120. data/funchook/distorm/examples/java/jdistorm.sln +20 -0
  121. data/funchook/distorm/examples/java/jdistorm.vcproj +208 -0
  122. data/funchook/distorm/examples/linux/Makefile +15 -0
  123. data/funchook/distorm/examples/linux/main.c +181 -0
  124. data/funchook/distorm/examples/tests/Makefile +15 -0
  125. data/funchook/distorm/examples/tests/main.cpp +42 -0
  126. data/funchook/distorm/examples/tests/main.py +66 -0
  127. data/funchook/distorm/examples/tests/test_distorm3.py +1672 -0
  128. data/funchook/distorm/examples/tests/tests.sln +20 -0
  129. data/funchook/distorm/examples/tests/tests.vcxproj +82 -0
  130. data/funchook/distorm/examples/tests/tests.vcxproj.filters +22 -0
  131. data/funchook/distorm/examples/win32/disasm.sln +25 -0
  132. data/funchook/distorm/examples/win32/disasm.vcxproj +201 -0
  133. data/funchook/distorm/examples/win32/disasm.vcxproj.filters +14 -0
  134. data/funchook/distorm/examples/win32/main.cpp +163 -0
  135. data/funchook/distorm/include/distorm.h +482 -0
  136. data/funchook/distorm/include/mnemonics.h +301 -0
  137. data/funchook/distorm/make/linux/Makefile +28 -0
  138. data/funchook/distorm/make/mac/Makefile +24 -0
  139. data/funchook/distorm/make/win32/cdistorm.vcxproj +239 -0
  140. data/funchook/distorm/make/win32/cdistorm.vcxproj.filters +80 -0
  141. data/funchook/distorm/make/win32/distorm.sln +25 -0
  142. data/funchook/distorm/make/win32/resource.h +14 -0
  143. data/funchook/distorm/make/win32/resource.rc +99 -0
  144. data/funchook/distorm/python/distorm3/__init__.py +957 -0
  145. data/funchook/distorm/python/distorm3/sample.py +51 -0
  146. data/funchook/distorm/setup.cfg +10 -0
  147. data/funchook/distorm/setup.py +266 -0
  148. data/funchook/distorm/src/config.h +169 -0
  149. data/funchook/distorm/src/decoder.c +641 -0
  150. data/funchook/distorm/src/decoder.h +33 -0
  151. data/funchook/distorm/src/distorm.c +413 -0
  152. data/funchook/distorm/src/instructions.c +597 -0
  153. data/funchook/distorm/src/instructions.h +463 -0
  154. data/funchook/distorm/src/insts.c +7939 -0
  155. data/funchook/distorm/src/insts.h +64 -0
  156. data/funchook/distorm/src/mnemonics.c +284 -0
  157. data/funchook/distorm/src/operands.c +1290 -0
  158. data/funchook/distorm/src/operands.h +28 -0
  159. data/funchook/distorm/src/prefix.c +368 -0
  160. data/funchook/distorm/src/prefix.h +64 -0
  161. data/funchook/distorm/src/textdefs.c +172 -0
  162. data/funchook/distorm/src/textdefs.h +57 -0
  163. data/funchook/distorm/src/wstring.c +47 -0
  164. data/funchook/distorm/src/wstring.h +35 -0
  165. data/funchook/distorm/src/x86defs.h +82 -0
  166. data/funchook/include/funchook.h +123 -0
  167. data/funchook/install-sh +527 -0
  168. data/funchook/src/Makefile +70 -0
  169. data/funchook/src/Makefile.in +70 -0
  170. data/funchook/src/__strerror.h +109 -0
  171. data/funchook/src/config.h +101 -0
  172. data/funchook/src/config.h.in +100 -0
  173. data/funchook/src/decoder.o +0 -0
  174. data/funchook/src/distorm.o +0 -0
  175. data/funchook/src/funchook.c +440 -0
  176. data/funchook/src/funchook.o +0 -0
  177. data/funchook/src/funchook_internal.h +155 -0
  178. data/funchook/src/funchook_io.c +182 -0
  179. data/funchook/src/funchook_io.h +64 -0
  180. data/funchook/src/funchook_io.o +0 -0
  181. data/funchook/src/funchook_syscall.S +134 -0
  182. data/funchook/src/funchook_syscall.o +0 -0
  183. data/funchook/src/funchook_unix.c +480 -0
  184. data/funchook/src/funchook_unix.o +0 -0
  185. data/funchook/src/funchook_windows.c +397 -0
  186. data/funchook/src/funchook_x86.c +622 -0
  187. data/funchook/src/funchook_x86.o +0 -0
  188. data/funchook/src/instructions.o +0 -0
  189. data/funchook/src/insts.o +0 -0
  190. data/funchook/src/libfunchook.so +0 -0
  191. data/funchook/src/mnemonics.o +0 -0
  192. data/funchook/src/operands.o +0 -0
  193. data/funchook/src/os_func.c +115 -0
  194. data/funchook/src/os_func.h +75 -0
  195. data/funchook/src/os_func.o +0 -0
  196. data/funchook/src/os_func_unix.c +94 -0
  197. data/funchook/src/os_func_unix.o +0 -0
  198. data/funchook/src/os_func_windows.c +32 -0
  199. data/funchook/src/prefix.o +0 -0
  200. data/funchook/src/printf_base.c +1688 -0
  201. data/funchook/src/printf_base.h +46 -0
  202. data/funchook/src/printf_base.o +0 -0
  203. data/funchook/src/textdefs.o +0 -0
  204. data/funchook/src/wstring.o +0 -0
  205. data/funchook/test/Makefile +43 -0
  206. data/funchook/test/Makefile.in +43 -0
  207. data/funchook/test/funchook_test +0 -0
  208. data/funchook/test/libfunchook_test.c +25 -0
  209. data/funchook/test/libfunchook_test.so +0 -0
  210. data/funchook/test/libfunchook_test2.c +18 -0
  211. data/funchook/test/suffix.list +600 -0
  212. data/funchook/test/test_main.c +430 -0
  213. data/funchook/test/test_main.o +0 -0
  214. data/funchook/test/x86_64_test.S +10 -0
  215. data/funchook/test/x86_64_test.o +0 -0
  216. data/funchook/test/x86_test.S +339 -0
  217. data/funchook/win32/config.h +1 -0
  218. data/funchook/win32/funchook.sln +52 -0
  219. data/funchook/win32/funchook.vcxproj +188 -0
  220. data/funchook/win32/funchook.vcxproj.filters +84 -0
  221. data/funchook/win32/funchook_test.vcxproj +170 -0
  222. data/funchook/win32/funchook_test.vcxproj.filters +22 -0
  223. data/funchook/win32/funchook_test_dll.vcxproj +184 -0
  224. data/funchook/win32/funchook_test_dll.vcxproj.filters +30 -0
  225. data/funchook/win32/funchook_test_exe.def +3 -0
  226. data/lib/contrast-agent.rb +8 -0
  227. data/lib/contrast.rb +57 -0
  228. data/lib/contrast/agent.rb +80 -0
  229. data/lib/contrast/agent/assess.rb +45 -0
  230. data/lib/contrast/agent/assess/adjusted_span.rb +25 -0
  231. data/lib/contrast/agent/assess/class_reverter.rb +82 -0
  232. data/lib/contrast/agent/assess/contrast_event.rb +398 -0
  233. data/lib/contrast/agent/assess/frozen_properties.rb +41 -0
  234. data/lib/contrast/agent/assess/insulator.rb +53 -0
  235. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +78 -0
  236. data/lib/contrast/agent/assess/policy/patcher.rb +85 -0
  237. data/lib/contrast/agent/assess/policy/policy.rb +116 -0
  238. data/lib/contrast/agent/assess/policy/policy_node.rb +289 -0
  239. data/lib/contrast/agent/assess/policy/policy_scanner.rb +44 -0
  240. data/lib/contrast/agent/assess/policy/preshift.rb +94 -0
  241. data/lib/contrast/agent/assess/policy/propagation_method.rb +260 -0
  242. data/lib/contrast/agent/assess/policy/propagation_node.rb +127 -0
  243. data/lib/contrast/agent/assess/policy/propagator.rb +35 -0
  244. data/lib/contrast/agent/assess/policy/propagator/append.rb +54 -0
  245. data/lib/contrast/agent/assess/policy/propagator/base.rb +37 -0
  246. data/lib/contrast/agent/assess/policy/propagator/center.rb +73 -0
  247. data/lib/contrast/agent/assess/policy/propagator/custom.rb +36 -0
  248. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +62 -0
  249. data/lib/contrast/agent/assess/policy/propagator/insert.rb +55 -0
  250. data/lib/contrast/agent/assess/policy/propagator/keep.rb +26 -0
  251. data/lib/contrast/agent/assess/policy/propagator/next.rb +42 -0
  252. data/lib/contrast/agent/assess/policy/propagator/prepend.rb +50 -0
  253. data/lib/contrast/agent/assess/policy/propagator/remove.rb +76 -0
  254. data/lib/contrast/agent/assess/policy/propagator/replace.rb +27 -0
  255. data/lib/contrast/agent/assess/policy/propagator/reverse.rb +38 -0
  256. data/lib/contrast/agent/assess/policy/propagator/select.rb +86 -0
  257. data/lib/contrast/agent/assess/policy/propagator/splat.rb +60 -0
  258. data/lib/contrast/agent/assess/policy/propagator/split.rb +49 -0
  259. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +169 -0
  260. data/lib/contrast/agent/assess/policy/propagator/trim.rb +81 -0
  261. data/lib/contrast/agent/assess/policy/rewriter_patch.rb +79 -0
  262. data/lib/contrast/agent/assess/policy/source_method.rb +209 -0
  263. data/lib/contrast/agent/assess/policy/source_node.rb +62 -0
  264. data/lib/contrast/agent/assess/policy/trigger_method.rb +209 -0
  265. data/lib/contrast/agent/assess/policy/trigger_node.rb +198 -0
  266. data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +77 -0
  267. data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +31 -0
  268. data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +40 -0
  269. data/lib/contrast/agent/assess/properties.rb +392 -0
  270. data/lib/contrast/agent/assess/rule.rb +18 -0
  271. data/lib/contrast/agent/assess/rule/base.rb +72 -0
  272. data/lib/contrast/agent/assess/rule/csrf.rb +66 -0
  273. data/lib/contrast/agent/assess/rule/csrf/csrf_action.rb +28 -0
  274. data/lib/contrast/agent/assess/rule/csrf/csrf_applicator.rb +69 -0
  275. data/lib/contrast/agent/assess/rule/csrf/csrf_watcher.rb +132 -0
  276. data/lib/contrast/agent/assess/rule/provider.rb +21 -0
  277. data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +62 -0
  278. data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +73 -0
  279. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +121 -0
  280. data/lib/contrast/agent/assess/rule/redos.rb +68 -0
  281. data/lib/contrast/agent/assess/rule/response_scanning_rule.rb +47 -0
  282. data/lib/contrast/agent/assess/rule/response_watcher.rb +36 -0
  283. data/lib/contrast/agent/assess/rule/watcher.rb +36 -0
  284. data/lib/contrast/agent/assess/tag.rb +151 -0
  285. data/lib/contrast/agent/at_exit_hook.rb +33 -0
  286. data/lib/contrast/agent/class_reopener.rb +195 -0
  287. data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +26 -0
  288. data/lib/contrast/agent/deadzone/policy/policy.rb +57 -0
  289. data/lib/contrast/agent/disable_reaction.rb +24 -0
  290. data/lib/contrast/agent/exclusion_matcher.rb +190 -0
  291. data/lib/contrast/agent/feature_state.rb +379 -0
  292. data/lib/contrast/agent/inventory/policy/policy.rb +32 -0
  293. data/lib/contrast/agent/inventory/policy/trigger_node.rb +22 -0
  294. data/lib/contrast/agent/logger_manager.rb +116 -0
  295. data/lib/contrast/agent/middleware.rb +352 -0
  296. data/lib/contrast/agent/module_data.rb +16 -0
  297. data/lib/contrast/agent/patching/policy/after_load_patch.rb +37 -0
  298. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +58 -0
  299. data/lib/contrast/agent/patching/policy/method_policy.rb +94 -0
  300. data/lib/contrast/agent/patching/policy/module_policy.rb +116 -0
  301. data/lib/contrast/agent/patching/policy/patch.rb +312 -0
  302. data/lib/contrast/agent/patching/policy/patch_status.rb +192 -0
  303. data/lib/contrast/agent/patching/policy/patcher.rb +310 -0
  304. data/lib/contrast/agent/patching/policy/policy.rb +138 -0
  305. data/lib/contrast/agent/patching/policy/policy_node.rb +80 -0
  306. data/lib/contrast/agent/patching/policy/policy_unpatcher.rb +28 -0
  307. data/lib/contrast/agent/patching/policy/trigger_node.rb +81 -0
  308. data/lib/contrast/agent/protect/policy/policy.rb +37 -0
  309. data/lib/contrast/agent/protect/policy/trigger_node.rb +23 -0
  310. data/lib/contrast/agent/protect/rule.rb +58 -0
  311. data/lib/contrast/agent/protect/rule/base.rb +300 -0
  312. data/lib/contrast/agent/protect/rule/base_service.rb +88 -0
  313. data/lib/contrast/agent/protect/rule/cmd_injection.rb +156 -0
  314. data/lib/contrast/agent/protect/rule/csrf.rb +118 -0
  315. data/lib/contrast/agent/protect/rule/csrf/csrf_evaluator.rb +103 -0
  316. data/lib/contrast/agent/protect/rule/csrf/csrf_token_injector.rb +85 -0
  317. data/lib/contrast/agent/protect/rule/default_scanner.rb +300 -0
  318. data/lib/contrast/agent/protect/rule/deserialization.rb +193 -0
  319. data/lib/contrast/agent/protect/rule/http_method_tampering.rb +80 -0
  320. data/lib/contrast/agent/protect/rule/no_sqli.rb +101 -0
  321. data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +40 -0
  322. data/lib/contrast/agent/protect/rule/path_traversal.rb +143 -0
  323. data/lib/contrast/agent/protect/rule/sqli.rb +101 -0
  324. data/lib/contrast/agent/protect/rule/sqli/default_sql_scanner.rb +16 -0
  325. data/lib/contrast/agent/protect/rule/sqli/mysql_sql_scanner.rb +38 -0
  326. data/lib/contrast/agent/protect/rule/sqli/postgres_sql_scanner.rb +22 -0
  327. data/lib/contrast/agent/protect/rule/sqli/sqlite_sql_scanner.rb +19 -0
  328. data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +20 -0
  329. data/lib/contrast/agent/protect/rule/xss.rb +24 -0
  330. data/lib/contrast/agent/protect/rule/xxe.rb +120 -0
  331. data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +82 -0
  332. data/lib/contrast/agent/railtie.rb +30 -0
  333. data/lib/contrast/agent/reaction_processor.rb +47 -0
  334. data/lib/contrast/agent/request.rb +493 -0
  335. data/lib/contrast/agent/request_context.rb +225 -0
  336. data/lib/contrast/agent/require_state.rb +61 -0
  337. data/lib/contrast/agent/response.rb +215 -0
  338. data/lib/contrast/agent/rewriter.rb +244 -0
  339. data/lib/contrast/agent/scope.rb +28 -0
  340. data/lib/contrast/agent/service_heartbeat.rb +37 -0
  341. data/lib/contrast/agent/settings_state.rb +148 -0
  342. data/lib/contrast/agent/socket_client.rb +125 -0
  343. data/lib/contrast/agent/thread.rb +26 -0
  344. data/lib/contrast/agent/tracepoint_hook.rb +51 -0
  345. data/lib/contrast/agent/version.rb +8 -0
  346. data/lib/contrast/api.rb +17 -0
  347. data/lib/contrast/api/.gitkeep +0 -0
  348. data/lib/contrast/api/connection_status.rb +49 -0
  349. data/lib/contrast/api/socket.rb +43 -0
  350. data/lib/contrast/api/speedracer.rb +206 -0
  351. data/lib/contrast/api/tcp_socket.rb +31 -0
  352. data/lib/contrast/api/unix_socket.rb +25 -0
  353. data/lib/contrast/common_agent_configuration.rb +86 -0
  354. data/lib/contrast/components/agent.rb +85 -0
  355. data/lib/contrast/components/app_context.rb +188 -0
  356. data/lib/contrast/components/assess.rb +67 -0
  357. data/lib/contrast/components/config.rb +135 -0
  358. data/lib/contrast/components/contrast_service.rb +113 -0
  359. data/lib/contrast/components/heap_dump.rb +34 -0
  360. data/lib/contrast/components/interface.rb +178 -0
  361. data/lib/contrast/components/inventory.rb +23 -0
  362. data/lib/contrast/components/logger.rb +92 -0
  363. data/lib/contrast/components/protect.rb +38 -0
  364. data/lib/contrast/components/sampling.rb +41 -0
  365. data/lib/contrast/components/scope.rb +106 -0
  366. data/lib/contrast/components/settings.rb +140 -0
  367. data/lib/contrast/config.rb +33 -0
  368. data/lib/contrast/config/agent_configuration.rb +24 -0
  369. data/lib/contrast/config/application_configuration.rb +27 -0
  370. data/lib/contrast/config/assess_configuration.rb +22 -0
  371. data/lib/contrast/config/assess_rules_configuration.rb +18 -0
  372. data/lib/contrast/config/base_configuration.rb +105 -0
  373. data/lib/contrast/config/default_value.rb +16 -0
  374. data/lib/contrast/config/exception_configuration.rb +21 -0
  375. data/lib/contrast/config/heap_dump_configuration.rb +23 -0
  376. data/lib/contrast/config/inventory_configuration.rb +20 -0
  377. data/lib/contrast/config/logger_configuration.rb +20 -0
  378. data/lib/contrast/config/protect_configuration.rb +20 -0
  379. data/lib/contrast/config/protect_rule_configuration.rb +37 -0
  380. data/lib/contrast/config/protect_rules_configuration.rb +30 -0
  381. data/lib/contrast/config/root_configuration.rb +26 -0
  382. data/lib/contrast/config/ruby_configuration.rb +39 -0
  383. data/lib/contrast/config/sampling_configuration.rb +22 -0
  384. data/lib/contrast/config/server_configuration.rb +23 -0
  385. data/lib/contrast/config/service_configuration.rb +22 -0
  386. data/lib/contrast/configuration.rb +214 -0
  387. data/lib/contrast/core_extensions/assess.rb +51 -0
  388. data/lib/contrast/core_extensions/assess/array.rb +58 -0
  389. data/lib/contrast/core_extensions/assess/assess_extension.rb +145 -0
  390. data/lib/contrast/core_extensions/assess/basic_object.rb +15 -0
  391. data/lib/contrast/core_extensions/assess/erb.rb +42 -0
  392. data/lib/contrast/core_extensions/assess/exec_trigger.rb +48 -0
  393. data/lib/contrast/core_extensions/assess/fiber.rb +125 -0
  394. data/lib/contrast/core_extensions/assess/hash.rb +22 -0
  395. data/lib/contrast/core_extensions/assess/kernel.rb +95 -0
  396. data/lib/contrast/core_extensions/assess/module.rb +14 -0
  397. data/lib/contrast/core_extensions/assess/regexp.rb +206 -0
  398. data/lib/contrast/core_extensions/assess/string.rb +75 -0
  399. data/lib/contrast/core_extensions/assess/tilt_template_trigger.rb +73 -0
  400. data/lib/contrast/core_extensions/delegator.rb +14 -0
  401. data/lib/contrast/core_extensions/eval_trigger.rb +52 -0
  402. data/lib/contrast/core_extensions/inventory.rb +22 -0
  403. data/lib/contrast/core_extensions/inventory/datastores.rb +37 -0
  404. data/lib/contrast/core_extensions/module.rb +42 -0
  405. data/lib/contrast/core_extensions/object.rb +27 -0
  406. data/lib/contrast/core_extensions/protect.rb +20 -0
  407. data/lib/contrast/core_extensions/protect/applies_command_injection_rule.rb +70 -0
  408. data/lib/contrast/core_extensions/protect/applies_deserialization_rule.rb +58 -0
  409. data/lib/contrast/core_extensions/protect/applies_no_sqli_rule.rb +81 -0
  410. data/lib/contrast/core_extensions/protect/applies_path_traversal_rule.rb +119 -0
  411. data/lib/contrast/core_extensions/protect/applies_sqli_rule.rb +63 -0
  412. data/lib/contrast/core_extensions/protect/applies_xxe_rule.rb +141 -0
  413. data/lib/contrast/core_extensions/protect/kernel.rb +30 -0
  414. data/lib/contrast/core_extensions/protect/psych.rb +7 -0
  415. data/lib/contrast/core_extensions/thread.rb +31 -0
  416. data/lib/contrast/internal_exception.rb +8 -0
  417. data/lib/contrast/rails_extensions/assess/action_controller_inheritance.rb +48 -0
  418. data/lib/contrast/rails_extensions/assess/active_record.rb +32 -0
  419. data/lib/contrast/rails_extensions/assess/active_record_named.rb +61 -0
  420. data/lib/contrast/rails_extensions/assess/configuration.rb +26 -0
  421. data/lib/contrast/rails_extensions/buffer.rb +30 -0
  422. data/lib/contrast/rails_extensions/rack.rb +45 -0
  423. data/lib/contrast/security_exception.rb +14 -0
  424. data/lib/contrast/sinatra_extensions/assess/cookie.rb +26 -0
  425. data/lib/contrast/sinatra_extensions/inventory/sinatra_base.rb +59 -0
  426. data/lib/contrast/tasks/service.rb +95 -0
  427. data/lib/contrast/utils/assess/sampling_util.rb +96 -0
  428. data/lib/contrast/utils/assess/tracking_util.rb +39 -0
  429. data/lib/contrast/utils/boolean_util.rb +33 -0
  430. data/lib/contrast/utils/cache.rb +69 -0
  431. data/lib/contrast/utils/class_util.rb +58 -0
  432. data/lib/contrast/utils/comment_range.rb +19 -0
  433. data/lib/contrast/utils/data_store_util.rb +23 -0
  434. data/lib/contrast/utils/duck_utils.rb +58 -0
  435. data/lib/contrast/utils/env_configuration_item.rb +52 -0
  436. data/lib/contrast/utils/environment_util.rb +152 -0
  437. data/lib/contrast/utils/freeze_util.rb +36 -0
  438. data/lib/contrast/utils/gemfile_reader.rb +191 -0
  439. data/lib/contrast/utils/hash_digest.rb +148 -0
  440. data/lib/contrast/utils/heap_dump_util.rb +113 -0
  441. data/lib/contrast/utils/invalid_configuration_util.rb +88 -0
  442. data/lib/contrast/utils/inventory_util.rb +126 -0
  443. data/lib/contrast/utils/io_util.rb +61 -0
  444. data/lib/contrast/utils/object_share.rb +117 -0
  445. data/lib/contrast/utils/operating_environment.rb +38 -0
  446. data/lib/contrast/utils/os.rb +49 -0
  447. data/lib/contrast/utils/path_util.rb +151 -0
  448. data/lib/contrast/utils/performs_logging.rb +152 -0
  449. data/lib/contrast/utils/preflight_util.rb +13 -0
  450. data/lib/contrast/utils/prevent_serialization.rb +52 -0
  451. data/lib/contrast/utils/rack_assess_session_cookie.rb +104 -0
  452. data/lib/contrast/utils/rails_assess_configuration.rb +95 -0
  453. data/lib/contrast/utils/random_util.rb +22 -0
  454. data/lib/contrast/utils/resource_loader.rb +23 -0
  455. data/lib/contrast/utils/ruby_ast_rewriter.rb +74 -0
  456. data/lib/contrast/utils/scope_util.rb +99 -0
  457. data/lib/contrast/utils/service_response_util.rb +116 -0
  458. data/lib/contrast/utils/service_sender_util.rb +98 -0
  459. data/lib/contrast/utils/sha256_builder.rb +69 -0
  460. data/lib/contrast/utils/sinatra_helper.rb +49 -0
  461. data/lib/contrast/utils/stack_trace_utils.rb +209 -0
  462. data/lib/contrast/utils/string_utils.rb +72 -0
  463. data/lib/contrast/utils/tag_util.rb +139 -0
  464. data/lib/contrast/utils/thread_tracker.rb +54 -0
  465. data/lib/contrast/utils/timer.rb +78 -0
  466. data/resources/assess/policy.json +1673 -0
  467. data/resources/csrf/inject.js +44 -0
  468. data/resources/deadzone/policy.json +55 -0
  469. data/resources/factory-bot-spec/spec_helper.rb +30 -0
  470. data/resources/inventory/policy.json +110 -0
  471. data/resources/protect/policy.json +417 -0
  472. data/resources/rubocops/kernel/catch_cop.rb +37 -0
  473. data/resources/rubocops/kernel/require_cop.rb +37 -0
  474. data/resources/rubocops/kernel/require_relative_cop.rb +33 -0
  475. data/resources/rubocops/module/autoload_cop.rb +37 -0
  476. data/resources/rubocops/module/const_defined_cop.rb +37 -0
  477. data/resources/rubocops/module/const_get_cop.rb +37 -0
  478. data/resources/rubocops/module/const_set_cop.rb +37 -0
  479. data/resources/rubocops/module/constants_cop.rb +37 -0
  480. data/resources/rubocops/module/name_cop.rb +37 -0
  481. data/resources/rubocops/object/class_cop.rb +37 -0
  482. data/resources/rubocops/object/freeze_cop.rb +37 -0
  483. data/resources/rubocops/object/frozen_cop.rb +37 -0
  484. data/resources/rubocops/object/is_a_cop.rb +37 -0
  485. data/resources/rubocops/object/method_cop.rb +37 -0
  486. data/resources/rubocops/object/respond_to_cop.rb +37 -0
  487. data/resources/rubocops/object/singleton_class_cop.rb +37 -0
  488. data/resources/rubocops/regexp/spelling_cop.rb +44 -0
  489. data/resources/rubocops/thread/new_cop.rb +39 -0
  490. data/resources/ruby-spec/ancestors_spec.rb +70 -0
  491. data/resources/ruby-spec/modulo_spec.rb +831 -0
  492. data/resources/ruby-spec/parameters_spec.rb +261 -0
  493. data/resources/ruby-spec/ruby_spec_spec_helper.rb +35 -0
  494. data/resources/test_marker.txt +1 -0
  495. data/ruby-agent.gemspec +129 -0
  496. data/service_executables/.gitkeep +0 -0
  497. data/service_executables/VERSION +1 -0
  498. data/service_executables/linux/contrast-service +0 -0
  499. data/service_executables/mac/contrast-service +0 -0
  500. 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