pwn 0.5.450 → 0.5.453
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 +4 -4
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +8 -4
- data/Gemfile +4 -9
- data/README.md +3 -3
- data/etc/pwn.yaml.EXAMPLE +34 -33
- data/find_latest_gem_versions_per_Gemfile.sh +3 -0
- data/lib/pwn/ai/grok.rb +20 -38
- data/lib/pwn/ai/introspection.rb +42 -44
- data/lib/pwn/ai/ollama.rb +21 -38
- data/lib/pwn/ai/open_ai.rb +20 -149
- data/lib/pwn/blockchain/btc.rb +4 -4
- data/lib/pwn/config.rb +56 -54
- data/lib/pwn/plugins/assembly.rb +14 -3
- data/lib/pwn/plugins/repl.rb +8 -67
- data/lib/pwn/plugins/transparent_browser.rb +333 -147
- data/lib/pwn/reports/sast.rb +1 -54
- data/lib/pwn/sast/pom_version.rb +8 -14
- data/lib/pwn/sast/test_case_engine.rb +8 -15
- data/lib/pwn/version.rb +1 -1
- data/third_party/pwn_rdoc.jsonl +29 -25
- metadata +35 -7
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'diffy'
|
|
3
4
|
require 'em/pure_ruby'
|
|
4
5
|
require 'faye/websocket'
|
|
5
|
-
|
|
6
|
+
require 'nokogiri'
|
|
6
7
|
require 'openssl'
|
|
7
8
|
require 'rest-client'
|
|
8
9
|
require 'securerandom'
|
|
10
|
+
require 'selenium/devtools/v140'
|
|
9
11
|
require 'selenium/webdriver'
|
|
10
|
-
require 'selenium/devtools'
|
|
11
12
|
require 'socksify'
|
|
12
13
|
require 'timeout'
|
|
13
14
|
require 'watir'
|
|
@@ -336,14 +337,21 @@ module PWN
|
|
|
336
337
|
chrome_types = %i[chrome headless_chrome]
|
|
337
338
|
firefox_types = %i[firefox headless_firefox]
|
|
338
339
|
|
|
339
|
-
#
|
|
340
|
-
|
|
340
|
+
# Switch to the last opened window which should be the active tab
|
|
341
|
+
# if it doesn't work, try the first window handle. In chrome they
|
|
342
|
+
# get reversed sometimes ¯\_(ツ)_/¯
|
|
341
343
|
target_window_handle = browser_obj[:browser].driver.window_handles.last
|
|
342
|
-
|
|
344
|
+
begin
|
|
345
|
+
browser_obj[:browser].driver.switch_to.window(target_window_handle)
|
|
346
|
+
|
|
347
|
+
url = 'about:about'
|
|
348
|
+
url = 'chrome://chrome-urls' if chrome_types.include?(browser_type)
|
|
349
|
+
browser_obj[:browser].goto(url)
|
|
350
|
+
rescue Selenium::WebDriver::Error::WebDriverError
|
|
351
|
+
target_window_handle = browser_obj[:browser].driver.window_handles.first
|
|
352
|
+
retry
|
|
353
|
+
end
|
|
343
354
|
|
|
344
|
-
url = 'about:about'
|
|
345
|
-
url = 'chrome://chrome-urls/' if chrome_types.include?(browser_type)
|
|
346
|
-
browser_obj[:browser].goto(url)
|
|
347
355
|
rand_tab = SecureRandom.hex(8)
|
|
348
356
|
browser_obj[:browser].execute_script("document.title = 'about:about-#{rand_tab}'")
|
|
349
357
|
|
|
@@ -467,6 +475,8 @@ module PWN
|
|
|
467
475
|
case js
|
|
468
476
|
when 'clear', 'clear;', 'clear()', 'clear();'
|
|
469
477
|
script = 'console.clear()'
|
|
478
|
+
when 'debugger', 'debugger;', 'debugger()', 'debugger();'
|
|
479
|
+
script = 'debugger'
|
|
470
480
|
else
|
|
471
481
|
case return_to.to_s.downcase.to_sym
|
|
472
482
|
when :stdout
|
|
@@ -931,205 +941,387 @@ module PWN
|
|
|
931
941
|
end
|
|
932
942
|
|
|
933
943
|
# Supported Method Parameters::
|
|
934
|
-
# PWN::Plugins::TransparentBrowser.
|
|
935
|
-
# browser_obj: 'required - browser_obj returned from #open method)'
|
|
936
|
-
# action: 'optional - action to take :pause|:resume (Defaults to :pause)',
|
|
937
|
-
# url: 'optional - URL to navigate to after pausing debugger (Defaults to nil)'
|
|
944
|
+
# current_dom = PWN::Plugins::TransparentBrowser.dom(
|
|
945
|
+
# browser_obj: 'required - browser_obj returned from #open method)'
|
|
938
946
|
# )
|
|
939
947
|
|
|
940
|
-
public_class_method def self.
|
|
948
|
+
public_class_method def self.dom(opts = {})
|
|
941
949
|
browser_obj = opts[:browser_obj]
|
|
942
|
-
|
|
943
|
-
verified = verify_devtools_browser(browser_obj: browser_obj, supported: supported)
|
|
950
|
+
verified = verify_devtools_browser(browser_obj: browser_obj)
|
|
944
951
|
puts 'This browser is not supported for DevTools operations.' unless verified
|
|
945
952
|
return unless verified
|
|
946
953
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
case action.to_s.downcase.to_sym
|
|
951
|
-
when :pause
|
|
952
|
-
browser_obj[:devtools].send_cmd(
|
|
953
|
-
'EventBreakpoints.setInstrumentationBreakpoint',
|
|
954
|
-
eventName: 'scriptFirstStatement'
|
|
955
|
-
)
|
|
956
|
-
# browser_obj[:devtools].send_cmd('Debugger.enable')
|
|
957
|
-
# browser_obj[:devtools].send_cmd(
|
|
958
|
-
# 'Debugger.setInstrumentationBreakpoint',
|
|
959
|
-
# instrumentation: 'beforeScriptExecution'
|
|
960
|
-
# )
|
|
961
|
-
|
|
962
|
-
# browser_obj[:devtools].send_cmd(
|
|
963
|
-
# 'EventBreakpoints.setInstrumentationBreakpoint',
|
|
964
|
-
# eventName: 'load'
|
|
965
|
-
# )
|
|
966
|
-
|
|
967
|
-
# browser_obj[:devtools].send_cmd(
|
|
968
|
-
# 'Debugger.setPauseOnExceptions',
|
|
969
|
-
# state: 'all'
|
|
970
|
-
# )
|
|
954
|
+
dom_str = console(browser_obj: browser_obj, js: 'document.documentElement.outerHTML', return_to: :stdout)
|
|
955
|
+
raise 'DOM capture failed: returned nil or empty string. Check DevTools connection.' if dom_str.nil? || dom_str.strip.empty?
|
|
971
956
|
|
|
972
|
-
|
|
973
|
-
Timeout.timeout(1) do
|
|
974
|
-
browser_obj[:browser].refresh if url.nil?
|
|
975
|
-
browser_obj[:browser].goto(url) unless url.nil?
|
|
976
|
-
end
|
|
977
|
-
rescue Timeout::Error
|
|
978
|
-
url
|
|
979
|
-
end
|
|
980
|
-
when :resume
|
|
981
|
-
browser_obj[:devtools].send_cmd(
|
|
982
|
-
'EventBreakpoints.removeInstrumentationBreakpoint',
|
|
983
|
-
eventName: 'scriptFirstStatement'
|
|
984
|
-
)
|
|
985
|
-
browser_obj[:devtools].send_cmd('Debugger.resume')
|
|
986
|
-
else
|
|
987
|
-
raise 'ERROR: action parameter must be :pause or :resume'
|
|
988
|
-
end
|
|
957
|
+
Nokogiri::HTML.parse(dom_str)
|
|
989
958
|
rescue StandardError => e
|
|
990
959
|
raise e
|
|
991
960
|
end
|
|
992
961
|
|
|
993
962
|
# Supported Method Parameters::
|
|
994
|
-
#
|
|
963
|
+
# page_state = PWN::Plugins::TransparentBrowser.get_page_state(
|
|
995
964
|
# browser_obj: 'required - browser_obj returned from #open method)'
|
|
996
965
|
# )
|
|
997
966
|
|
|
998
|
-
public_class_method def self.
|
|
967
|
+
public_class_method def self.get_page_state(opts = {})
|
|
999
968
|
browser_obj = opts[:browser_obj]
|
|
1000
|
-
|
|
1001
|
-
verified = verify_devtools_browser(browser_obj: browser_obj, supported: supported)
|
|
969
|
+
verified = verify_devtools_browser(browser_obj: browser_obj)
|
|
1002
970
|
puts 'This browser is not supported for DevTools operations.' unless verified
|
|
1003
971
|
return unless verified
|
|
1004
972
|
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
973
|
+
js = <<~JS.strip
|
|
974
|
+
(function() {
|
|
975
|
+
try {
|
|
976
|
+
let ls = {};
|
|
977
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
978
|
+
let key = localStorage.key(i);
|
|
979
|
+
ls[key] = localStorage.getItem(key);
|
|
980
|
+
}
|
|
981
|
+
let ss = {};
|
|
982
|
+
for (let i = 0; i < sessionStorage.length; i++) {
|
|
983
|
+
let key = sessionStorage.key(i);
|
|
984
|
+
ss[key] = sessionStorage.getItem(key);
|
|
985
|
+
}
|
|
1013
986
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
# )
|
|
987
|
+
let scripts = Array.from(document.scripts).map(s => ({
|
|
988
|
+
src: s.src,
|
|
989
|
+
innerHTML: s.innerHTML
|
|
990
|
+
})).filter(s => s.src || s.innerHTML);
|
|
1019
991
|
|
|
1020
|
-
|
|
1021
|
-
browser_obj = opts[:browser_obj]
|
|
1022
|
-
supported = %i[chrome headless_chrome]
|
|
1023
|
-
verified = verify_devtools_browser(browser_obj: browser_obj, supported: supported)
|
|
1024
|
-
puts 'This browser is not supported for DevTools operations.' unless verified
|
|
1025
|
-
return unless verified
|
|
992
|
+
let stylesheets = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).map(l => l.href).filter(h => h);
|
|
1026
993
|
|
|
1027
|
-
|
|
1028
|
-
steps = 1 if steps.zero? || steps.negative?
|
|
994
|
+
let inline_styles = Array.from(document.querySelectorAll('style')).map(s => s.innerHTML).filter(c => c);
|
|
1029
995
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
996
|
+
let forms = Array.from(document.forms).map(f => ({
|
|
997
|
+
action: f.action,
|
|
998
|
+
method: f.method,
|
|
999
|
+
elements: Array.from(f.elements).map(e => ({
|
|
1000
|
+
name: e.name,
|
|
1001
|
+
type: e.type,
|
|
1002
|
+
value: e.value
|
|
1003
|
+
}))
|
|
1004
|
+
}));
|
|
1035
1005
|
|
|
1036
|
-
|
|
1037
|
-
diff_hash[:dom_before_step] = dom_before
|
|
1006
|
+
let iframes = Array.from(document.querySelectorAll('iframe')).map(i => i.src).filter(s => s);
|
|
1038
1007
|
|
|
1039
|
-
|
|
1008
|
+
let csp_meta = document.querySelector('meta[http-equiv="Content-Security-Policy"]');
|
|
1009
|
+
let csp = csp_meta ? csp_meta.content : null;
|
|
1040
1010
|
|
|
1041
|
-
|
|
1042
|
-
|
|
1011
|
+
let feature_policy = [];
|
|
1012
|
+
if (document.featurePolicy) {
|
|
1013
|
+
feature_policy = document.featurePolicy.allowedFeatures().sort();
|
|
1014
|
+
}
|
|
1043
1015
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1016
|
+
let is_framed = false;
|
|
1017
|
+
try {
|
|
1018
|
+
if (window.top !== window.self) {
|
|
1019
|
+
is_framed = true;
|
|
1020
|
+
}
|
|
1021
|
+
} catch (e) {
|
|
1022
|
+
is_framed = true;
|
|
1023
|
+
}
|
|
1046
1024
|
|
|
1047
|
-
|
|
1048
|
-
|
|
1025
|
+
let resources = window.performance.getEntriesByType('resource').map(e => ({
|
|
1026
|
+
name: e.name,
|
|
1027
|
+
initiatorType: e.initiatorType
|
|
1028
|
+
}));
|
|
1029
|
+
|
|
1030
|
+
// Enhanced globals capture with values
|
|
1031
|
+
let globals = {};
|
|
1032
|
+
let propNames = Object.getOwnPropertyNames(window).sort();
|
|
1033
|
+
const safeStringify = (value, depth = 0) => {
|
|
1034
|
+
if (depth > 5) return '[Max depth exceeded]'; // Prevent deep recursion
|
|
1035
|
+
try {
|
|
1036
|
+
return JSON.stringify(value, (key, val) => {
|
|
1037
|
+
if (typeof val === 'function') {
|
|
1038
|
+
return val.toString(); // Capture function source
|
|
1039
|
+
} else if (typeof val === 'symbol') {
|
|
1040
|
+
return val.toString();
|
|
1041
|
+
} else if (val === window) {
|
|
1042
|
+
return '[Window reference]'; // Avoid circularity
|
|
1043
|
+
} else if (val && typeof val === 'object') {
|
|
1044
|
+
if (depth > 5) return '[Object (depth limit)]';
|
|
1045
|
+
return val; // Let JSON handle, recurse with depth
|
|
1046
|
+
}
|
|
1047
|
+
return val;
|
|
1048
|
+
});
|
|
1049
|
+
} catch (e) {
|
|
1050
|
+
return '[Stringify error: ' + e.message + ']';
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
|
|
1054
|
+
for (let name of propNames) {
|
|
1055
|
+
try {
|
|
1056
|
+
let value = window[name];
|
|
1057
|
+
globals[name] = safeStringify(value);
|
|
1058
|
+
} catch (e) {
|
|
1059
|
+
globals[name] = '[Access error: ' + e.message + ']';
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1049
1062
|
|
|
1050
|
-
|
|
1063
|
+
return JSON.stringify({
|
|
1064
|
+
cookies: document.cookie,
|
|
1065
|
+
localStorage: ls,
|
|
1066
|
+
sessionStorage: ss,
|
|
1067
|
+
globals: globals, // Now an object with name: stringified_value
|
|
1068
|
+
scripts: scripts,
|
|
1069
|
+
stylesheets: stylesheets,
|
|
1070
|
+
inline_styles: inline_styles,
|
|
1071
|
+
stack: new Error().stack,
|
|
1072
|
+
location: {
|
|
1073
|
+
href: location.href,
|
|
1074
|
+
origin: location.origin,
|
|
1075
|
+
pathname: location.pathname,
|
|
1076
|
+
search: location.search,
|
|
1077
|
+
hash: location.hash
|
|
1078
|
+
},
|
|
1079
|
+
referrer: document.referrer,
|
|
1080
|
+
userAgent: navigator.userAgent,
|
|
1081
|
+
html_snapshot: document.documentElement.outerHTML,
|
|
1082
|
+
forms: forms,
|
|
1083
|
+
iframes: iframes,
|
|
1084
|
+
csp: csp,
|
|
1085
|
+
feature_policy: feature_policy,
|
|
1086
|
+
is_framed: is_framed,
|
|
1087
|
+
has_service_worker: 'serviceWorker' in navigator,
|
|
1088
|
+
resources: resources
|
|
1089
|
+
});
|
|
1090
|
+
} catch (e) {
|
|
1091
|
+
return JSON.stringify({
|
|
1092
|
+
error: e.message,
|
|
1093
|
+
stack: e.stack
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
})()
|
|
1097
|
+
JS
|
|
1098
|
+
|
|
1099
|
+
browser_obj[:devtools].send_cmd('Console.clearMessages')
|
|
1100
|
+
browser_obj[:devtools].send_cmd('Log.clear')
|
|
1101
|
+
console_events = []
|
|
1102
|
+
browser_obj[:browser].driver.on_log_event(:console) { |event| console_events.push(event) }
|
|
1103
|
+
|
|
1104
|
+
# page_state = console(browser_obj: browser_obj, js: js, return_to: :stdout)
|
|
1105
|
+
console_cmd = { expression: js }
|
|
1106
|
+
runtime_resp = browser_obj[:devtools].send_cmd('Runtime.evaluate', **console_cmd)
|
|
1107
|
+
page_state = runtime_resp['result']['result']['value']
|
|
1108
|
+
JSON.parse(page_state, symbolize_names: true)
|
|
1109
|
+
rescue JSON::ParserError => e
|
|
1110
|
+
raise "Failed to parse state JSON: #{e.message}. Raw output: #{state_json.inspect}"
|
|
1051
1111
|
rescue StandardError => e
|
|
1052
1112
|
raise e
|
|
1053
1113
|
end
|
|
1054
1114
|
|
|
1055
1115
|
# Supported Method Parameters::
|
|
1056
|
-
# PWN::Plugins::TransparentBrowser.
|
|
1057
|
-
# browser_obj: 'required - browser_obj returned from #open method)'
|
|
1058
|
-
# steps: 'optional - number of steps taken (Defaults to 1)'
|
|
1116
|
+
# messages = PWN::Plugins::TransparentBrowser.devtools_websocket_messages(
|
|
1117
|
+
# browser_obj: 'required - browser_obj returned from #open method)'
|
|
1059
1118
|
# )
|
|
1060
1119
|
|
|
1061
|
-
public_class_method def self.
|
|
1120
|
+
public_class_method def self.devtools_websocket_messages(opts = {})
|
|
1062
1121
|
browser_obj = opts[:browser_obj]
|
|
1063
|
-
|
|
1064
|
-
verified = verify_devtools_browser(browser_obj: browser_obj, supported: supported)
|
|
1122
|
+
verified = verify_devtools_browser(browser_obj: browser_obj)
|
|
1065
1123
|
puts 'This browser is not supported for DevTools operations.' unless verified
|
|
1066
1124
|
return unless verified
|
|
1067
1125
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
step = s + 1
|
|
1075
|
-
diff_hash[:step] = step
|
|
1126
|
+
devtools = browser_obj[:devtools]
|
|
1127
|
+
websocket = devtools.instance_variable_get(:@ws)
|
|
1128
|
+
websocket.instance_variable_get(:@messages)[nil]
|
|
1129
|
+
rescue StandardError => e
|
|
1130
|
+
raise e
|
|
1131
|
+
end
|
|
1076
1132
|
|
|
1077
|
-
|
|
1078
|
-
|
|
1133
|
+
# Supported Method Parameters::
|
|
1134
|
+
# PWN::Plugins::TransparentBrowser.debugger(
|
|
1135
|
+
# browser_obj: 'required - browser_obj returned from #open method)',
|
|
1136
|
+
# action: 'optional - action to take :enable|:pause|:resume|:disable (Defaults to :enable)',
|
|
1137
|
+
# )
|
|
1079
1138
|
|
|
1080
|
-
|
|
1139
|
+
public_class_method def self.debugger(opts = {})
|
|
1140
|
+
browser_obj = opts[:browser_obj]
|
|
1141
|
+
verified = verify_devtools_browser(browser_obj: browser_obj)
|
|
1142
|
+
puts 'This browser is not supported for DevTools operations.' unless verified
|
|
1143
|
+
return unless verified
|
|
1081
1144
|
|
|
1082
|
-
|
|
1083
|
-
|
|
1145
|
+
valid_actions = %i[enable pause resume disable]
|
|
1146
|
+
action = opts[:action] ||= :enable
|
|
1147
|
+
action = action.to_s.downcase.to_sym
|
|
1148
|
+
raise 'ERROR: action parameter must be :enable|:pause|:resume|:disable' unless valid_actions.include?(action)
|
|
1084
1149
|
|
|
1085
|
-
|
|
1086
|
-
|
|
1150
|
+
devtools = browser_obj[:devtools]
|
|
1151
|
+
debugger_state = devtools.instance_variable_get(:@debugger_state)
|
|
1087
1152
|
|
|
1088
|
-
|
|
1153
|
+
case action
|
|
1154
|
+
when :enable
|
|
1155
|
+
if debugger_state.is_a?(Hash)
|
|
1156
|
+
debugger_state = devtools.instance_variable_get(:@debugger_state)
|
|
1157
|
+
devtools.remove_instance_variable(:@debugger_state) if debugger_state.is_a?(Hash)
|
|
1158
|
+
devtools.debugger.disable
|
|
1159
|
+
end
|
|
1160
|
+
debugger_state = {}
|
|
1161
|
+
breakpoint_arr = []
|
|
1162
|
+
|
|
1163
|
+
# breakpoint = devtools.debugger.set_instrumentation_breakpoint(instrumentation: 'beforeScriptExecution')
|
|
1164
|
+
bcmd = 'EventBreakpoints.setInstrumentationBreakpoint'
|
|
1165
|
+
event = 'load'
|
|
1166
|
+
breakpoint = devtools.send_cmd(bcmd, eventName: event)
|
|
1167
|
+
breakpoint['result']['breakpointId'] = "#{bcmd}.#{event}.#{SecureRandom.uuid}"
|
|
1168
|
+
breakpoint_arr.push(breakpoint)
|
|
1169
|
+
debugger_state[:breakpoints] = breakpoint_arr
|
|
1170
|
+
|
|
1171
|
+
devtools.runtime.disable
|
|
1172
|
+
devtools.log.disable
|
|
1173
|
+
devtools.network.disable
|
|
1174
|
+
devtools.page.disable
|
|
1175
|
+
devtools.debugger.enable
|
|
1176
|
+
when :pause
|
|
1177
|
+
devtools.debugger.pause
|
|
1178
|
+
Timeout.timeout(5) { browser_obj[:browser].refresh }
|
|
1179
|
+
when :resume
|
|
1180
|
+
devtools.debugger.resume
|
|
1181
|
+
when :disable
|
|
1182
|
+
debugger_state = devtools.instance_variable_get(:@debugger_state)
|
|
1183
|
+
devtools.remove_instance_variable(:@debugger_state) if debugger_state.is_a?(Hash)
|
|
1184
|
+
devtools.debugger.disable
|
|
1089
1185
|
end
|
|
1090
1186
|
|
|
1091
|
-
|
|
1187
|
+
devtools_websocket_messages = devtools_websocket_messages(browser_obj: browser_obj)
|
|
1188
|
+
debugger_state[:method] = devtools_websocket_messages['method']
|
|
1189
|
+
devtools.instance_variable_set(:@debugger_state, debugger_state)
|
|
1190
|
+
devtools
|
|
1191
|
+
rescue Timeout::Error
|
|
1192
|
+
devtools
|
|
1193
|
+
rescue Selenium::WebDriver::Error::WebDriverError => e
|
|
1194
|
+
puts e.message
|
|
1092
1195
|
rescue StandardError => e
|
|
1093
1196
|
raise e
|
|
1094
1197
|
end
|
|
1095
1198
|
|
|
1096
1199
|
# Supported Method Parameters::
|
|
1097
|
-
# PWN::Plugins::TransparentBrowser.
|
|
1200
|
+
# page_state_arr = PWN::Plugins::TransparentBrowser.step(
|
|
1098
1201
|
# browser_obj: 'required - browser_obj returned from #open method)',
|
|
1202
|
+
# action: 'optional - action to take :into|:out|:over (Defaults to :into)',
|
|
1099
1203
|
# steps: 'optional - number of steps taken (Defaults to 1)'
|
|
1100
1204
|
# )
|
|
1101
1205
|
|
|
1102
|
-
public_class_method def self.
|
|
1206
|
+
public_class_method def self.step(opts = {})
|
|
1103
1207
|
browser_obj = opts[:browser_obj]
|
|
1104
1208
|
supported = %i[chrome headless_chrome]
|
|
1105
1209
|
verified = verify_devtools_browser(browser_obj: browser_obj, supported: supported)
|
|
1106
1210
|
puts 'This browser is not supported for DevTools operations.' unless verified
|
|
1107
1211
|
return unless verified
|
|
1108
1212
|
|
|
1213
|
+
valid_actions = %i[into out over]
|
|
1214
|
+
action = opts[:action] ||= :into
|
|
1215
|
+
action = action.to_s.downcase.to_sym
|
|
1216
|
+
raise 'ERROR: action parameter must be :into|:out|:over' unless valid_actions.include?(action)
|
|
1217
|
+
|
|
1109
1218
|
steps = opts[:steps].to_i
|
|
1110
1219
|
steps = 1 if steps.zero? || steps.negative?
|
|
1111
1220
|
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1221
|
+
devtools = browser_obj[:devtools]
|
|
1222
|
+
debugger_state = devtools.instance_variable_get(:@debugger_state)
|
|
1223
|
+
method = debugger_state[:method]
|
|
1224
|
+
if method != 'Debugger.paused'
|
|
1225
|
+
puts 'The debugger must be paused before stepping. Pausing now...'
|
|
1226
|
+
return devtools
|
|
1227
|
+
end
|
|
1117
1228
|
|
|
1118
|
-
|
|
1119
|
-
|
|
1229
|
+
page_state_arr = []
|
|
1230
|
+
steps.times do |s|
|
|
1231
|
+
step_num = s + 1
|
|
1232
|
+
puts "Stepping #{action} (step #{step_num}/#{steps})..."
|
|
1233
|
+
|
|
1234
|
+
# before = get_page_state(browser_obj: browser_obj)
|
|
1235
|
+
# puts before.inspect
|
|
1236
|
+
before = devtools_websocket_messages(browser_obj: browser_obj)
|
|
1237
|
+
method = before['method']
|
|
1238
|
+
# puts before
|
|
1239
|
+
puts "\n"
|
|
1240
|
+
|
|
1241
|
+
if method == 'Debugger.paused'
|
|
1242
|
+
before_location = before['params']['callFrames'].first['location']
|
|
1243
|
+
start_location = before['params']['callFrames'].first['scopeChain'].first['startLocation']
|
|
1244
|
+
before_script_id = start_location['scriptId']
|
|
1245
|
+
from_line_num = start_location['lineNumber']
|
|
1246
|
+
from_column_num = start_location['columnNumber']
|
|
1247
|
+
|
|
1248
|
+
end_location = before['params']['callFrames'].first['scopeChain'].first['endLocation']
|
|
1249
|
+
to_line_num = end_location['lineNumber']
|
|
1250
|
+
to_column_num = end_location['columnNumber']
|
|
1251
|
+
|
|
1252
|
+
source_obj = devtools.debugger.get_script_source(script_id: before_script_id)
|
|
1253
|
+
source_code = source_obj['result']['scriptSource']
|
|
1254
|
+
# puts source_code
|
|
1255
|
+
# gets
|
|
1256
|
+
|
|
1257
|
+
source_lines = source_code.split("\n")
|
|
1258
|
+
source_lines_str = source_lines[from_line_num..to_line_num].join("\n")
|
|
1259
|
+
source_to_review = source_lines_str[from_column_num..to_column_num]
|
|
1260
|
+
|
|
1261
|
+
puts source_to_review
|
|
1262
|
+
request = source_lines_str[from_column_num..to_column_num]
|
|
1263
|
+
ai_analysis = PWN::AI::Introspection.reflect_on(request: request)
|
|
1264
|
+
puts "^^^ #{ai_analysis}" unless ai_analysis.nil?
|
|
1265
|
+
# gets
|
|
1266
|
+
end
|
|
1120
1267
|
|
|
1121
|
-
|
|
1268
|
+
case action
|
|
1269
|
+
when :into
|
|
1270
|
+
devtools.debugger.step_into
|
|
1271
|
+
when :out
|
|
1272
|
+
devtools.debugger.step_out
|
|
1273
|
+
when :over
|
|
1274
|
+
devtools.debugger.step_over
|
|
1275
|
+
end
|
|
1122
1276
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1277
|
+
puts "\n" * 3
|
|
1278
|
+
after = devtools_websocket_messages(browser_obj: browser_obj)
|
|
1279
|
+
method = after['method']
|
|
1280
|
+
# puts after
|
|
1281
|
+
puts "\n"
|
|
1282
|
+
|
|
1283
|
+
if method == 'Debugger.paused'
|
|
1284
|
+
after_location = after['params']['callFrames'].first['scopeChain'].first['object']
|
|
1285
|
+
start_location = after['params']['callFrames'].first['scopeChain'].first['startLocation']
|
|
1286
|
+
after_script_id = start_location['scriptId']
|
|
1287
|
+
from_line_num = start_location['lineNumber']
|
|
1288
|
+
from_column_num = start_location['columnNumber']
|
|
1289
|
+
|
|
1290
|
+
end_location = after['params']['callFrames'].first['scopeChain'].first['endLocation']
|
|
1291
|
+
to_line_num = end_location['lineNumber']
|
|
1292
|
+
to_column_num = end_location['columnNumber']
|
|
1293
|
+
|
|
1294
|
+
source_obj = devtools.debugger.get_script_source(script_id: after_script_id)
|
|
1295
|
+
source_code = source_obj['result']['scriptSource']
|
|
1296
|
+
# puts source_code
|
|
1297
|
+
# gets
|
|
1298
|
+
|
|
1299
|
+
source_lines = source_code.split("\n")
|
|
1300
|
+
source_lines_str = source_lines[from_line_num..to_line_num].join("\n")
|
|
1301
|
+
source_to_review = source_lines_str[from_column_num..to_column_num]
|
|
1302
|
+
|
|
1303
|
+
puts source_to_review
|
|
1304
|
+
request = source_lines_str[from_column_num..to_column_num]
|
|
1305
|
+
ai_analysis = PWN::AI::Introspection.reflect_on(request: request)
|
|
1306
|
+
puts "^^^ #{ai_analysis}" unless ai_analysis.nil?
|
|
1307
|
+
# gets
|
|
1308
|
+
end
|
|
1309
|
+
puts "\n" * 6
|
|
1125
1310
|
|
|
1126
|
-
|
|
1127
|
-
|
|
1311
|
+
# step_hash = {
|
|
1312
|
+
# step: step_num,
|
|
1313
|
+
# action: action,
|
|
1314
|
+
# before: before,
|
|
1315
|
+
# after: after,
|
|
1316
|
+
# diff: diff.to_s(:text)
|
|
1317
|
+
# }
|
|
1128
1318
|
|
|
1129
|
-
|
|
1319
|
+
# page_state_arr.push(step_hash)
|
|
1130
1320
|
end
|
|
1131
1321
|
|
|
1132
|
-
|
|
1322
|
+
devtools
|
|
1323
|
+
rescue Selenium::WebDriver::Error::WebDriverError
|
|
1324
|
+
devtools
|
|
1133
1325
|
rescue StandardError => e
|
|
1134
1326
|
raise e
|
|
1135
1327
|
end
|
|
@@ -1392,28 +1584,22 @@ module PWN
|
|
|
1392
1584
|
keyword: 'optional - keyword in title or url used to close tabs (defaults to closing active tab)'
|
|
1393
1585
|
)
|
|
1394
1586
|
|
|
1395
|
-
#{self}.debugger(
|
|
1396
|
-
browser_obj: 'required - browser_obj returned from #open method)',
|
|
1397
|
-
action: 'optional - action to take :pause|:resume (Defaults to :pause)',
|
|
1398
|
-
url: 'optional - URL to navigate to after pausing debugger (Defaults to nil)'
|
|
1399
|
-
)
|
|
1400
|
-
|
|
1401
1587
|
current_dom = #{self}.dom(
|
|
1402
1588
|
browser_obj: 'required - browser_obj returned from #open method)'
|
|
1403
1589
|
)
|
|
1404
1590
|
|
|
1405
|
-
#{self}.
|
|
1406
|
-
browser_obj: 'required - browser_obj returned from #open method)'
|
|
1407
|
-
steps: 'optional - number of steps taken (Defaults to 1)'
|
|
1591
|
+
page_state = #{self}.get_page_state(
|
|
1592
|
+
browser_obj: 'required - browser_obj returned from #open method)'
|
|
1408
1593
|
)
|
|
1409
1594
|
|
|
1410
|
-
#{self}.
|
|
1595
|
+
#{self}.debugger(
|
|
1411
1596
|
browser_obj: 'required - browser_obj returned from #open method)',
|
|
1412
|
-
|
|
1597
|
+
action: 'optional - action to take :enable|:pause|:resume|:disable (Defaults to :enable)'
|
|
1413
1598
|
)
|
|
1414
1599
|
|
|
1415
|
-
#{self}.
|
|
1600
|
+
#{self}.step(
|
|
1416
1601
|
browser_obj: 'required - browser_obj returned from #open method)',
|
|
1602
|
+
action: 'optional - action to take :into|:out|:over (Defaults to :into)',
|
|
1417
1603
|
steps: 'optional - number of steps taken (Defaults to 1)'
|
|
1418
1604
|
)
|
|
1419
1605
|
|
data/lib/pwn/reports/sast.rb
CHANGED
|
@@ -22,61 +22,8 @@ module PWN
|
|
|
22
22
|
report_name: HTMLEntities.new.encode(report_name.to_s.scrub.strip.chomp),
|
|
23
23
|
data: []
|
|
24
24
|
}
|
|
25
|
-
report_name = opts[:report_name] ||= File.basename(Dir.pwd)
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
# total_entries = results_hash[:data].sum { |entry| entry[:line_no_and_contents].size }
|
|
29
|
-
# puts "Total entries to analyze: #{total_entries}" if engine
|
|
30
|
-
|
|
31
|
-
# percent_complete = 0.0
|
|
32
|
-
# entry_count = 0
|
|
33
|
-
# spin = TTY::Spinner.new(
|
|
34
|
-
# '[:spinner] Report Generation Progress: :percent_complete :entry_count of :total_entries',
|
|
35
|
-
# format: :dots,
|
|
36
|
-
# hide_cursor: true
|
|
37
|
-
# )
|
|
38
|
-
# spin.auto_spin
|
|
39
|
-
|
|
40
|
-
# ai_instrospection = PWN::Env[:ai][:introspection]
|
|
41
|
-
# puts "Analyzing source code using AI engine: #{engine}\nModel: #{model}\nSystem Role Content: #{system_role_content}\nTemperature: #{temp}" if ai_instrospection
|
|
42
|
-
|
|
43
|
-
# results_hash[:data].each do |hash_line|
|
|
44
|
-
# git_repo_root_uri = hash_line[:filename][:git_repo_root_uri]
|
|
45
|
-
# filename = hash_line[:filename][:entry]
|
|
46
|
-
# hash_line[:line_no_and_contents].each do |src_detail|
|
|
47
|
-
# entry_count += 1
|
|
48
|
-
# percent_complete = (entry_count.to_f / total_entries * 100).round(2)
|
|
49
|
-
# line_no = src_detail[:line_no]
|
|
50
|
-
# source_code_snippet = src_detail[:contents]
|
|
51
|
-
# author = src_detail[:author].to_s.scrub.chomp.strip
|
|
52
|
-
# response = nil
|
|
53
|
-
# if ai_instrospection
|
|
54
|
-
# request = {
|
|
55
|
-
# scm_uri: "#{git_repo_root_uri}/#{filename}",
|
|
56
|
-
# line: line_no,
|
|
57
|
-
# source_code_snippet: source_code_snippet
|
|
58
|
-
# }.to_json
|
|
59
|
-
# response = PWN::AI::Introspection.reflect(request: request)
|
|
60
|
-
# end
|
|
61
|
-
# ai_analysis = nil
|
|
62
|
-
# if response.is_a?(Hash)
|
|
63
|
-
# ai_analysis = response[:choices].last[:text] if response[:choices].last.keys.include?(:text)
|
|
64
|
-
# ai_analysis = response[:choices].last[:content] if response[:choices].last.keys.include?(:content)
|
|
65
|
-
# puts "AI Analysis Progress: #{percent_complete}% Line: #{line_no} | Author: #{author} | AI Analysis: #{ai_analysis}\n\n\n" if ai_analysis
|
|
66
|
-
# end
|
|
67
|
-
# src_detail[:ai_analysis] = ai_analysis.to_s.scrub.chomp.strip
|
|
68
|
-
# spin.update(
|
|
69
|
-
# percent_complete: "#{percent_complete}%",
|
|
70
|
-
# entry_count: entry_count,
|
|
71
|
-
# total_entries: total_entries
|
|
72
|
-
# )
|
|
73
|
-
# end
|
|
74
|
-
# end
|
|
75
|
-
|
|
76
|
-
# JSON object Completion
|
|
77
|
-
# File.open("#{dir_path}/pwn_scan_git_source.json", 'w') do |f|
|
|
78
|
-
# f.print(results_hash.to_json)
|
|
79
|
-
# end
|
|
26
|
+
report_name = opts[:report_name] ||= File.basename(Dir.pwd)
|
|
80
27
|
File.write(
|
|
81
28
|
"#{dir_path}/#{report_name}.json",
|
|
82
29
|
JSON.pretty_generate(results_hash)
|