contrast-agent 3.8.4

Sign up to get free protection for your applications and to get access to all the features.
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