Commit 8892ed3a authored by Lisa's avatar Lisa

fix: repair browser control runtime and republish bundle

parent 2a9456fc
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
**Version:** 1.0 **Version:** 1.0
**Date:** 2026-04-29 **Date:** 2026-04-29
**Purpose:** Reverse-connection node execution with permission model, compatible with existing OpenClaw `sexec.sh` scripts. **Purpose:** Reverse-connection node execution with an integrated node-local permission model enforced by the agent.
--- ---
...@@ -48,14 +48,15 @@ ...@@ -48,14 +48,15 @@
│ └───────────────────────────┬──────────────────────┘ │ │ └───────────────────────────┬──────────────────────┘ │
│ │ receives │ │ │ receives │
│ ┌───────────────────────────▼──────────────────────┐ │ │ ┌───────────────────────────▼──────────────────────┐ │
│ │ Command Executor (sexec wrapper) │ │ │ │ Integrated Command Executor │ │
│ │ - Runs: /path/to/sexec.sh run --command ... │ │ │ │ - Evaluates allow/deny/ask rules │ │
│ │ - Executes approved commands directly │ │
│ │ - Streams stdout/stderr back │ │ │ │ - Streams stdout/stderr back │ │
│ │ - Returns exit code │ │ │ │ - Returns exit code │ │
│ └──────────────────────────────────────────────────┘ │ │ └──────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘ └──────────────────────────────────────────────────────────┘
Permission System (sexec.sh + config.json) - reused exactly Permission System (config.json permissions block) - enforced by the agent
``` ```
--- ---
...@@ -72,8 +73,14 @@ All messages are JSON over WebSocket. ...@@ -72,8 +73,14 @@ All messages are JSON over WebSocket.
"type": "register", "type": "register",
"node_name": "sissy", "node_name": "sissy",
"version": "1.0", "version": "1.0",
"capabilities": ["exec", "sysinfo"], "capabilities": ["exec"],
"sexec_path": "/home/openclaw/.openclaw/skills/sexec/sexec.sh" "capability_info": {
"enable_browser": false,
"enable_computer_control": false,
"enable_desktop_observe": false,
"enable_audio_control": false,
"enable_camera_control": false
}
} }
``` ```
...@@ -121,7 +128,7 @@ All messages are JSON over WebSocket. ...@@ -121,7 +128,7 @@ All messages are JSON over WebSocket.
- `id`: Unique command ID for tracking - `id`: Unique command ID for tracking
- `command`: Array of command + args (e.g., `["df", "-h"]`) - `command`: Array of command + args (e.g., `["df", "-h"]`)
- `timeout`: Max execution time in seconds - `timeout`: Max execution time in seconds
- `approved`: If `true`, bypass "ask" list (user explicitly approved) - `approved`: If `true`, bypass the node's `permissions.ask` rules (user explicitly approved)
### 4. Node → Gateway: Command Output (streaming) ### 4. Node → Gateway: Command Output (streaming)
...@@ -158,7 +165,7 @@ All messages are JSON over WebSocket. ...@@ -158,7 +165,7 @@ All messages are JSON over WebSocket.
### 6. Node → Gateway: Approval Required ### 6. Node → Gateway: Approval Required
**When command matches "ask" list:** **When command matches the node's `permissions.ask` rules:**
```json ```json
{ {
"type": "exec_approval_required", "type": "exec_approval_required",
...@@ -180,7 +187,7 @@ All messages are JSON over WebSocket. ...@@ -180,7 +187,7 @@ All messages are JSON over WebSocket.
### 7. Node → Gateway: Command Denied ### 7. Node → Gateway: Command Denied
**When command matches "deny" list:** **When command matches the node's `permissions.deny` rules:**
```json ```json
{ {
"type": "exec_denied", "type": "exec_denied",
...@@ -257,8 +264,14 @@ Node registers optional tools in its `tools` list during registration and expose ...@@ -257,8 +264,14 @@ Node registers optional tools in its `tools` list during registration and expose
"type": "register", "type": "register",
"node_name": "sissy", "node_name": "sissy",
"version": "1.0", "version": "1.0",
"capabilities": ["exec", "sysinfo", "browser_control"], "capabilities": ["exec", "browser_control"],
"sexec_path": "/home/openclaw/.openclaw/skills/sexec/sexec.sh" "capability_info": {
"enable_browser": true,
"enable_computer_control": false,
"enable_desktop_observe": false,
"enable_audio_control": false,
"enable_camera_control": false
}
} }
``` ```
...@@ -271,13 +284,22 @@ Node registers optional tools in its `tools` list during registration and expose ...@@ -271,13 +284,22 @@ Node registers optional tools in its `tools` list during registration and expose
**Node config file** (`/etc/hermes-node/config.json`): **Node config file** (`/etc/hermes-node/config.json`):
```json ```json
{ {
"gateway_url": "ws://192.168.42.115:8765", "gateway_url": "wss://192.168.42.115:8765",
"node_name": "sissy", "node_name": "sissy",
"token": "node-sissy-secret-token-abc123", "token": "node-sissy-secret-token-abc123",
"sexec_path": "/home/openclaw/.openclaw/skills/sexec/sexec.sh",
"reconnect_interval": 5, "reconnect_interval": 5,
"heartbeat_interval": 30, "heartbeat_interval": 30,
"enable_camera_control": false "capabilities": ["exec"],
"enable_browser": false,
"enable_computer_control": false,
"enable_desktop_observe": false,
"enable_audio_control": false,
"enable_camera_control": false,
"permissions": {
"deny": ["rm -rf /", "mkfs", "dd if="],
"ask": ["sudo", "su", "passwd", "mount", "umount", "fdisk", "parted"],
"allow": ["ls", "pwd", "whoami", "hostname", "uname", "date", "uptime", "df", "free", "ps", "cat", "head", "tail", "grep", "find", "du", "ip", "ifconfig", "ping", "curl", "wget", "git", "python", "python3", "node", "npm", "dmesg", "docker", "kubectl"]
}
} }
``` ```
...@@ -351,7 +373,7 @@ For Hermes skill to submit commands. ...@@ -351,7 +373,7 @@ For Hermes skill to submit commands.
"last_seen": 1714392000, "last_seen": 1714392000,
"uptime": 86400, "uptime": 86400,
"version": "1.0", "version": "1.0",
"capabilities": ["exec", "sysinfo"] "capabilities": ["exec"]
} }
``` ```
...@@ -369,21 +391,21 @@ For Hermes skill to submit commands. ...@@ -369,21 +391,21 @@ For Hermes skill to submit commands.
- Tokens stored in `/etc/hermes-node/config.json` on node - Tokens stored in `/etc/hermes-node/config.json` on node
- Tokens stored in gateway registry (file or DB) - Tokens stored in gateway registry (file or DB)
### 3. Permission System (Reused from sexec) ### 3. Permission System
- Each node keeps existing `sexec.sh` + `config.json` - Each node stores its permission rules in `config.json`
- `allow` list: auto-execute - `permissions.allow`: auto-execute
- `ask` list: require user approval - `permissions.ask`: require user approval
- `deny` list: reject immediately - `permissions.deny`: reject immediately
### 4. Command Approval Flow ### 4. Command Approval Flow
``` ```
User → Hermes → Gateway → Node User → Hermes → Gateway → Node
sexec checks config.json agent checks config.json permissions
matches "ask" list matches `permissions.ask`
sends approval_required sends approval_required
Gateway → User: "Approve 'sudo gnt-instance stop prod-db'?" Gateway → User: "Approve 'sudo gnt-instance stop prod-db'?"
...@@ -391,7 +413,7 @@ User: "yes" ...@@ -391,7 +413,7 @@ User: "yes"
Gateway → Node: approved=true Gateway → Node: approved=true
sexec executes agent executes command
``` ```
--- ---
...@@ -427,7 +449,7 @@ Gateway → Node: approved=true ...@@ -427,7 +449,7 @@ Gateway → Node: approved=true
### Node Side ### Node Side
1. Install `hermes-node-agent` package 1. Install `hermes-node-agent` package
2. Configure `/etc/hermes-node/config.json` with gateway URL + token 2. Configure `/etc/hermes-node/config.json` with gateway URL + token
3. Ensure `sexec.sh` is installed and configured 3. Review the `permissions` block and capability toggles for the node
4. Start service: `/etc/init.d/hermes-node-agent start` 4. Start service: `/etc/init.d/hermes-node-agent start`
5. Verify connection: `tail -f /var/log/hermes-node-agent.log` 5. Verify connection: `tail -f /var/log/hermes-node-agent.log`
...@@ -435,18 +457,18 @@ Gateway → Node: approved=true ...@@ -435,18 +457,18 @@ Gateway → Node: approved=true
## Compatibility ## Compatibility
### With OpenClaw sexec ### With node-local permission policies
-Reuses exact same `sexec.sh` binary -Keeps the permission policy in `config.json`
-Reuses exact same `config.json` format -Preserves allow/deny/ask semantics
-Reuses exact same permission logic -Moves execution and approval checks fully into the agent
-No changes needed to existing sexec installations -Removes external wrapper dependencies from deployment
### Migration from OpenClaw ### Migration from earlier wrapper-based setups
1. Keep existing sexec on nodes 1. Move any legacy allow/deny/ask rules into the node's `permissions` block
2. Install node agent alongside 2. Install or upgrade the node agent
3. Configure gateway URL + token 3. Configure gateway URL + token
4. Start agent 4. Start agent
5. Disable OpenClaw gateway (optional) 5. Remove obsolete wrapper references from local docs/scripts
--- ---
......
...@@ -16,7 +16,7 @@ Cross-platform node agent for the Hermes Node Protocol. Connects to a central ga ...@@ -16,7 +16,7 @@ Cross-platform node agent for the Hermes Node Protocol. Connects to a central ga
- **Cross-platform**: Linux and Windows support - **Cross-platform**: Linux and Windows support
- **Reverse connection**: Nodes connect to gateway (firewall-friendly) - **Reverse connection**: Nodes connect to gateway (firewall-friendly)
- **Token authentication**: Secure per-node tokens - **Token authentication**: Secure per-node tokens
- **Permission system**: sexec-based allow/deny/ask rules - **Permission system**: integrated allow/deny/ask rules enforced by the agent
- **Auto-reconnect**: Exponential backoff on disconnect - **Auto-reconnect**: Exponential backoff on disconnect
- **Heartbeat**: Keep-alive mechanism - **Heartbeat**: Keep-alive mechanism
- **Optional capabilities**: Browser control, computer control - **Optional capabilities**: Browser control, computer control
...@@ -72,9 +72,19 @@ sudo /etc/init.d/hermes-node-agent start ...@@ -72,9 +72,19 @@ sudo /etc/init.d/hermes-node-agent start
"gateway_url": "wss://gateway-host:8765", "gateway_url": "wss://gateway-host:8765",
"node_name": "my-node", "node_name": "my-node",
"token": "your-token-here", "token": "your-token-here",
"sexec_path": "/path/to/sexec.sh",
"reconnect_interval": 5, "reconnect_interval": 5,
"heartbeat_interval": 30 "heartbeat_interval": 30,
"capabilities": ["exec"],
"enable_browser": false,
"enable_computer_control": false,
"enable_desktop_observe": false,
"enable_audio_control": false,
"enable_camera_control": false,
"permissions": {
"deny": ["rm -rf /", "mkfs", "dd if="],
"ask": ["sudo", "su", "passwd", "mount", "umount", "fdisk", "parted"],
"allow": ["ls", "pwd", "whoami", "hostname", "uname", "date", "uptime", "df", "free", "ps", "cat", "head", "tail", "grep", "find", "du", "ip", "ifconfig", "ping", "curl", "wget", "git", "python", "python3", "node", "npm", "dmesg", "docker", "kubectl"]
}
} }
``` ```
...@@ -99,7 +109,7 @@ node-agent/ ...@@ -99,7 +109,7 @@ node-agent/
├── install.sh # Linux installer ├── install.sh # Linux installer
├── install-windows.ps1 # Windows PowerShell installer (legacy) ├── install-windows.ps1 # Windows PowerShell installer (legacy)
├── hermes-node-agent.init.d # SysV init script ├── hermes-node-agent.init.d # SysV init script
├── hermes-node-agent.service # systemd unit (alternative) ├── hermes-node-agent.service # service file (legacy artifact; SysV init is canonical)
├── requirements.txt # Python dependencies ├── requirements.txt # Python dependencies
└── windows/ # Windows-specific components └── windows/ # Windows-specific components
├── agent-manager.py # System tray GUI ├── agent-manager.py # System tray GUI
......
...@@ -18,6 +18,7 @@ Implements the interface expected by hermes_node_agent.py. ...@@ -18,6 +18,7 @@ Implements the interface expected by hermes_node_agent.py.
import asyncio import asyncio
import base64 import base64
import inspect
import logging import logging
from typing import Dict, Any, Optional from typing import Dict, Any, Optional
from pathlib import Path from pathlib import Path
...@@ -401,6 +402,63 @@ class BrowserController: ...@@ -401,6 +402,63 @@ class BrowserController:
cdp = await page.context.new_cdp_session(page) cdp = await page.context.new_cdp_session(page)
result = await cdp.send(method, params or {}) result = await cdp.send(method, params or {})
return {"success": True, "result": result} return {"success": True, "result": result}
async def execute(self, command: str, params: Dict[str, Any] = None) -> Dict[str, Any]:
"""Dispatch a browser_control command from the node-agent wire protocol."""
params = params or {}
layer = params.get("layer", "high_level")
page_id = params.get("page_id")
if layer == "playwright":
return await self.playwright_command(
page_id=page_id,
command=command,
args=params.get("args") or [],
kwargs=params.get("kwargs") or {},
)
if layer == "cdp":
return await self.cdp_command(
page_id=page_id,
method=command,
params=params.get("params") or {},
)
high_level_map = {
"launch": self.launch,
"create_context": self.create_context,
"new_page": self.new_page,
"navigate": self.navigate,
"click": self.click,
"fill": self.fill,
"type_text": self.type_text,
"wait_for_selector": self.wait_for_selector,
"execute_script": self.execute_script,
"evaluate": self.evaluate,
"screenshot": self.screenshot,
"get_content": self.get_content,
"get_title": self.get_title,
"get_url": self.get_url,
"close_page": self.close_page,
"close_context": self.close_context,
"close": self.close,
"list_pages": self.list_pages,
"list_contexts": self.list_contexts,
"load_extension": self.load_extension,
"execute_extension_script": self.execute_extension_script,
"list_extensions": self.list_extensions,
}
handler = high_level_map.get(command)
if handler is None:
return {"success": False, "error": f"Unknown browser command: {command}"}
call_params = dict(params)
call_params.pop("layer", None)
signature = inspect.signature(handler)
accepted = set(signature.parameters.keys())
filtered = {k: v for k, v in call_params.items() if k in accepted}
return await handler(**filtered)
# Helpers # Helpers
......
This diff is collapsed.
...@@ -33,6 +33,7 @@ import ssl ...@@ -33,6 +33,7 @@ import ssl
import subprocess import subprocess
import sys import sys
import time import time
from contextlib import suppress
from pathlib import Path from pathlib import Path
from typing import Optional, Dict, Any, List from typing import Optional, Dict, Any, List
...@@ -1127,6 +1128,7 @@ class NodeAgent: ...@@ -1127,6 +1128,7 @@ class NodeAgent:
except Exception as e: except Exception as e:
logger.warning(f"BrowserController init failed: {e}") logger.warning(f"BrowserController init failed: {e}")
self.capabilities = self._detect_capabilities() self.capabilities = self._detect_capabilities()
self._pending_tasks: set[asyncio.Task] = set()
def _load_config(self, path: Optional[str]) -> Dict[str, Any]: def _load_config(self, path: Optional[str]) -> Dict[str, Any]:
"""Load node configuration from JSON.""" """Load node configuration from JSON."""
...@@ -1227,7 +1229,9 @@ class NodeAgent: ...@@ -1227,7 +1229,9 @@ class NodeAgent:
try: try:
async for raw in ws: async for raw in ws:
msg = json.loads(raw) msg = json.loads(raw)
await self._handle_message(ws, msg) task = asyncio.create_task(self._handle_message(ws, msg))
self._pending_tasks.add(task)
task.add_done_callback(self._pending_tasks.discard)
except Exception as e: except Exception as e:
disconnect_reason = e disconnect_reason = e
raise raise
...@@ -1237,6 +1241,13 @@ class NodeAgent: ...@@ -1237,6 +1241,13 @@ class NodeAgent:
await heartbeat_task await heartbeat_task
except asyncio.CancelledError: except asyncio.CancelledError:
pass pass
pending = list(self._pending_tasks)
for task in pending:
task.cancel()
for task in pending:
with suppress(asyncio.CancelledError):
await task
self._pending_tasks.clear()
_log_disconnected(disconnect_reason) _log_disconnected(disconnect_reason)
except Exception as e: except Exception as e:
...@@ -1433,9 +1444,11 @@ class NodeAgent: ...@@ -1433,9 +1444,11 @@ class NodeAgent:
_log_browser_received(command) _log_browser_received(command)
try: try:
if hasattr(self.browser, 'execute'): if hasattr(self.browser, 'execute'):
result = self.browser.execute(command, params) result = await self.browser.execute(command, params)
elif hasattr(self.browser, 'run'): elif hasattr(self.browser, 'run'):
result = self.browser.run(command, params) result = self.browser.run(command, params)
if asyncio.iscoroutine(result) or isinstance(result, asyncio.Future):
result = await result
else: else:
result = {'success': False, 'error': 'BrowserController has no execute/run entrypoint'} result = {'success': False, 'error': 'BrowserController has no execute/run entrypoint'}
except Exception as e: except Exception as e:
......
...@@ -96,13 +96,23 @@ if (-not (Test-Path $configPath)) { ...@@ -96,13 +96,23 @@ if (-not (Test-Path $configPath)) {
$token = ($tokenBytes | ForEach-Object { $_.ToString("x2") }) -join '' $token = ($tokenBytes | ForEach-Object { $_.ToString("x2") }) -join ''
$config = @{ $config = @{
gateway_url = "wss://YOUR-GATEWAY-HOST:8765" gateway_url = "wss://YOUR-GATEWAY-HOST:8765"
node_name = $env:COMPUTERNAME node_name = $env:COMPUTERNAME
token = $token token = $token
sexec_path = "$env:USERPROFILE\.openclaw\skills\sexec\sexec.ps1" reconnect_interval = 5
reconnect_interval = 5 heartbeat_interval = 30
heartbeat_interval = 30 capabilities = @("exec")
} | ConvertTo-Json -Depth 3 enable_browser = $false
enable_computer_control = $false
enable_desktop_observe = $false
enable_audio_control = $false
enable_camera_control = $false
permissions = @{
deny = @("rm -rf /", "mkfs", "dd if=")
ask = @("sudo", "su", "passwd", "mount", "umount", "fdisk", "parted")
allow = @("ls", "pwd", "whoami", "hostname", "uname", "date", "uptime", "df", "free", "ps", "cat", "head", "tail", "grep", "find", "du", "ip", "ifconfig", "ping", "curl", "wget", "git", "python", "python3", "node", "npm", "systemctl", "journalctl", "dmesg", "docker", "kubectl")
}
} | ConvertTo-Json -Depth 5
$config | Out-File -FilePath $configPath -Encoding UTF8 $config | Out-File -FilePath $configPath -Encoding UTF8
Write-Host " Config written to: $configPath" -ForegroundColor Yellow Write-Host " Config written to: $configPath" -ForegroundColor Yellow
...@@ -165,5 +175,5 @@ Write-Host " Get-Content $configDir\hermes-node-agent.log -Wait -Tail 50" -For ...@@ -165,5 +175,5 @@ Write-Host " Get-Content $configDir\hermes-node-agent.log -Wait -Tail 50" -For
Write-Host "" Write-Host ""
Write-Host "=== Important ===" -ForegroundColor Yellow Write-Host "=== Important ===" -ForegroundColor Yellow
Write-Host "The agent runs as SYSTEM if using Task Scheduler, or as LocalSystem if using NSSM." -ForegroundColor Gray Write-Host "The agent runs as SYSTEM if using Task Scheduler, or as LocalSystem if using NSSM." -ForegroundColor Gray
Write-Host "Ensure the sexec.ps1 script exists at the configured path if using permissions." -ForegroundColor Gray Write-Host "Review the permissions block in config.json before enabling broader command access." -ForegroundColor Gray
Write-Host "" Write-Host ""
...@@ -70,13 +70,22 @@ This is a fully self-contained offline Windows installer. ...@@ -70,13 +70,22 @@ This is a fully self-contained offline Windows installer.
The installer writes to `C:\ProgramData\HermesNode\config.json`: The installer writes to `C:\ProgramData\HermesNode\config.json`:
```json ```json
{ {
"gateway_url": "ws://gateway.example.com:8765", "gateway_url": "wss://gateway.example.com:8765",
"node_name": "WORKSTATION-01", "node_name": "WORKSTATION-01",
"token": "YOUR-SECRET-TOKEN", "token": "YOUR-SECRET-TOKEN",
"sexec_path": ".\sexec-template.ps1",
"reconnect_interval": 5, "reconnect_interval": 5,
"heartbeat_interval": 30, "heartbeat_interval": 30,
"capabilities": ["exec", "browser_control", "computer_control"] "capabilities": ["exec"],
"enable_browser": false,
"enable_computer_control": false,
"enable_desktop_observe": false,
"enable_audio_control": false,
"enable_camera_control": false,
"permissions": {
"deny": ["rm -rf /", "mkfs", "dd if="],
"ask": ["sudo", "su", "passwd", "mount", "umount", "fdisk", "parted"],
"allow": ["ls", "pwd", "whoami", "hostname", "uname", "date", "uptime", "df", "free", "ps", "cat", "head", "tail", "grep", "find", "du", "ip", "ifconfig", "ping", "curl", "wget", "git", "python", "python3", "node", "npm", "systemctl", "journalctl", "dmesg", "docker", "kubectl"]
}
} }
``` ```
......
...@@ -195,10 +195,19 @@ Section "Install" ...@@ -195,10 +195,19 @@ Section "Install"
''gateway_url'': ''$0'', ''gateway_url'': ''$0'',
''node_name'': ''$1'', ''node_name'': ''$1'',
''token'': ''$2'', ''token'': ''$2'',
''sexec_path'': ''.\sexec-template.ps1'',
''reconnect_interval'': 5, ''reconnect_interval'': 5,
''heartbeat_interval'': 30, ''heartbeat_interval'': 30,
''capabilities'': [''exec'', ''browser_control'', ''computer_control''] ''capabilities'': [''exec''],
''enable_browser'': $false,
''enable_computer_control'': $false,
''enable_desktop_observe'': $false,
''enable_audio_control'': $false,
''enable_camera_control'': $false,
''permissions'': @{
''deny'': @(''rm -rf /'', ''mkfs'', ''dd if=''),
''ask'': @(''sudo'', ''su'', ''passwd'', ''mount'', ''umount'', ''fdisk'', ''parted''),
''allow'': @(''ls'', ''pwd'', ''whoami'', ''hostname'', ''uname'', ''date'', ''uptime'', ''df'', ''free'', ''ps'', ''cat'', ''head'', ''tail'', ''grep'', ''find'', ''du'', ''ip'', ''ifconfig'', ''ping'', ''curl'', ''wget'', ''git'', ''python'', ''python3'', ''node'', ''npm'', ''systemctl'', ''journalctl'', ''dmesg'', ''docker'', ''kubectl'')
}
}}")"' }}")"'
; Write uninstaller ; Write uninstaller
......
...@@ -106,10 +106,15 @@ Section \"Install\" ...@@ -106,10 +106,15 @@ Section \"Install\"
FWrite $0 \" \"gateway_url\": \"\"$GatewayURL\"\",\" FWrite $0 \" \"gateway_url\": \"\"$GatewayURL\"\",\"
FWrite $0 \" \"node_name\": \"\"$NodeName\"\",\" FWrite $0 \" \"node_name\": \"\"$NodeName\"\",\"
FWrite $0 \" \"token\": \"\"$Token\"\",\" FWrite $0 \" \"token\": \"\"$Token\"\",\"
FWrite $0 \" \"sexec_path\": \"./sexec-template.ps1\",\"
FWrite $0 \" \"reconnect_interval\": 5,\" FWrite $0 \" \"reconnect_interval\": 5,\"
FWrite $0 \" \"heartbeat_interval\": 30,\" FWrite $0 \" \"heartbeat_interval\": 30,\"
FWrite $0 \" \"capabilities\": [\"exec\", \"browser_control\", \"computer_control\"]\" FWrite $0 \" \"capabilities\": [\"exec\"],\"
FWrite $0 \" \"enable_browser\": false,\"
FWrite $0 \" \"enable_computer_control\": false,\"
FWrite $0 \" \"enable_desktop_observe\": false,\"
FWrite $0 \" \"enable_audio_control\": false,\"
FWrite $0 \" \"enable_camera_control\": false,\"
FWrite $0 \" \"permissions\": {\"deny\": [\"rm -rf /\", \"mkfs\", \"dd if=\"], \"ask\": [\"sudo\", \"su\", \"passwd\", \"mount\", \"umount\", \"fdisk\", \"parted\"], \"allow\": [\"ls\", \"pwd\", \"whoami\", \"hostname\", \"uname\", \"date\", \"uptime\", \"df\", \"free\", \"ps\", \"cat\", \"head\", \"tail\", \"grep\", \"find\", \"du\", \"ip\", \"ifconfig\", \"ping\", \"curl\", \"wget\", \"git\", \"python\", \"python3\", \"node\", \"npm\", \"systemctl\", \"journalctl\", \"dmesg\", \"docker\", \"kubectl\"]}\"
FWrite $0 \"}\" FWrite $0 \"}\"
FileClose $0 FileClose $0
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment