pwn 0.5.657 → 0.5.658

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: af75906b3c582dea27fb9ec31f19c7d3774aabcc928549cee27df74ad75220f3
4
- data.tar.gz: c3424966d7b298c78b3edac59a777ee8e927f130ecefbf84fd0a46371256900e
3
+ metadata.gz: 84155e448029c5d41ebd8dda5e4c00147a3283ebf8258c42f2fdf7cb3f159616
4
+ data.tar.gz: ceecb743a07ddf4d0a2f45434f1b7816e859754762d60f8932f9dd1f4a83d80a
5
5
  SHA512:
6
- metadata.gz: 396a1f16a96e071396689cdfde74364ee9bae88544275f037137007b5ddd94dc1fc37f181a99ef239db5339b5fb24ecf750605ba67dd28bd62969ec9360ed5f6
7
- data.tar.gz: 976c310a1e93bac33b383bcb7702a6350f2268062e71a0bb73ee28454d6ea23798fc654744b88dad048472d721d2417713d5664e81f39e819ddd2ccefe51c555
6
+ metadata.gz: 5cb1bf7948fa027253fa5e2aeb2204d917bf51ab4ebf2710073b8ca3c4fa07f53012a3e8f98e5db5f3dabda050b405119dadf78b7a5583476da35fd11af5c5c7
7
+ data.tar.gz: 07e6832ea6946949d3042f62b03930d5e6f882dd8f246449f314c994459b4f16ce4e32700ab4467b55d44e01f5ba35dbc3788f742d44a7fd79b36026b2d61ccf
@@ -6,6 +6,13 @@ require 'pwn/ai/agent/registry'
6
6
 
7
7
  # Run a shell command on the pwn host. Lifted from the bash branch of the
8
8
  # legacy :pwn_ai_hook (repl.rb).
9
+ #
10
+ # Timeout path must NOT leave Open3's pipe-reader threads racing a closed
11
+ # pipe — that surfaces as noisy:
12
+ # #<Thread:0x... open3.rb:664 run> terminated with exception
13
+ # (report_on_exception is true): IOError stream closed in another thread
14
+ # Fix: own the popen3 lifecycle, process-group kill on timeout, and silence
15
+ # report_on_exception on the reader threads before closing pipes.
9
16
  PWN::AI::Agent::Registry.register(
10
17
  name: 'shell',
11
18
  toolset: 'terminal',
@@ -29,16 +36,84 @@ PWN::AI::Agent::Registry.register(
29
36
  timeout = (args[:timeout] || 120).to_i
30
37
  raise ArgumentError, 'command is required' if cmd.strip.empty?
31
38
 
32
- stdout = stderr = ''
33
- status = nil
34
- begin
35
- Timeout.timeout(timeout) do
36
- stdout, stderr, status = Open3.capture3(cmd)
39
+ stdout = +''
40
+ stderr = +''
41
+ exitstatus = nil
42
+ timed_out = false
43
+
44
+ # pgroup: true => child is session/process-group leader; kill(-pid)
45
+ # reaps the whole tree (shell + grandchildren) on timeout.
46
+ Open3.popen3(cmd, pgroup: true) do |stdin, out_io, err_io, wait_thr|
47
+ stdin.close
48
+ pid = wait_thr.pid
49
+
50
+ out_reader = Thread.new { out_io.read.to_s }
51
+ err_reader = Thread.new { err_io.read.to_s }
52
+ # Prevent Ruby from dumping IOError to $stderr when we close pipes
53
+ # out from under these readers on the timeout path.
54
+ out_reader.report_on_exception = false
55
+ err_reader.report_on_exception = false
56
+
57
+ begin
58
+ Timeout.timeout(timeout) do
59
+ # Wait for the child; readers drain pipes concurrently.
60
+ wait_thr.value
61
+ end
62
+ rescue Timeout::Error
63
+ timed_out = true
64
+ begin
65
+ Process.kill('TERM', -pid)
66
+ rescue Errno::ESRCH, Errno::EPERM
67
+ nil
68
+ end
69
+ # Brief grace, then hard kill the process group.
70
+ sleep 0.2
71
+ begin
72
+ Process.kill('KILL', -pid)
73
+ rescue Errno::ESRCH, Errno::EPERM
74
+ nil
75
+ end
76
+ begin
77
+ wait_thr.value
78
+ rescue StandardError
79
+ nil
80
+ end
81
+ end
82
+
83
+ # Ensure pipes are closed so readers unblock even if the child
84
+ # ignored signals or left descendants holding fds.
85
+ begin
86
+ out_io.close unless out_io.closed?
87
+ rescue StandardError
88
+ nil
89
+ end
90
+ begin
91
+ err_io.close unless err_io.closed?
92
+ rescue StandardError
93
+ nil
94
+ end
95
+
96
+ # Join readers; swallow the IOError that arrives when the pipe was
97
+ # closed mid-read (timeout path). report_on_exception is already off.
98
+ begin
99
+ stdout = out_reader.value.to_s
100
+ rescue StandardError
101
+ stdout = +''
102
+ end
103
+ begin
104
+ stderr = err_reader.value.to_s
105
+ rescue StandardError
106
+ stderr = +''
107
+ end
108
+
109
+ unless timed_out
110
+ status = wait_thr.value
111
+ exitstatus = status&.exitstatus
37
112
  end
38
- rescue Timeout::Error
39
- return { stdout: stdout, stderr: stderr, exit: nil, error: "timeout after #{timeout}s" }
40
113
  end
41
114
 
42
- { stdout: stdout, stderr: stderr, exit: status&.exitstatus }
115
+ return { stdout: stdout, stderr: stderr, exit: nil, error: "timeout after #{timeout}s" } if timed_out
116
+
117
+ { stdout: stdout, stderr: stderr, exit: exitstatus }
43
118
  }
44
119
  )
@@ -15,7 +15,7 @@ module PWN
15
15
  / \ / \
16
16
  { } { }
17
17
  { { } }
18
- \ \ / /
18
+ \ \ / / R.I.P. Houdini
19
19
  \ Y /
20
20
  .-"`"`"-.
21
21
  ,` `.
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.657'
4
+ VERSION = '0.5.658'
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.657
4
+ version: 0.5.658
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.