Back to skill

Security audit

Deployment Kit

Security checks for vulnerabilities and agentic risk

Overview

This deployment skill matches its stated purpose, but it needs review because its Docker manager can execute unsafe shell commands from configuration values and uses mutable container images.

Review before installing on any real host. Use it only in a controlled environment unless the shell-command construction is fixed with argument-based process execution and input validation, images are pinned, and production deployments require explicit approval. Expect it to start, stop, remove, and persist Docker services and volumes.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
src/deploy-manager.js:33
Finding
Shell Command Injection Through Deployment Configuration<![CDATA[ ## Vulnerability Details **File Location**: `src/deploy-manager.js:33-35, 52-58, 94-96, 106-108, 119-121, 132` **Vulnerability Type**: OS command injection through unsafe shell interpolation **Risk Level**: High ### Vulnerable Code ```javascript const { stdout, stderr } = await execAsync( `docker build -t ${this.config.imageName}:latest .` ); ``` ```javascript // Stop the old container await execAsync(`docker stop ${this.config.containerName} 2>nul || true`); await execAsync(`docker rm ${this.config.containerName} 2>nul || true`); // Start the new container const { stdout } = await execAsync( `docker run -d -p ${this.config.port}:${this.config.port} ` + `--name ${this.config.containerName} ${this.config.imageName}:latest` ); ``` ```javascript const { stdout } = await execAsync( `docker ps -q -f name=${this.config.containerName}` ); ``` ```javascript const { stdout } = await execAsync( `netstat -an | findstr :${this.config.port}` ); ``` ```javascript const { stdout } = await execAsync( `docker logs --tail ${lines} ${this.config.containerName}` ); ``` ```javascript await execAsync(`docker stop ${this.config.containerName}`); ``` ### Technical Analysis The application executes Docker and operating-system commands through `child_process.exec`. Unlike an argument-based process API, `exec` passes the constructed string to a command shell. The following caller-controlled values are directly interpolated into shell commands without validation or escaping: - `config.imageName` - `config.containerName` - `config.port` - The `lines` argument passed to `getLogs()` An attacker who can control any of these values can introduce shell metacharacters such as command separators, substitutions, redirections, or pipelines. The shell will interpret the injected content as an additional command rather than as a Docker argument. The constructor also applies `...config` after the default properties, so arbitrary supplied values remain accepted wit ...[truncated 1748 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `exec` with `execFile` or `spawn` and pass every command argument as a separate array element: ```javascript import { execFile } from 'child_process'; import { promisify } from 'util'; const execFileAsync = promisify(execFile); await execFileAsync('docker', [ 'build', '-t', `${this.config.imageName}:latest`, '.' ]); await execFileAsync('docker', [ 'run', '-d', '-p', `${port}:${port}`, '--name', this.config.containerName, `${this.config.imageName}:latest` ]); ``` 2. Do not use shell operators such as `|| true` for expected failure handling. Catch expected `docker stop` and `docker rm` errors in JavaScript instead. 3. Validate configuration before storing or using it: - Require `port` to be an integer from 1 through 65535. - Require `lines` to be a bounded positive integer. - Enforce Docker-compatible allowlists for image and container names. - Reject whitespace, control characters, shell metacharacters, and unexpected types. 4. Avoid enabling the `shell` option when using `spawn` or `execFile`. 5. Run the deployment process under a dedicated, least-privileged account. Limit Docker daemon access wherever possible because Docker control can imply host-level control. 6. Add security tests that submit shell metacharacters and verify that they are rejected and never create additional processes. ]]>

T08 · Insecure Dependencies

Warning
Location
docker/docker-compose.yml:10
Finding
Mutable and Unpinned Container Images Create a Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `docker/docker-compose.yml:10, 39, 55` **Vulnerability Type**: Use of mutable container image tags **Risk Level**: Medium ### Vulnerable Code ```yaml services: openclaw: build: context: .. dockerfile: docker/Dockerfile image: openclaw:latest ``` ```yaml prometheus: image: prom/prometheus:latest container_name: openclaw-prometheus ``` ```yaml loki: image: grafana/loki:latest container_name: openclaw-loki ``` ### Technical Analysis All declared images use the mutable `latest` tag. A tag is a registry reference that may be reassigned to a different image at any time. Consequently, the source repository does not uniquely identify the code that will execute when the services are pulled or recreated. This makes deployments non-reproducible and bypasses repository-level review of image changes. Even if an image is safe during the initial audit, a later upstream release, registry compromise, publisher-account compromise, or accidental retagging can change the effective runtime contents. The local `openclaw:latest` name is also ambiguous and may resolve to a stale or unintended local image when build behavior fails or differs between environments. ### Attack Path 1. A maintainer, upstream publisher, or compromised registry account reassigns one of the `latest` tags to a different image. 2. An operator runs a pull, recreates the Compose stack, or deploys on a host that does not already have the prior image. 3. Docker resolves the mutable tag to the newly published image. 4. The unreviewed image executes with the service's configured network access, volumes, exposed ports, and restart policy. 5. A malicious image can access service data available inside its container, communicate across the Compose network, and attack adjacent services. ### Impact Assessment The immediate privileges are those granted to the affected container. The potential scope includes: - Pers ...[truncated 631 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin each third-party image to both a reviewed version and an immutable digest: ```yaml prometheus: image: prom/prometheus:vX.Y.Z@sha256:REVIEWED_DIGEST loki: image: grafana/loki:X.Y.Z@sha256:REVIEWED_DIGEST ``` 2. Assign the locally built application image a release-specific tag rather than `latest`, such as an immutable version or source-commit identifier. 3. Introduce a controlled image-update process that: - Reviews release notes. - Scans images for known vulnerabilities. - Verifies image signatures or attestations. - Tests updated images before production deployment. - Updates locked digests through reviewed changes. 4. Configure deployment automation to reject mutable tags in production. 5. Generate and retain an SBOM for every released application image and monitor pinned components for newly disclosed vulnerabilities. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (12)

Ae1

High
Category
analysis-evasion
Content
node scripts/health-check.js
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/health-check.js
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The skill documents automatic deployment to production on pushes to main and on release publication, but it does not include a prominent warning that this can trigger real system changes in live environments. In an agent skill context, omission of explicit safeguards increases the chance that a user or automation will invoke deployment behavior without understanding the operational impact, potentially causing outages or unintended releases.

Rp1

Medium
Category
MCP Rug Pull
Confidence
75% confidence
Finding
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.

Rp1

Medium
Category
MCP Rug Pull
Confidence
75% confidence
Finding
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The code stops and removes an existing container via subprocess calls, which is a destructive operational change. Although comments describe the action, there is no user-facing confirmation, warning, or visible disclosure that an existing container will be terminated and deleted.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The markdown specifies the timezone as Asia/Shanghai in the image features and environment variable defaults, which is a locale-specific setting. The file does not offer a user choice or explain why this locale is required for a region-specific use case.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The configuration hard-codes `TZ=Asia/Shanghai`, which imposes a specific locale/timezone choice for the deployed service. Under the policy, forcing a language or locale setting without offering user choice or documenting a justified regional constraint is a natural-language policy concern.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
The description string is written only in Chinese, which creates a natural-language locale constraint in the skill metadata without offering any language choice or documenting why the skill is region-specific. Under the policy, locale-specific language is only acceptable when user opt-in or clear justification is provided.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
This code includes user-facing natural-language strings and comments in Chinese, such as the script description and status labels, with no indication that the user can select another language. The policy specifically calls for flagging language or locale constraints when they are imposed without opt-in.

Missing User Warnings

Low
Confidence
78% confidence
Finding
The code starts a new Docker container by invoking a shell command, which changes system state and exposes a network port. While this is aligned with a deployment tool's purpose, the file itself lacks a user-facing warning, prompt, or log message explaining that a container will be started and a port bound.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
This code file contains natural-language content in Chinese in the header and comments, while other output strings are in English. Under the policy rule, forcing a specific language without user opt-in can be a locale-policy violation when no rationale or language choice is provided.

Static analysis

No suspicious patterns detected.