Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b7a81f1
fix: expose `NMWiredProxy` and propogate speed through
cachebag Dec 16, 2025
705853b
feat: bluetooth device builder
cachebag Dec 18, 2025
e11b393
feat: add Bluetooth device connection support
cachebag Dec 19, 2025
64fdfb5
fix: default object path to "/" for bluetooth device
cachebag Dec 20, 2025
ef656d8
chore: revert breaking change to `Device` struct
cachebag Dec 20, 2025
865fe48
feat: unit + integration tests for Bluetooth device components
cachebag Dec 20, 2025
4bc9320
chore: mark more enums `#[non_exhaustive]`
cachebag Dec 20, 2025
41dbc3d
fix: proxy field name for `bd_addr` replaced with correct `hw_addr`
cachebag Dec 22, 2025
0d8a4b2
chore: bump cargo version to `2.0.0-dev`
cachebag Dec 22, 2025
02db432
docs: update example for `connect_bluetooth()` and bump version
cachebag Dec 22, 2025
f7c8dbf
chore: update imports and dead code from rebase
cachebag Jan 12, 2026
0ea382d
chore: cleanup conflict residue
cachebag Jan 18, 2026
68dbc5c
fix(#176): better error for missing psks
cachebag Jan 18, 2026
b307b67
chore: `non_exhaustive` on more stuff
cachebag Jan 18, 2026
5aa407b
feat: mark public structs/enums as #[non_exhaustive] for future exten…
cachebag Jan 18, 2026
0786a04
chore: update dev deps
cachebag Jan 19, 2026
b7afc72
chore: cleanup conflict residue
cachebag Jan 19, 2026
dd499ee
test: update doc + integration tests to match new enum behavior
cachebag Jan 19, 2026
3b897b1
feat(#188): builder pattern for `VpnCredentials` and `EapOptions`
cachebag Jan 19, 2026
aeb0216
feat(#185): configurable timeout values for all code paths
cachebag Jan 19, 2026
7884c58
chore: add security policy
cachebag Jan 19, 2026
3788b77
chore: add discord channel to README
cachebag Jan 19, 2026
437b3ba
chore: update CHANGELOG
cachebag Jan 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@ dirs = "6.0.0"
fs2 = "0.4.3"
anyhow = "1.0.100"
clap = { version = "4.5.53", features = ["derive"] }
async-trait = "0.1.89"
tokio = { version = "1.48.0", features = ["rt-multi-thread", "macros", "sync", "time"] }
tokio-util = { version = "0.7.18" }
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ async fn main() -> nmrs::Result<()> {
Ok(())
}
```

To follow and/or discuss the development of nmrs, you can join the [public Discord channel](https://discord.gg/Sk3VfrHrN4).

# <p align="center"> nmrs-gui </p>

![Version](https://img.shields.io/badge/nmrs--gui-1.1.0-orange?style=flat-square)
Expand Down Expand Up @@ -152,7 +155,7 @@ Edit `~/.config/nmrs/style.css` to customize the interface. There are also pre-d
- [ ] Any
- [X] Wired
- [ ] ADSL
- [ ] Bluetooth
- [X] Bluetooth
- [ ] Bond
- [ ] Bridge
- [ ] Dummy
Expand Down Expand Up @@ -202,7 +205,7 @@ Edit `~/.config/nmrs/style.css` to customize the interface. There are also pre-d
- [ ] DNS Manager
- [ ] PPP
- [ ] Secret Agent
- [ ] VPN Connection
- [X] VPN Connection (WireGuard)
- [ ] VPN Plugin
- [ ] Wi-Fi P2P
- [ ] WiMAX NSP
Expand Down
101 changes: 101 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Security Policy

## Supported Versions

We take security seriously and provide security updates for the latest version of nmrs and nmrs-gui alike.
We strongly recommend keeping your nmrs dependencies up to date.

## Reporting a Vulnerability

**Please do not report security vulnerabilities through public GitHub issues.**

If you discover a security vulnerability in nmrs or any of the related crates, please report it privately by emailing
**alhakimiakrmjATgmailDOTcom**.

Please include the following information in your report:

- A clear description of the vulnerability
- Steps to reproduce the issue
- Potential impact and attack scenarios
- Any suggested fixes or mitigations
- Your contact information for follow-up questions

### What constitutes a security vulnerability?

For nmrs, security vulnerabilities may include but are not limited to:

- **Authentication bypass**: Ability to connect to protected networks without proper credentials
- **Privilege escalation**: Unauthorized access to NetworkManager operations that should require elevated permissions
- **Credential exposure**: Leaking WiFi passwords, VPN keys, or other sensitive connection data through logs, errors, or memory
- **D-Bus injection**: Malicious D-Bus messages that could manipulate network connections or device state
- **Denial of service**: Crashes, hangs, or resource exhaustion that prevent legitimate network management
- **Information disclosure**: Exposing network SSIDs, MAC addresses, or connection details to unauthorized processes
- **Input validation failures**: Improper handling of malformed SSIDs, credentials, or configuration data leading to undefined behavior
- **Race conditions**: Timing vulnerabilities in connection state management that could lead to security issues
- **Dependency vulnerabilities**: Security issues in upstream crates (zbus, tokio, etc.) that affect nmrs

For nmrs-gui specifically:
- **UI injection**: Malicious network names or data that could execute unintended actions in the GUI
- **File system access**: Unauthorized reading or writing of configuration files outside the intended scope


## Response Timeline

We are committed to responding to security reports promptly:

- **Acknowledgment**: We will acknowledge receipt of your vulnerability report within
**24 hours**
- **Initial assessment**: We will provide an initial assessment of the report within
**5 business days**
- **Regular updates**: We will provide progress updates at least every **7 days** until
resolution
- **Resolution**: We aim to provide a fix or mitigation within **30 days** for critical
vulnerabilities

Response times may vary based on the complexity of the issue and availability of maintainers.

## Disclosure Policy

We follow a coordinated disclosure process:

1. **Private disclosure**: We will work with you to understand and validate the vulnerability
2. **Fix development**: We will develop and test a fix in a private repository if necessary
3. **Coordinated release**: We will coordinate the public disclosure with the release of a fix
4. **Public disclosure**: After a fix is available, we will publish a security advisory

We request that you:
- Give us reasonable time to address the vulnerability before making it public
- Avoid accessing or modifying data beyond what is necessary to demonstrate the vulnerability
- Act in good faith and avoid privacy violations or destructive behavior

## Security Advisories

Published security advisories will be available through:

- GitHub Security Advisories on the
[nmrs repository](https://github.com/cachebag/nmrs/security/advisories)
- [RustSec Advisory Database](https://rustsec.org/)
- Release notes and changelog entries

## Recognition

We appreciate the security research community's efforts to improve the security of nmrs. With
your permission, we will acknowledge your contribution in:

- Security advisories
- Release notes
- Project documentation

If you prefer to remain anonymous, please let us know in your report.

## Scope

This security policy covers both nmrs and nmrs-gui alike.

## Additional Resources

- [Contributing Guidelines](CONTRIBUTING.md)
- [Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct)
- [Rust Security Policy](https://www.rust-lang.org/policies/security)

Thank you for helping to keep nmrs and the Rust ecosystem secure!
20 changes: 9 additions & 11 deletions nmrs-gui/src/ui/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,18 +191,16 @@ fn draw_connect_modal(

glib::MainContext::default().spawn_local(async move {
let creds = if is_eap {
WifiSecurity::WpaEap {
opts: EapOptions {
identity: username,
password: pwd,
anonymous_identity: None,
domain_suffix_match: None,
ca_cert_path: cert_path,
system_ca_certs: use_system_ca,
method: EapMethod::Peap,
phase2: Phase2::Mschapv2,
},
let mut opts = EapOptions::new(username, pwd)
.with_method(EapMethod::Peap)
.with_phase2(Phase2::Mschapv2)
.with_system_ca_certs(use_system_ca);

if let Some(cert) = cert_path {
opts = opts.with_ca_cert_path(format!("file://{}", cert));
}

WifiSecurity::WpaEap { opts }
} else {
WifiSecurity::WpaPsk { psk: pwd }
};
Expand Down
11 changes: 2 additions & 9 deletions nmrs-gui/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use gtk::{
};
use std::cell::Cell;
use std::rc::Rc;
use tokio::sync::watch;

use crate::ui::header::THEMES;

Expand Down Expand Up @@ -49,8 +48,6 @@ pub fn build_ui(app: &Application) {
}
}

let (_shutdown_tx, shutdown_rx) = watch::channel(());

let vbox = GtkBox::new(Orientation::Vertical, 0);
let status = Label::new(None);
let list_container = GtkBox::new(Orientation::Vertical, 0);
Expand Down Expand Up @@ -164,18 +161,16 @@ pub fn build_ui(app: &Application) {
let is_scanning_device = is_scanning_clone.clone();
let ctx_device = ctx.clone();
let pending_device_refresh = Rc::new(std::cell::RefCell::new(false));
let shutdown_rx_device = shutdown_rx.clone();

glib::MainContext::default().spawn_local(async move {
loop {
let ctx_device_clone = ctx_device.clone();
let list_container_clone = list_container_device.clone();
let is_scanning_clone = is_scanning_device.clone();
let pending_device_refresh_clone = pending_device_refresh.clone();
let shutdown_rx_monitor = shutdown_rx_device.clone();

let result = nm_device_monitor
.monitor_device_changes(shutdown_rx_monitor, move || {
.monitor_device_changes(move || {
let ctx = ctx_device_clone.clone();
let list_container = list_container_clone.clone();
let is_scanning = is_scanning_clone.clone();
Expand Down Expand Up @@ -219,18 +214,16 @@ pub fn build_ui(app: &Application) {
let is_scanning_network = is_scanning_clone.clone();
let ctx_network = ctx.clone();
let pending_network_refresh = Rc::new(std::cell::RefCell::new(false));
let shutdown_rx_network = shutdown_rx.clone();

glib::MainContext::default().spawn_local(async move {
loop {
let ctx_network_clone = ctx_network.clone();
let list_container_clone = list_container_network.clone();
let is_scanning_clone = is_scanning_network.clone();
let pending_network_refresh_clone = pending_network_refresh.clone();
let shutdown_rx_monitor = shutdown_rx_network.clone();

let result = nm_network_monitor
.monitor_network_changes(shutdown_rx_monitor, move || {
.monitor_network_changes(move || {
let ctx = ctx_network_clone.clone();
let list_container = list_container_clone.clone();
let is_scanning = is_scanning_clone.clone();
Expand Down
16 changes: 14 additions & 2 deletions nmrs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,25 @@
All notable changes to the `nmrs` crate will be documented in this file.

## [Unreleased]

## [2.0.0] - 2026-01-19
### Added
- Configurable timeout values for connection and disconnection operations ([#185](https://github.com/cachebag/nmrs/issues/185))
- Builder pattern for `VpnCredentials` and `EapOptions` ([#188](https://github.com/cachebag/nmrs/issues/188))
- Bluetooth device support ([#198](https://github.com/cachebag/nmrs/pull/198))
- Input validation before any D-Bus operations ([#173](https://github.com/cachebag/nmrs/pull/173))
- CI: adjust workflow to auto-update nix hashes on PRs ([#182](https://github.com/cachebag/nmrs/pull/182))
~~- CI: adjust workflow to auto-update nix hashes on PRs ([#182](https://github.com/cachebag/nmrs/pull/182))~~
- More helpful methods to `network_manager` facade ([#190](https://github.com/cachebag/nmrs/pull/190))
- Explicitly clean up signal streams to ensure unsubscription ([#197](https://github.com/cachebag/nmrs/pull/197))

### Fixed
- Better error message for empty passkeys ([#198](https://github.com/cachebag/nmrs/pull/198))
- Race condition in signal subscription ([#191](https://github.com/cachebag/nmrs/pull/191))

### Changed
- Various enums and structs marked non-exhaustive ([#198](https://github.com/cachebag/nmrs/pull/198))
- Expose `NMWiredProxy` and propogate speed through + write in field and display for BT device type ([#198](https://github.com/cachebag/nmrs/pull/198))

## [1.3.5] - 2026-01-13
### Changed
- Add `Debug` derive to `NetworkManager` ([#171](https://github.com/cachebag/nmrs/pull/171))
Expand Down Expand Up @@ -138,7 +148,9 @@ All notable changes to the `nmrs` crate will be documented in this file.
[1.2.0]: https://github.com/cachebag/nmrs/compare/nmrs-v1.1.0...nmrs-v1.2.0
[1.3.0]: https://github.com/cachebag/nmrs/compare/nmrs-v1.2.0...nmrs-v1.3.0
[1.3.5]: https://github.com/cachebag/nmrs/compare/nmrs-v1.2.0...nmrs-v1.3.5
[Unreleased]: https://github.com/cachebag/nmrs/compare/nmrs-v1.3.5...HEAD
[2.0.0]: https://github.com/cachebag/nmrs/compare/nmrs-v1.2.0...nmrs-v2.0.0
[2.0.0]: https://github.com/cachebag/nmrs/compare/nmrs-v1.2.0...nmrs-v2.0.0
[Unreleased]: https://github.com/cachebag/nmrs/compare/nmrs-v2.0.0...HEAD
[1.1.0]: https://github.com/cachebag/nmrs/compare/nmrs-v1.0.1...nmrs-v1.1.0
[1.0.1]: https://github.com/cachebag/nmrs/compare/nmrs-v1.0.0...nmrs-v1.0.1
[1.0.0]: https://github.com/cachebag/nmrs/compare/v0.5.0-beta...nmrs-v1.0.0
Expand Down
6 changes: 2 additions & 4 deletions nmrs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "nmrs"
version = "1.3.5"
version = "2.0.0"
authors = ["Akrm Al-Hakimi <alhakimiakrmj@gmail.com>"]
edition.workspace = true
rust-version = "1.78.0"
Expand All @@ -23,9 +23,7 @@ futures.workspace = true
futures-timer.workspace = true
base64.workspace = true
tokio.workspace = true

[dev-dependencies]
tokio.workspace = true
async-trait.workspace = true

[package.metadata.docs.rs]
all-features = true
Expand Down
2 changes: 1 addition & 1 deletion nmrs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Rust bindings for NetworkManager via D-Bus.

```toml
[dependencies]
nmrs = "1.2.0"
nmrs = "2.0.0"
```
or
```bash
Expand Down
17 changes: 17 additions & 0 deletions nmrs/examples/bluetooth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/// List Bluetooth devices using NetworkManager
use nmrs::{NetworkManager, Result};

#[tokio::main]
async fn main() -> Result<()> {
let nm = NetworkManager::new().await?;

println!("Scanning for Bluetooth devices...");
let devices = nm.list_bluetooth_devices().await?;

// List bluetooth devices
for d in devices {
println!("{d}");
}

Ok(())
}
54 changes: 54 additions & 0 deletions nmrs/examples/bluetooth_connect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/// Connect to a Bluetooth device using NetworkManager.
use nmrs::models::BluetoothIdentity;
use nmrs::{NetworkManager, Result};
#[tokio::main]
async fn main() -> Result<()> {
let nm = NetworkManager::new().await?;

println!("Scanning for Bluetooth devices...");
let devices = nm.list_bluetooth_devices().await?;

if devices.is_empty() {
println!("No Bluetooth devices found.");
println!("\nMake sure:");
println!(" 1. Bluetooth is enabled");
println!(" 2. Device is paired (use 'bluetoothctl')");
return Ok(());
}

// This will print all devices that have been explicitly paired using
// `bluetoothctl pair <MAC_ADDRESS>`
println!("\nAvailable Bluetooth devices:");
for (i, device) in devices.iter().enumerate() {
println!(" {}. {}", i + 1, device);
}

// Connect to the first device in the list
if let Some(device) = devices.first() {
println!("\nConnecting to: {}", device);

let settings = BluetoothIdentity::new(device.bdaddr.clone(), device.bt_caps.into());

let name = device
.alias
.as_ref()
.or(device.name.as_ref())
.map(|s| s.as_str())
.unwrap_or("Bluetooth Device");

match nm.connect_bluetooth(name, &settings).await {
Ok(_) => println!("✓ Successfully connected to {name}"),
Err(e) => {
eprintln!("✗ Failed to connect: {}", e);
return Ok(());
}
}

/* match nm.forget_bluetooth(name).await {
Ok(_) => println!("Disconnected {name}"),
Err(e) => eprintln!("Failed to forget: {e}"),
}*/
}

Ok(())
}
Loading
Loading