pwn 0.5.480 → 0.5.481
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/README.md +3 -3
- data/lib/pwn/plugins/repl.rb +136 -52
- data/lib/pwn/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f8f0ace17ae36a1a8e60fd015ad3c0a37dcaa22fd3490806b4fc033bd974ae70
|
|
4
|
+
data.tar.gz: 4a3b5eb17658a0a7c63e6e49bd87cad5740112ed1c2d77207fd0cde9949a50d6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d97493c22667ef54489045fd1bdd5ff8940701fa266e923849f8784feacc546be605154bc215a15823b9c424a4ba025e6bd95d3a1b3e86eba3d70987bc55c6c0
|
|
7
|
+
data.tar.gz: 0636daf478dff9780efe1b14478d40d06e109927c955d8d8c50f8c1c8e5c1bd7faaad9cfc8f98254084f4f384cdaa90c4698d563e57d386be38449193600c2e4
|
data/.rubocop.yml
CHANGED
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.
|
|
40
|
+
pwn[v0.5.481]:001 >>> PWN.help
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
[](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.
|
|
55
|
+
pwn[v0.5.481]: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.
|
|
65
|
+
pwn[v0.5.481]: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:
|
data/lib/pwn/plugins/repl.rb
CHANGED
|
@@ -64,19 +64,10 @@ module PWN
|
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
region = PWN::Env[:plugins][:meshtastic][:channel][active_channel][:region]
|
|
70
|
-
topic = PWN::Env[:plugins][:meshtastic][:channel][active_channel][:topic]
|
|
71
|
-
channel_num = PWN::Env[:plugins][:meshtastic][:channel][active_channel][:channel_num]
|
|
67
|
+
ps1_proc = "#{name}[#{version}]:#{line_count} #{dchars} ".to_s.scrub
|
|
68
|
+
ps1_proc = '' if pi.config.pwn_mesh
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
name = "\001\e[1m\002\001\e[32m\002#{pi.config.prompt_name}\001\e[0m\002"
|
|
75
|
-
dchars = "\001\e[32m\002>>>\001\e[33m\002"
|
|
76
|
-
dchars = "\001\e[33m\002***\001\e[33m\002" if mode == :splat
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
"#{name}[#{version}]:#{line_count} #{dchars} ".to_s.scrub
|
|
70
|
+
ps1_proc
|
|
80
71
|
end
|
|
81
72
|
rescue StandardError => e
|
|
82
73
|
raise e
|
|
@@ -472,24 +463,77 @@ module PWN
|
|
|
472
463
|
# Init ncurses UI (idempotent) with separate RX (top) and TX (bottom) panes
|
|
473
464
|
Curses.init_screen
|
|
474
465
|
Curses.curs_set(0)
|
|
475
|
-
|
|
476
|
-
|
|
466
|
+
Curses.noecho
|
|
467
|
+
Curses.cbreak
|
|
468
|
+
Curses.crmode
|
|
469
|
+
Curses.ESCDELAY = 0
|
|
477
470
|
Curses.start_color
|
|
478
471
|
Curses.use_default_colors
|
|
479
472
|
|
|
473
|
+
mesh_highlight_colors = [
|
|
474
|
+
{ fg: Curses::COLOR_RED, bg: Curses::COLOR_WHITE },
|
|
475
|
+
{ fg: Curses::COLOR_GREEN, bg: Curses::COLOR_BLACK },
|
|
476
|
+
{ fg: Curses::COLOR_YELLOW, bg: Curses::COLOR_BLACK },
|
|
477
|
+
{ fg: Curses::COLOR_BLUE, bg: Curses::COLOR_WHITE },
|
|
478
|
+
{ fg: Curses::COLOR_CYAN, bg: Curses::COLOR_BLACK },
|
|
479
|
+
{ fg: Curses::COLOR_MAGENTA, bg: Curses::COLOR_WHITE },
|
|
480
|
+
{ fg: Curses::COLOR_WHITE, bg: Curses::COLOR_BLUE }
|
|
481
|
+
]
|
|
482
|
+
mesh_highlight_colors.each_with_index do |hash, idx|
|
|
483
|
+
color_id = idx + 1
|
|
484
|
+
color_fg = hash[:fg]
|
|
485
|
+
color_bg = hash[:bg]
|
|
486
|
+
Curses.init_pair(color_id, color_fg, color_bg)
|
|
487
|
+
end
|
|
488
|
+
PWN.const_set(:MeshColors, (1..mesh_highlight_colors.length).to_a)
|
|
489
|
+
PWN.const_set(:MeshLastPair, PWN::MeshColors.sample)
|
|
490
|
+
|
|
491
|
+
mesh_ui_colors = []
|
|
492
|
+
mesh_highlight_colors.each_with_index do |hl_hash, idx|
|
|
493
|
+
ui_hash = {
|
|
494
|
+
color_id: idx + 10,
|
|
495
|
+
fg: hl_hash[:fg],
|
|
496
|
+
bg: -1
|
|
497
|
+
}
|
|
498
|
+
Curses.init_pair(ui_hash[:color_id], ui_hash[:fg], ui_hash[:bg])
|
|
499
|
+
mesh_ui_colors.push(ui_hash)
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
red = mesh_ui_colors[0][:color_id]
|
|
503
|
+
green = mesh_ui_colors[1][:color_id]
|
|
504
|
+
yellow = mesh_ui_colors[2][:color_id]
|
|
505
|
+
blue = mesh_ui_colors[3][:color_id]
|
|
506
|
+
cyan = mesh_ui_colors[4][:color_id]
|
|
507
|
+
magenta = mesh_ui_colors[5][:color_id]
|
|
508
|
+
white = mesh_ui_colors[6][:color_id]
|
|
509
|
+
|
|
480
510
|
rx_height = Curses.lines - 4
|
|
481
|
-
rx_win = Curses::Window.new(rx_height, Curses.cols,
|
|
511
|
+
rx_win = Curses::Window.new(rx_height, Curses.cols, 0, 0)
|
|
482
512
|
rx_win.scrollok(true)
|
|
483
513
|
rx_win.nodelay = true
|
|
484
|
-
rx_win.
|
|
485
|
-
|
|
514
|
+
rx_win.attron(Curses.color_pair(cyan) | Curses::A_BOLD)
|
|
515
|
+
|
|
516
|
+
# Make rx_header bold and green
|
|
517
|
+
rx_win.attron(Curses.color_pair(green) | Curses::A_BOLD)
|
|
518
|
+
rx_header = "<<< #{host}:#{port} | #{region}/#{topic} | ch:#{channel_num} >>>"
|
|
519
|
+
rx_header_len = rx_header.length
|
|
520
|
+
rx_header_pos = (Curses.cols / 2) - (rx_header_len / 2)
|
|
521
|
+
rx_win.setpos(1, rx_header_pos)
|
|
522
|
+
rx_win.addstr(rx_header)
|
|
523
|
+
rx_win.attroff(Curses.color_pair(green) | Curses::A_BOLD)
|
|
524
|
+
# Jump two lines below header before messages begin
|
|
525
|
+
rx_win.setpos(2, 1)
|
|
526
|
+
rx_win.attron(Curses.color_pair(cyan) | Curses::A_BOLD)
|
|
527
|
+
rx_header_bottom_line = "\u2014" * (Curses.cols - 2)
|
|
528
|
+
rx_header_bottom_line_pos = (Curses.cols / 2) - (rx_header_bottom_line.length / 2)
|
|
529
|
+
rx_win.addstr(rx_header_bottom_line)
|
|
530
|
+
rx_win.attroff(Curses.color_pair(cyan) | Curses::A_BOLD)
|
|
531
|
+
|
|
486
532
|
rx_win.refresh
|
|
487
533
|
|
|
488
534
|
tx_win = Curses::Window.new(4, Curses.cols, rx_height, 0)
|
|
489
535
|
tx_win.scrollok(false)
|
|
490
536
|
tx_win.nodelay = true
|
|
491
|
-
tx_win.box('|', "\u2500")
|
|
492
|
-
tx_win.addstr("TX Pane (echo while typing, last sent shown after Enter)\n")
|
|
493
537
|
tx_win.refresh
|
|
494
538
|
|
|
495
539
|
PWN.const_set(:MeshRxWin, rx_win)
|
|
@@ -498,24 +542,61 @@ module PWN
|
|
|
498
542
|
PWN.const_set(:MeshPi, pi)
|
|
499
543
|
|
|
500
544
|
# Live typing echo thread (idempotent)
|
|
501
|
-
tx_prompt = "#{region}/#{topic}
|
|
545
|
+
tx_prompt = "#{region}/#{topic} >>>"
|
|
502
546
|
echo_thread = Thread.new do
|
|
547
|
+
last_drawn = nil
|
|
503
548
|
loop do
|
|
504
549
|
break unless pi.config.pwn_mesh
|
|
505
550
|
|
|
506
551
|
tx_win = PWN.const_get(:MeshTxWin)
|
|
507
552
|
mutex = PWN.const_get(:MeshMutex)
|
|
508
553
|
msg_input = pi.input.line_buffer.to_s
|
|
509
|
-
ts = Time.now.strftime('%H:%M:%S')
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
554
|
+
ts = Time.now.strftime('%H:%M:%S%z')
|
|
555
|
+
cursor_pos = (Readline.respond_to?(:point) ? Readline.point : msg_input.length)
|
|
556
|
+
prefix = "[#{ts}] [TX] #{tx_prompt} "
|
|
557
|
+
base_line = "#{prefix}#{msg_input}"
|
|
558
|
+
cursor_abs_index = prefix.length + cursor_pos
|
|
559
|
+
current_line = base_line
|
|
560
|
+
if current_line != last_drawn
|
|
561
|
+
mutex.synchronize do
|
|
562
|
+
# Only show draft if after_read hook hasn't just written final TX (heuristic)
|
|
563
|
+
tx_win.clear
|
|
564
|
+
tx_win.attron(Curses.color_pair(red) | Curses::A_BOLD)
|
|
565
|
+
tx_header_bottom_line = "\u2014" * (Curses.cols - 2)
|
|
566
|
+
tx_header_bottom_line_pos = (Curses.cols / 2) - (rx_header_bottom_line.length / 2)
|
|
567
|
+
tx_win.addstr(rx_header_bottom_line)
|
|
568
|
+
tx_win.attroff(Curses.color_pair(red) | Curses::A_BOLD)
|
|
569
|
+
tx_win.attron(Curses.color_pair(yellow) | Curses::A_BOLD)
|
|
570
|
+
|
|
571
|
+
inner_width = Curses.cols - 2
|
|
572
|
+
segments = current_line.chars.each_slice(inner_width).map(&:join)
|
|
573
|
+
available_rows = tx_win.maxy - 2
|
|
574
|
+
segments.first(available_rows).each_with_index do |seg, idx|
|
|
575
|
+
tx_win.setpos(1 + idx, 1)
|
|
576
|
+
start_index = idx * inner_width
|
|
577
|
+
end_index = start_index + inner_width - 1
|
|
578
|
+
if cursor_abs_index.between?(start_index, end_index)
|
|
579
|
+
cursor_col = cursor_abs_index - start_index
|
|
580
|
+
(0..inner_width).each do |col|
|
|
581
|
+
ch = seg[col] || ' '
|
|
582
|
+
if col == cursor_col
|
|
583
|
+
tx_win.attron(Curses.color_pair(red) | Curses::A_REVERSE | Curses::A_BOLD)
|
|
584
|
+
tx_win.addch(ch)
|
|
585
|
+
tx_win.attroff(Curses.color_pair(red) | Curses::A_REVERSE | Curses::A_BOLD)
|
|
586
|
+
else
|
|
587
|
+
tx_win.addch(ch)
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
else
|
|
591
|
+
tx_win.addstr(seg.ljust(inner_width))
|
|
592
|
+
end
|
|
593
|
+
end
|
|
594
|
+
tx_win.attroff(Curses.color_pair(yellow) | Curses::A_BOLD)
|
|
595
|
+
tx_win.refresh
|
|
596
|
+
end
|
|
597
|
+
last_drawn = current_line
|
|
517
598
|
end
|
|
518
|
-
sleep 0.
|
|
599
|
+
sleep 0.005
|
|
519
600
|
end
|
|
520
601
|
end
|
|
521
602
|
echo_thread.abort_on_exception = false
|
|
@@ -545,22 +626,38 @@ module PWN
|
|
|
545
626
|
mutex = PWN.const_get(:MeshMutex)
|
|
546
627
|
|
|
547
628
|
from = packet[:node_id_from]
|
|
548
|
-
absolute_topic = "#{region}/#{topic.gsub('#', from)}
|
|
629
|
+
absolute_topic = "#{region}/#{topic.gsub('#', from)}"
|
|
549
630
|
to = packet[:node_id_to]
|
|
550
631
|
rx_text = decoded[:payload]
|
|
551
|
-
ts = Time.now.strftime('%H:%M:%S')
|
|
632
|
+
ts = Time.now.strftime('%H:%M:%S%z')
|
|
633
|
+
|
|
634
|
+
# Select a random color pair different from the last used one
|
|
635
|
+
colors_arr = PWN.const_get(:MeshColors)
|
|
636
|
+
last_pair = PWN.const_get(:MeshLastPair)
|
|
637
|
+
PWN.send(:remove_const, :MeshLastPair)
|
|
638
|
+
pair_choices = colors_arr.reject { |c| c == last_pair }
|
|
639
|
+
pair = pair_choices.sample
|
|
640
|
+
PWN.const_set(:MeshLastPair, pair)
|
|
641
|
+
rx_win.attron(Curses.color_pair(pair) | Curses::A_REVERSE)
|
|
642
|
+
|
|
643
|
+
current_line = "\n[#{ts}] [RX] #{absolute_topic} >>> #{rx_text}"
|
|
644
|
+
current_line = "\n[#{ts}] [RX][DM INTERCEPTED] #{absolute_topic} to: #{to} >>> #{rx_text}" unless to == '!ffffffff'
|
|
645
|
+
|
|
552
646
|
mutex.synchronize do
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
647
|
+
inner_width = Curses.cols - 2
|
|
648
|
+
content = current_line.sub(/\A\n/, '')
|
|
649
|
+
segments = content.scan(/.{1,#{inner_width}}/)
|
|
650
|
+
segments.each do |seg|
|
|
651
|
+
rx_win.addstr("\n")
|
|
652
|
+
rx_win.setpos(rx_win.cury, 1)
|
|
653
|
+
line = seg.ljust(inner_width)
|
|
654
|
+
rx_win.addstr(line)
|
|
557
655
|
end
|
|
558
|
-
# rx_win.addstr("#{msg.inspect}\n\n\n")
|
|
559
656
|
rx_win.refresh
|
|
560
657
|
end
|
|
658
|
+
rx_win.attroff(Curses.color_pair(pair) | Curses::A_REVERSE)
|
|
561
659
|
end
|
|
562
660
|
end
|
|
563
|
-
PWN.const_set(:MqttSubThread, true)
|
|
564
661
|
rescue StandardError => e
|
|
565
662
|
raise e
|
|
566
663
|
end
|
|
@@ -648,6 +745,8 @@ module PWN
|
|
|
648
745
|
PWN.const_get(:MeshTxWin).close
|
|
649
746
|
PWN.send(:remove_const, :MeshTxWin)
|
|
650
747
|
end
|
|
748
|
+
PWN.send(:remove_const, :MeshColors) if PWN.const_defined?(:MeshColors)
|
|
749
|
+
PWN.send(:remove_const, :MeshLastPair) if PWN.const_defined?(:MeshLastPair)
|
|
651
750
|
PWN.send(:remove_const, :MeshPi) if PWN.const_defined?(:MeshPi)
|
|
652
751
|
PWN.send(:remove_const, :MeshMutex) if PWN.const_defined?(:MeshMutex)
|
|
653
752
|
PWN.send(:remove_const, :MqttSubThread) if PWN.const_defined?(:MqttSubThread)
|
|
@@ -805,21 +904,6 @@ module PWN
|
|
|
805
904
|
text: tx_text,
|
|
806
905
|
psks: psks
|
|
807
906
|
)
|
|
808
|
-
|
|
809
|
-
# Update TX pane (bottom) with last sent message
|
|
810
|
-
if PWN.const_defined?(:MeshTxWin)
|
|
811
|
-
tx_win = PWN.const_get(:MeshTxWin)
|
|
812
|
-
mutex = PWN.const_get(:MeshMutex)
|
|
813
|
-
absolute_topic = "#{region}/#{topic.gsub('#', from)}:#{channel_num}"
|
|
814
|
-
ts = Time.now.strftime('%H:%M:%S')
|
|
815
|
-
mutex.synchronize do
|
|
816
|
-
tx_win.clear
|
|
817
|
-
tx_win.box('|', "\u2500")
|
|
818
|
-
tx_win.setpos(1, 1)
|
|
819
|
-
tx_win.addstr("[#{ts}] [TX] #{absolute_topic} >>> #{to}: #{tx_text}")
|
|
820
|
-
tx_win.refresh
|
|
821
|
-
end
|
|
822
|
-
end
|
|
823
907
|
end
|
|
824
908
|
end
|
|
825
909
|
rescue StandardError => e
|
data/lib/pwn/version.rb
CHANGED