Back to skill

Security audit

Elegant Sync

Security checks for vulnerabilities and agentic risk

Overview

This skill is a real backup/sync tool, but it handles sensitive OpenClaw data and repository tokens in ways that can expose data or allow unsafe command execution.

Review this carefully before installing. Only use it with a private, access-controlled repository and a narrowly scoped token, and do not run sync until command construction, recursive secret filtering, token handling, confirmation prompts, and restore documentation are fixed.

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

Error
Location
index.js:288
Finding
Shell Command Injection Through Untrusted Backup Configuration<![CDATA[ ## Vulnerability Details **File Location**: `index.js:25-35`, `index.js:288-301` **Vulnerability Type**: OS command injection through shell command construction **Risk Level**: Critical ### Vulnerable Code ```js function getInstanceId() { const envFile = path.join(OPENCLAW_DIR, '.backup.env'); if (fs.existsSync(envFile)) { const content = fs.readFileSync(envFile, 'utf8'); const match = content.match(/INSTANCE_ID=(.+)/); if (match) return match[1].trim(); } return os.hostname(); } ``` ```js const repoUrl = config.BACKUP_REPO.replace( 'https://', `https://${config.BACKUP_TOKEN}@` ); process.chdir(STAGING_DIR); safeExec('git init'); safeExec('git config user.email "sync@elegant.local"'); safeExec('git config user.name "Elegant Sync"'); safeExec('git add -A'); safeExec('git commit -m "Sync: ' + new Date().toISOString() + '"'); const tag = generateTag(); const branch = getInstanceId(); safeExec(`git push ${repoUrl} HEAD:${branch} --force`); safeExec(`git tag ${tag}`); safeExec(`git push ${repoUrl} ${tag}`); ``` The invoked helper executes each constructed string through a shell: ```js function safeExec(cmd, options = {}) { try { return execSync(cmd, { ...options, encoding: 'utf8', stdio: 'pipe' }); } catch (err) { const config = loadConfig(); const token = config.BACKUP_TOKEN; let msg = err.message; if (token) msg = msg.replace(token, '***TOKEN***'); error(`Git error: ${msg}`); } } ``` ### Technical Analysis `INSTANCE_ID`, `BACKUP_REPO`, and `BACKUP_TOKEN` are loaded from `~/.openclaw/.backup.env`. They are interpolated into strings passed to `execSync`, which executes strings through a command shell. The repository URL validation only checks the parsed hostname. It does not make shell interpolation safe and does not validate the token or instance identifier. Shell metacharacters in an attacker-controlled configuration value can therefore terminate or extend the intended G ...[truncated 1121 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace shell-string execution with argument-array execution: ```js const { execFileSync } = require('child_process'); execFileSync( 'git', ['push', authenticatedRepoUrl, `HEAD:${branch}`, '--force'], { cwd: STAGING_DIR, encoding: 'utf8', stdio: 'pipe', shell: false } ); ``` 2. Validate `INSTANCE_ID` with an allowlist suitable for Git branch names, such as a restrictive alphanumeric pattern, and additionally validate it with `git check-ref-format --branch`. 3. Reject control characters, whitespace, and shell metacharacters in configuration fields. 4. Do not place credentials in the repository URL. Use a credential helper or temporary `GIT_ASKPASS` integration. 5. Apply the same argument-array approach to every Git invocation, even commands that currently use constant values. 6. Restrict the configuration file to the owning user, ideally with mode `0600`. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
index.js:196
Finding
Recursive Skill Backup Bypasses Secret and Gitignore Exclusions<![CDATA[ ## Vulnerability Details **File Location**: `index.js:180-213`, `index.js:253-267` **Vulnerability Type**: Sensitive-file disclosure caused by incomplete recursive filtering **Risk Level**: High ### Vulnerable Code ```js const ignorePatterns = [ '.git', '.gitignore', 'benchmarks/', 'logs/', 'media/', 'canvas/', 'completions/', 'delivery-queue/', 'memory/daily/', 'memory/sessions/', 'elegant-sync/', '.elegant-sync-staging/' ]; const customIgnore = loadGitignore(WORKSPACE_DIR); ignorePatterns.push(...customIgnore); ``` The copy helper checks only the path supplied to it. For a directory, it then copies every descendant without applying the ignore rules again: ```js const copyIfExists = (src, dest, name) => { if (!fs.existsSync(src)) return false; if (shouldIgnore(src, ignorePatterns)) return false; if (dryRun) { log(` ${name}`, 'blue'); } else { fs.mkdirSync(path.dirname(dest), { recursive: true }); if (fs.statSync(src).isDirectory()) { fs.cpSync(src, dest, { recursive: true }); } else { fs.copyFileSync(src, dest); } log(` ${name}`, 'green'); } files.push(name); return true; }; ``` Entire Skill directories are passed to that helper: ```js const skillsDir = path.join(WORKSPACE_DIR, 'skills'); if ( fs.existsSync(skillsDir) && (target === 'all' || target === 'skills') ) { fs.mkdirSync( path.join(STAGING_DIR, instanceDir, 'skills'), { recursive: true } ); for (const skill of fs.readdirSync(skillsDir)) { const skillPath = path.join(skillsDir, skill); if (fs.statSync(skillPath).isDirectory()) { copyIfExists( skillPath, path.join(STAGING_DIR, instanceDir, 'skills', skill), `skills/${skill}` ); } } } ``` ### Technical Analysis The built-in denylist does not explicitly include `.env`, `openclaw.json`, credential directories, private keys, or common token files, despite the documentation claiming that `.env` and `openclaw. ...[truncated 1293 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace unfiltered `fs.cpSync` directory copies with an explicit recursive walker. 2. Evaluate ignore rules for every file and directory before copying it. 3. Use a well-tested Gitignore implementation rather than substring matching. 4. Add mandatory exclusions for at least: - `.env` and `.env.*` - `openclaw.json` - `credentials/` - private keys and certificate key files - token and secret files - `.git/` - logs and temporary files 5. Do not allow custom negation rules to re-include mandatory secret exclusions. 6. Before committing, enumerate staged paths with `git diff --cached --name-only` and reject suspicious filenames. 7. Add a secret-scanning step before every push. 8. Update documentation so that safety claims precisely match enforced behavior. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
index.js:288
Finding
Repository Access Token Exposed in Git Process Arguments<![CDATA[ ## Vulnerability Details **File Location**: `index.js:288-301` **Vulnerability Type**: Plaintext credential exposure through command-line arguments **Risk Level**: High ### Vulnerable Code ```js const repoUrl = config.BACKUP_REPO.replace( 'https://', `https://${config.BACKUP_TOKEN}@` ); process.chdir(STAGING_DIR); safeExec('git init'); safeExec('git config user.email "sync@elegant.local"'); safeExec('git config user.name "Elegant Sync"'); safeExec('git add -A'); safeExec('git commit -m "Sync: ' + new Date().toISOString() + '"'); const tag = generateTag(); const branch = getInstanceId(); safeExec(`git push ${repoUrl} HEAD:${branch} --force`); safeExec(`git tag ${tag}`); safeExec(`git push ${repoUrl} ${tag}`); ``` ### Technical Analysis The backup token is inserted directly into the HTTPS URL and then included in the command passed to the shell and Git. This exposes the credential as part of the process command line. Depending on the operating system and execution environment, command arguments can be captured by process-monitoring utilities, auditing systems, diagnostics, crash reports, or telemetry. The error handler replaces only the first exact token occurrence in `err.message`; it does not protect process metadata or every possible encoded or transformed representation of the credential. The documented configuration procedure also does not establish restrictive permissions for `~/.openclaw/.backup.env`. ### Attack Path 1. A user starts a backup synchronization. 2. Elegant Sync constructs a Git URL containing the plaintext repository token. 3. The URL is passed as part of the command line to a shell and Git process. 4. A local observer, process monitor, auditing system, or diagnostic collector records the command line. 5. The observer extracts and reuses the token against the repository provider. ### Impact Assessment A disclosed token may permit unauthorized reading, modification, deletion, or force-pushing of repository content acco ...[truncated 157 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never embed access tokens in command-line URLs. 2. Use a provider-supported credential helper or a temporary `GIT_ASKPASS` script with tightly restricted permissions. 3. Remove temporary credential material immediately after Git exits. 4. Create and verify `~/.openclaw/.backup.env` with mode `0600`. 5. Require narrowly scoped, repository-specific, short-lived credentials where supported. 6. Ensure logs and exceptions redact URL user-info components and encoded token variants. 7. Document token rotation procedures and recommend immediate rotation after suspected exposure. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
index.js:217
Finding
Sensitive Agent State Can Be Uploaded Without Repository Privacy Enforcement<![CDATA[ ## Vulnerability Details **File Location**: `index.js:217-251`, `index.js:288-301` **Vulnerability Type**: Insecure transmission and publication of sensitive Agent data **Risk Level**: High ### Vulnerable Code The synchronization operation explicitly collects identity, user, policy, tool, and heartbeat files: ```js const coreFiles = [ 'AGENTS.md', 'IDENTITY.md', 'USER.md', 'SOUL.md', 'TOOLS.md', 'HEARTBEAT.md' ]; for (const file of coreFiles) { if (target === 'all' || target === 'config') { copyIfExists( path.join(WORKSPACE_DIR, file), path.join(STAGING_DIR, instanceDir, file), file ); } } ``` It also recursively collects Markdown memory files: ```js const memoryDir = path.join(WORKSPACE_DIR, 'memory'); if ( fs.existsSync(memoryDir) && (target === 'all' || target === 'memory') ) { function findMdFiles(dir, prefix = '') { const items = fs.readdirSync(dir); for (const item of items) { const fullPath = path.join(dir, item); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { findMdFiles(fullPath, prefix + item + '/'); } else if (item.endsWith('.md')) { const destPath = path.join( STAGING_DIR, instanceDir, 'memory', prefix + item ); copyIfExists( fullPath, destPath, `memory/${prefix}${item}` ); } } } findMdFiles(memoryDir); } ``` The resulting repository is pushed without checking its visibility: ```js const repoUrl = config.BACKUP_REPO.replace( 'https://', `https://${config.BACKUP_TOKEN}@` ); const tag = generateTag(); const branch = getInstanceId(); safeExec(`git push ${repoUrl} HEAD:${branch} --force`); safeExec(`git tag ${tag}`); safeExec(`git push ${repoUrl} ${tag}`); ``` ### Technical Analysis Agent memory, identity, user information, operational instructions, and tool configuration are inherently sensitive. The implementa ...[truncated 1351 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Query the hosting provider API and refuse synchronization unless repository privacy has been verified. 2. Require explicit confirmation before the first upload and whenever the repository URL or account changes. 3. Present a complete file manifest and sensitivity warning before committing. 4. Encrypt backup contents locally with a user-controlled key before upload. 5. Add secret and personal-data scanning before each push. 6. Minimize the default backup set and require opt-in for identity, user, tool, and memory files. 7. Record and display the verified repository owner, visibility, and destination before upload. 8. Provide a safe dry-run mode that lists all descendant files, including those inside Skill directories. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (13)

Credential Access

High
Category
Privilege Escalation
Content
## 安全特性

- 不上传 .env 和 openclaw.json
- URL 验证
- Token 不出现在错误信息中
- 本地备份后再恢复
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 和 openclaw.json
- URL 验证
- Token 不出现在错误信息中
- 本地备份后再恢复
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill is presented as a 'safe' configuration sync tool, but the documented behavior includes sending highly sensitive workspace data, identity/configuration files, and custom skills to a remote Git repository using an authentication token. That is a material capability beyond a benign local sync description, and the mismatch reduces informed user consent while increasing the risk of unintended data exfiltration.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
const tag = generateTag();
  const branch = getInstanceId();
  safeExec(`git push ${repoUrl} HEAD:${branch} --force`);
  safeExec(`git tag ${tag}`);
  safeExec(`git push ${repoUrl} ${tag}`);
Confidence
98% confidence
Finding
The branch name used in the shell command comes from getInstanceId(), which reads INSTANCE_ID from ~/.openclaw/.backup.env or falls back to hostname, and that value is interpolated directly into an execSync shell string. An attacker who can influence INSTANCE_ID or hostname can inject shell metacharacters and execute arbitrary commands when sync runs, making this a real command-injection issue with potentially full user-level code execution.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The README instructs users to place a GitHub token in a local config file but does not warn about least-privilege scopes, rotation, filesystem permissions, or the risk of accidental inclusion in backups or shell history. For a backup/sync skill that handles repositories, this omission can lead to credential exposure and unauthorized access if users follow the example insecurely.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The documented backup set includes extremely sensitive personal and agent state data such as memory, identity, user profile, and custom skills, yet the warning model is weak and does not prominently require a private repository or explain the privacy consequences of remote storage. Users may unknowingly publish or broadly expose irreplaceable sensitive data if the repository is misconfigured or later made public.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The top-of-file comment lists "灾难恢复" as a supported function, creating the impression that restoration from backups is implemented. In reality, the restore command stops with a TODO/error after making only a local backup of the current workspace, so the documentation overstates the skill's actual behavior.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The manifest describes a configuration synchronization tool with backups, .gitignore handling, and version control, but the implementation achieves this by invoking system git commands via execSync and performing remote force-push operations. Spawning subprocesses and executing shell commands is a distinct capability with broader security impact than ordinary file synchronization and is not explicitly disclosed in the stated purpose.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The tool transmits local workspace contents to a remote repository and force-pushes branch state without an explicit warning or confirmation, which can cause unintended exfiltration of sensitive files and overwrite remote history. In a skill context that copies memory, config, and skills content from ~/.openclaw/workspace, this is more dangerous because users may not realize personal or sensitive agent data is being published remotely.

Natural-Language Policy Violations

Low
Confidence
80% confidence
Finding
The file's user-facing instructions and descriptions are presented only in Chinese, and there is no indication that this skill is intentionally region-specific or that alternative language support is available. This can constitute a natural-language policy issue when a specific language is effectively required without user opt-in or documented justification.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The natural-language content consistently uses Chinese for the description and operational instructions, and there is no indication that users can choose another language. Under the policy provided, forcing a specific language without opt-in is a language/locale policy violation unless clearly justified as region-specific.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The file's natural-language interface and instructions are entirely in Chinese, with no option for users to select another language or locale. The policy specifically calls for flagging language or locale constraints when they are forced without user opt-in.

Description-Behavior Mismatch

Low
Confidence
75% confidence
Finding
Beyond straightforward file synchronization, the code reads ~/.openclaw/.backup.env, derives an instance identity from configuration or hostname, and uses that identity in remote git branch management. This host-identification and multi-instance branch orchestration is additional behavior not reflected in the manifest description of selective backup, .gitignore support, and version control.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
index.js:129