add linux
This commit is contained in:
214
client/internal/blocker/blocker_linux.go
Normal file
214
client/internal/blocker/blocker_linux.go
Normal file
@@ -0,0 +1,214 @@
|
||||
//go:build linux
|
||||
|
||||
package blocker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var systemAllowed = map[string]bool{
|
||||
"gnome-shell": true,
|
||||
"mutter": true,
|
||||
"xterm": true,
|
||||
"gnome-terminal": true,
|
||||
"konsole": true,
|
||||
"ptyxis": true, // Ubuntu terminal
|
||||
"bash": true,
|
||||
"zsh": true,
|
||||
"sh": true,
|
||||
"wails": true,
|
||||
"code": true,
|
||||
"cursor": true,
|
||||
"windsurf": true,
|
||||
"goland": true,
|
||||
"idea": true,
|
||||
"client": true,
|
||||
"simple_care_v1.0": true,
|
||||
"xwayland": true,
|
||||
"mutter-x11-fram": true,
|
||||
"ibus-x11": true,
|
||||
"ibus-daemon": true,
|
||||
"gsd-": true, // prefix
|
||||
"ibus-": true, // prefix
|
||||
"xdg-": true, // prefix
|
||||
"at-spi": true, // prefix
|
||||
"evolution-": true, // prefix
|
||||
"goa-daemon": true,
|
||||
"gjs": true,
|
||||
"snapd-": true, // prefix
|
||||
"systemd": true,
|
||||
"webkit": true, // WebKit subprocesses
|
||||
"glycin": true, // GNOME image helper
|
||||
"zenity": true, // Dialog utility
|
||||
"rustdesk": true, // Remote support safety
|
||||
"rustdesk-bin": true,
|
||||
"anydesk": true,
|
||||
"teamviewer": true,
|
||||
"remmina": true,
|
||||
}
|
||||
|
||||
func isSystemAllowed(name string) bool {
|
||||
name = strings.ToLower(name)
|
||||
if systemAllowed[name] {
|
||||
return true
|
||||
}
|
||||
for pattern := range systemAllowed {
|
||||
if strings.HasPrefix(name, pattern) && pattern != name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getPPID(pid uint32) uint32 {
|
||||
statusBytes, err := os.ReadFile(fmt.Sprintf("/proc/%d/status", pid))
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
lines := strings.Split(string(statusBytes), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "PPid:") {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) >= 2 {
|
||||
if ppid, err := strconv.ParseUint(parts[1], 10, 32); err == nil {
|
||||
return uint32(ppid)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func isDescendantOf(pid, targetPid uint32) bool {
|
||||
curr := pid
|
||||
for i := 0; i < 10; i++ { // limits lookup to 10 ancestor levels
|
||||
ppid := getPPID(curr)
|
||||
if ppid == 0 {
|
||||
return false
|
||||
}
|
||||
if ppid == targetPid {
|
||||
return true
|
||||
}
|
||||
curr = ppid
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type ProcessInfo struct {
|
||||
PID uint32
|
||||
Name string
|
||||
}
|
||||
|
||||
func getVisibleProcesses() (map[uint32]ProcessInfo, error) {
|
||||
files, err := os.ReadDir("/proc")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
procs := make(map[uint32]ProcessInfo)
|
||||
for _, f := range files {
|
||||
if !f.IsDir() {
|
||||
continue
|
||||
}
|
||||
pid, err := strconv.ParseUint(f.Name(), 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
mapsPath := fmt.Sprintf("/proc/%d/maps", pid)
|
||||
mapsBytes, err := os.ReadFile(mapsPath)
|
||||
if err != nil {
|
||||
// Skip processes we don't own (permission denied)
|
||||
continue
|
||||
}
|
||||
|
||||
mapsStr := string(mapsBytes)
|
||||
isGUI := strings.Contains(mapsStr, "libgtk") ||
|
||||
strings.Contains(mapsStr, "libQt") ||
|
||||
strings.Contains(mapsStr, "libX11") ||
|
||||
strings.Contains(mapsStr, "libwayland-client")
|
||||
|
||||
if !isGUI {
|
||||
continue
|
||||
}
|
||||
|
||||
// Read process name from /proc/PID/comm
|
||||
commBytes, err := os.ReadFile(fmt.Sprintf("/proc/%d/comm", pid))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procName := strings.TrimSpace(string(commBytes))
|
||||
|
||||
if procName != "" {
|
||||
procs[uint32(pid)] = ProcessInfo{
|
||||
PID: uint32(pid),
|
||||
Name: procName,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return procs, nil
|
||||
}
|
||||
|
||||
func (b *Blocker) checkAndKill() {
|
||||
b.mu.Lock()
|
||||
keywords := make([]string, len(b.allowedKeywords))
|
||||
copy(keywords, b.allowedKeywords)
|
||||
b.mu.Unlock()
|
||||
|
||||
if len(keywords) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
currentExec := ""
|
||||
if execPath, err := os.Executable(); err == nil {
|
||||
currentExec = strings.ToLower(filepath.Base(execPath))
|
||||
}
|
||||
|
||||
procs, err := getVisibleProcesses()
|
||||
if err != nil {
|
||||
log.Printf("[BLOCKER] Failed to get visible processes: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
myPid := uint32(os.Getpid())
|
||||
for pid, info := range procs {
|
||||
pNameLower := strings.ToLower(info.Name)
|
||||
|
||||
// 1. Always allow our app, our sub-processes, system/critical developer tools, or agent helpers
|
||||
isOurSubprocess := pid == myPid || isDescendantOf(pid, myPid)
|
||||
isAntigravity := strings.Contains(pNameLower, "antigravity")
|
||||
if isOurSubprocess || (currentExec != "" && pNameLower == currentExec) || isSystemAllowed(pNameLower) || isAntigravity {
|
||||
continue
|
||||
}
|
||||
|
||||
// 2. Check if the process name contains any allowed keywords
|
||||
allowed := false
|
||||
for _, kw := range keywords {
|
||||
if matchesAllowedKeyword(kw, pNameLower, pNameLower) {
|
||||
allowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 3. If not allowed, kill the application
|
||||
if !allowed {
|
||||
if b.OnBlocked != nil {
|
||||
b.OnBlocked(info.Name, info.Name)
|
||||
}
|
||||
log.Printf("[BLOCKER] KILLED unauthorized application: %s (PID: %d)", info.Name, pid)
|
||||
proc, err := os.FindProcess(int(pid))
|
||||
if err == nil {
|
||||
errKill := proc.Kill()
|
||||
if errKill == nil && b.OnKill != nil {
|
||||
b.OnKill(info.Name, info.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !windows && !darwin
|
||||
//go:build !windows && !darwin && !linux
|
||||
|
||||
package blocker
|
||||
|
||||
|
||||
Reference in New Issue
Block a user