# Macos Enterprise App Cleanup

> Clean up macOS enterprise, MDM, VPN, EDR, security, and company-internal software remnants after a user leaves a company. Use this skill whenever the user asks to remove or audit corporate software, agents, proxies, VPNs, endpoint security, launch agents, system extensions, login/background items, package receipts, or company-branded leftovers on a Mac. Especially use it for Meituan/Sankuai, MOA, 文枢/wenshu, Kaspersky, Cisco AnyConnect, wsAssistant, EDR/MEDR, DNS proxy, Network Extension, Socket Filter, or similar enterprise tools.

- Skill: `ceilf6/macos-enterprise-app-cleanup` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ceilf6/macos-enterprise-app-cleanup`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ceilf6/macos-enterprise-app-cleanup/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: ceilf6 (https://skillmd.com/u/ceilf6)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ceilf6/macos-enterprise-app-cleanup

---


# macOS Enterprise App Cleanup

This skill turns a one-off cleanup into a careful, repeatable macOS workflow. The goal is to remove confirmed company-related software and configuration while avoiding accidental deletion of unrelated tools.

## Core Principles

- Start with read-only discovery. Do not delete anything until the candidate list is understood.
- Treat uncertainty as a stop point. If a path, bundle id, app, or vendor is not clearly related to the user's target company, ask before deleting.
- Prefer official uninstallers for security, VPN, EDR, and enterprise apps. They usually unregister services, filters, receipts, firewall rules, and system extensions more cleanly than manual deletion.
- Use exact matches and scoped paths. Avoid broad `rm -rf` or deleting every file containing a generic term like `vpn`, `agent`, `assistant`, or `security`.
- Expect macOS protection boundaries. Root can remove many files, but SIP, TCC, System Extensions, and Background Task Management may require a reboot, System Settings action, or supported system command.
- Report residual state precisely. Distinguish active processes, deleted files, stale background-item cache, and system extensions waiting to uninstall on reboot.

## When To Use

Use this workflow when a user says things like:

- "我离职了，帮我删掉公司软件/配置"
- "清理美团/三快/MOA/文枢/Kaspersky/Cisco"
- "Mac 上还有代理/VPN/EDR/安全软件残留"
- "系统设置里还有某个后台项目/系统扩展"
- "删除企业软件后还弹系统扩展/Socket Filter/Network Extension 提示"

Known examples from the source cleanup:

- Meituan/Sankuai: `meituan`, `sankuai`, `MOA`, `moadeploy`, `moatray`, `moalogin`, `wenshu`, `文枢`, `.MTEDR`, `MEDR`, `syswframework`, `EncryptionBox`, `cn.neixin.pc`
- Kaspersky: `Kaspersky Anti-Virus For Mac.app`, `Kaspersky Lab`, `klnagent`, `com.kaspersky.*`
- Cisco AnyConnect: `/opt/cisco/anyconnect`, `com.cisco.anyconnect.*`, `Cisco AnyConnect Socket Filter`
- Sankuai wsAssistant: `/Library/Application Support/wsAssistant`, logs calling `cryptbox.sankuai.com` or `sec_shared_pid_wenshu`

## Workflow

### 1. Establish Scope

Ask for or infer the company/product names and any known apps from the user. If the user says "delete all related", still separate:

- Clearly related items
- Unclear but suspicious items
- Unrelated third-party products

Do not assume a vendor is company-related merely because it appeared during cleanup. For example, Cisco AnyConnect is Cisco software; only delete it once the user confirms or the configuration clearly ties it to the target company.

### 2. Read-Only Discovery

Use parallel shell reads where possible. Search applications, launchd jobs, packages, processes, system extensions, background items, profiles, and support folders.

Useful discovery commands:

```bash
find /Applications /Users/"$USER"/Applications -maxdepth 2 \
  \( -iname '*meituan*' -o -iname '*sankuai*' -o -iname '*kaspersky*' -o -iname '*moa*' -o -iname '*wenshu*' -o -name '*文枢*' \) -print

pkgutil --pkgs | rg -i 'meituan|sankuai|kaspersky|installmoa|moadeploy|EncryptionBox|anyconnect|cisco'

launchctl list | rg -i 'meituan|sankuai|kaspersky|moa|wenshu|medr|anyconnect|cisco'

systemextensionsctl list | rg -i 'meituan|sankuai|kaspersky|moa|wenshu|medr|anyconnect|cisco'

ps aux | rg -i 'meituan|sankuai|kaspersky|moa|wslaunch|wenshu|文枢|medr|mtdr|syswframework|neixin|anyconnect|cisco|wsAssistant'

sfltool dumpbtm | rg -i 'meituan|sankuai|kaspersky|moa|wenshu|medr|anyconnect|cisco|wsAssistant'

profiles list -all 2>&1 | rg -i -C 4 'meituan|sankuai|kaspersky|moa|wenshu|medr|anyconnect|cisco|vpn'
```

Search common file locations:

```bash
find \
  /Library/Application\ Support \
  "$HOME/Library/Application Support" \
  /Library/Preferences \
  "$HOME/Library/Preferences" \
  /Library/LaunchAgents \
  /Library/LaunchDaemons \
  "$HOME/Library/LaunchAgents" \
  "$HOME/Library/Caches" \
  "$HOME/Library/WebKit" \
  "$HOME/Library/Saved Application State" \
  "$HOME/Library/Group Containers" \
  -maxdepth 4 \
  \( -iname '*meituan*' -o -iname '*sankuai*' -o -iname '*kaspersky*' -o -iname '*moa*' -o -iname '*wenshu*' -o -name '*文枢*' -o -iname '*MTEDR*' -o -iname '*MEDR*' -o -iname '*neixin*' -o -iname '*syswframework*' \) -print 2>/dev/null
```

If a generic-looking folder appears, verify content before deleting:

```bash
rg -a -i -n -m 20 'meituan|sankuai|wenshu|文枢|moa|medr|neixin|daxiang|ssosv|cryptbox\.sankuai' /path/to/candidate 2>/dev/null
```

### 3. Classify Candidates

Create a short deletion plan before acting:

- Apps: `/Applications/MOA.app`, `/Applications/wenshu.app`, `/Applications/.MTEDR.app`, `/Applications/Kaspersky Anti-Virus For Mac.app`
- Support dirs: `/Library/Application Support/MOADeploy`, `/Library/Application Support/.MTEDR`, `/Library/Application Support/Kaspersky Lab`, `/Library/Application Support/wsAssistant`
- Launch items: `com.meituan.*`, `com.kaspersky.*`, `com.cisco.anyconnect.*`, `com.syswframework.launch.plist`
- User data: `~/Library/Application Support/cn.neixin.pc`, `~/Library/Preferences/cn.neixin.pc.plist`, Kaspersky group containers
- Receipts: `pkgutil --pkgs` matches
- System extensions: Meituan network extension, Cisco socket filter, Kaspersky system extension

Ask before deleting any uncertain or unrelated vendor item. If the user confirms "delete all" or specifically names the item, proceed.

### 4. Use Official Uninstallers First

Prefer vendor uninstall scripts/apps when present:

```bash
# Kaspersky examples
/bin/sh "/Library/Application Support/Kaspersky Lab/KAV/Binaries/UninstallScript"
/bin/sh "/Library/Application Support/Kaspersky Lab/klnagent/Binaries/UninstallScript"

# Cisco AnyConnect examples
/bin/sh "/opt/cisco/anyconnect/bin/anyconnect_uninstall.sh"
# or
/bin/bash "/opt/cisco/anyconnect/bin/vpn_uninstall.sh"
```

Run these with administrator privileges. In Codex Desktop on macOS, `osascript` can request an admin prompt:

```bash
osascript -e 'do shell script "id -u" with administrator privileges'
```

If the test returns `0`, the admin path is working.

### 5. Stop Services And Processes

Unload launchd jobs before deleting files:

```bash
launchctl bootout system /Library/LaunchDaemons/com.example.daemon.plist
launchctl bootout "gui/$(id -u "$USER")" /Library/LaunchAgents/com.example.agent.plist
launchctl disable system/com.example.daemon
launchctl disable "gui/$(id -u "$USER")/com.example.agent"
```

Then terminate remaining processes by exact path or exact vendor pattern:

```bash
pkill -f '/Applications/MOA.app|/Library/Application Support/MOADeploy|/Applications/wenshu.app|/Applications/.MTEDR.app'
pkill -f '/opt/cisco/anyconnect|Cisco AnyConnect'
sleep 1
pkill -9 -f '/opt/cisco/anyconnect|Cisco AnyConnect'
```

### 6. Remove Files, Receipts, And User Data

Remove only confirmed paths. Clear immutable flags first when needed:

```bash
chflags -R nouchg,noschg "$path" 2>/dev/null
chmod -R u+w "$path" 2>/dev/null
rm -rf "$path"
```

Forget package receipts:

```bash
pkgutil --pkgs | rg -i 'meituan|sankuai|kaspersky|installmoa|moadeploy|EncryptionBox|anyconnect|cisco' |
while IFS= read -r pkg; do
  sudo pkgutil --forget "$pkg"
done
```

For protected user group containers that return `Operation not permitted`, inspect xattrs:

```bash
ls -laOe@ "$HOME/Library/Group Containers/<container>"
xattr -lr "$HOME/Library/Group Containers/<container>"
```

If the user owns the folder, removing `com.apple.macl` and clearing xattrs may allow deletion:

```bash
xattr -dr com.apple.macl "$HOME/Library/Group Containers/<container>" 2>/dev/null
xattr -cr "$HOME/Library/Group Containers/<container>" 2>/dev/null
rm -rf "$HOME/Library/Group Containers/<container>"
```

### 7. Handle System Extensions

System extensions have special lifecycle behavior.

Check status:

```bash
systemextensionsctl list
```

Try supported cleanup:

```bash
systemextensionsctl uninstall TEAMID bundle.id
systemextensionsctl gc
```

Interpret common states:

- `activated enabled`: still active.
- `terminated waiting to uninstall on reboot`: cleanup succeeded; ask the user to reboot.
- No matching extension after reboot: fully removed.

If `systemextensionsctl uninstall` says SIP prevents use, do not fight SIP by default. Try `systemextensionsctl gc`, remove the owning app and launchd jobs, reboot, then recheck. Only discuss disabling SIP if the user explicitly wants a forceful Recovery-mode cleanup and understands the risk.

### 8. Clear Background Item Cache

After deleting launchd files, System Settings may still show stale "Login Items & Extensions" entries.

First verify the referenced executable/plist is gone. Then clear Background Task Management cache:

```bash
sfltool dumpbtm | rg -i 'cisco|anyconnect|meituan|sankuai|kaspersky|moa|wenshu|medr'
sfltool resetbtm
sfltool dumpbtm | rg -i 'cisco|anyconnect|meituan|sankuai|kaspersky|moa|wenshu|medr'
```

Use `resetbtm` only after explaining that it resets the background-task management database and may cause legitimate background items to reappear as newly managed by macOS.

### 9. Final Verification

Run a final no-residue check:

```bash
find /Applications /Users/"$USER"/Applications /opt /Library/Application\ Support /Library/LaunchAgents /Library/LaunchDaemons "$HOME/Library/Application Support" "$HOME/Library/Preferences" "$HOME/Library/Caches" "$HOME/Library/Saved Application State" -maxdepth 4 \
  \( -iname '*meituan*' -o -iname '*sankuai*' -o -iname '*kaspersky*' -o -iname '*moa*' -o -iname '*wenshu*' -o -name '*文枢*' -o -iname '*MTEDR*' -o -iname '*MEDR*' -o -iname '*neixin*' -o -iname '*syswframework*' -o -iname '*anyconnect*' -o -iname '*cisco*' -o -iname '*wsAssistant*' \) -print 2>/dev/null

ps -axo pid,ppid,user,command | awk 'BEGIN{IGNORECASE=1} /\/opt\/cisco|Cisco AnyConnect|vpnagentd|wsAssistant|meituan|sankuai|wenshu|MOA|Kaspersky|MEDR|MTEDR/ && $0 !~ /awk|ps -axo|zsh -c|zsh -lc/ {print}'

pkgutil --pkgs | rg -i 'meituan|sankuai|kaspersky|installmoa|moadeploy|EncryptionBox|neixin|anyconnect|cisco'

systemextensionsctl list | rg -i 'meituan|sankuai|kaspersky|moa|wenshu|medr|anyconnect|cisco'

sfltool dumpbtm | rg -i 'meituan|sankuai|kaspersky|moa|wenshu|medr|anyconnect|cisco|wsAssistant'
```

Be careful not to count the verification command itself as a matching process. Use filters or rerun process checks after `find` completes.

## Reporting Format

Keep the final report concise:

- State what was removed.
- State what checks are clean.
- State any remaining macOS-managed status, such as "waiting to uninstall on reboot".
- Mention if a reboot is needed.
- Mention any intentionally preserved unrelated software.

Example:

```text
已删除 MOA、文枢、Kaspersky、Cisco AnyConnect、wsAssistant 及相关启动项、进程、包收据、用户配置和后台项目缓存。

复查结果：无相关应用、LaunchAgent/LaunchDaemon、进程、pkg 收据或系统扩展。systemextensionsctl 只剩 OBS 虚拟摄像头。
```

## Safety Notes

- Do not delete unrelated VPNs, proxies, EDRs, or security apps unless the user confirms them. Examples: Cisco AnyConnect, GlobalProtect, OpenVPN, Clash, Sangfor, Avast, OBS system extension.
- Do not edit `/Library/SystemExtensions/db.plist` or protected network extension plists directly. SIP may block root writes, and manual edits can corrupt system extension state.
- Do not use destructive global commands such as `git reset`, broad `rm -rf /Library/*agent*`, or deleting all VPN/network files.
- If a command requires administrator privileges, tell the user what category of change it will make before triggering the prompt.

