Back to skill

Security audit

Claw Asset & Privacy Guardian

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to be a local privacy scanner, but its scan boundaries and privacy promises do not fully match its implementation.

Review this before installing if you expect strict privacy boundaries. It is local and I found no exfiltration code, but do not rely on excludePatterns, avoid scanning untrusted repositories with symlinks, and review generated reports before sharing because file paths and locations may reveal sensitive context.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
claw_asset_privacy_guardian.py:596
Finding
Scan-root boundary bypass through symbolic links<![CDATA[ ## Vulnerability Details **File Location**: `claw_asset_privacy_guardian.py:596-602` and `claw_asset_privacy_guardian.py:633-639` **Vulnerability Type**: Scan-root boundary violation through symbolic-link following **Risk Level**: Medium ### Vulnerable Code ```python for file_path in all_files: if not self._should_scan_file(file_path): continue try: with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: content = f.read() ``` ```python all_files = [] for root, dirs, files in os.walk(directory): dirs[:] = [d for d in dirs if d not in ['node_modules', '.git', '__pycache__']] for file in files: file_path = os.path.join(root, file) all_files.append(file_path) ``` ### Technical Analysis The scanner constructs candidate paths from directory entries and subsequently opens each path without checking whether it is a symbolic link. It also does not resolve the candidate path and verify that the resolved target remains under the requested scan root. Although `os.walk()` does not traverse symbolic links to directories by default, symbolic links appearing as files are included in the `files` collection. Python's `open()` follows these links. Consequently, a supported file inside the scanned project can reference a file outside the authorized scan directory. The file extension check operates on the link's path rather than enforcing a boundary on its resolved target. For example, a link named `external.env` can point to a readable environment file elsewhere on the system and will be accepted for scanning. ### Attack Path 1. An attacker creates or contributes a project containing a symbolic link such as: ```text linked-secrets.env -> /home/victim/private/application.env ``` 2. The victim invokes the scanner on the attacker-controlled project directory. 3. `_collect_files()` includes `linked-secrets.env` in `all_files`. 4. `_should_scan_file()` accepts the path because `.env` i ...[truncated 1183 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve the scan root once before traversal: ```python scan_root = Path(directory).resolve(strict=True) ``` 2. Resolve every candidate and verify that it remains under the scan root. For Python 3.8 compatibility, use `os.path.commonpath()`: ```python candidate = Path(file_path) if candidate.is_symlink(): logger.warning("Skipping symbolic link: %s", candidate) continue resolved = candidate.resolve(strict=True) if os.path.commonpath([str(scan_root), str(resolved)]) != str(scan_root): logger.warning("Skipping path outside scan root: %s", candidate) continue ``` 3. Reject symbolic links by default. If link scanning is required, expose an explicit opt-in option and still require resolved targets to remain inside the scan root. 4. Where supported, open files using no-follow semantics such as `os.open()` with `O_NOFOLLOW`, then wrap the descriptor with `os.fdopen()`. This reduces time-of-check/time-of-use exposure. 5. After opening a file, compare descriptor metadata against the previously validated file metadata when scanning directories writable by untrusted users. 6. Add regression tests covering: - A file symlink targeting a file outside the scan root. - A file symlink targeting a file inside the scan root. - Broken links. - Link replacement during scanning. - Nested paths whose normalized representation escapes the root. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
claw_asset_privacy_guardian.py:551
Finding
Configured scan exclusions are stored but never enforced<![CDATA[ ## Vulnerability Details **File Location**: `claw_asset_privacy_guardian.py:551-552` and `claw_asset_privacy_guardian.py:633-635`; documented at `README.md:114-121` and `SKILL.md:93-104` **Vulnerability Type**: Failure to enforce a documented privacy boundary **Risk Level**: Medium ### Vulnerable Code ```python def __init__(self, config: Optional[Dict[str, Any]] = None): self.config = config or {} ``` ```python all_files = [] for root, dirs, files in os.walk(directory): dirs[:] = [d for d in dirs if d not in ['node_modules', '.git', '__pycache__']] ``` The documented configuration includes user-controlled exclusions: ```json { "assetPrivacyGuardian": { "excludePatterns": [ "node_modules", ".git", "personal_files" ] } } ``` ### Technical Analysis The constructor accepts and stores configuration in `self.config`, but the directory collector and file-selection logic never consult that configuration. Instead, traversal excludes only three hard-coded directory names. The documentation explicitly presents `excludePatterns` as a privacy feature that allows users to prevent selected directories from being scanned. Because the implementation silently ignores this setting, users can reasonably believe that sensitive directories are outside the scanner's access scope when their files are actually enumerated, opened, and analyzed. This is a fail-open configuration defect: an unavailable or unrecognized privacy control results in broader access rather than a visible error or a conservative refusal to scan. ### Attack Path 1. A user places especially sensitive files under a directory such as `personal_files`. 2. The user configures: ```json "excludePatterns": ["personal_files"] ``` 3. The user scans a parent directory, relying on the documented exclusion. 4. `PrivacyGuardian` stores the configuration but does not apply it. 5. `_collect_files()` traverses `personal_files` because it only excludes `node_module ...[truncated 813 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define and validate a concrete configuration schema. Reject malformed or unsupported privacy settings instead of silently ignoring them. 2. Normalize `excludePatterns` when constructing the scanner: ```python self.exclude_patterns = self.config.get("excludePatterns", []) if not isinstance(self.exclude_patterns, list): raise ValueError("excludePatterns must be a list") ``` 3. Apply exclusions while pruning directories and again immediately before opening each file. The second check provides defense in depth. 4. Match exclusions against paths relative to the scan root rather than unrestricted absolute paths. Clearly document whether patterns use exact names, shell-style globs, or regular expressions. 5. Prevent wildcard and normalization ambiguities by using a well-defined matcher such as `pathlib.PurePath.match()` or `fnmatch`, and normalize path separators across supported platforms. 6. Log which exclusions were loaded and how many files or directories were skipped. Invalid patterns should produce a visible error. 7. Treat excluded paths as strict boundaries: do not resolve or inspect file contents within them merely to determine whether they should be scanned. 8. Add tests that instrument file opening and verify that excluded files are never opened, including: - Exact directory exclusions. - Nested path exclusions. - File-pattern exclusions. - Absolute and relative scan roots. - Platform-specific path separators. - Symlinks entering or leaving excluded directories. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (17)

Credential Access

High
Category
Privilege Escalation
Content
pass
        
        # 检查环境文件
        if '.env' in file_path.lower():
            findings.extend(self._check_env_security(content, file_path))
        
        return findings
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
pass
        
        # 检查环境文件
        if '.env' in file_path.lower():
            findings.extend(self._check_env_security(content, file_path))
        
        return findings
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
pass
        
        # 检查环境文件
        if '.env' in file_path.lower():
            findings.extend(self._check_env_security(content, file_path))
        
        return findings
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
""",
        
        # 环境文件测试
        ".env": """
DATABASE_URL=postgresql://user:password@localhost/prod
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
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
95% confidence
Finding
The skill description is presented in Chinese and sets the user-facing framing in a single language, with no indication that other languages are supported or that the user can opt in to this locale. The policy specifically calls for flagging language or locale constraints when they are imposed without user choice or documented justification.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
This markdown file contains user-facing instructions and operational guidance almost exclusively in Chinese, but it does not state that the skill is Chinese-only or provide any language/locale choice. That can violate language/locale policy expectations when users are not given an explicit opt-in or alternative.

Session Persistence

Medium
Category
Rogue Agent
Content
clawdhub install claw-asset-privacy-guardian

# 或手动安装
mkdir -p ~/.openclaw/skills/claw-asset-privacy-guardian
cp -r ./* ~/.openclaw/skills/claw-asset-privacy-guardian/
```
Confidence
60% confidence
Finding
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.

Session Persistence

Medium
Category
Rogue Agent
Content
clawdhub install claw-asset-privacy-guardian

# 或手动安装
mkdir -p ~/.openclaw/skills/claw-asset-privacy-guardian
cp -r ./* ~/.openclaw/skills/claw-asset-privacy-guardian/
```
Confidence
60% confidence
Finding
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.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The module name, docstrings, user-facing report text, CLI help, and console/markdown output are written in Chinese, indicating the skill is designed to communicate in a fixed language. There is no natural-language indication that users may opt into another language or that the locale restriction is required for a region-specific purpose.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The tool explicitly promises that reports contain no specific sensitive information, yet it still includes file locations with only partial path anonymization. File paths, filenames, and line references can themselves disclose usernames, project names, infrastructure details, or the presence of sensitive files, so the privacy guarantee is overstated and may leak contextual secrets.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
This Python file contains user-facing descriptions, status messages, and test assertions primarily in Chinese, and the tests expect Chinese phrases such as "隐私保护声明". That implies a locale-specific behavior without any visible user opt-in or documented justification, which matches the language/locale policy violation criteria.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 测试基本扫描
        print("  运行命令行扫描...")
        result = subprocess.run(
            [sys.executable, "claw_asset_privacy_guardian.py", test_dir],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 测试JSON输出
        print("  运行JSON输出测试...")
        result = subprocess.run(
            [sys.executable, "claw_asset_privacy_guardian.py", test_dir, "--format", "json"],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Intent-Code Divergence

Low
Confidence
77% confidence
Finding
The markdown output asserts 'all analysis is completed locally', which is a strong operational guarantee. The current file itself does not perform network operations, but it also does not implement any enforcement, isolation, or runtime check to substantiate that guarantee beyond the present implementation, making the statement stronger than what the code guarantees.

Intent-Code Divergence

Low
Confidence
78% confidence
Finding
The footer says 'all analysis is local, no data is sent to external parties'. While this file does not show outbound communication, the statement is presented as a hard guarantee in documentation/output rather than a property actively enforced by code, so it overstates what is proven by the implementation.

Missing User Warnings

Low
Confidence
83% confidence
Finding
The code writes generated report content directly to the path provided by --output, which can overwrite an existing file. While it prints a message after saving, there is no pre-write warning, confirmation prompt, or explicit disclosure near the write operation about this side effect.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
The package description is entirely in Chinese, which can constitute a language/locale policy issue when the skill does not offer an explicit language choice or document that it is intended only for Chinese-speaking users. In a manifest file, this kind of metadata may affect how the skill is presented or discovered without user opt-in.

Static analysis

No suspicious patterns detected.