T09 · Insecure Skill Coding Practices
Error
- Location
- ops-monitor.py:556
- Finding
- Self-Declared Risk Labels Do Not Enforce Read-Only Execution## Vulnerability Details **File Location**: `ops-monitor.py:304-330`, `ops-monitor.py:556-600` **Vulnerability Type**: Arbitrary command execution through unenforced trust labels **Risk Level**: High ### Vulnerable Code ```python risk = raw.get("risk") if not isinstance(risk, str) or risk not in ALLOWED_RISKS: raise ValueError(f"Invalid job {jid}: risk must be one of {sorted(ALLOWED_RISKS)}") cwd_raw = raw.get("cwd") if cwd_raw is None: cwd = OPENCLAW_HOME elif isinstance(cwd_raw, str) and cwd_raw.strip(): cwd = Path(cwd_raw).expanduser() else: raise ValueError(f"Invalid job {jid}: cwd must be a non-empty string path") commands_raw = raw.get("commands") if not isinstance(commands_raw, dict): raise ValueError(f"Invalid job {jid}: commands must be an object") commands: dict[str, list[str]] = {} for k, v in commands_raw.items(): if not isinstance(k, str): continue argv = _as_argv(v) if argv is None: raise ValueError(f"Invalid job {jid}: commands.{k} must be a non-empty argv list") commands[k] = argv ``` ```python def maybe_autorun_start( *, job: JobConfig, status: JobStatus, now: float, state_job: dict[str, Any], defaults: JobDefaults, dry_run: bool, ) -> str | None: if job.kind != "long_running_read": return None if not job.enabled: return None if job.risk != "read_only": return "AUTORUN: blocked (risk != read_only)" if status.running or status.completed: return None if not _policy_bool(job, defaults, "autoResume"): return None last = state_job.get("lastAutoResumeAt") try: last_ts = float(last) if isinstance(last, (int, float)) else 0.0 except Exception: last_ts = 0.0 backoff = float(_policy_int(job, defaults, "autoResumeBackoffSeconds") or 0) if backoff and now - last_ts < ba ...[truncated 3138 chars]
- Remediation
- ## Remediation Suggestions 1. Explicitly document that the job configuration is executable trusted code, not a security policy boundary. 2. Restrict ownership and permissions on the configuration, state directory, and invoked scripts so only the dedicated monitor administrator can modify them. 3. Run the monitor under a dedicated, unprivileged operating-system account with access only to required paths. 4. Replace arbitrary command arrays for automatic execution with an allowlist of audited executable paths and fixed argument schemas. 5. Resolve executable paths to canonical absolute paths and reject writable, relative, unexpected, or symlink-substituted executables. 6. Store approved command manifests or hashes and verify their integrity before automatic execution. 7. Apply operating-system sandboxing to restrict filesystem writes, process access, and network destinations. 8. Disable networking for genuinely local read jobs unless a reviewed job explicitly requires it. 9. Keep `autoResume` disabled by default and require a separate, protected approval record rather than an approval field in the same mutable configuration. 10. Add negative tests proving that mislabeled write commands cannot modify files or access the network.
