pwn 0.5.372 → 0.5.373

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30f6af2864825daf73f441f4514d403f6ec8285c1aca533f12478897d311517d
4
- data.tar.gz: d403f602bcf9dfc75d57d11cc96ab5a9fffc8cda9fcf81ef0dbc557c3a355ea1
3
+ metadata.gz: 1d53fa832e82071124a716c089d39ecff41d53ea434454c38f26091a2f20e0c0
4
+ data.tar.gz: 48c9ab88c5b07a6ff83b7f8e6f4598181fc1fd779ae517a034b0c29a85d9700b
5
5
  SHA512:
6
- metadata.gz: 28741ba677e71a721874d623f6876e3ea68f8437ff58e102d45ba66f627b531bbcb788cd1d5997151b5f012b0e6a061fad98129a405af244b6dd0b3c4e712170
7
- data.tar.gz: 842169d61f868c45c9c68259abe9cf547e559176a314a29a5d75e5a2702921e0a99c611f84237b0b85a1ebd8edf2d14d5012a327da26fa80fa0586dd9f1f9035
6
+ metadata.gz: d060f1a0ebf1330018ca27bbfce143336958ed2416181b22383438ce8a61739bafe7b725592a78cc6f11ad5ec1e887ec3474ea33f7c94d9e38dee68cb0ff29b2
7
+ data.tar.gz: eb35a40d3f69cb8ec3ad884d6502371c119094ec5358a0eb4224c193e0d11dc5768486c3019c75f263be0f526c6075d77e150e82404fba1604bbdd3bbf7dbb4d
data/.rubocop.yml CHANGED
@@ -8,19 +8,19 @@ Lint/UselessRescue:
8
8
  Metrics/AbcSize:
9
9
  Max: 537.6
10
10
  Metrics/BlockLength:
11
- Max: 220
11
+ Max: 292
12
12
  Metrics/BlockNesting:
13
- Max: 5
13
+ Max: 6
14
14
  Metrics/ClassLength:
15
15
  Max: 134
16
16
  Metrics/CyclomaticComplexity:
17
- Max: 123
17
+ Max: 157
18
18
  Metrics/MethodLength:
19
19
  Max: 485
20
20
  Metrics/ModuleLength:
21
21
  Max: 1000
22
22
  Metrics/PerceivedComplexity:
23
- Max: 122
23
+ Max: 156
24
24
  Style/HashEachMethods:
25
25
  Enabled: true
26
26
  Style/HashSyntax:
data/README.md CHANGED
@@ -37,7 +37,7 @@ $ cd /opt/pwn
37
37
  $ ./install.sh
38
38
  $ ./install.sh ruby-gem
39
39
  $ pwn
40
- pwn[v0.5.372]:001 >>> PWN.help
40
+ pwn[v0.5.373]:001 >>> PWN.help
41
41
  ```
42
42
 
43
43
  [![Installing the pwn Security Automation Framework](https://raw.githubusercontent.com/0dayInc/pwn/master/documentation/pwn_install.png)](https://youtu.be/G7iLUY4FzsI)
@@ -52,7 +52,7 @@ $ rvm use ruby-3.4.4@pwn
52
52
  $ gem uninstall --all --executables pwn
53
53
  $ gem install --verbose pwn
54
54
  $ pwn
55
- pwn[v0.5.372]:001 >>> PWN.help
55
+ pwn[v0.5.373]:001 >>> PWN.help
56
56
  ```
57
57
 
58
58
  If you're using a multi-user install of RVM do:
@@ -62,7 +62,7 @@ $ rvm use ruby-3.4.4@pwn
62
62
  $ rvmsudo gem uninstall --all --executables pwn
63
63
  $ rvmsudo gem install --verbose pwn
64
64
  $ pwn
65
- pwn[v0.5.372]:001 >>> PWN.help
65
+ pwn[v0.5.373]:001 >>> PWN.help
66
66
  ```
67
67
 
68
68
  PWN periodically upgrades to the latest version of Ruby which is reflected in `/opt/pwn/.ruby-version`. The easiest way to upgrade to the latest version of Ruby from a previous PWN installation is to run the following script:
@@ -460,9 +460,14 @@ module PWN
460
460
  debug = opts[:debug] || false
461
461
 
462
462
  # Parse the OpenAPI JSON or YAML specification file
463
- # If the opeenapi_spec is YAML, convert it to JSON
464
- openapi = JSON.parse(File.read(openapi_spec), symbolize_names: true) if openapi_spec.end_with?('.json')
465
- openapi = YAML.safe_load_file(openapi_spec, permitted_classes: [Symbol, Date, Time], aliases: true, symbolize_names: true) if openapi_spec.end_with?('.yaml', '.yml')
463
+ # If the openapi_spec is YAML, convert it to JSON
464
+ openapi = if openapi_spec.end_with?('.json')
465
+ JSON.parse(File.read(openapi_spec), symbolize_names: true)
466
+ elsif openapi_spec.end_with?('.yaml', '.yml')
467
+ YAML.safe_load_file(openapi_spec, permitted_classes: [Symbol, Date, Time], aliases: true, symbolize_names: true)
468
+ else
469
+ raise "ERROR: Unsupported file extension for #{openapi_spec}. Expected .json, .yaml, or .yml."
470
+ end
466
471
 
467
472
  # Initialize result array
468
473
  sitemap_arr = []
@@ -477,6 +482,19 @@ module PWN
477
482
  # Valid HTTP methods for validation
478
483
  valid_methods = %w[get post put patch delete head options trace connect]
479
484
 
485
+ # Helper lambda to resolve $ref in schemas
486
+ resolve_ref = lambda do |openapi, ref|
487
+ return nil unless ref&.start_with?('#/')
488
+
489
+ parts = ref.sub('#/', '').split('/')
490
+ resolved = openapi
491
+ parts.each do |part|
492
+ resolved = resolved[part.to_sym]
493
+ return nil unless resolved
494
+ end
495
+ resolved
496
+ end
497
+
480
498
  # Iterate through each server
481
499
  servers.each do |server|
482
500
  server_url = server[:url]
@@ -609,6 +627,88 @@ module PWN
609
627
  all_parameters = path_parameters + operation_parameters
610
628
  warn("[DEBUG] All parameters for #{method_str.upcase} #{full_path}: #{all_parameters.inspect}") if debug && !all_parameters.empty?
611
629
 
630
+ # Determine response code from operation[:responses].keys
631
+ fallback_response_code = 200
632
+ response_keys = operation[:responses].keys
633
+ response_key = response_keys.find { |key| key.to_s.to_i.between?(100, 599) } || fallback_response_code.to_s
634
+ response_code = response_key.to_s.to_i
635
+
636
+ # Construct response body from operation responses schema example, schema $ref example, etc.
637
+ response_obj = operation[:responses][response_key] || {}
638
+ content = response_obj[:content] || {}
639
+ content_type = content.keys.first&.to_s || 'text/plain'
640
+
641
+ response_body = ''
642
+ unless [204, 304].include?(response_code)
643
+ content_obj = content[content_type.to_sym] || {}
644
+ example = content_obj[:example]
645
+ if example.nil? && content_obj[:examples].is_a?(Hash)
646
+ ex_key = content_obj[:examples].keys.first
647
+ if ex_key
648
+ ex = content_obj[:examples][ex_key]
649
+ if ex[:$ref]
650
+ resolved_ex = resolve_ref.call(openapi, ex[:$ref])
651
+ example = resolved_ex[:value] if resolved_ex
652
+ else
653
+ example = ex[:value]
654
+ end
655
+ end
656
+ end
657
+
658
+ if example.nil?
659
+ schema = content_obj[:schema]
660
+ if schema
661
+ if schema[:$ref]
662
+ ref = schema[:$ref]
663
+ if ref.start_with?('#/')
664
+ parts = ref.sub('#/', '').split('/')
665
+ resolved = openapi
666
+ parts.each do |part|
667
+ resolved = resolved[part.to_sym]
668
+ break unless resolved
669
+ end
670
+ schema = resolved if resolved
671
+ end
672
+ end
673
+
674
+ example = schema[:example]
675
+ if example.nil? && schema[:examples].is_a?(Hash)
676
+ ex_key = schema[:examples].keys.first
677
+ if ex_key
678
+ ex = schema[:examples][ex_key]
679
+ if ex[:$ref]
680
+ resolved_ex = resolve_ref.call(openapi, ex[:$ref])
681
+ example = resolved_ex[:value] if resolved_ex
682
+ else
683
+ example = ex[:value]
684
+ end
685
+ end
686
+ end
687
+ end
688
+ end
689
+
690
+ response_body = example || response_obj[:description] || "INFO: Unable to resolve response body from #{openapi_spec} => { 'http_method': '#{method_str.upcase}', 'path': '#{full_path}', 'response_code': '#{response_code}' }"
691
+ end
692
+
693
+ # Try to extract query samples from response example if it's a links object
694
+ query_hash = nil
695
+ if response_body.is_a?(Hash) && response_body[:links]
696
+ href = response_body.dig(:links, :self, :href)
697
+ # href ||= response_body[:links].values.first&.dig(:href) rescue nil
698
+ if href.nil? && response_body[:links].is_a?(Hash) && !response_body[:links].empty?
699
+ first_value = response_body[:links].values.first
700
+ href = first_value[:href] if first_value.is_a?(Hash)
701
+ end
702
+ if href
703
+ begin
704
+ parsed_uri = URI.parse(href)
705
+ query_hash = URI.decode_www_form(parsed_uri.query).to_h if parsed_uri.path.end_with?(path_str) && parsed_uri.query
706
+ rescue URI::InvalidURIError => e
707
+ warn("[DEBUG] Invalid href in response example: #{href} - #{e.message}") if debug
708
+ end
709
+ end
710
+ end
711
+
612
712
  # Process path parameters for substitution
613
713
  request_path = full_path.dup
614
714
  query_params = []
@@ -617,21 +717,67 @@ module PWN
617
717
  next unless param.is_a?(Hash) && param[:name] && param[:in]
618
718
 
619
719
  param_name = param[:name].to_s
720
+
721
+ # Get param_value with precedence: param.examples > param.example > schema.examples > schema.example > 'FUZZ'
722
+ param_value = if param[:examples].is_a?(Hash) && !param[:examples].empty?
723
+ first_ex = param[:examples].values.first
724
+ if first_ex.is_a?(Hash)
725
+ if first_ex[:$ref]
726
+ # Resolve $ref for example if present
727
+ resolved_ex = resolve_ref.call(openapi, first_ex[:$ref])
728
+ resolved_ex[:value] if resolved_ex
729
+ else
730
+ first_ex[:value]
731
+ end
732
+ else
733
+ first_ex
734
+ end || 'FUZZ'
735
+ elsif param.key?(:example)
736
+ param[:example]
737
+ else
738
+ schema = param[:schema]
739
+ if schema
740
+ if schema[:$ref]
741
+ resolved_schema = resolve_ref.call(openapi, schema[:$ref])
742
+ schema = resolved_schema if resolved_schema
743
+ end
744
+ if schema[:examples].is_a?(Hash) && !schema[:examples].empty?
745
+ first_ex = schema[:examples].values.first
746
+ if first_ex.is_a?(Hash)
747
+ if first_ex[:$ref]
748
+ resolved_ex = resolve_ref.call(openapi, first_ex[:$ref])
749
+ resolved_ex[:value] if resolved_ex
750
+ else
751
+ first_ex[:value]
752
+ end
753
+ else
754
+ first_ex
755
+ end || 'FUZZ'
756
+ elsif schema.key?(:example)
757
+ schema[:example]
758
+ else
759
+ 'FUZZ'
760
+ end
761
+ else
762
+ 'FUZZ'
763
+ end
764
+ end
765
+
766
+ # If still 'FUZZ' and it's a query param, try to get from response example query_hash
767
+ param_value = query_hash[param_name] if param_value == 'FUZZ' && param[:in] == 'query' && query_hash&.key?(param_name)
768
+
620
769
  case param[:in]
621
770
  when 'header'
622
771
  # Aggregate remaining HTTP header names from spec,
623
772
  # reference as keys, and assign their respective
624
773
  # values to the request_headers hash
625
774
  param_key = param_name.downcase
626
- param_value = param[:schema]&.dig(:example) || 'FUZZ'
627
775
  request_headers[param_key] = param_value.to_s
628
776
  when 'path'
629
- # Substitute path parameter with a default value (e.g., 'FUZZ')
630
- param_value = param[:schema]&.dig(:example) || 'FUZZ'
777
+ # Substitute path parameter with the resolved value
631
778
  request_path.gsub!("{#{param_name}}", param_value.to_s)
632
779
  when 'query'
633
780
  # Collect query parameters
634
- param_value = param[:schema]&.dig(:example) || 'FUZZ'
635
781
  query_params.push("#{URI.encode_www_form_component(param_name)}=#{URI.encode_www_form_component(param_value.to_s)}")
636
782
  end
637
783
  end
@@ -653,12 +799,6 @@ module PWN
653
799
  request = request_lines.join("\r\n")
654
800
  encoded_request = Base64.strict_encode64(request)
655
801
 
656
- # Determine response code from operation[:responses].keys
657
- fallback_response_code = 200
658
- response_keys = operation[:responses].keys
659
- response_key = response_keys.find { |key| key.to_s.to_i.between?(100, 599) } || fallback_response_code.to_s
660
- response_code = response_key.to_s.to_i
661
-
662
802
  response_status = case response_code
663
803
  when 200 then '200 OK'
664
804
  when 201 then '201 Created'
@@ -680,52 +820,11 @@ module PWN
680
820
  else "#{fallback_response_code} OK"
681
821
  end
682
822
 
683
- # Construct response body from operation responses schema example, schema $ref example, etc.
684
- response_obj = operation[:responses][response_key] || {}
685
- content = response_obj[:content] || {}
686
- content_type = content.keys.first&.to_s || 'text/plain'
687
-
688
- response_body = ''
689
- unless [204, 304].include?(response_code)
690
- content_obj = content[content_type.to_sym] || {}
691
- example = content_obj[:example]
692
- if example.nil? && content_obj[:examples].is_a?(Hash)
693
- ex_key = content_obj[:examples].keys.first
694
- example = content_obj[:examples][ex_key][:value] if ex_key
695
- end
696
-
697
- if example.nil?
698
- schema = content_obj[:schema]
699
- if schema
700
- if schema[:$ref]
701
- ref = schema[:$ref]
702
- if ref.start_with?('#/')
703
- parts = ref.sub('#/', '').split('/')
704
- resolved = openapi
705
- parts.each do |part|
706
- resolved = resolved[part.to_sym]
707
- break unless resolved
708
- end
709
- schema = resolved if resolved
710
- end
711
- end
712
-
713
- example = schema[:example]
714
- if example.nil? && schema[:examples].is_a?(Hash)
715
- ex_key = schema[:examples].keys.first
716
- example = schema[:examples][ex_key][:value] if ex_key
717
- end
718
- end
719
- end
720
-
721
- response_body = example || response_obj[:description] || "INFO: Unable to resolve response body from #{openapi_spec} => { 'http_method': '#{method_str.upcase}', 'path': '#{request_path}', 'response_code': '#{response_code}' }"
722
-
723
- # Serialize based on content_type
724
- if content_type =~ /json/i && (response_body.is_a?(Hash) || response_body.is_a?(Array))
725
- response_body = JSON.generate(response_body)
726
- else
727
- response_body = response_body.to_s
728
- end
823
+ # Serialize response_body based on content_type
824
+ if content_type =~ /json/i && (response_body.is_a?(Hash) || response_body.is_a?(Array))
825
+ response_body = JSON.generate(response_body)
826
+ else
827
+ response_body = response_body.to_s
729
828
  end
730
829
 
731
830
  response_lines = [
data/lib/pwn/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PWN
4
- VERSION = '0.5.372'
4
+ VERSION = '0.5.373'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pwn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.372
4
+ version: 0.5.373
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.