ronin-exploits 1.0.0.beta2 → 1.0.0

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -0
  3. data/README.md +29 -13
  4. data/gemspec.yml +10 -8
  5. data/lib/ronin/exploits/cli/commands/new.rb +1 -1
  6. data/lib/ronin/exploits/cli/commands/run.rb +55 -5
  7. data/lib/ronin/exploits/exploit.rb +7 -5
  8. data/lib/ronin/exploits/lfi.rb +1 -1
  9. data/lib/ronin/exploits/metadata/arch.rb +1 -1
  10. data/lib/ronin/exploits/metadata/default_filename.rb +1 -1
  11. data/lib/ronin/exploits/metadata/default_port.rb +1 -1
  12. data/lib/ronin/exploits/mixins/file_builder.rb +2 -2
  13. data/lib/ronin/exploits/mixins/nops.rb +1 -1
  14. data/lib/ronin/exploits/params/base_url.rb +1 -1
  15. data/lib/ronin/exploits/version.rb +1 -1
  16. data/ronin-exploits.gemspec +2 -1
  17. metadata +24 -129
  18. data/spec/advisory_spec.rb +0 -71
  19. data/spec/cli/exploit_command_spec.rb +0 -68
  20. data/spec/cli/exploit_methods_spec.rb +0 -208
  21. data/spec/cli/ruby_shell_spec.rb +0 -14
  22. data/spec/client_side_web_vuln_spec.rb +0 -117
  23. data/spec/exploit_spec.rb +0 -538
  24. data/spec/exploits_spec.rb +0 -8
  25. data/spec/heap_overflow_spec.rb +0 -14
  26. data/spec/lfi_spec.rb +0 -162
  27. data/spec/loot/file_spec.rb +0 -131
  28. data/spec/loot_spec.rb +0 -138
  29. data/spec/memory_corruption_spec.rb +0 -22
  30. data/spec/metadata/arch_spec.rb +0 -82
  31. data/spec/metadata/cookie_param_spec.rb +0 -67
  32. data/spec/metadata/default_filename_spec.rb +0 -62
  33. data/spec/metadata/default_port_spec.rb +0 -62
  34. data/spec/metadata/header_name_spec.rb +0 -67
  35. data/spec/metadata/os_spec.rb +0 -164
  36. data/spec/metadata/shouts_spec.rb +0 -100
  37. data/spec/metadata/url_path_spec.rb +0 -67
  38. data/spec/metadata/url_query_param_spec.rb +0 -67
  39. data/spec/mixins/binary_spec.rb +0 -129
  40. data/spec/mixins/build_dir.rb +0 -66
  41. data/spec/mixins/file_builder_spec.rb +0 -67
  42. data/spec/mixins/format_string_spec.rb +0 -44
  43. data/spec/mixins/has_payload_spec.rb +0 -333
  44. data/spec/mixins/has_targets_spec.rb +0 -434
  45. data/spec/mixins/html_spec.rb +0 -772
  46. data/spec/mixins/http_spec.rb +0 -1227
  47. data/spec/mixins/loot_spec.rb +0 -20
  48. data/spec/mixins/nops_spec.rb +0 -165
  49. data/spec/mixins/remote_tcp_spec.rb +0 -217
  50. data/spec/mixins/remote_udp_spec.rb +0 -217
  51. data/spec/mixins/seh_spec.rb +0 -89
  52. data/spec/mixins/stack_overflow_spec.rb +0 -87
  53. data/spec/mixins/text_spec.rb +0 -43
  54. data/spec/open_redirect_spec.rb +0 -71
  55. data/spec/params/base_url_spec.rb +0 -71
  56. data/spec/params/bind_host_spec.rb +0 -34
  57. data/spec/params/bind_port_spec.rb +0 -35
  58. data/spec/params/filename_spec.rb +0 -77
  59. data/spec/params/host_spec.rb +0 -34
  60. data/spec/params/port_spec.rb +0 -77
  61. data/spec/rfi_spec.rb +0 -107
  62. data/spec/seh_overflow_spec.rb +0 -18
  63. data/spec/spec_helper.rb +0 -8
  64. data/spec/sqli_spec.rb +0 -306
  65. data/spec/ssti_spec.rb +0 -121
  66. data/spec/stack_overflow_spec.rb +0 -18
  67. data/spec/target_spec.rb +0 -92
  68. data/spec/test_result_spec.rb +0 -32
  69. data/spec/use_after_free_spec.rb +0 -14
  70. data/spec/web_spec.rb +0 -12
  71. data/spec/web_vuln_spec.rb +0 -854
  72. data/spec/xss_spec.rb +0 -69
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b7d601e1580ea5719c365a686d579659df4deb60d77eff285d74ac1280d93b3
4
- data.tar.gz: 87e9cef6168d40862c0442aabdaaa69d51563cbae6ff93abb8a3c4c0ef0b6704
3
+ metadata.gz: f4fb437a6634a2eef3dae27f5d940718e94d1e5d7839113e9b02d849daeb2f81
4
+ data.tar.gz: 661bbae01973ba0ec2e367abef7fd137534dff1597b8486dde94e64afcc6e780
5
5
  SHA512:
6
- metadata.gz: 37e77fed488fdcfed0357d792738b3bb231cbeae749631291aa12544a8b225ca14fe548cf70985ae88d9946f5e1f8ead8c1d0078d486fa4d0ed837b33fca9aa6
7
- data.tar.gz: e032ca57e0b63ca63b93e79d6479c631a8939eaa5406d4d4d68bafebfd5b16ffdfc5e6f58f245440036ec0f2869937709c84cf915ce78e10efc4fb03c0bcc303
6
+ metadata.gz: 9c40d3ba6b13842ddb66b762cac6aaa26a22f3feb03a8f68abf1fdc7d90696fb4cc03da0fd4472ea77af9e861eb92cf4a7165a5fc023b4ecb4e59f0ef13fa234
7
+ data.tar.gz: 8b81f266e3b256f028a01b1d10a85d0d1d09cfbcdfecab07e5bf25ebae0f96adee5b7881515eb4cfaccae5ae0e4807377315f50db9213ab5268b179db3e87640
@@ -21,6 +21,7 @@ jobs:
21
21
  uses: ruby/setup-ruby@v1
22
22
  with:
23
23
  ruby-version: ${{ matrix.ruby }}
24
+ bundler-cache: true
24
25
  - name: Install libsqlite3
25
26
  run: |
26
27
  sudo apt update -y && \
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![CI](https://github.com/ronin-rb/ronin-exploits/actions/workflows/ruby.yml/badge.svg)](https://github.com/ronin-rb/ronin-exploits/actions/workflows/ruby.yml)
4
4
  [![Code Climate](https://codeclimate.com/github/ronin-rb/ronin-exploits.svg)](https://codeclimate.com/github/ronin-rb/ronin-exploits)
5
+ [![Gem Version](https://badge.fury.io/rb/ronin-exploits.svg)](https://badge.fury.io/rb/ronin-exploits)
5
6
 
6
7
  * [Source](https://github.com/ronin-rb/ronin-exploits)
7
8
  * [Issues](https://github.com/ronin-rb/ronin-exploits/issues)
@@ -17,27 +18,30 @@ ronin-exploits allows one to write exploits as plain old Ruby classes.
17
18
  ronin-exploits can be distributed as Ruby files or as git repositories that can
18
19
  be installed using [ronin-repos].
19
20
 
21
+ **tl;dr** It's like a simpler and more modular version of
22
+ [Metasploit](https://www.metasploit.com/).
23
+
20
24
  ronin-exploits is part of the [ronin-rb] project, a [Ruby] toolkit for security
21
25
  research and development.
22
26
 
23
27
  ## Features
24
28
 
25
- * Provides a succinct syntax and API for writing exploits in as few lines as
26
- possible.
27
- * Supports defining exploits as plain old Ruby classes.
29
+ * Provides a succinct [syntax](#examples) and [API][docs-exploit] for writing
30
+ exploits in as few lines as possible.
31
+ * Supports [defining exploits as plain old Ruby classes][docs-exploit].
28
32
  * Supports loading exploits from Ruby files or from installed 3rd-party
29
33
  git repositories.
30
34
  * Provides base classes and mixin modules for a variety of exploit types:
31
- * Stack Overflows
32
- * SEH Overflows
33
- * Heap Overflows
34
- * Use After Free (UAF)
35
- * Open Redirect
36
- * Local File Inclusions (LFI)
37
- * Remote File Inclusions (RFI)
38
- * SQL injections (SQLi)
39
- * Cross-Site Scripting (XSS)
40
- * Server-Side Template Injection (SSTI)
35
+ * [Stack Overflows][docs-stack-overflow]
36
+ * [SEH Overflows][docs-seh-overflow]
37
+ * [Heap Overflows][docs-heap-overflow]
38
+ * [Use After Free (UAF)][docs-use-after-free]
39
+ * [Open Redirect][docs-open-redirect]
40
+ * [Local File Inclusions (LFI)][docs-lfi]
41
+ * [Remote File Inclusions (RFI)][docs-rfi]
42
+ * [SQL injections (SQLi)][docs-sqli]
43
+ * [Cross-Site Scripting (XSS)][docs-xss]
44
+ * [Server-Side Template Injection (SSTI)][docs-ssti]
41
45
  * Uses the [ronin-payloads] library for exploit payloads.
42
46
  * Uses the [ronin-post_ex] library for post-exploitation.
43
47
  * Provides a simple CLI for listing, displaying, running, and generating new
@@ -46,6 +50,18 @@ research and development.
46
50
  * Has 86% documentation coverage.
47
51
  * Small memory footprint (~47Kb).
48
52
 
53
+ [docs-exploit]: https://ronin-rb.dev/docs/ronin-exploits/Ronin/Exploits/Exploit.html
54
+ [docs-stack-overflow]: https://ronin-rb.dev/docs/ronin-exploits/Ronin/Exploits/StackOverflow.html
55
+ [docs-seh-overflow]: https://ronin-rb.dev/docs/ronin-exploits/Ronin/Exploits/SEHOverflow.html
56
+ [docs-heap-overflow]: https://ronin-rb.dev/docs/ronin-exploits/Ronin/Exploits/HeapOverflow.html
57
+ [docs-use-after-free]: https://ronin-rb.dev/docs/ronin-exploits/Ronin/Exploits/UseAfterFree.html
58
+ [docs-open-redirect]: https://ronin-rb.dev/docs/ronin-exploits/Ronin/Exploits/OpenRedirect.html
59
+ [docs-lfi]: https://ronin-rb.dev/docs/ronin-exploits/Ronin/Exploits/LFI.html
60
+ [docs-rfi]: https://ronin-rb.dev/docs/ronin-exploits/Ronin/Exploits/RFI.html
61
+ [docs-sqli]: https://ronin-rb.dev/docs/ronin-exploits/Ronin/Exploits/SQLI.html
62
+ [docs-xss]: https://ronin-rb.dev/docs/ronin-exploits/Ronin/Exploits/XSS.html
63
+ [docs-ssti]: https://ronin-rb.dev/docs/ronin-exploits/Ronin/Exploits/SSTI.html
64
+
49
65
  ## Anti-Features
50
66
 
51
67
  * No magic: exploits are defined as classes in files.
data/gemspec.yml CHANGED
@@ -1,11 +1,13 @@
1
1
  name: ronin-exploits
2
2
  summary: A Ruby micro-framework for writing and running exploits and payloads.
3
- description:
3
+ description: |
4
4
  ronin-exploits is a Ruby micro-framework for writing and running exploits.
5
5
  ronin-exploits allows one to write exploits as plain old Ruby classes.
6
6
  ronin-exploits can be distributed as Ruby files or as git repositories that
7
7
  can be installed using ronin-reps.
8
8
 
9
+ It's like a simpler version of Metasploit.
10
+
9
11
  license: LGPL-3.0
10
12
  authors: Postmodern
11
13
  email: postmodern.mod3@gmail.com
@@ -32,13 +34,13 @@ generated_files:
32
34
  dependencies:
33
35
  uri-query_params: ~> 0.6
34
36
  # Ronin dependencies:
35
- ronin-support: ~> 1.0.0.beta1
36
- ronin-code-sql: ~> 2.0.0.beta1
37
- ronin-payloads: ~> 0.1.0.beta1
38
- ronin-vulns: ~> 0.1.0.beta1
39
- ronin-post_ex: ~> 0.1.0.beta1
40
- ronin-core: ~> 0.1.0.beta1
41
- ronin-repos: ~> 0.1.0.beta1
37
+ ronin-support: ~> 1.0
38
+ ronin-code-sql: ~> 2.0
39
+ ronin-payloads: ~> 0.1
40
+ ronin-vulns: ~> 0.1
41
+ ronin-post_ex: ~> 0.1
42
+ ronin-core: ~> 0.1
43
+ ronin-repos: ~> 0.1
42
44
 
43
45
  development_dependencies:
44
46
  bundler: ~> 2.0
@@ -252,7 +252,7 @@ module Ronin
252
252
  man_page 'ronin-exploits-new.1'
253
253
 
254
254
  #
255
- # Initialies the `ronin-exploits new` command.
255
+ # Initializes the `ronin-exploits new` command.
256
256
  #
257
257
  # @param [Hash{Symbol => Object}] kwargs
258
258
  # Additional keyword arguments.
@@ -118,7 +118,11 @@ module Ronin
118
118
  type: /\A[^=\s]+=.+\z/,
119
119
  usage: 'NAME=VALUE'
120
120
  },
121
- desc: 'Sets a param on the payload'
121
+ desc: 'Sets a param on the payload' do |param|
122
+ name, value = param.split('=',2)
123
+
124
+ @payload_params[name.to_sym] = value
125
+ end
122
126
 
123
127
  # Encoder options
124
128
  option :encoder_file, value: {
@@ -228,6 +232,7 @@ module Ronin
228
232
 
229
233
  @load_encoders = []
230
234
  @encoder_params = Hash.new { |hash,key| hash[key] = {} }
235
+ @payload_params = {}
231
236
  @target_kwargs = {}
232
237
  end
233
238
 
@@ -244,6 +249,7 @@ module Ronin
244
249
  load_payload
245
250
  initialize_encoders
246
251
  initialize_payload
252
+ validate_payload
247
253
  initialize_exploit
248
254
  validate_exploit
249
255
  run_exploit
@@ -257,6 +263,10 @@ module Ronin
257
263
  perform_cleanup
258
264
  end
259
265
 
266
+ #
267
+ # Loads the payload encoder classes specified by `--encoder` or
268
+ # `--encoder-file`.
269
+ #
260
270
  def load_encoders
261
271
  @encoder_classes = @load_encoders.map do |(type,value)|
262
272
  case type
@@ -266,12 +276,20 @@ module Ronin
266
276
  end
267
277
  end
268
278
 
279
+ #
280
+ # Initializes the payload encoders specified by `--encoder` or
281
+ # `--encoder-file`.
282
+ #
269
283
  def initialize_encoders
270
284
  @encoders = @encoder_classes.map do |encoder_class|
271
285
  encoder_class.new(params: @encoder_params[encoder_class.id])
272
286
  end
273
287
  end
274
288
 
289
+ #
290
+ # Loads the payload class specified by `--payload` or
291
+ # `--payload-file`.
292
+ #
275
293
  def load_payload
276
294
  @payload_class = if options[:payload]
277
295
  super(options[:payload])
@@ -280,6 +298,10 @@ module Ronin
280
298
  end
281
299
  end
282
300
 
301
+ #
302
+ # Initializes the payload specified by `--payload`, `--payload-file`,
303
+ # `--read-payload`, or `--payload-string`.
304
+ #
283
305
  def initialize_payload
284
306
  @payload = if @payload_class
285
307
  super(@payload_class, params: @payload_params,
@@ -291,6 +313,16 @@ module Ronin
291
313
  end
292
314
  end
293
315
 
316
+ #
317
+ # Validates the payload.
318
+ #
319
+ def validate_payload
320
+ super(@payload)
321
+ end
322
+
323
+ #
324
+ # Initializes the exploit.
325
+ #
294
326
  def initialize_exploit
295
327
  kwargs = {params: @params}
296
328
 
@@ -309,13 +341,16 @@ module Ronin
309
341
  super(**kwargs)
310
342
  end
311
343
 
344
+ #
345
+ # Runs the exploit.
346
+ #
312
347
  def run_exploit
313
348
  log_info "Running exploit #{@exploit.class_id} ..."
314
349
 
315
350
  begin
316
351
  @exploit.exploit(dry_run: options[:dry_run])
317
352
  rescue ExploitError => error
318
- print_error("failed to run exploit #{@exploit.class_id}: #{error.message}")
353
+ print_error "failed to run exploit #{@exploit.class_id}: #{error.message}"
319
354
  exit(1)
320
355
  rescue => error
321
356
  print_exception(error)
@@ -324,6 +359,9 @@ module Ronin
324
359
  end
325
360
  end
326
361
 
362
+ #
363
+ # Starts an interactive ruby shell within the exploit object.
364
+ #
327
365
  def start_shell
328
366
  log_info "Exploit #{@exploit.class_id} launched!"
329
367
  log_info "Starting interactive Ruby shell ..."
@@ -331,12 +369,15 @@ module Ronin
331
369
  RubyShell.start(name: @exploit_class.name, context: @exploit)
332
370
  end
333
371
 
372
+ #
373
+ # Performs the post-exploitation stage.
374
+ #
334
375
  def post_exploitation
335
376
  if @exploit_class.include?(Mixins::HasPayload) &&
336
377
  @exploit.payload.kind_of?(Ronin::Payloads::Payload) &&
337
- @exploit.payload.kind_of?(Ronin::Payloads::Mixins::PostExt)
378
+ @exploit.payload.kind_of?(Ronin::Payloads::Mixins::PostEx)
338
379
  unless @exploit.payload.session
339
- print_error("payload (#{@exploit.payload.class_id}) did not create a post-exploitation session")
380
+ print_error "payload (#{@exploit.payload.class_id}) did not create a post-exploitation session"
340
381
 
341
382
  perform_cleanup
342
383
  eixt(1)
@@ -349,6 +390,9 @@ module Ronin
349
390
  end
350
391
  end
351
392
 
393
+ #
394
+ # Prints any loot collected by the exploit.
395
+ #
352
396
  def print_loot
353
397
  unless @exploit.loot.empty?
354
398
  log_info "Exploit found the following loot:"
@@ -372,15 +416,21 @@ module Ronin
372
416
  end
373
417
  end
374
418
 
419
+ #
420
+ # Saves the collected loot to the `--save-loot` directory.
421
+ #
375
422
  def save_loot
376
423
  @exploit.loot.save(options.fetch(:save_loot))
377
424
  end
378
425
 
426
+ #
427
+ # Performs the cleanup stage of the exploit.
428
+ #
379
429
  def perform_cleanup
380
430
  begin
381
431
  @exploit.perform_cleanup
382
432
  rescue ExploitError => error
383
- print_error("failed to cleanup exploit #{@exploit.class_id}: #{error.message}")
433
+ print_error "failed to cleanup exploit #{@exploit.class_id}: #{error.message}"
384
434
  exit(1)
385
435
  rescue => error
386
436
  print_exception(error)
@@ -52,7 +52,7 @@ module Ronin
52
52
  # The {Exploit} class defines six key parts:
53
53
  #
54
54
  # 1. Metadata - defines information about the exploit.
55
- # 2. Params - user configurable parameters.
55
+ # 2. [Params] - user configurable parameters.
56
56
  # 3. {Exploit#test test} - optional method that tests whether the target is
57
57
  # vulnerable or not.
58
58
  # 4. {Exploit#build build} - method which builds the exploit.
@@ -60,6 +60,8 @@ module Ronin
60
60
  # 6. {Exploit#cleanup cleanup} - optional Method which performs additional
61
61
  # cleanup steps.
62
62
  #
63
+ # [Params]: https://ronin-rb.dev/docs/ronin-core/Ronin/Core/Params/Mixin.html
64
+ #
63
65
  # ## Example
64
66
  #
65
67
  # require 'ronin/exploits/exploit'
@@ -136,7 +138,7 @@ module Ronin
136
138
  #
137
139
  # ### description
138
140
  #
139
- # Defines a longer multi-paragraph escription of the exploit.
141
+ # Defines a longer multi-paragraph description of the exploit.
140
142
  #
141
143
  # description <<~EOS
142
144
  # This is my first exploit.
@@ -313,7 +315,7 @@ module Ronin
313
315
  end
314
316
 
315
317
  #
316
- # Determines whether the exploit has been publically released yet.
318
+ # Determines whether the exploit has been publicly released yet.
317
319
  #
318
320
  # @return [Boolean]
319
321
  #
@@ -526,7 +528,7 @@ module Ronin
526
528
  end
527
529
 
528
530
  #
529
- # Builds the exploit and then launchs the exploit.
531
+ # Builds the exploit and then launches the exploit.
530
532
  #
531
533
  # @param [Boolean] dry_run
532
534
  # If `true` performs a dry-run by only calling {#build} and **not**
@@ -633,7 +635,7 @@ module Ronin
633
635
  end
634
636
 
635
637
  #
636
- # Place holder method for testing whether the targeet is vulnerable.
638
+ # Place holder method for testing whether the target is vulnerable.
637
639
  #
638
640
  # @return [Test::Vulnerable, Test::NotVulnerable, Test::Unknown]
639
641
  #
@@ -73,7 +73,7 @@ module Ronin
73
73
  # Gets or sets the directory traversal depth for the LFI vulnerability.
74
74
  #
75
75
  # @param [Integer, nil] new_depth
76
- # The optional new directory trasversal depth to set.
76
+ # The optional new directory traversal depth to set.
77
77
  #
78
78
  # @return [Integer]
79
79
  # The LFI vulnerability's directory traverse depth.
@@ -40,7 +40,7 @@ module Ronin
40
40
 
41
41
  module ClassMethods
42
42
  #
43
- # Gets or sets the exploits's targetted architecture.
43
+ # Gets or sets the exploit's targeted architecture.
44
44
  #
45
45
  # @param [:x86, :x86_64, :ia64, :amd64, :ppc, :ppc64, :mips, :mips_le, :mips_be, :mips64, :mips64_le, :mips64_be, :arm, :arm_le, :arm_be, :arm64, :arm64_le, :arm64_be, nil] new_arch
46
46
  # The optional new architecture to set.
@@ -40,7 +40,7 @@ module Ronin
40
40
 
41
41
  module ClassMethods
42
42
  #
43
- # Gets or sets the exploits's default filename.
43
+ # Gets or sets the exploit's default filename.
44
44
  #
45
45
  # @param [Integer, nil] new_default_filename
46
46
  # The optional new default filename to set.
@@ -40,7 +40,7 @@ module Ronin
40
40
 
41
41
  module ClassMethods
42
42
  #
43
- # Gets or sets the exploits's default port.
43
+ # Gets or sets the exploit's default port.
44
44
  #
45
45
  # @param [Integer, nil] new_default_port
46
46
  # The optional new default port number to set.
@@ -24,8 +24,8 @@ module Ronin
24
24
  module Exploits
25
25
  module Mixins
26
26
  #
27
- # Adds methods for building exploit files. Also adds a `filenam`
28
- # param and a
27
+ # Adds methods for building exploit files. Also adds a `filename` param
28
+ # and a
29
29
  # {Metadata::DefaultFilename::ClassMethods#default_filename default_filename}
30
30
  # class method.
31
31
  #
@@ -81,7 +81,7 @@ module Ronin
81
81
  end
82
82
 
83
83
  #
84
- # An individual NOP instructure for the target architecture of the
84
+ # An individual NOP instruction for the target architecture of the
85
85
  # exploit.
86
86
  #
87
87
  # @return [String]
@@ -64,7 +64,7 @@ module Ronin
64
64
  end
65
65
 
66
66
  #
67
- # Expands the URL or path into a fully qualitifed URL.
67
+ # Expands the URL or path into a fully qualified URL.
68
68
  #
69
69
  # @param [String] path
70
70
  # The URL or path to expand.
@@ -21,6 +21,6 @@
21
21
  module Ronin
22
22
  module Exploits
23
23
  # ronin-exploits version
24
- VERSION = '1.0.0.beta2'
24
+ VERSION = '1.0.0'
25
25
  end
26
26
  end
@@ -27,6 +27,8 @@ Gem::Specification.new do |gem|
27
27
  gem.files = `git ls-files`.split($/)
28
28
  gem.files = glob[gemspec['files']] if gemspec['files']
29
29
  gem.files += Array(gemspec['generated_files'])
30
+ # exclude test files from the packages gem
31
+ gem.files -= glob[gemspec['test_files'] || 'spec/{**/}*']
30
32
 
31
33
  gem.executables = gemspec.fetch('executables') do
32
34
  glob['bin/*'].map { |path| File.basename(path) }
@@ -34,7 +36,6 @@ Gem::Specification.new do |gem|
34
36
  gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
35
37
 
36
38
  gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
37
- gem.test_files = glob[gemspec['test_files'] || 'spec/{**/}*_spec.rb']
38
39
  gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
39
40
 
40
41
  gem.require_paths = Array(gemspec.fetch('require_paths') {