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,37 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Security
7
+ module Object
8
+ # This cop checks for the use of `Object#is_a?`.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # Object.is_a?
14
+ #
15
+ # # good
16
+ # Object.cs__is_a?
17
+ class IsA < Cop
18
+ MSG = 'The use of `Object#is_a?` is a compatibility risk.'
19
+
20
+ def eligible_node? node
21
+ node.method?(:is_a?)
22
+ end
23
+
24
+ def on_send node
25
+ return false unless eligible_node?(node)
26
+
27
+ add_offense(node, location: :selector)
28
+ end
29
+
30
+ def autocorrect node
31
+ ->(corrector) { corrector.replace(node.loc.selector, 'cs__is_a?') }
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Security
7
+ module Object
8
+ # This cop checks for the use of `Object#respond_to?`.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # Object.respond_to?
14
+ #
15
+ # # good
16
+ # Object.cs__respond_to?
17
+ class RespondTo < Cop
18
+ MSG = 'The use of `Object#respond_to?` is a compatibility risk.'
19
+
20
+ def eligible_node? node
21
+ node.method?(:respond_to?)
22
+ end
23
+
24
+ def on_send node
25
+ return false unless eligible_node?(node)
26
+
27
+ add_offense(node, location: :selector)
28
+ end
29
+
30
+ def autocorrect node
31
+ ->(corrector) { corrector.replace(node.loc.selector, 'cs__respond_to?') }
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Security
7
+ module Object
8
+ # This cop checks for the use of `Object#method`.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # Object.method
14
+ #
15
+ # # good
16
+ # Object.cs__method
17
+ class Method < Cop
18
+ MSG = 'The use of `Object#method` is a compatibility risk.'
19
+
20
+ def eligible_node? node
21
+ node.method?(:method)
22
+ end
23
+
24
+ def on_send node
25
+ return false unless eligible_node?(node)
26
+
27
+ add_offense(node, location: :selector)
28
+ end
29
+
30
+ def autocorrect node
31
+ ->(corrector) { corrector.replace(node.loc.selector, 'cs__method') }
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Security
7
+ module Object
8
+ # This cop checks for the use of `Object#singleton_class`.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # Object.singleton_class
14
+ #
15
+ # # good
16
+ # Object.cs__singleton_class
17
+ class SingletonClass < Cop
18
+ MSG = 'The use of `Object#singleton_class` is a compatibility risk.'
19
+
20
+ def eligible_node? node
21
+ node.method?(:singleton_class)
22
+ end
23
+
24
+ def on_send node
25
+ return false unless eligible_node?(node)
26
+
27
+ add_offense(node, location: :selector)
28
+ end
29
+
30
+ def autocorrect node
31
+ ->(corrector) { corrector.replace(node.loc.selector, 'cs__singleton_class') }
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,44 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Security
7
+ module Regexp
8
+ # This cop checks for the use of `Object#singleton_class`.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # Object.singleton_class
14
+ #
15
+ # # good
16
+ # Object.cs__singleton_class
17
+ class Spelling < Cop
18
+ include RangeHelp
19
+ MSG = 'The use of the string "regex" without a following "p" is inconsistent'
20
+
21
+ def eligible_line? line
22
+ line =~ /(regex)[^p]/im
23
+ end
24
+
25
+ def on_send node
26
+ add_offense(node, location: :selector) if eligible_line?(node.source)
27
+
28
+ processed_source.ast_with_comments.each do |_node, comment_lines|
29
+ comment_lines.each do |comment_node|
30
+ next unless eligible_line?(comment_node.text)
31
+
32
+ add_offense(comment_node, location: comment_node.location.expression)
33
+ end
34
+ end
35
+ end
36
+
37
+ def autocorrect node
38
+ ->(corrector) { corrector.replace(node.location.expression, node.source.gsub(/regex([^p])/, 'regexp\1')) }
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,39 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Security
7
+ module Thread
8
+ # This cop checks for the use of `Thread#new`.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # Thread.new
14
+ #
15
+ # # good
16
+ # Contrast::Agent::Thread.new
17
+ class New < Cop
18
+ MSG = '`Thread#new` will not run its contents in Contrast scope. Use Contrast::Agent::Thread.'
19
+
20
+ def_node_matcher :thread_new, <<~PATTERN
21
+ (send (const {nil? cbase} :Thread) ${:new} ...)
22
+ PATTERN
23
+
24
+ def on_send node
25
+ thread_new(node) do |method|
26
+ add_offense(node.receiver,
27
+ location: :expression,
28
+ message: format(MSG, method: method))
29
+ end
30
+ end
31
+
32
+ def autocorrect node
33
+ ->(corrector) { corrector.replace(node.loc.expression, 'Contrast::Agent::Thread') }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,70 @@
1
+ require_relative '../../spec_helper'
2
+ require_relative 'fixtures/classes'
3
+
4
+ describe "Module#ancestors" do
5
+ it "returns a list of modules included in self (including self)" do
6
+ BasicObject.ancestors.should == [BasicObject]
7
+ ModuleSpecs.ancestors.should == [ModuleSpecs]
8
+ ModuleSpecs::Basic.ancestors.should == [ModuleSpecs::Basic]
9
+ ModuleSpecs::Super.ancestors.should == [ModuleSpecs::Super, ModuleSpecs::Basic]
10
+ ModuleSpecs.without_test_modules(ModuleSpecs::Parent.ancestors).should ==
11
+ [ModuleSpecs::Parent, Object, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject] # REASON: ADDING JSON CHANGES THIS
12
+ ModuleSpecs.without_test_modules(ModuleSpecs::Child.ancestors).should ==
13
+ [ModuleSpecs::Child, ModuleSpecs::Super, ModuleSpecs::Basic, ModuleSpecs::Parent, Object, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject] # REASON: ADDING JSON CHANGES THIS
14
+ end
15
+
16
+ it "returns only modules and classes" do
17
+ class << ModuleSpecs::Child; self; end.ancestors.should include(ModuleSpecs::Internal, Class, Module, Object, Kernel)
18
+ end
19
+
20
+ it "has 1 entry per module or class" do
21
+ ModuleSpecs::Parent.ancestors.should == ModuleSpecs::Parent.ancestors.uniq
22
+ end
23
+
24
+ describe "when called on a singleton class" do
25
+ it "includes the singleton classes of ancestors" do
26
+ parent = Class.new
27
+ child = Class.new(parent)
28
+ schild = child.singleton_class
29
+
30
+ schild.ancestors.should include(schild,
31
+ parent.singleton_class,
32
+ Object.singleton_class,
33
+ BasicObject.singleton_class,
34
+ Class,
35
+ Module,
36
+ Object,
37
+ Kernel,
38
+ BasicObject)
39
+
40
+ end
41
+
42
+ describe 'for a standalone module' do
43
+ it 'does not include Class' do
44
+ s_mod = ModuleSpecs.singleton_class
45
+ s_mod.ancestors.should_not include(Class)
46
+ end
47
+
48
+ it 'does not include other singleton classes' do
49
+ s_standalone_mod = ModuleSpecs.singleton_class
50
+ s_module = Module.singleton_class
51
+ s_object = Object.singleton_class
52
+ s_basic_object = BasicObject.singleton_class
53
+
54
+ s_standalone_mod.ancestors.should_not include(s_module, s_object, s_basic_object)
55
+ end
56
+
57
+ it 'includes its own singleton class' do
58
+ s_mod = ModuleSpecs.singleton_class
59
+
60
+ s_mod.ancestors.should include(s_mod)
61
+ end
62
+
63
+ it 'includes standard chain' do
64
+ s_mod = ModuleSpecs.singleton_class
65
+
66
+ s_mod.ancestors.should include(Module, Object, Kernel, BasicObject)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,831 @@
1
+ require_relative '../../spec_helper'
2
+ require_relative '../kernel/shared/sprintf'
3
+ require_relative '../kernel/shared/sprintf_encoding'
4
+ require_relative 'fixtures/classes'
5
+ require_relative '../../shared/hash/key_error'
6
+
7
+ describe "String#%" do
8
+ it_behaves_like :kernel_sprintf, -> format, *args {
9
+ format % args
10
+ }
11
+
12
+ it_behaves_like :kernel_sprintf_encoding, -> format, *args {
13
+ format % args
14
+ }
15
+ end
16
+
17
+ # TODO: these specs are mostly redundant with kernel/shared/sprintf.rb specs.
18
+ # These specs should be moved there and deduplicated.
19
+ describe "String#%" do
20
+ context "when key is missing from passed-in hash" do
21
+ it_behaves_like :key_error, -> obj, key { "%{#{key}}" % obj }, { a: 5 }
22
+ end
23
+
24
+ it "formats multiple expressions" do
25
+ ("%b %x %d %s" % [10, 10, 10, 10]).should == "1010 a 10 10"
26
+ end
27
+
28
+ it "formats expressions mid string" do
29
+ ("hello %s!" % "world").should == "hello world!"
30
+ end
31
+
32
+ it "formats %% into %" do
33
+ ("%d%% %s" % [10, "of chickens!"]).should == "10% of chickens!"
34
+ end
35
+
36
+ describe "output's encoding" do
37
+ it "is the same as the format string if passed value is encoding-compatible" do
38
+ [Encoding::BINARY, Encoding::US_ASCII, Encoding::UTF_8, Encoding::SHIFT_JIS].each do |encoding|
39
+ ("hello %s!".encode(encoding) % "world").encoding.should == encoding
40
+ end
41
+ end
42
+
43
+ it "negotiates a compatible encoding if necessary" do
44
+ ("hello %s" % 195.chr).encoding.should == Encoding::BINARY
45
+ ("hello %s".encode("shift_jis") % "wörld").encoding.should == Encoding::UTF_8
46
+ end
47
+
48
+ it "raises if a compatible encoding can't be found" do
49
+ -> { "hello %s".encode("utf-8") % "world".encode("UTF-16LE") }.should raise_error(Encoding::CompatibilityError)
50
+ end
51
+ end
52
+
53
+ ruby_version_is ""..."2.5" do
54
+ it "formats single % character at the end as literal %" do
55
+ ("%" % []).should == "%"
56
+ ("foo%" % []).should == "foo%"
57
+ end
58
+ end
59
+
60
+ ruby_version_is "2.5" do
61
+ it "raises an error if single % appears at the end" do
62
+ -> { ("%" % []) }.should raise_error(ArgumentError)
63
+ -> { ("foo%" % [])}.should raise_error(ArgumentError)
64
+ end
65
+ end
66
+
67
+ it "formats single % character before a newline as literal %" do
68
+ ("%\n" % []).should == "%\n"
69
+ ("foo%\n" % []).should == "foo%\n"
70
+ ("%\n.3f" % 1.2).should == "%\n.3f"
71
+ end
72
+
73
+ it "formats single % character before a NUL as literal %" do
74
+ ("%\0" % []).should == "%\0"
75
+ ("foo%\0" % []).should == "foo%\0"
76
+ ("%\0.3f" % 1.2).should == "%\0.3f"
77
+ end
78
+
79
+ it "raises an error if single % appears anywhere else" do
80
+ -> { (" % " % []) }.should raise_error(ArgumentError)
81
+ -> { ("foo%quux" % []) }.should raise_error(ArgumentError)
82
+ end
83
+
84
+
85
+ it "raises an error if NULL or \\n appear anywhere else in the format string" do
86
+ begin
87
+ old_debug, $DEBUG = $DEBUG, false
88
+
89
+ -> { "%.\n3f" % 1.2 }.should raise_error(ArgumentError)
90
+ -> { "%.3\nf" % 1.2 }.should raise_error(ArgumentError)
91
+ -> { "%.\03f" % 1.2 }.should raise_error(ArgumentError)
92
+ -> { "%.3\0f" % 1.2 }.should raise_error(ArgumentError)
93
+ ensure
94
+ $DEBUG = old_debug
95
+ end
96
+ end
97
+
98
+ it "ignores unused arguments when $DEBUG is false" do
99
+ begin
100
+ old_debug = $DEBUG
101
+ $DEBUG = false
102
+
103
+ ("" % [1, 2, 3]).should == ""
104
+ ("%s" % [1, 2, 3]).should == "1"
105
+ ensure
106
+ $DEBUG = old_debug
107
+ end
108
+ end
109
+
110
+ # RUBY-569 - this segfaults
111
+ # it "raises an ArgumentError for unused arguments when $DEBUG is true" do
112
+ # begin
113
+ # old_debug = $DEBUG
114
+ # $DEBUG = true
115
+ # s = $stderr
116
+ # $stderr = IOStub.new
117
+ #
118
+ # -> { "" % [1, 2, 3] }.should raise_error(ArgumentError)
119
+ # -> { "%s" % [1, 2, 3] }.should raise_error(ArgumentError)
120
+ # ensure
121
+ # $DEBUG = old_debug
122
+ # $stderr = s
123
+ # end
124
+ # end
125
+
126
+ it "always allows unused arguments when positional argument style is used" do
127
+ begin
128
+ old_debug = $DEBUG
129
+ $DEBUG = false
130
+
131
+ ("%2$s" % [1, 2, 3]).should == "2"
132
+ $DEBUG = true
133
+ ("%2$s" % [1, 2, 3]).should == "2"
134
+ ensure
135
+ $DEBUG = old_debug
136
+ end
137
+ end
138
+
139
+ it "replaces trailing absolute argument specifier without type with percent sign" do
140
+ ("hello %1$" % "foo").should == "hello %"
141
+ end
142
+
143
+ it "raises an ArgumentError when given invalid argument specifiers" do
144
+ -> { "%1" % [] }.should raise_error(ArgumentError)
145
+ -> { "%+" % [] }.should raise_error(ArgumentError)
146
+ -> { "%-" % [] }.should raise_error(ArgumentError)
147
+ -> { "%#" % [] }.should raise_error(ArgumentError)
148
+ -> { "%0" % [] }.should raise_error(ArgumentError)
149
+ -> { "%*" % [] }.should raise_error(ArgumentError)
150
+ -> { "%." % [] }.should raise_error(ArgumentError)
151
+ -> { "%_" % [] }.should raise_error(ArgumentError)
152
+ -> { "%0$s" % "x" }.should raise_error(ArgumentError)
153
+ -> { "%*0$s" % [5, "x"] }.should raise_error(ArgumentError)
154
+ -> { "%*1$.*0$1$s" % [1, 2, 3] }.should raise_error(ArgumentError)
155
+ end
156
+
157
+ it "raises an ArgumentError when multiple positional argument tokens are given for one format specifier" do
158
+ -> { "%1$1$s" % "foo" }.should raise_error(ArgumentError)
159
+ end
160
+
161
+ it "respects positional arguments and precision tokens given for one format specifier" do
162
+ ("%2$1d" % [1, 0]).should == "0"
163
+ ("%2$1d" % [0, 1]).should == "1"
164
+
165
+ ("%2$.2f" % [1, 0]).should == "0.00"
166
+ ("%2$.2f" % [0, 1]).should == "1.00"
167
+ end
168
+
169
+ it "allows more than one digit of position" do
170
+ ("%50$d" % (0..100).to_a).should == "49"
171
+ end
172
+
173
+ it "raises an ArgumentError when multiple width star tokens are given for one format specifier" do
174
+ -> { "%**s" % [5, 5, 5] }.should raise_error(ArgumentError)
175
+ end
176
+
177
+ it "raises an ArgumentError when a width star token is seen after a width token" do
178
+ -> { "%5*s" % [5, 5] }.should raise_error(ArgumentError)
179
+ end
180
+
181
+ it "raises an ArgumentError when multiple precision tokens are given" do
182
+ -> { "%.5.5s" % 5 }.should raise_error(ArgumentError)
183
+ -> { "%.5.*s" % [5, 5] }.should raise_error(ArgumentError)
184
+ -> { "%.*.5s" % [5, 5] }.should raise_error(ArgumentError)
185
+ end
186
+
187
+ it "raises an ArgumentError when there are less arguments than format specifiers" do
188
+ ("foo" % []).should == "foo"
189
+ -> { "%s" % [] }.should raise_error(ArgumentError)
190
+ -> { "%s %s" % [1] }.should raise_error(ArgumentError)
191
+ end
192
+
193
+ it "raises an ArgumentError when absolute and relative argument numbers are mixed" do
194
+ -> { "%s %1$s" % "foo" }.should raise_error(ArgumentError)
195
+ -> { "%1$s %s" % "foo" }.should raise_error(ArgumentError)
196
+
197
+ -> { "%s %2$s" % ["foo", "bar"] }.should raise_error(ArgumentError)
198
+ -> { "%2$s %s" % ["foo", "bar"] }.should raise_error(ArgumentError)
199
+
200
+ -> { "%*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
201
+ -> { "%*.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
202
+ -> { "%*2$.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
203
+ -> { "%*.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
204
+ end
205
+
206
+ it "allows reuse of the one argument multiple via absolute argument numbers" do
207
+ ("%1$s %1$s" % "foo").should == "foo foo"
208
+ ("%1$s %2$s %1$s %2$s" % ["foo", "bar"]).should == "foo bar foo bar"
209
+ end
210
+
211
+ it "always interprets an array argument as a list of argument parameters" do
212
+ -> { "%p" % [] }.should raise_error(ArgumentError)
213
+ ("%p" % [1]).should == "1"
214
+ ("%p %p" % [1, 2]).should == "1 2"
215
+ end
216
+
217
+ it "always interprets an array subclass argument as a list of argument parameters" do
218
+ -> { "%p" % StringSpecs::MyArray[] }.should raise_error(ArgumentError)
219
+ ("%p" % StringSpecs::MyArray[1]).should == "1"
220
+ ("%p %p" % StringSpecs::MyArray[1, 2]).should == "1 2"
221
+ end
222
+
223
+ it "allows positional arguments for width star and precision star arguments" do
224
+ ("%*1$.*2$3$d" % [10, 5, 1]).should == " 00001"
225
+ end
226
+
227
+ it "allows negative width to imply '-' flag" do
228
+ ("%*1$.*2$3$d" % [-10, 5, 1]).should == "00001 "
229
+ ("%-*1$.*2$3$d" % [10, 5, 1]).should == "00001 "
230
+ ("%-*1$.*2$3$d" % [-10, 5, 1]).should == "00001 "
231
+ end
232
+
233
+ it "ignores negative precision" do
234
+ ("%*1$.*2$3$d" % [10, -5, 1]).should == " 1"
235
+ end
236
+
237
+ it "allows a star to take an argument number to use as the width" do
238
+ ("%1$*2$s" % ["a", 8]).should == " a"
239
+ ("%1$*10$s" % ["a",0,0,0,0,0,0,0,0,8]).should == " a"
240
+ end
241
+
242
+ it "calls to_int on width star and precision star tokens" do
243
+ w = mock('10')
244
+ w.should_receive(:to_int).and_return(10)
245
+
246
+ p = mock('5')
247
+ p.should_receive(:to_int).and_return(5)
248
+
249
+ ("%*.*f" % [w, p, 1]).should == " 1.00000"
250
+
251
+
252
+ w = mock('10')
253
+ w.should_receive(:to_int).and_return(10)
254
+
255
+ p = mock('5')
256
+ p.should_receive(:to_int).and_return(5)
257
+
258
+ ("%*.*d" % [w, p, 1]).should == " 00001"
259
+ end
260
+
261
+ it "does not call #to_a to convert the argument" do
262
+ x = mock("string modulo to_a")
263
+ x.should_not_receive(:to_a)
264
+ x.should_receive(:to_s).and_return("x")
265
+
266
+ ("%s" % x).should == "x"
267
+ end
268
+
269
+ it "calls #to_ary to convert the argument" do
270
+ x = mock("string modulo to_ary")
271
+ x.should_not_receive(:to_s)
272
+ x.should_receive(:to_ary).and_return(["x"])
273
+
274
+ ("%s" % x).should == "x"
275
+ end
276
+
277
+ it "wraps the object in an Array if #to_ary returns nil" do
278
+ x = mock("string modulo to_ary")
279
+ x.should_receive(:to_ary).and_return(nil)
280
+ x.should_receive(:to_s).and_return("x")
281
+
282
+ ("%s" % x).should == "x"
283
+ end
284
+
285
+ it "raises a TypeError if #to_ary does not return an Array" do
286
+ x = mock("string modulo to_ary")
287
+ x.should_receive(:to_ary).and_return("x")
288
+
289
+ -> { "%s" % x }.should raise_error(TypeError)
290
+ end
291
+
292
+ it "tries to convert the argument to Array by calling #to_ary" do
293
+ obj = mock('[1,2]')
294
+ def obj.to_ary() [1, 2] end
295
+ def obj.to_s() "obj" end
296
+ ("%s %s" % obj).should == "1 2"
297
+ ("%s" % obj).should == "1"
298
+ end
299
+
300
+ it "doesn't return subclass instances when called on a subclass" do
301
+ universal = mock('0')
302
+ def universal.to_int() 0 end
303
+ def universal.to_str() "0" end
304
+ def universal.to_f() 0.0 end
305
+
306
+ [
307
+ "", "foo",
308
+ "%b", "%B", "%c", "%d", "%e", "%E",
309
+ "%f", "%g", "%G", "%i", "%o", "%p",
310
+ "%s", "%u", "%x", "%X"
311
+ ].each do |format|
312
+ (StringSpecs::MyString.new(format) % universal).should be_an_instance_of(String)
313
+ end
314
+ end
315
+
316
+ ruby_version_is ''...'2.7' do
317
+ it "always taints the result when the format string is tainted" do
318
+ universal = mock('0')
319
+ def universal.to_int() 0 end
320
+ def universal.to_str() "0" end
321
+ def universal.to_f() 0.0 end
322
+
323
+ [
324
+ "", "foo",
325
+ "%b", "%B", "%c", "%d", "%e", "%E",
326
+ "%f", "%g", "%G", "%i", "%o", "%p",
327
+ "%s", "%u", "%x", "%X"
328
+ ].each do |format|
329
+ subcls_format = StringSpecs::MyString.new(format)
330
+ subcls_format.taint
331
+ format.taint
332
+
333
+ (format % universal).tainted?.should == true
334
+ (subcls_format % universal).tainted?.should == true
335
+ end
336
+ end
337
+ end
338
+
339
+ it "supports binary formats using %b for positive numbers" do
340
+ ("%b" % 10).should == "1010"
341
+ ("% b" % 10).should == " 1010"
342
+ ("%1$b" % [10, 20]).should == "1010"
343
+ ("%#b" % 10).should == "0b1010"
344
+ ("%+b" % 10).should == "+1010"
345
+ ("%-9b" % 10).should == "1010 "
346
+ ("%05b" % 10).should == "01010"
347
+ ("%*b" % [10, 6]).should == " 110"
348
+ ("%*b" % [-10, 6]).should == "110 "
349
+ ("%.4b" % 2).should == "0010"
350
+ ("%.32b" % 2147483648).should == "10000000000000000000000000000000"
351
+ end
352
+
353
+ it "supports binary formats using %b for negative numbers" do
354
+ ("%b" % -5).should == "..1011"
355
+ ("%0b" % -5).should == "..1011"
356
+ ("%.1b" % -5).should == "..1011"
357
+ ("%.7b" % -5).should == "..11011"
358
+ ("%.10b" % -5).should == "..11111011"
359
+ ("% b" % -5).should == "-101"
360
+ ("%+b" % -5).should == "-101"
361
+ not_supported_on :opal do
362
+ ("%b" % -(2 ** 64 + 5)).should ==
363
+ "..101111111111111111111111111111111111111111111111111111111111111011"
364
+ end
365
+ end
366
+
367
+ it "supports binary formats using %B with same behaviour as %b except for using 0B instead of 0b for #" do
368
+ ("%B" % 10).should == ("%b" % 10)
369
+ ("% B" % 10).should == ("% b" % 10)
370
+ ("%1$B" % [10, 20]).should == ("%1$b" % [10, 20])
371
+ ("%+B" % 10).should == ("%+b" % 10)
372
+ ("%-9B" % 10).should == ("%-9b" % 10)
373
+ ("%05B" % 10).should == ("%05b" % 10)
374
+ ("%*B" % [10, 6]).should == ("%*b" % [10, 6])
375
+ ("%*B" % [-10, 6]).should == ("%*b" % [-10, 6])
376
+
377
+ ("%B" % -5).should == ("%b" % -5)
378
+ ("%0B" % -5).should == ("%0b" % -5)
379
+ ("%.1B" % -5).should == ("%.1b" % -5)
380
+ ("%.7B" % -5).should == ("%.7b" % -5)
381
+ ("%.10B" % -5).should == ("%.10b" % -5)
382
+ ("% B" % -5).should == ("% b" % -5)
383
+ ("%+B" % -5).should == ("%+b" % -5)
384
+ not_supported_on :opal do
385
+ ("%B" % -(2 ** 64 + 5)).should == ("%b" % -(2 ** 64 + 5))
386
+ end
387
+
388
+ ("%#B" % 10).should == "0B1010"
389
+ end
390
+
391
+ it "supports character formats using %c" do
392
+ ("%c" % 10).should == "\n"
393
+ ("%2$c" % [10, 11, 14]).should == "\v"
394
+ ("%-4c" % 10).should == "\n "
395
+ ("%*c" % [10, 3]).should == " \003"
396
+ ("%c" % 42).should == "*"
397
+
398
+ -> { "%c" % Object }.should raise_error(TypeError)
399
+ end
400
+
401
+ it "supports single character strings as argument for %c" do
402
+ ("%c" % 'A').should == "A"
403
+ end
404
+
405
+ it "raises an exception for multiple character strings as argument for %c" do
406
+ -> { "%c" % 'AA' }.should raise_error(ArgumentError)
407
+ end
408
+
409
+ it "calls to_str on argument for %c formats" do
410
+ obj = mock('A')
411
+ obj.should_receive(:to_str).and_return('A')
412
+
413
+ ("%c" % obj).should == "A"
414
+ end
415
+
416
+ it "calls #to_ary on argument for %c formats" do
417
+ obj = mock('65')
418
+ obj.should_receive(:to_ary).and_return([65])
419
+ ("%c" % obj).should == ("%c" % [65])
420
+ end
421
+
422
+ it "calls #to_int on argument for %c formats, if the argument does not respond to #to_ary" do
423
+ obj = mock('65')
424
+ obj.should_receive(:to_int).and_return(65)
425
+
426
+ ("%c" % obj).should == ("%c" % 65)
427
+ end
428
+
429
+ %w(d i).each do |f|
430
+ format = "%" + f
431
+
432
+ it "supports integer formats using #{format}" do
433
+ ("%#{f}" % 10).should == "10"
434
+ ("% #{f}" % 10).should == " 10"
435
+ ("%1$#{f}" % [10, 20]).should == "10"
436
+ ("%+#{f}" % 10).should == "+10"
437
+ ("%-7#{f}" % 10).should == "10 "
438
+ ("%04#{f}" % 10).should == "0010"
439
+ ("%*#{f}" % [10, 4]).should == " 4"
440
+ ("%6.4#{f}" % 123).should == " 0123"
441
+ end
442
+
443
+ it "supports negative integers using #{format}" do
444
+ ("%#{f}" % -5).should == "-5"
445
+ ("%3#{f}" % -5).should == " -5"
446
+ ("%03#{f}" % -5).should == "-05"
447
+ ("%+03#{f}" % -5).should == "-05"
448
+ ("%+.2#{f}" % -5).should == "-05"
449
+ ("%-3#{f}" % -5).should == "-5 "
450
+ ("%6.4#{f}" % -123).should == " -0123"
451
+ end
452
+
453
+ it "supports negative integers using #{format}, giving priority to `-`" do
454
+ ("%-03#{f}" % -5).should == "-5 "
455
+ ("%+-03#{f}" % -5).should == "-5 "
456
+ end
457
+ end
458
+
459
+ it "supports float formats using %e" do
460
+ ("%e" % 10).should == "1.000000e+01"
461
+ ("% e" % 10).should == " 1.000000e+01"
462
+ ("%1$e" % 10).should == "1.000000e+01"
463
+ ("%#e" % 10).should == "1.000000e+01"
464
+ ("%+e" % 10).should == "+1.000000e+01"
465
+ ("%-7e" % 10).should == "1.000000e+01"
466
+ ("%05e" % 10).should == "1.000000e+01"
467
+ ("%*e" % [10, 9]).should == "9.000000e+00"
468
+ end
469
+
470
+ it "supports float formats using %e, but Inf, -Inf, and NaN are not floats" do
471
+ ("%e" % 1e1020).should == "Inf"
472
+ ("%e" % -1e1020).should == "-Inf"
473
+ ("%e" % -Float::NAN).should == "NaN"
474
+ ("%e" % Float::NAN).should == "NaN"
475
+ end
476
+
477
+ it "supports float formats using %E, but Inf, -Inf, and NaN are not floats" do
478
+ ("%E" % 1e1020).should == "Inf"
479
+ ("%E" % -1e1020).should == "-Inf"
480
+ ("%-10E" % 1e1020).should == "Inf "
481
+ ("%10E" % 1e1020).should == " Inf"
482
+ ("%+E" % 1e1020).should == "+Inf"
483
+ ("% E" % 1e1020).should == " Inf"
484
+ ("%E" % Float::NAN).should == "NaN"
485
+ ("%E" % -Float::NAN).should == "NaN"
486
+ end
487
+
488
+ it "supports float formats using %E" do
489
+ ("%E" % 10).should == "1.000000E+01"
490
+ ("% E" % 10).should == " 1.000000E+01"
491
+ ("%1$E" % 10).should == "1.000000E+01"
492
+ ("%#E" % 10).should == "1.000000E+01"
493
+ ("%+E" % 10).should == "+1.000000E+01"
494
+ ("%-7E" % 10).should == "1.000000E+01"
495
+ ("%05E" % 10).should == "1.000000E+01"
496
+ ("%*E" % [10, 9]).should == "9.000000E+00"
497
+ end
498
+
499
+ it "pads with spaces for %E with Inf, -Inf, and NaN" do
500
+ ("%010E" % -1e1020).should == " -Inf"
501
+ ("%010E" % 1e1020).should == " Inf"
502
+ ("%010E" % Float::NAN).should == " NaN"
503
+ end
504
+
505
+ it "supports float formats using %f" do
506
+ ("%f" % 10).should == "10.000000"
507
+ ("% f" % 10).should == " 10.000000"
508
+ ("%1$f" % 10).should == "10.000000"
509
+ ("%#f" % 10).should == "10.000000"
510
+ ("%#0.3f" % 10).should == "10.000"
511
+ ("%+f" % 10).should == "+10.000000"
512
+ ("%-7f" % 10).should == "10.000000"
513
+ ("%05f" % 10).should == "10.000000"
514
+ ("%0.5f" % 10).should == "10.00000"
515
+ ("%*f" % [10, 9]).should == " 9.000000"
516
+ end
517
+
518
+ it "supports float formats using %g" do
519
+ ("%g" % 10).should == "10"
520
+ ("% g" % 10).should == " 10"
521
+ ("%1$g" % 10).should == "10"
522
+ ("%#g" % 10).should == "10.0000"
523
+ ("%#.3g" % 10).should == "10.0"
524
+ ("%+g" % 10).should == "+10"
525
+ ("%-7g" % 10).should == "10 "
526
+ ("%05g" % 10).should == "00010"
527
+ ("%g" % 10**10).should == "1e+10"
528
+ ("%*g" % [10, 9]).should == " 9"
529
+ end
530
+
531
+ it "supports float formats using %G" do
532
+ ("%G" % 10).should == "10"
533
+ ("% G" % 10).should == " 10"
534
+ ("%1$G" % 10).should == "10"
535
+ ("%#G" % 10).should == "10.0000"
536
+ ("%#.3G" % 10).should == "10.0"
537
+ ("%+G" % 10).should == "+10"
538
+ ("%-7G" % 10).should == "10 "
539
+ ("%05G" % 10).should == "00010"
540
+ ("%G" % 10**10).should == "1E+10"
541
+ ("%*G" % [10, 9]).should == " 9"
542
+ end
543
+
544
+ it "supports octal formats using %o for positive numbers" do
545
+ ("%o" % 10).should == "12"
546
+ ("% o" % 10).should == " 12"
547
+ ("%1$o" % [10, 20]).should == "12"
548
+ ("%#o" % 10).should == "012"
549
+ ("%+o" % 10).should == "+12"
550
+ ("%-9o" % 10).should == "12 "
551
+ ("%05o" % 10).should == "00012"
552
+ ("%*o" % [10, 6]).should == " 6"
553
+ end
554
+
555
+ it "supports octal formats using %o for negative numbers" do
556
+ # These are incredibly wrong. -05 == -5, not 7177777...whatever
557
+ ("%o" % -5).should == "..73"
558
+ ("%0o" % -5).should == "..73"
559
+ ("%.4o" % 20).should == "0024"
560
+ ("%.1o" % -5).should == "..73"
561
+ ("%.7o" % -5).should == "..77773"
562
+ ("%.10o" % -5).should == "..77777773"
563
+
564
+ ("% o" % -26).should == "-32"
565
+ ("%+o" % -26).should == "-32"
566
+ not_supported_on :opal do
567
+ ("%o" % -(2 ** 64 + 5)).should == "..75777777777777777777773"
568
+ end
569
+ end
570
+
571
+ it "supports inspect formats using %p" do
572
+ ("%p" % 10).should == "10"
573
+ ("%1$p" % [10, 5]).should == "10"
574
+ ("%-22p" % 10).should == "10 "
575
+ ("%*p" % [10, 10]).should == " 10"
576
+ ("%p" % {capture: 1}).should == "{:capture=>1}"
577
+ ("%p" % "str").should == "\"str\""
578
+ end
579
+
580
+ it "calls inspect on arguments for %p format" do
581
+ obj = mock('obj')
582
+ def obj.inspect() "obj" end
583
+ ("%p" % obj).should == "obj"
584
+
585
+ # undef is not working
586
+ # obj = mock('obj')
587
+ # class << obj; undef :inspect; end
588
+ # def obj.method_missing(*args) "obj" end
589
+ # ("%p" % obj).should == "obj"
590
+ end
591
+
592
+ ruby_version_is ''...'2.7' do
593
+ it "taints result for %p when argument.inspect is tainted" do
594
+ obj = mock('x')
595
+ def obj.inspect() "x".taint end
596
+
597
+ ("%p" % obj).tainted?.should == true
598
+
599
+ obj = mock('x'); obj.taint
600
+ def obj.inspect() "x" end
601
+
602
+ ("%p" % obj).tainted?.should == false
603
+ end
604
+ end
605
+
606
+ it "supports string formats using %s" do
607
+ ("%s" % "hello").should == "hello"
608
+ ("%s" % "").should == ""
609
+ ("%s" % 10).should == "10"
610
+ ("%1$s" % [10, 8]).should == "10"
611
+ ("%-5s" % 10).should == "10 "
612
+ ("%*s" % [10, 9]).should == " 9"
613
+ end
614
+
615
+ it "respects a space padding request not as part of the width" do
616
+ x = "% -5s" % ["foo"]
617
+ x.should == "foo "
618
+ end
619
+
620
+ it "calls to_s on non-String arguments for %s format" do
621
+ obj = mock('obj')
622
+ def obj.to_s() "obj" end
623
+
624
+ ("%s" % obj).should == "obj"
625
+
626
+ # undef doesn't work
627
+ # obj = mock('obj')
628
+ # class << obj; undef :to_s; end
629
+ # def obj.method_missing(*args) "obj" end
630
+ #
631
+ # ("%s" % obj).should == "obj"
632
+ end
633
+
634
+ ruby_version_is ''...'2.7' do
635
+ it "taints result for %s when argument is tainted" do
636
+ ("%s" % "x".taint).tainted?.should == true
637
+ ("%s" % mock('x').taint).tainted?.should == true
638
+ end
639
+ end
640
+
641
+ # MRI crashes on this one.
642
+ # See http://groups.google.com/group/ruby-core-google/t/c285c18cd94c216d
643
+ it "raises an ArgumentError for huge precisions for %s" do
644
+ block = -> { "%.25555555555555555555555555555555555555s" % "hello world" }
645
+ block.should raise_error(ArgumentError)
646
+ end
647
+
648
+ # Note: %u has been changed to an alias for %d in 1.9.
649
+ it "supports unsigned formats using %u" do
650
+ ("%u" % 10).should == "10"
651
+ ("% u" % 10).should == " 10"
652
+ ("%1$u" % [10, 20]).should == "10"
653
+ ("%+u" % 10).should == "+10"
654
+ ("%-7u" % 10).should == "10 "
655
+ ("%04u" % 10).should == "0010"
656
+ ("%*u" % [10, 4]).should == " 4"
657
+ end
658
+
659
+ it "formats negative values with a leading sign using %u" do
660
+ ("% u" % -26).should == "-26"
661
+ ("%+u" % -26).should == "-26"
662
+ end
663
+
664
+ it "supports negative bignums with %u or %d" do
665
+ ("%u" % -(2 ** 64 + 5)).should == "-18446744073709551621"
666
+ ("%d" % -(2 ** 64 + 5)).should == "-18446744073709551621"
667
+ end
668
+
669
+ it "supports hex formats using %x for positive numbers" do
670
+ ("%x" % 10).should == "a"
671
+ ("% x" % 10).should == " a"
672
+ ("%1$x" % [10, 20]).should == "a"
673
+ ("%#x" % 10).should == "0xa"
674
+ ("%+x" % 10).should == "+a"
675
+ ("%-9x" % 10).should == "a "
676
+ ("%05x" % 10).should == "0000a"
677
+ ("%*x" % [10, 6]).should == " 6"
678
+ ("%.4x" % 20).should == "0014"
679
+ ("%x" % 0xFFFFFFFF).should == "ffffffff"
680
+ end
681
+
682
+ it "supports hex formats using %x for negative numbers" do
683
+ ("%x" % -5).should == "..fb"
684
+ ("%0x" % -5).should == "..fb"
685
+ ("%.1x" % -5).should == "..fb"
686
+ ("%.7x" % -5).should == "..ffffb"
687
+ ("%.10x" % -5).should == "..fffffffb"
688
+ ("% x" % -26).should == "-1a"
689
+ ("%+x" % -26).should == "-1a"
690
+ not_supported_on :opal do
691
+ ("%x" % -(2 ** 64 + 5)).should == "..fefffffffffffffffb"
692
+ end
693
+ end
694
+
695
+ it "supports hex formats using %X for positive numbers" do
696
+ ("%X" % 10).should == "A"
697
+ ("% X" % 10).should == " A"
698
+ ("%1$X" % [10, 20]).should == "A"
699
+ ("%#X" % 10).should == "0XA"
700
+ ("%+X" % 10).should == "+A"
701
+ ("%-9X" % 10).should == "A "
702
+ ("%05X" % 10).should == "0000A"
703
+ ("%*X" % [10, 6]).should == " 6"
704
+ ("%X" % 0xFFFFFFFF).should == "FFFFFFFF"
705
+ end
706
+
707
+ it "supports hex formats using %X for negative numbers" do
708
+ ("%X" % -5).should == "..FB"
709
+ ("%0X" % -5).should == "..FB"
710
+ ("%.1X" % -5).should == "..FB"
711
+ ("%.7X" % -5).should == "..FFFFB"
712
+ ("%.10X" % -5).should == "..FFFFFFFB"
713
+ ("% X" % -26).should == "-1A"
714
+ ("%+X" % -26).should == "-1A"
715
+ not_supported_on :opal do
716
+ ("%X" % -(2 ** 64 + 5)).should == "..FEFFFFFFFFFFFFFFFB"
717
+ end
718
+ end
719
+
720
+ it "formats zero without prefix using %#x" do
721
+ ("%#x" % 0).should == "0"
722
+ end
723
+
724
+ it "formats zero without prefix using %#X" do
725
+ ("%#X" % 0).should == "0"
726
+ end
727
+
728
+ %w(b d i o u x X).each do |f|
729
+ format = "%" + f
730
+
731
+ it "behaves as if calling Kernel#Integer for #{format} argument, if it does not respond to #to_ary" do
732
+ (format % "10").should == (format % Kernel.Integer("10"))
733
+ (format % "0x42").should == (format % Kernel.Integer("0x42"))
734
+ (format % "0b1101").should == (format % Kernel.Integer("0b1101"))
735
+ (format % "0b1101_0000").should == (format % Kernel.Integer("0b1101_0000"))
736
+ (format % "0777").should == (format % Kernel.Integer("0777"))
737
+ -> {
738
+ # see [ruby-core:14139] for more details
739
+ (format % "0777").should == (format % Kernel.Integer("0777"))
740
+ }.should_not raise_error(ArgumentError)
741
+
742
+ -> { format % "0__7_7_7" }.should raise_error(ArgumentError)
743
+
744
+ -> { format % "" }.should raise_error(ArgumentError)
745
+ -> { format % "x" }.should raise_error(ArgumentError)
746
+ -> { format % "5x" }.should raise_error(ArgumentError)
747
+ -> { format % "08" }.should raise_error(ArgumentError)
748
+ -> { format % "0b2" }.should raise_error(ArgumentError)
749
+ -> { format % "123__456" }.should raise_error(ArgumentError)
750
+
751
+ obj = mock('5')
752
+ obj.should_receive(:to_i).and_return(5)
753
+ (format % obj).should == (format % 5)
754
+
755
+ obj = mock('6')
756
+ obj.stub!(:to_i).and_return(5)
757
+ obj.should_receive(:to_int).and_return(6)
758
+ (format % obj).should == (format % 6)
759
+ end
760
+ end
761
+
762
+ %w(e E f g G).each do |f|
763
+ format = "%" + f
764
+
765
+ it "tries to convert the passed argument to an Array using #to_ary" do
766
+ obj = mock('3.14')
767
+ obj.should_receive(:to_ary).and_return([3.14])
768
+ (format % obj).should == (format % [3.14])
769
+ end
770
+
771
+ it "behaves as if calling Kernel#Float for #{format} arguments, when the passed argument does not respond to #to_ary" do
772
+ (format % 10).should == (format % 10.0)
773
+ (format % "-10.4e-20").should == (format % -10.4e-20)
774
+ (format % ".5").should == (format % 0.5)
775
+ (format % "-.5").should == (format % -0.5)
776
+ # Something's strange with this spec:
777
+ # it works just fine in individual mode, but not when run as part of a group
778
+ (format % "10_1_0.5_5_5").should == (format % 1010.555)
779
+
780
+ (format % "0777").should == (format % 777)
781
+
782
+ -> { format % "" }.should raise_error(ArgumentError)
783
+ -> { format % "x" }.should raise_error(ArgumentError)
784
+ -> { format % "." }.should raise_error(ArgumentError)
785
+ -> { format % "10." }.should raise_error(ArgumentError)
786
+ -> { format % "5x" }.should raise_error(ArgumentError)
787
+ -> { format % "0b1" }.should raise_error(ArgumentError)
788
+ -> { format % "10e10.5" }.should raise_error(ArgumentError)
789
+ -> { format % "10__10" }.should raise_error(ArgumentError)
790
+ -> { format % "10.10__10" }.should raise_error(ArgumentError)
791
+
792
+ obj = mock('5.0')
793
+ obj.should_receive(:to_f).and_return(5.0)
794
+ (format % obj).should == (format % 5.0)
795
+ end
796
+
797
+ it "behaves as if calling Kernel#Float for #{format} arguments, when the passed argument is hexadecimal string" do
798
+ (format % "0xA").should == (format % 0xA)
799
+ end
800
+
801
+ ruby_version_is ''...'2.7' do
802
+ it "doesn't taint the result for #{format} when argument is tainted" do
803
+ (format % "5".taint).tainted?.should == false
804
+ end
805
+ end
806
+ end
807
+
808
+ describe "when format string contains %{} sections" do
809
+ it "replaces %{} sections with values from passed-in hash" do
810
+ ("%{foo}bar" % {foo: 'oof'}).should == "oofbar"
811
+ end
812
+
813
+ it "should raise ArgumentError if no hash given" do
814
+ -> {"%{foo}" % []}.should raise_error(ArgumentError)
815
+ end
816
+ end
817
+
818
+ describe "when format string contains %<> formats" do
819
+ it "uses the named argument for the format's value" do
820
+ ("%<foo>d" % {foo: 1}).should == "1"
821
+ end
822
+
823
+ it "raises KeyError if key is missing from passed-in hash" do
824
+ -> {"%<foo>d" % {}}.should raise_error(KeyError)
825
+ end
826
+
827
+ it "should raise ArgumentError if no hash given" do
828
+ -> {"%<foo>" % []}.should raise_error(ArgumentError)
829
+ end
830
+ end
831
+ end