pwn 0.5.728 → 0.5.729

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: '0018be9794020a4b996e0d67a076e2741ca124d475f1439a047a77ecc0667d9d'
4
- data.tar.gz: 46eae2746ab67af7ace35e7dc4caa6e09897a187ec7295a6fe5b103a33262722
3
+ metadata.gz: '04697e334aa79768d74f64b19c7e90c8a8b0372899abdf68d83bcfd4f2dcf96b'
4
+ data.tar.gz: 8e8934c327d039dbfaad46d62837d1e6241af718b915deba0ed3aac240ded64b
5
5
  SHA512:
6
- metadata.gz: dd8e061914bb0941234e210a8ea346d4f11dbd0babcd5137e93fe529b094826459b469f6cfb5d2e8cabdc306bbb8471df5703275035a9e8d27fc36352b50ec84
7
- data.tar.gz: 4f361334ec04c20998333623be5c2d5b97ab27189dc52f1d4fda470ff40e743c38f58b1ae37b85244557a1f7eaf93954abd3baafdcb96c8bc8d57eb4fc71ff98
6
+ metadata.gz: 68965dcb416fa10c340a8aaa4345409313f8949de2ea17529fbe570b35022e0eaa782639941f5d1ed873542dc6d1432a20372c0c16c1365ff32c199635829921
7
+ data.tar.gz: cb5176d386d0d26308ad52ac292714ddb11134fe384cf994ce275326a6d6dffbfa628f5616af99b7f6d44c9cf7df0add18a4f4d9669805e85278851da1b32a17
@@ -30,6 +30,36 @@ rake # rubocop + rspec - must be zero offenses
30
30
 
31
31
  (`rvmsudo rake` on multi-user RVM installs.)
32
32
 
33
+ ### Optional native decoder integration tests
34
+
35
+ The default `bundle exec rake` does not require `proxmark3` or `rtl_433`.
36
+ Ruby EM4100/Acurite decoding, native input validation, missing-executable errors,
37
+ and cancellation-before-spawn tests always run. Only real native file replays
38
+ are excluded with RSpec metadata (`proxmark3_integration`, `rtl433_integration`),
39
+ not marked pending or silently passed inside examples.
40
+
41
+ After installing the optional clients, explicitly enable their fixture tests:
42
+
43
+ ```bash
44
+ PWN_TEST_PROXMARK3=1 bundle exec rspec spec/lib/pwn/sdr/decoder/rfid_spec.rb
45
+ PWN_TEST_RTL433=1 bundle exec rspec spec/lib/pwn/sdr/decoder/rtl433_spec.rb
46
+ # Enable both in the full gate:
47
+ PWN_TEST_PROXMARK3=1 PWN_TEST_RTL433=1 bundle exec rake
48
+ ```
49
+
50
+ These tests use committed offline amplitude/IQ captures, not RF hardware.
51
+ Opt-in runs fail if their client is unavailable or cannot decode the reference;
52
+ installed versions can differ in supported protocols and output. Fixture provenance
53
+ and tested versions are recorded in `spec/fixtures/sdr/rfid/README.md` and
54
+ `spec/fixtures/sdr/rtl433/README.md`. Neither client is installed by the test suite.
55
+
56
+ At runtime, `RFID.decode(mode: :fdxb_pm3, ...)` and
57
+ `RTL433.decode(mode: :native, ...)` raise an actionable `IOError` when their
58
+ executable is missing, suggesting installation or an explicit `executable:` path.
59
+ An already-true `stop:` callback raises cancellation before spawning either client.
60
+ Invalid inputs still raise `ArgumentError`; native decoding never silently falls
61
+ back to another decoder or a detector.
62
+
33
63
  ## Adding a plugin
34
64
 
35
65
  1. `lib/pwn/plugins/my_thing.rb` following the conventions above.
@@ -191,12 +191,19 @@ module PWN
191
191
  data = File.read(path)
192
192
  raise ArgumentError, 'PM3 file must contain signed integer amplitude samples' unless data.lines.all? { |line| line.strip.match?(/\A-?\d+\z/) }
193
193
 
194
+ raise IOError, 'proxmark3 replay cancelled' if opts[:stop]&.call
195
+
194
196
  Tempfile.create(['pwn-fdxb-', '.pm3']) do |trace|
195
197
  Tempfile.create('pwn-fdxb-output-') do |output|
196
198
  trace.write(data)
197
199
  trace.flush
198
- pid = Process.spawn(opts.fetch(:executable, 'proxmark3').to_s, '--incognito', '-c',
199
- "data load -f #{trace.path}; lf fdxb demod", in: File::NULL, out: output, err: output)
200
+ executable = opts.fetch(:executable, 'proxmark3').to_s
201
+ begin
202
+ pid = Process.spawn(executable, '--incognito', '-c',
203
+ "data load -f #{trace.path}; lf fdxb demod", in: File::NULL, out: output, err: output)
204
+ rescue Errno::ENOENT
205
+ raise IOError, "proxmark3 executable #{executable.inspect} not found; install the optional proxmark3 client or set executable: to its path"
206
+ end
200
207
  deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + duration
201
208
  begin
202
209
  loop do
@@ -303,6 +310,7 @@ module PWN
303
310
  # file: explicit .pm3 signed integer AMPLITUDE trace, not SDR IQ; no hardware access.
304
311
  # Valid CRC16 required. Returns national/country ID, animal and data-block flags.
305
312
  # executable: 'proxmark3'; duration: 30s deadline; stop: callable; output/on_frame/log_file.
313
+ # Missing executable raises IOError with install/path guidance; already-stopped replay never spawns.
306
314
  # PM3 mode is finite offline replay; no ISO14443/ISO15693/EPC Gen2 coverage.
307
315
  # carrier_hz: 125000 default; clocks_per_bit: 64 default, or 32/16.
308
316
  # threshold: 0.5 default, normalized envelope amplitude; tune to the received levels.
@@ -206,10 +206,14 @@ module PWN
206
206
  command.push('-R', '0')
207
207
  ids.each { |id| command.push('-R', id.to_s) }
208
208
  end
209
+ raise IOError, 'rtl_433 replay cancelled' if opts[:stop]&.call
210
+
209
211
  count = 0
210
212
  output = opts.fetch(:output, $stdout)
211
213
  deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + duration
214
+ started = false
212
215
  Open3.popen3(*command) do |input, stdout, stderr, worker|
216
+ started = true
213
217
  input.close
214
218
  buffers = { stdout => +'', stderr => +'' }
215
219
  errors = +''
@@ -269,6 +273,10 @@ module PWN
269
273
  end
270
274
  end
271
275
  { protocol: 'RTL433', mode: :native, backend: 'rtl_433', frames: count, source: path }
276
+ rescue Errno::ENOENT
277
+ raise unless started == false
278
+
279
+ raise IOError, "rtl_433 executable #{command.first.inspect} not found; install the optional rtl_433 package or set executable: to its path"
272
280
  end
273
281
 
274
282
  public_class_method def self.detect(opts = {})
@@ -333,6 +341,7 @@ module PWN
333
341
  # Native mode is finite FILE replay only: file required, no auto RF/config loading.
334
342
  # Native options: format: :cu8/:cs16/:cf32, protocols: [positive native IDs],
335
343
  # executable: 'rtl_433', duration: 30 seconds (deadline), stop: callable.
344
+ # Missing executable raises IOError with install/path guidance; already-stopped replay never spawns.
336
345
  # Output is per-device native JSON, device_protocol preserves native numeric ID.
337
346
  # Individual sensors/integrity depend on installed version; mic absent means not-reported.
338
347
  # sample_rate: 250000 default, >=20000; threshold: 0.5 normalized envelope amplitude.
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.728'
4
+ VERSION = '0.5.729'
5
5
  end
@@ -3,7 +3,28 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe PWN::SDR::Decoder::RFID do
6
- it 'decodes independent ISO11784/11785 FDX-B amplitude captures with the native offline client' do
6
+ it 'reports an actionable missing native executable without emitting frames' do
7
+ Dir.mktmpdir('pwn-missing-backend-') do |dir|
8
+ executable = File.join(dir, 'proxmark3')
9
+ frames = []
10
+ output = StringIO.new
11
+ expect do
12
+ described_class.decode(mode: :fdxb_pm3, file: 'spec/fixtures/sdr/rfid/fdxb_animal.pm3',
13
+ executable: executable, output: output, on_frame: ->(frame) { frames << frame })
14
+ end.to raise_error(IOError, /proxmark3.*not found.*install.*executable:/i)
15
+ expect(frames).to be_empty
16
+ expect(output.string).to be_empty
17
+ end
18
+ end
19
+
20
+ it 'cancels an already stopped native replay before spawning' do
21
+ expect(Process).not_to receive(:spawn)
22
+ expect do
23
+ described_class.decode(mode: :fdxb_pm3, file: 'spec/fixtures/sdr/rfid/fdxb_animal.pm3', stop: -> { true })
24
+ end.to raise_error(IOError, /cancelled/)
25
+ end
26
+
27
+ it 'decodes independent ISO11784/11785 FDX-B amplitude captures with the native offline client', :proxmark3_integration do
7
28
  %w[animal extended].each do |variant|
8
29
  frames = []
9
30
  result = described_class.decode(mode: :fdxb_pm3, file: "spec/fixtures/sdr/rfid/fdxb_#{variant}.pm3",
@@ -17,12 +38,16 @@ describe PWN::SDR::Decoder::RFID do
17
38
  end
18
39
  end
19
40
 
20
- it 'rejects automatic RF and emits no identity for an incomplete native amplitude trace' do
41
+ it 'rejects automatic RF and supports cancellation without the native client' do
21
42
  allow(PWN::SDR::Decoder::Base).to receive(:run_iq).and_raise('unexpected automatic RF')
22
43
  expect { described_class.decode(mode: :fdxb_pm3) }.to raise_error(ArgumentError, /explicit/)
23
44
  path = 'spec/fixtures/sdr/rfid/fdxb_animal.pm3'
24
45
  expect { described_class.decode(mode: :fdxb_pm3, file: path, source: :rtl_sdr) }.to raise_error(ArgumentError, /live/)
25
46
  expect { described_class.decode(mode: :fdxb_pm3, file: path, stop: -> { true }) }.to raise_error(IOError, /cancelled/)
47
+ end
48
+
49
+ it 'emits no identity for an incomplete native amplitude trace', :proxmark3_integration do
50
+ path = 'spec/fixtures/sdr/rfid/fdxb_animal.pm3'
26
51
  Tempfile.create(['fdxb-short-', '.pm3']) do |file|
27
52
  file.write(File.readlines(path).first(100).join)
28
53
  file.flush
@@ -3,6 +3,27 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe PWN::SDR::Decoder::RTL433 do
6
+ it 'reports an actionable missing native executable without emitting frames' do
7
+ Dir.mktmpdir('pwn-missing-backend-') do |dir|
8
+ executable = File.join(dir, 'rtl_433')
9
+ frames = []
10
+ output = StringIO.new
11
+ expect do
12
+ described_class.decode(mode: :native, file: 'spec/fixtures/sdr/rtl433/toyota.cu8',
13
+ executable: executable, output: output, on_frame: ->(frame) { frames << frame })
14
+ end.to raise_error(IOError, /rtl_433.*not found.*install.*executable:/i)
15
+ expect(frames).to be_empty
16
+ expect(output.string).to be_empty
17
+ end
18
+ end
19
+
20
+ it 'cancels an already stopped native replay before spawning' do
21
+ expect(Open3).not_to receive(:popen3)
22
+ expect do
23
+ described_class.decode(mode: :native, file: 'spec/fixtures/sdr/rtl433/toyota.cu8', stop: -> { true })
24
+ end.to raise_error(IOError, /cancelled/)
25
+ end
26
+
6
27
  it 'decodes six real over-air Acurite packets with payloads matching the upstream reference' do
7
28
  demod = described_class::AcuriteIQ.new(rate: 250_000)
8
29
  frames = []
@@ -30,7 +51,7 @@ describe PWN::SDR::Decoder::RTL433 do
30
51
  expect { described_class.decode(mode: :all) }.to raise_error(ArgumentError, /mode/)
31
52
  end
32
53
 
33
- it 'replays OOK and FSK native catalogue decoders against published captures' do
54
+ it 'replays OOK and FSK native catalogue decoders against published captures', :rtl433_integration do
34
55
  %w[acurite_th_609_001 wh31 toyota].each do |name|
35
56
  frames = []
36
57
  result = described_class.decode(mode: :native, file: "spec/fixtures/sdr/rtl433/#{name}.cu8",
@@ -44,7 +65,7 @@ describe PWN::SDR::Decoder::RTL433 do
44
65
  end
45
66
  end
46
67
 
47
- it 'requires explicit native files, validates formats and supports native protocol selection' do
68
+ it 'requires explicit native files and validates formats and protocol selection without the native client' do
48
69
  allow(PWN::SDR::Decoder::Base).to receive(:run_iq).and_raise('unexpected automatic RF')
49
70
  expect { described_class.decode(mode: :native) }.to raise_error(ArgumentError, /explicit file/)
50
71
  path = 'spec/fixtures/sdr/rtl433/toyota.cu8'
@@ -52,6 +73,10 @@ describe PWN::SDR::Decoder::RTL433 do
52
73
  expect { described_class.decode(mode: :native, file: path, source: :rtl_sdr) }.to raise_error(ArgumentError, /live source/)
53
74
  expect { described_class.decode(mode: :native, file: path, protocols: [0]) }.to raise_error(ArgumentError, /protocol/)
54
75
  expect { described_class.decode(mode: :native, file: path, stop: -> { true }) }.to raise_error(IOError, /cancelled/)
76
+ end
77
+
78
+ it 'supports native protocol selection', :rtl433_integration do
79
+ path = 'spec/fixtures/sdr/rtl433/toyota.cu8'
55
80
  expect(described_class.decode(mode: :native, file: path, protocols: [11], output: StringIO.new)[:frames]).to eq(0)
56
81
  expect(described_class.decode(mode: :native, file: path, protocols: [88], output: StringIO.new)[:frames]).to eq(1)
57
82
  end
data/spec/spec_helper.rb CHANGED
@@ -19,6 +19,8 @@ Dir[File.join(__dir__, 'support', '**', '*.rb')].each { |f| require f }
19
19
  # ─────────────────────────────────────────────────────────────────────────────
20
20
  RSpec.configure do |config|
21
21
  config.filter_run_excluding :local_embeddings unless ENV['PWN_TEST_EMBED_ENDPOINT'] && ENV['PWN_SQLITE_VEC_EXTENSION']
22
+ config.filter_run_excluding :proxmark3_integration unless ENV['PWN_TEST_PROXMARK3'] == '1'
23
+ config.filter_run_excluding :rtl433_integration unless ENV['PWN_TEST_RTL433'] == '1'
22
24
 
23
25
  next if ENV['PWN_SPEC_VERBOSE']
24
26
 
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.728
4
+ version: 0.5.729
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.