134 lines
3.0 KiB
Go
134 lines
3.0 KiB
Go
//go:build darwin
|
|
|
|
package blocker
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
var systemAllowed = map[string]bool{
|
|
"finder": true,
|
|
"terminal": true,
|
|
"iterm": true,
|
|
"iterm2": true,
|
|
"bash": true,
|
|
"zsh": true,
|
|
"sh": true,
|
|
"wails": true,
|
|
"code": true,
|
|
"cursor": true,
|
|
"windsurf": true,
|
|
"goland": true,
|
|
"idea": true,
|
|
"clion": true,
|
|
"webstorm": true,
|
|
"pycharm": true,
|
|
"rider": true,
|
|
"studio": true,
|
|
"eclipse": true,
|
|
"sublime_text": true,
|
|
"git": true,
|
|
"docker": true,
|
|
"system events": true,
|
|
"osascript": true,
|
|
"client": true,
|
|
"simple_care_v1.0": true,
|
|
}
|
|
|
|
func getVisibleProcesses() (map[uint32]string, error) {
|
|
script := `tell application "System Events"
|
|
set nameList to name of every process whose visible is true
|
|
set pidList to unix id of every process whose visible is true
|
|
set out to ""
|
|
repeat with i from 1 to count of nameList
|
|
set out to out & item i of nameList & ":" & item i of pidList & "\n"
|
|
end repeat
|
|
return out
|
|
end tell`
|
|
cmd := exec.Command("osascript", "-e", script)
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
procs := make(map[uint32]string)
|
|
lines := strings.Split(string(out), "\n")
|
|
for _, line := range lines {
|
|
line = strings.TrimSpace(line)
|
|
if line == "" {
|
|
continue
|
|
}
|
|
idx := strings.LastIndex(line, ":")
|
|
if idx == -1 {
|
|
continue
|
|
}
|
|
pName := line[:idx]
|
|
pIdStr := line[idx+1:]
|
|
var pid uint32
|
|
if _, err := fmt.Sscanf(pIdStr, "%d", &pid); err == nil {
|
|
procs[pid] = pName
|
|
}
|
|
}
|
|
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, name := range procs {
|
|
pNameLower := strings.ToLower(name)
|
|
|
|
// 1. Always allow our app, system/critical developer tools, or agent helpers
|
|
if pid == myPid || (currentExec != "" && pNameLower == currentExec) || systemAllowed[pNameLower] || strings.Contains(pNameLower, "antigravity") {
|
|
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(name, name)
|
|
}
|
|
log.Printf("[BLOCKER] KILLED unauthorized application: %s (PID: %d)", name, pid)
|
|
proc, err := os.FindProcess(int(pid))
|
|
if err == nil {
|
|
errKill := proc.Kill()
|
|
if errKill == nil && b.OnKill != nil {
|
|
b.OnKill(name, name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|