T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/subagent_runner.py:108- Finding
Autonomous Agent Receives Trusted Tool Access and the Complete Host Environment
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This appears to be a real queued coding automation skill, but its unattended runner gives child agents broad host access and has filesystem and verification weaknesses that require careful review.
Install only in a disposable or dedicated workspace first, with no production secrets in the environment. Do not use this on sensitive repositories or multi-user hosts unless you sandbox the child agent, restrict environment variables, review plans immediately before execution, and inspect diffs before merging.
scripts/subagent_runner.py:108Autonomous Agent Receives Trusted Tool Access and the Complete Host Environment
scripts/git_manager.py:59Worktree Setup Recursively Weakens File Permissions and Changes Ownership
scripts/subagent_runner.py:102Predictable Prompt Files Are Written Insecurely to the Shared Temporary Directory
scripts/executor.py:269Failed Mechanical Verification Is Committed and Marked as Passed
scripts/git_manager.py:20Unvalidated Persisted Plan IDs Reach Recursive Filesystem Deletion
scripts/verifier.py:64Unknown Verification Methods Succeed by Default
The declared description centers on workflow orchestration for coding plans and delayed execution in isolated worktrees. This code does not queue plans, approve them, execute work in worktrees, inspect reports, or manage runners. Instead, it provides runtime budget/accounting functionality for phases, plans, and nights, including API usage and persistence. While such budgeting could be a supporting component of a larger background execution system, this chunk’s primary behavior is materially different from the declared purpose and introduces undeclared capabilities related to budget tracking and disk persistence.
The declared description focuses on plan queuing/approval, deferred execution in isolated worktrees, and report inspection. This code chunk does not implement those functions. Instead, its primary purpose is maintaining persistent failure memory for past runs, including storing structured failure metadata under the workspace data directory and using prompt hashes to detect repeated failures and skip retries. While this could be a supporting subsystem for a larger background execution tool, the behavior shown here is materially different from the declared user-facing purpose and introduces undeclared capabilities around persistent failure tracking and retry decisioning.
The declared description focuses on lifecycle management of plans: queueing, approval, deferred execution in isolated worktrees, and report inspection. This code chunk does not implement queue management, approval flows, execution scheduling, report inspection, or runner configuration. Instead, its primary function is generating structured phases and prompts for tasks. That is a materially different purpose from the declared description. While references to worktrees are loosely compatible with isolated execution, the substantive behavior here is automated plan/phase generation, including task-type detection and templates for cloning/analyzing external repos, extracting patterns, implementing skills, refactoring, and running tests. Those capabilities are undeclared, so this chunk is a description-behavior mismatch.
Passing {**os.environ} to the router subprocess exposes all inherited environment variables, which may include API keys, tokens, and other secrets. Because the child is an external routing script that may call third-party providers, this broad secret propagation creates unnecessary credential exposure and potential exfiltration paths.
capture_output=True,
text=True,
timeout=timeout + 30,
env={**os.environ},
cwd=worktree_path,
)
elapsed = time.time() - start
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.
then treat explicit auth errors as blockers. We intentionally do not run a
paid/model task here.
"""
env = {**os.environ, "HOME": os.environ.get("HOME", str(Path.home()))}
probes = ([cli, "--version"], [cli, "--help"])
last = ""
for cmd in probes:
Passing the entire parent environment to a trusted autonomous agent exposes all available credentials, tokens, and internal configuration to a process whose behavior is driven by prompts and repository content. In an agent-execution skill, this is especially dangerous because prompt injection or malicious workspace files can steer the child process into reading and exfiltrating those secrets.
text=True,
timeout=timeout + 30,
cwd=worktree_path,
env={**os.environ},
)
elapsed = time.time() - start
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
"exec(": "Code execution",
"__import__": "Dynamic imports",
"compile(": "Code compilation",
"rm -rf /": "Destructive file deletion",
"rm -rf ~": "Home directory deletion",
"dd if=": "Disk destruction",
":(){ :|:& };:": "Fork bomb",
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
"exec(": "Code execution",
"__import__": "Dynamic imports",
"compile(": "Code compilation",
"rm -rf /": "Destructive file deletion",
"rm -rf ~": "Home directory deletion",
"dd if=": "Disk destruction",
":(){ :|:& };:": "Fork bomb",
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
"__import__": "Dynamic imports",
"compile(": "Code compilation",
"rm -rf /": "Destructive file deletion",
"rm -rf ~": "Home directory deletion",
"dd if=": "Disk destruction",
":(){ :|:& };:": "Fork bomb",
"chmod 777 /": "Root permission escalation",
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
"rm -rf ~": "Home directory deletion",
"dd if=": "Disk destruction",
":(){ :|:& };:": "Fork bomb",
"chmod 777 /": "Root permission escalation",
"mkfs.": "Filesystem formatting",
"/dev/sda": "Direct disk access",
"> /dev/sda": "Direct disk write",
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
export OPENCLAW_WORKSPACE=/tmp/night-shift-demo
mkdir -p "$OPENCLAW_WORKSPACE"
python3 ~/.openclaw/workspace/skills/night-shift/scripts/preflight.py --json
python3 ~/.openclaw/workspace/skills/night-shift/scripts/executor.py dry-run
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
cp skills/night-shift/.night-shift.env.example ~/.openclaw/workspace/.night-shift.env
chmod 600 ~/.openclaw/workspace/.night-shift.env
Then edit values for your environment. Do not commit real secrets.
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.
Do **not** market it like:
- “set and forget magic coding forever”
- “safe for any repo with zero setup”
- “works equally well without verification or review”
The real pitch is better:
> a structured autonomous execution framework for deferred engineering work
The skill advertises powerful capabilities in metadata, including filesystem writes, git worktrees, subprocess execution, optional external CLIs, and inherited environment access, but it does not declare an explicit tool scope such as permissions or allowed-tools. That creates an authorization ambiguity where a host may expose more capability than users expect, which is especially risky for an unattended execution skill that queues and later runs approved work.
Using a common conversational phrase like "good night" as a trigger for starting queued execution can cause accidental invocation during ordinary chat, especially in multi-skill environments. Because this skill can later spawn subprocesses, mutate repositories, and run unattended workflows, an unintended trigger may initiate materially risky actions after prior approvals exist.
The bare trigger word "morning" is overly generic and may invoke reporting behavior unintentionally during normal conversation. The impact is lower than the execution trigger because it is read/report oriented, but it can still surface workspace status, plan details, or report content unexpectedly in the wrong context.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def _get_git_head(self, worktree_path: str) -> str:
try:
result = subprocess.run(
["git", "rev-parse", "HEAD"],
cwd=worktree_path,
capture_output=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def _get_git_diff(self, worktree_path: str) -> str:
try:
result = subprocess.run(
["git", "diff", "--stat"],
cwd=worktree_path,
capture_output=True,
This executor intentionally runs previously approved phases that may invoke shell or subagent actions which mutate code and the workspace without a fresh confirmation gate at execution time. In the context of an agentic overnight runner, stale approval, plan tampering, or overly broad phase prompts can translate directly into unattended code execution and repository modification, making the operational risk substantially higher than in an interactive tool.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def _check_memory(self) -> int:
"""Check available memory in bytes."""
try:
result = subprocess.run(
["awk", "/MemAvailable/ {print $2 * 1024}", "/proc/meminfo"],
capture_output=True,
text=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def _check_swap(self) -> int:
"""Check total swap in bytes."""
try:
result = subprocess.run(
["awk", "/SwapTotal/ {print $2 * 1024}", "/proc/meminfo"],
capture_output=True,
text=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
cmd = [sys.executable, str(MEMORY_GUARD_SCRIPT)]
if self.dry_run:
cmd.append("--dry-run")
self._memory_guard_proc = subprocess.Popen(
cmd,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
The execution log path is derived from OPENCLAW_WORKSPACE, which can come from the environment, and is later opened for append without validation. If an attacker can influence the environment when this skill runs, they can redirect log writes to arbitrary filesystem locations accessible to the process, causing clobbering, unauthorized file creation, or data leakage via misplaced logs.
def _save_log(self):
"""Save execution log to file."""
EXECUTION_LOG.parent.mkdir(parents=True, exist_ok=True)
with open(EXECUTION_LOG, "a") as f:
f.write("\n".join(self.log_entries) + "\n")
def _get_summary(self) -> str:
The stop path performs a process-termination operation using os.kill, which is a potentially disruptive system action. While the result is printed after the fact, there is no confirmation prompt or prior warning before terminating the process identified in the lock file.
This code persists failure metadata, including plan IDs, phase IDs, timestamps, details, and prompt-derived hashes, to a file under the workspace. There is no confirmation prompt, print/log disclosure, or comment/docstring near the write path informing users that their failure information will be stored on disk.
Detected: suspicious.dynamic_code_execution