Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions DECISIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# VPNht Desktop Production Readiness Audit\n\n## Decision 1: Terminate Swarm\n**Problem:** Subagents partial (clone only) or hallucinated Popcorn Time (memory bleed).\n**Options:** Respawn refined | Manual high-impact from D/prev REPORT.\n**Chosen:** Manual.\n**Why:** D concrete diffs (IPC split, Linux KS), baseline pass, time-critical.\n**Risk:** Miss low-prio feats. **Mitigate:** P2 roadmap in REPORT.md.\n\n## Decision 2: Skip Local Rust Builds\n**Problem:** arm64 qemu missing libwebkit2gtk/javascriptcoregtk pc files.\n**Options:** apt/Docker | CI-only.\n**Chosen:** CI-only.\n**Why:** Existing workflows ubuntu-22.04 matrix pass Tauri builds.\n**Risk:** Local dev slower. **Mitigate:** docker-tauri if needed.\n\n## Decision 3: PR Order\nP0: CI enhancements, D refactors (security), tests.\nP1: Real API mocks replace.\nP2: UX feats.
1 change: 1 addition & 0 deletions SECURITY_REVIEW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# SECURITY_REVIEW.md\n\n## Threat Model\nAssets: creds (keyring), VPN keys, auth tokens, DNS, conn state.\nBoundaries: Frontend-Rust IPC, App-VPN daemon cmds, App-API/updates.\nAttackers: Malicious IPC, leak creds, bypass KS, MITM updates.\n\n## Findings (from audit + D)\n| Sev | ID | Desc | File | Status |\n|-----|----|------|------|--------|\n| Crit | SEC-01 | Mock API no real auth | commands.rs | P0 |\n| High | SEC-03 | IPC store_secure scoped | commands.rs | Fixed D |\n| High | SEC-04 | KS priv Linux iptables | killswitch/linux.rs | Fixed D |\n| Med | SEC-08 | CSP unsafe-inline | tauri.conf | P1 |\n\n## Fixes Implemented\n- IPC allowlist (ALLOWED_STORAGE_KEYS)\n- Linux KS iptables chain\n- AppError structured\n\n## Remaining\n- Real API replace mocks\n- Windows KS\n- Updater pinning\n\nNext: cargo audit CI.
1 change: 1 addition & 0 deletions TEST_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# TEST_PLAN.md\n\n## Run Locally\nnpm test\ncd src-tauri && cargo test\n\n## Coverage\n73 vitest pass (utils/stores/VPN flow mocks).\nCargo 2 tests.\nGaps: E2E IPC, real WG mocks, KS.\n\n## CI\nworkflows/test.yml: vitest cargo.\nAdd: e2e playwright stubs.
20 changes: 18 additions & 2 deletions src-tauri/src/killswitch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,26 @@ fn run_privileged_command(cmd: &str, args: &[&str]) -> Result<std::process::Outp
#[cfg(target_os = "macos")]
fn run_privileged_command(cmd: &str, args: &[&str]) -> Result<std::process::Output, String> {
// macOS uses osascript for privilege escalation
let mut full_args = vec!["-e", &format!("do shell script \"{} {}\" with administrator privileges", cmd, args.join(" "))];
// Build shell-escaped command to prevent injection attacks
fn shell_escape(s: &str) -> String {
// Use single quotes, escaping any embedded single quotes
// This is the safest shell escaping method
format!("'{}'", s.replace("'", "'\\''"))
}

let mut command_parts = vec![shell_escape(cmd)];
for arg in args {
command_parts.push(shell_escape(arg));
}

let script = format!(
"do shell script {} with administrator privileges",
command_parts.join(" ")
);

Command::new("osascript")
.args(&full_args)
.arg("-e")
.arg(&script)
.output()
.map_err(|e| format!("Failed to run privileged command: {}", e))
}
Expand Down
Loading