Back to skill

Security audit

Shield Cn

Security checks for vulnerabilities and agentic risk

Overview

The skill appears defensive rather than malicious, but it overstates its protection features while scanning and storing sensitive local content.

Treat this as an experimental local scanner/classifier, not a reliable real-time shield. Run it only on trusted workspaces, review what it stores under ~/.openclaw, and do not rely on it to block credential reads, uploads, QR/link attacks, or platform sharing.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/shield-guard.py:145
Finding
Credential-access checks and block mode are not connected to enforceable operations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shield-guard.py:145-159`, `scripts/shield-guard.py:238-258`, and `scripts/shield-guard.py:264-268` **Vulnerability Type**: Non-enforcing security control **Risk Level**: Medium ### Evidence ```python def check_file_access(self, filepath: str) -> dict: """检查文件访问是否安全""" filepath_lower = filepath.lower() # 凭证文件黑名单 for pattern in self.CREDENTIAL_PATTERNS: if re.search(pattern, filepath_lower): return { "safe": False, "reason": f"凭证文件禁止访问: {filepath}", "type": "credential_access_denied" } return {"safe": True} ``` ```python def run(self): """运行防护""" print(f"{GREEN}🛡️ 安全卫士启动中...{RESET}") print(f"模式: {self.mode}") print(f"日志级别: {self.log_level}") print(f"告警渠道: {', '.join(self.alert_channels)}") print("\n输入文本进行安全检测(Ctrl+C 退出):\n") try: while True: user_input = input(f"{GREEN}> {RESET}") if not user_input.strip(): continue result = self.check_input(user_input) if not result["safe"]: self.log_threat(result, user_input[:50]) if self.mode == "block": print(f"\n{RED}⚠️ 检测到威胁,操作已阻断{RESET}") print("如需继续执行,请确认...") else: print(f"{GREEN}✓ 安全{RESET}") ``` ```python guard = ShieldGuard(args.config) if args.mode: guard.config["mode"] = args.mode ``` ### Technical Analysis `check_file_access()` can return a denial result, but no code invokes it before opening a file or performing an Agent tool operation. It therefore operates only as an unused classification helper and cannot prevent access to `.env`, private-key, SSH-key, or credential paths. Likewise, block mode only prints a message. It does not terminate an operation, return an enforceable authorizatio ...[truncated 1655 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Integrate `check_file_access()` into the actual Agent tool-authorization layer before any file read, write, upload, or attachment operation. 2. Return a machine-enforceable denial result rather than only printing a warning. 3. In block mode, abort the pending operation unless a separately authenticated and explicit approval is received. 4. Update the active mode correctly: ```python if args.mode: guard.mode = args.mode guard.config["mode"] = args.mode ``` 5. Normalize and resolve paths before matching, including symlink handling and checks that the resolved path remains within an approved root. 6. Add integration tests proving that protected-file reads are denied and that monitor, audit, and block modes behave differently. 7. Describe the current utility as a standalone classifier until runtime enforcement is implemented. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/shield-guard.py:203
Finding
Flagged user-input fragments are stored in persistent plaintext logs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shield-guard.py:203-221` and `scripts/shield-guard.py:251` **Vulnerability Type**: Plaintext storage of potentially sensitive input **Risk Level**: Medium ### Evidence ```python def _write_log(self, result: dict, context: str): """写入日志文件""" log_dir = Path.home() / ".openclaw" / "logs" / "shield-cn" log_dir.mkdir(parents=True, exist_ok=True) log_file = log_dir / f"threats-{datetime.now().strftime('%Y%m%d')}.jsonl" with open(log_file, 'a', encoding='utf-8') as f: log_entry = { **result, "context": context, "mode": self.mode } f.write(json.dumps(log_entry, ensure_ascii=False) + "\n") ``` ```python if not result["safe"]: self.log_threat(result, user_input[:50]) ``` ### Technical Analysis The first 50 characters of every flagged input are copied into the `context` field and appended to a persistent JSONL file. Threat-related input can contain passwords, API tokens, personal information, internal instructions, confidential URLs, or portions of credential-access requests. The code relies on process umask defaults and does not explicitly create the log directory with mode `0700` or the log file with mode `0600`. It also implements no redaction, retention limit, encryption, or user-controlled opt-out for raw context logging. Although the data remains local and no network transmission was found, storing raw security-sensitive input creates an additional data repository that may be collected by backups, diagnostics, malware, or other local users where permissions allow. ### Attack Path 1. A user enters a string containing sensitive information and a keyword that triggers detection. 2. `run()` passes the first 50 characters of that string to `log_threat()`. 3. `_write_log()` stores the fragment under `~/.openclaw/logs/shield-cn/`. 4. The entry remains on disk without automatic expiration or redaction. 5. A loc ...[truncated 605 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not store raw user input by default. Log only threat type, severity, timestamp, and a non-reversible event identifier. 2. If context is required, make it explicitly opt-in and apply credential and personal-data redaction before persistence. 3. Create the directory with mode `0700` and the log file with mode `0600`, independent of umask. 4. Define a short retention period and provide a secure log-deletion command. 5. Document exactly what is logged and obtain user consent before storing input fragments. 6. Consider keyed hashing for correlation where storing original content is unnecessary. 7. Add tests confirming that recognized API keys, passwords, tokens, and cloud credentials never appear in logs. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/shield-guard.py:70
Finding
Documented security capabilities and configuration controls are not implemented<![CDATA[ ## Vulnerability Details **File Location**: `README.md:24-55`, `SKILL.md:32-101`, and `scripts/shield-guard.py:70-86` **Vulnerability Type**: Misleading or inactive security controls **Risk Level**: Medium ### Evidence The guard accepts security-related options: ```python def _load_config(self, config_path: str = None) -> dict: """加载配置文件""" default_config = { "mode": "monitor", "log_level": "INFO", "alert_channels": ["console"], "blocked_keywords": [], "protected_files": [".env", "*.key", "*.pem"], "url_whitelist": ["docs.openclaw.ai", "github.com", "gitee.com"] } if config_path and os.path.exists(config_path): try: with open(config_path, 'r', encoding='utf-8') as f: user_config = json.load(f) default_config.update(user_config) except Exception as e: print(f"{YELLOW}⚠️ 配置文件加载失败: {e},使用默认配置{RESET}") return default_config ``` The documentation advertises broader controls, including encoded-instruction detection, QR and link checks, credential-file blocking, platform-specific data-loss prevention, split-attack analysis, and whitelist behavior. The implementation only applies regular expressions and substring searches to manually entered text. The `protected_files`, `url_whitelist`, and non-console `alert_channels` settings are loaded but are not used to enforce their documented purposes. ### Technical Analysis Security configuration that is accepted but ignored creates a fail-open condition. A user can configure protected files or approved domains and receive no error, while the runtime performs no matching or enforcement based on those values. No implementation was found for: - Base64 or Unicode decoding before inspection. - Multi-turn or split-attack correlation. - QR-code analysis. - URL extraction, domain validation, or whitelist enforcement. - WeChat, DingTalk, Feishu, email, SMS, webhook, or clou ...[truncated 1277 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove or clearly mark every unsupported capability in `README.md` and `SKILL.md`. 2. Reject unknown or inactive configuration options instead of silently accepting them. 3. Implement URL parsing and normalized hostname checks before claiming whitelist enforcement. 4. Implement protected-file enforcement at the Agent tool boundary rather than in an isolated text-input loop. 5. Add canonical decoding with strict resource limits before claiming Base64 or Unicode bypass detection. 6. Maintain bounded conversation state and tested correlation logic before claiming multi-turn attack detection. 7. Integrate actual outbound-tool authorization before claiming platform-specific data-loss prevention. 8. Add end-to-end tests for each documented capability and publish a precise capability matrix. 9. Until those controls exist, label the script as an experimental text classifier rather than a real-time protection or DLP system. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/security-audit.py:61
Finding
Literal substring matching causes sensitive-file scan false negatives<![CDATA[ ## Vulnerability Details **File Location**: `scripts/security-audit.py:61-70` and `scripts/security-audit.py:108-122` **Vulnerability Type**: Incorrect sensitive-file pattern matching **Risk Level**: Medium ### Evidence ```python # 高危文件 DANGEROUS_FILES = [ ".env", ".env.local", ".env.production", "id_rsa", "id_ed25519", "credentials", "*.pem", "*.key", ] ``` ```python for root, dirs, files in os.walk(self.workspace): # 跳过 node_modules 等目录 dirs[:] = [d for d in dirs if d not in ['node_modules', '.git', '__pycache__', 'venv']] for file in files: filepath = Path(root) / file rel_path = filepath.relative_to(self.workspace) # 检查是否为高危文件 if any(d in str(rel_path).lower() for d in self.DANGEROUS_FILES): self.issues.append({ "type": "dangerous_file", "severity": "HIGH", "file": str(rel_path), "description": f"高危文件: {file}", "suggestion": "从版本控制中排除,或移到 ~/.config/ 等目录" }) ``` ### Technical Analysis Entries such as `"*.pem"` and `"*.key"` are glob patterns, but the code evaluates them as literal substrings with the `in` operator. A normal filename such as `server.pem` does not contain the literal characters `"*.pem"` and therefore is not classified as a dangerous file. Content scanning is separately limited to files ending in `.md`, `.json`, `.txt`, `.yaml`, `.yml`, or `.env`. Consequently, a missed `.pem` or `.key` file is also not content-scanned. This creates a direct false-negative path for common private-key formats. The `id_rsa` and `id_ed25519` entries only detect matching names. The script does not write to SSH-key files. Scanning for such filenames is appropriate for the declared audit function, but the current matching implementation is incomplete. ### Attack Path 1. A private key or credential is stored as `server.pem`, `client.key`, or another filename ...[truncated 816 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use `fnmatch.fnmatch()`, `Path.match()`, or distinct exact-name and extension checks rather than substring matching. 2. Normalize filename case consistently before comparison. 3. For example: ```python from fnmatch import fnmatch dangerous = any( fnmatch(file.lower(), pattern.lower()) for pattern in self.DANGEROUS_FILES ) ``` 4. Scan recognized key and certificate formats with bounded file-size limits and binary-file handling. 5. Add tests covering `server.pem`, `client.key`, `id_rsa`, `id_ed25519`, `.env.production`, cloud credential files, nested paths, and case variations. 6. Handle symlinks explicitly and ensure resolved targets remain within the user-approved workspace. 7. Report unreadable or skipped files instead of silently suppressing all exceptions, so users can distinguish a clean scan from an incomplete scan. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The documented behavior claims real-time prompt-injection defense and platform-specific protections, but the described implementation is primarily local file scanning and report generation with workspace traversal and filesystem writes. This mismatch is dangerous because users may rely on protections that are not actually present, while unknowingly granting the skill sensitive file access not clearly disclosed in the description.

Credential Access

High
Category
Privilege Escalation
Content
# 高危文件
    DANGEROUS_FILES = [
        ".env",
        ".env.local",
        ".env.production",
        "id_rsa",
        "id_ed25519",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
DANGEROUS_FILES = [
        ".env",
        ".env.local",
        ".env.production",
        "id_rsa",
        "id_ed25519",
        "credentials",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
})
                
                # 扫描文件内容
                if file.endswith(('.md', '.json', '.txt', '.yaml', '.yml', '.env')):
                    self._scan_file_content(filepath, rel_path)
    
    def _scan_file_content(self, filepath: Path, rel_path: Path):
Confidence
67% confidence
Finding
The scanner reads full contents of markdown, config, text, YAML, and .env files across the workspace, which can include secrets and private material. Although the intent is defensive, broad content ingestion increases exposure because sensitive data is processed in memory and later partially echoed into findings, creating a larger attack surface if reports are mishandled or the tool is run on untrusted/shared workspaces.

Credential Access

High
Category
Privilege Escalation
Content
"""检查文件权限"""
        print(f"{BLUE}🔐 检查文件权限...{RESET}")
        
        # 检查 .env 文件权限
        env_files = list(self.workspace.glob("**/.env*"))
        
        for env_file in env_files:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
"""检查文件权限"""
        print(f"{BLUE}🔐 检查文件权限...{RESET}")
        
        # 检查 .env 文件权限
        env_files = list(self.workspace.glob("**/.env*"))
        
        for env_file in env_files:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
"""检查文件权限"""
        print(f"{BLUE}🔐 检查文件权限...{RESET}")
        
        # 检查 .env 文件权限
        env_files = list(self.workspace.glob("**/.env*"))
        
        for env_file in env_files:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
"""检查文件权限"""
        print(f"{BLUE}🔐 检查文件权限...{RESET}")
        
        # 检查 .env 文件权限
        env_files = list(self.workspace.glob("**/.env*"))
        
        for env_file in env_files:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 凭证文件模式
    CREDENTIAL_PATTERNS = [
        r"\.env",
        r"\.pem",
        r"\.key",
        r"id_rsa",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 凭证文件模式
    CREDENTIAL_PATTERNS = [
        r"\.env",
        r"\.pem",
        r"\.key",
        r"id_rsa",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 凭证文件模式
    CREDENTIAL_PATTERNS = [
        r"\.env",
        r"\.pem",
        r"\.key",
        r"id_rsa",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 凭证文件模式
    CREDENTIAL_PATTERNS = [
        r"\.env",
        r"\.pem",
        r"\.key",
        r"id_rsa",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 凭证文件模式
    CREDENTIAL_PATTERNS = [
        r"\.env",
        r"\.pem",
        r"\.key",
        r"id_rsa",
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The README repeatedly states that the skill is designed specifically for Chinese users and Chinese scenarios, including "专为中文场景设计" and "专为中文用户打造." This presents a locale/language restriction in the skill description without offering a user choice or opt-in, which matches the language-policy violation criteria.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill advertises executable commands and file-scanning behavior but does not declare any explicit tool scope such as permissions or allowed-tools. That creates an implicit trust gap: an agent may grant broader-than-necessary file and shell access to a skill that can read workspace contents and write reports, increasing the chance of unintended data exposure or unsafe execution.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The markdown states the skill is not suitable when users 'must use an English environment,' which signals a language restriction rather than offering language choice. This can violate language/locale policy expectations because the skill appears to force a specific language context without explicit opt-in or a justified compliance-only constraint.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The file states that it scans the workspace and generates a Chinese report, and the implementation consistently emits Chinese messages and report content. This imposes a specific language/locale on users without offering a choice or documenting a justified region-specific constraint.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
"severity": "MEDIUM",
                        "file": str(env_file.relative_to(self.workspace)),
                        "description": f".env 文件权限过宽: {oct(mode)}",
                        "suggestion": "运行: chmod 600 .env"
                    })
            except Exception:
                pass
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The module description and embedded operational text are primarily Chinese and describe Chinese-specific detection scenarios, but the script provides no language selection or opt-in. This can violate language/locale policy because users are implicitly forced into a specific language experience without a documented regional justification.

Static analysis

No suspicious patterns detected.