Skip to content

Commit b8f9ba3

Browse files
committed
test(bitbox): document the upstream bridge findings
Adds the audit reproducers for the BITBOX findings that cannot be fixed in this repository — they live in the bull_sdk Rust/Kotlin bridge and the issues stay open until fixed upstream: - #2615: connected devices are not cryptographically attested - #2647: pairing failures are swallowed and misreported - #2648: Android USB permission returns before the user responds - #2649: unsupported P2WSH signing panics via todo!() - #2651: abandoned operations hold the device lock up to 60s
1 parent 225e086 commit b8f9ba3

5 files changed

Lines changed: 104 additions & 0 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Security audit reproducer for https://github.com/SatoshiPortal/bullbitcoin-mobile/issues/2615
2+
// Finding: BitBox devices are accepted without cryptographic attestation.
3+
// This test PASSES while the vulnerability exists: it documents the current
4+
// vulnerable behavior. When the issue is fixed, flip the assertions to the
5+
// secure behavior so this becomes a regression test.
6+
import 'dart:io';
7+
8+
import 'package:test/test.dart';
9+
10+
void main() {
11+
group('Security audit #2615 device attestation', () {
12+
test('connection and pairing contain no attestation verification', () {
13+
final source = File(
14+
'lib/core/bitbox/data/datasources/bitbox_device_datasource.dart',
15+
).readAsStringSync();
16+
expect(source, contains('BitBoxApi.openDevice'));
17+
expect(source, contains('bitbox.startPairing'));
18+
expect(source, isNot(contains('attest')));
19+
expect(source, isNot(contains('attestation')));
20+
});
21+
});
22+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Security audit reproducer for https://github.com/SatoshiPortal/bullbitcoin-mobile/issues/2647
2+
// Finding: pairing failures are converted to an empty optional and misreported.
3+
// This test PASSES while the vulnerability exists: it documents the current
4+
// vulnerable behavior. When the issue is fixed, flip the assertions to the
5+
// secure behavior so this becomes a regression test.
6+
import 'dart:io';
7+
8+
import 'package:test/test.dart';
9+
10+
void main() {
11+
group('Security audit #2647 pairing failure propagation', () {
12+
test('empty startPairing result is treated as success-like empty code', () {
13+
final source = File(
14+
'lib/core/bitbox/data/datasources/bitbox_device_datasource.dart',
15+
).readAsStringSync();
16+
expect(source, contains('final pairingCode = await bitbox.startPairing'));
17+
expect(source, contains("return pairingCode ?? '';"));
18+
});
19+
});
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Security audit reproducer for https://github.com/SatoshiPortal/bullbitcoin-mobile/issues/2648
2+
// Finding: the Android USB permission bridge is not present in this checkout.
3+
// This test PASSES while the vulnerability exists: it documents the current
4+
// vulnerable behavior. When the issue is fixed, flip the assertions to the
5+
// secure behavior so this becomes a regression test.
6+
import 'dart:io';
7+
8+
import 'package:test/test.dart';
9+
10+
void main() {
11+
group('Security audit #2648 USB permission bridge', () {
12+
test('plugin source is unavailable for automated verification', () {
13+
final candidates = <String>[
14+
'android/app/src/main/kotlin/com/bullbitcoin/mobile/BitboxFlutterPlugin.kt',
15+
'android/app/src/main/java/com/bullbitcoin/mobile/BitboxFlutterPlugin.kt',
16+
];
17+
expect(candidates.any((path) => File(path).existsSync()), isFalse);
18+
});
19+
});
20+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Security audit reproducer for https://github.com/SatoshiPortal/bullbitcoin-mobile/issues/2649
2+
// Finding: the referenced BitBox Rust dependency source is not locally available.
3+
// This test PASSES while the vulnerability exists: it documents the current
4+
// vulnerable behavior. When the issue is fixed, flip the assertions to the
5+
// secure behavior so this becomes a regression test.
6+
import 'dart:io';
7+
8+
import 'package:test/test.dart';
9+
10+
void main() {
11+
group('Security audit #2649 P2WSH signing', () {
12+
test('Rust dependency is not vendored in the checkout', () {
13+
final lock = File('pubspec.lock').readAsStringSync();
14+
expect(lock, contains('https://github.com/SatoshiPortal/bull_sdk'));
15+
expect(Directory('packages/bitbox-transport').existsSync(), isFalse);
16+
});
17+
});
18+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Security audit reproducer for https://github.com/SatoshiPortal/bullbitcoin-mobile/issues/2651
2+
// Finding: disposal disconnects but does not cancel an in-flight operation.
3+
// This test PASSES while the vulnerability exists: it documents the current
4+
// vulnerable behavior. When the issue is fixed, flip the assertions to the
5+
// secure behavior so this becomes a regression test.
6+
import 'dart:io';
7+
8+
import 'package:test/test.dart';
9+
10+
void main() {
11+
group('Security audit #2651 abandoned operations', () {
12+
test('datasource has no operation cancellation in disposal path', () {
13+
final source = File(
14+
'lib/core/bitbox/data/datasources/bitbox_device_datasource.dart',
15+
).readAsStringSync();
16+
final dispose = source.substring(
17+
source.indexOf('Future<void> dispose()'),
18+
);
19+
expect(dispose, contains('_bleConnector.stopScan()'));
20+
expect(dispose, isNot(contains('cancel')));
21+
expect(source, contains('Duration(seconds: 20)'));
22+
});
23+
});
24+
}

0 commit comments

Comments
 (0)