Back to skill

Security audit

Agent Changelog

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly aligned with changelog/versioning, but it installs persistent automatic hooks and includes unsafe Git/PromptLayer paths that can expose or modify workspace data.

Install only if you want persistent automatic versioning of this workspace and are comfortable auditing what gets tracked. Before enabling sync, narrow .agent-changelog.json, verify .gitignore covers secrets, avoid pulling from untrusted PromptLayer collections, and prefer a version with shell-safe Git calls and validated archive extraction.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
hooks/agent-changelog-commit/handler.ts:82
Finding

Automatic Hook Command Injection Through Tracked Path Configuration

Content
View full analysis

Vulnerability Details

File Location: hooks/agent-changelog-commit/handler.ts:19-25, 82-85
Vulnerability Type: Shell command injection through workspace-controlled configuration
Risk Level: High

Vulnerable Code

ts
function getTracked(workspace: string): string[] {
  const cfgPath = join(workspace, ".agent-changelog.json");
  try {
    const cfg = JSON.parse(readFileSync(cfgPath, "utf-8"));
    if (Array.isArray(cfg.tracked) && cfg.tracked.length > 0) return cfg.tracked;
  } catch {}
  return [];
}
ts
// Stage tracked files
for (const f of getTracked(workspace)) {
  run(`git add "${f}" 2>/dev/null || true`, workspace);
}

The constructed command is passed to a shell by:

ts
function run(cmd: string, cwd: string): string {
  try {
    return execSync(cmd, { cwd, encoding: "utf-8", timeout: 15_000 }).trim();
  } catch {
    return "";
  }
}

Technical Analysis

The tracked array is read from the workspace-controlled .agent-changelog.json file without validation. Each value is interpolated into a command string passed to execSync, which invokes a shell.

Wrapping the value in double quotes does not prevent shell evaluation. Command substitutions using $() or backticks are still evaluated inside double-quoted shell strings. Shell metacharacters capable of terminating the quoted argument may also become dangerous if a value contains a quote.

This operation occurs in the message:sent hook, so exploitation does not require a user to invoke the affected command manually after the malicious configuration has been introduced.

Attack Path

  1. An attacker, compromised tool, or untrusted workspace content modifies .agent-changelog.json.
  2. The attacker adds a malicious tracked value containing shell syntax, such as a path with a command substitution.
  3. OpenClaw emits a message:sent event.
  4. The automatic commit hook read ...[truncated 895 chars]
Remediation
View remediation

Remediation Suggestions

Do not construct shell command strings from configuration values.

  1. Replace execSync with spawnSync or execFileSync and pass arguments separately:

    ts
    import { spawnSync } from "node:child_process";
    
    for (const f of getTracked(workspace)) {
      const result = spawnSync("git", ["add", "--", f], {
        cwd: workspace,
        encoding: "utf-8",
        timeout: 15_000,
        shell: false,
      });
    
      if (result.error || result.status !== 0) {
        console.error(`[agent-changelog-commit] Failed to stage tracked path`);
      }
    }
    
  2. Validate every configured path before use:

    • Require a nonempty string.
    • Reject NUL bytes and control characters.
    • Reject absolute paths.
    • Resolve the path and ensure it remains inside the workspace.
    • Consider rejecting traversal components such as ...
  3. Treat .agent-changelog.json as security-sensitive configuration and restrict its write permissions where practical.

  4. Add tests covering filenames containing quotes, $(), backticks, leading hyphens, whitespace, and newline characters.

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/pl-pull.js:198
Finding

Remote Filename Command Injection During PromptLayer Pull

Content
View full analysis

Vulnerability Details

File Location: scripts/pl-pull.js:157-166, 198-204
Vulnerability Type: Remote archive filename converted into a shell command
Risk Level: High

Vulnerable Code

The archive is downloaded from PromptLayer and extracted:

js
const zipRes = await fetch(`${collectionUrl}?${zipParams}`, { headers: { 'X-API-KEY': apiKeyValue } });
if (!zipRes.ok) {
  const err = await zipRes.text();
  console.error(`PromptLayer API error ${zipRes.status}: ${err}`);
  process.exit(1);
}
const outerZipBuffer = Buffer.from(await zipRes.arrayBuffer());
let tmpDir;
try {
  const extracted = unzipToDir(outerZipBuffer);
  tmpDir = extracted.tmpDir;
  const { all: snapshotFiles, changed, added } = diffAndApply(extracted.extractDir);

Names derived from that archive are then interpolated into a shell command:

js
// Stage only the files that changed
for (const filePath of [...changed, ...added]) {
  try {
    execSync(`git add ${JSON.stringify(filePath)}`, { cwd: WORKSPACE });
  } catch { /* skip unstage-able paths */ }
}

Technical Analysis

changed and added contain filenames obtained from the remotely downloaded archive. The code uses JSON.stringify(filePath) as if it were shell escaping, then passes the resulting string to execSync.

JSON string encoding is not shell-safe encoding. It ordinarily surrounds the filename with double quotes, but POSIX shells still evaluate command substitutions such as $() and backticks inside double quotes. Consequently, a crafted remote filename can cause local commands to execute when the code attempts to stage that file.

The effective command-bearing content comes from an external collection and can change independently of the locally reviewed Skill package. This creates a remote payload execution channel when a user connects to or pulls from an untrusted or compromised PromptLayer collection.

Attack Path

  1. An a ...[truncated 1279 chars]
Remediation
View remediation

Remediation Suggestions

  1. Eliminate shell interpretation when invoking Git:

    js
    const { execFileSync } = require('child_process');
    
    for (const filePath of [...changed, ...added]) {
      try {
        execFileSync('git', ['add', '--', filePath], {
          cwd: WORKSPACE,
          stdio: 'ignore',
        });
      } catch {
        // Report or record the rejected path.
      }
    }
    
  2. Validate remote filenames before copying or staging:

    • Reject NUL bytes and control characters.
    • Reject absolute paths and drive-qualified paths.
    • Reject traversal components.
    • Reject paths resolving outside the extraction root or workspace.
    • Reject symbolic links and special files.
    • Apply reasonable path-length and file-count limits.
  3. Require explicit confirmation before connecting to a new collection and clearly display the collection identity and files that will be changed.

  4. Consider cryptographically pinning or verifying the expected collection owner where the PromptLayer API supports that capability.

  5. Add regression tests using filenames containing $(), backticks, quotes, leading hyphens, newlines, and traversal sequences.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/pl-pull.js:45
Finding

Unvalidated Remote ZIP Extraction and Workspace File Overwrite

Content
View full analysis

Vulnerability Details

File Location: scripts/pl-pull.js:45-65, 75-90
Vulnerability Type: Unsafe extraction and application of an untrusted archive
Risk Level: Medium

Vulnerable Code

js
function unzipToDir(zipBuffer) {
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-changelog-'));
  const zipPath = path.join(tmpDir, 'snapshot.zip');
  const extractDir = path.join(tmpDir, 'extract');
  fs.writeFileSync(zipPath, zipBuffer);
  fs.mkdirSync(extractDir, { recursive: true });
  if (process.platform === 'win32') {
    const psZip = zipPath.replace(/'/g, "''");
    const psDest = extractDir.replace(/'/g, "''");
    execSync(`powershell -NoProfile -Command "Expand-Archive -LiteralPath '${psZip}' -DestinationPath '${psDest}' -Force"`);
  } else {
    execSync(`unzip -o ${JSON.stringify(zipPath)} -d ${JSON.stringify(extractDir)}`);
  }
  // PromptLayer wraps files under a root_path prefix (e.g. .openclaw/).
  // If there's a single top-level directory, treat its contents as the root.
  const topEntries = fs.readdirSync(extractDir, { withFileTypes: true });
  if (topEntries.length === 1 && topEntries[0].isDirectory()) {
    return { tmpDir, extractDir: path.join(extractDir, topEntries[0].name) };
  }
  return { tmpDir, extractDir };
}

Extracted files are subsequently copied into the workspace:

js
for (const filePath of remoteFiles) {
  const src = path.join(extractDir, filePath);
  const dest = path.join(WORKSPACE, filePath);
  const srcContent = fs.readFileSync(src);

  if (!fs.existsSync(dest)) {
    fs.mkdirSync(path.dirname(dest), { recursive: true });
    fs.copyFileSync(src, dest);
    added.push(filePath);
  } else {
    const destContent = fs.readFileSync(dest);
    if (!srcContent.equals(destContent)) {
      fs.copyFileSync(src, dest);
      changed.push(filePath);
    }
  }
}

Technical Analysis

A remote ...[truncated 2402 chars]

Remediation
View remediation

Remediation Suggestions

  1. Replace direct system-tool extraction with a ZIP library that exposes entries before writing them.

  2. For every archive entry:

    • Normalize separators.
    • Reject absolute and drive-qualified paths.
    • Reject .. path components.
    • Resolve the destination path and verify that it starts with the canonical extraction-root path plus a path separator.
    • Reject symbolic links, hard links, devices, and other special entries.
    • Enforce limits on total uncompressed size, individual file size, entry count, and compression ratio.
  3. Before copying a file into the workspace, perform a second containment check:

    js
    const workspaceRoot = fs.realpathSync(WORKSPACE);
    const destination = path.resolve(workspaceRoot, filePath);
    const prefix = workspaceRoot.endsWith(path.sep)
      ? workspaceRoot
      : workspaceRoot + path.sep;
    
    if (!destination.startsWith(prefix)) {
      throw new Error(`Unsafe archive path rejected: ${filePath}`);
    }
    
  4. Reject writes to security-sensitive workspace paths unless explicitly authorized, including .git, hook directories, and agent configuration files.

  5. Present a manifest of additions and overwrites before applying the snapshot, particularly during initial collection connection.

  6. Extract with least privilege into a newly created private directory and retain the existing guaranteed cleanup behavior.

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
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (32)

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · README.md (reported line 3)May include surrounding context.

md
# agent-changelog

A versioning skill for OpenClaw that keeps a clear history of workspace changes with sender attribution.

Use it to answer questions like:

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The finding indicates the skill installs hooks, edits OpenClaw configuration, registers a cron job, initializes git, and creates workspace files, which are persistent system/workspace changes beyond simple request handling. Persistence mechanisms and environment modification are sensitive because they can continue operating after the initial invocation and may broaden data collection or side effects over time.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The finding indicates the skill installs hooks, edits OpenClaw configuration, registers a cron job, initializes git, and creates workspace files, which are persistent system/workspace changes beyond simple request handling. Persistence mechanisms and environment modification are sensitive because they can continue operating after the initial invocation and may broaden data collection or side effects over time.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The finding indicates the skill installs hooks, edits OpenClaw configuration, registers a cron job, initializes git, and creates workspace files, which are persistent system/workspace changes beyond simple request handling. Persistence mechanisms and environment modification are sensitive because they can continue operating after the initial invocation and may broaden data collection or side effects over time.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The finding indicates the skill installs hooks, edits OpenClaw configuration, registers a cron job, initializes git, and creates workspace files, which are persistent system/workspace changes beyond simple request handling. Persistence mechanisms and environment modification are sensitive because they can continue operating after the initial invocation and may broaden data collection or side effects over time.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The finding indicates the skill installs hooks, edits OpenClaw configuration, registers a cron job, initializes git, and creates workspace files, which are persistent system/workspace changes beyond simple request handling. Persistence mechanisms and environment modification are sensitive because they can continue operating after the initial invocation and may broaden data collection or side effects over time.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The finding indicates the skill installs hooks, edits OpenClaw configuration, registers a cron job, initializes git, and creates workspace files, which are persistent system/workspace changes beyond simple request handling. Persistence mechanisms and environment modification are sensitive because they can continue operating after the initial invocation and may broaden data collection or side effects over time.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 54)May include surrounding context.

md
ely in the git commit message, which is handled automatically by the hooks and `commit.sh`. Files should contain only their actual content — clean, annotation-f

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The code uses git archive to package the repository and then later sends that archive to an external API, creating a direct exfiltration mechanism for repository contents. This is especially risky because the skill's stated purpose does not clearly require off-platform transfer of the full codebase, so users may not reasonably expect this behavior.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.sh (reported line 116)May include surrounding context.

sh
if [ ! -f "$WORKSPACE/.gitignore" ]; then
  cat > "$WORKSPACE/.gitignore" << 'GITIGNORE'
# secrets
.env
.env.*
*.env
.envrc

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.sh (reported line 118)May include surrounding context.

sh
if [ ! -f "$WORKSPACE/.gitignore" ]; then
  cat > "$WORKSPACE/.gitignore" << 'GITIGNORE'
# secrets
.env
.env.*
*.env
.envrc

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.sh (reported line 142)May include surrounding context.

sh
*.ppk
client_secret*.json
service_account*.json
*-credentials.json
.aws/
.azure/
.gcloud/

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.sh (reported line 144)May include surrounding context.

sh
service_account*.json
*-credentials.json
.aws/
.azure/
.gcloud/
gcloud*.json
aws_credentials

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding

The README instructs users to run npx agent-changelog without pinning a specific package version. That can cause users to fetch and execute whatever version is current at install time, which increases supply-chain risk if a malicious or compromised release is published or if behavior changes unexpectedly.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill declares required binaries and environment variables, and its documented commands include network-capable PromptLayer operations, but it does not declare an explicit tool scope such as allowed-tools or permissions. That makes the skill's effective authority opaque to reviewers and increases the risk of overbroad execution, especially where external API access and workspace mutation are involved.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The hook explicitly auto-commits workspace file changes after every outbound message, but the description does not clearly warn users about this persistent side effect. That can surprise users into creating an unintended audit trail of potentially sensitive edits, credentials, or intermediary content, and may also trigger downstream automation tied to git commits.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The handler reads user, userId, and channel from .version-context and persists them to pending_commits.jsonl on every qualifying message without any consent, minimization, or retention controls visible in this code. This creates a local audit trail of user-linked activity that could expose sensitive metadata to other local processes, backups, or later tooling, especially because the skill is explicitly designed to track changelog history.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The hook automatically stages files via git add as part of message handling, with no confirmation or disclosure in this code path. In an agent skill centered on changelogs and rollback history, silent staging can still capture user edits unexpectedly and may commit sensitive or unintended tracked content into version history, making later exposure more likely.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script does more than create a local commit: based on workspace configuration it may push to GitHub or invoke a PromptLayer sync script, causing commit metadata and repository state to leave the local environment. In an agent skill context, this broadens the trust boundary and can expose sensitive code or operational metadata unexpectedly, especially because the behavior is embedded in a commit helper rather than a clearly separate sync step.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

When GitHub sync is enabled, the script automatically performs git push after committing without interactive confirmation at execution time. This can leak newly committed code, secrets, or internal history to a remote repository immediately, which is especially risky in an agent-driven workflow where commits may be triggered indirectly or automatically.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The PromptLayer branch invokes a Node sync script and passes the commit subject, sending metadata off-host when enabled. Even though only the subject is shown here, commit subjects can contain filenames, summaries, usernames, and workflow details, so this creates an undisclosed data egress path in a tool users may expect to be local-only.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

This script creates a full ZIP of the current git HEAD and uploads it to a third-party PromptLayer API, which is a data exfiltration path for repository contents. In the context of a changelog/versioning skill described as git/OpenClaw-based, silently transmitting the entire workspace externally is broader than expected and can expose source code, secrets committed to the repo, or proprietary data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The repository snapshot is transmitted to a remote service without any explicit user-facing confirmation at the point of upload, increasing the chance that sensitive data is sent unknowingly. Even if the feature is intended, lack of transparent notice and consent makes the behavior unsafe for a developer tool handling local workspaces.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This script creates a full git snapshot of the workspace and uploads it to PromptLayer whenever the feature is enabled and an API key is present. That behavior expands the skill from local changelog/git handling into external data exfiltration, and the broad repository archive may include proprietary code, prompts, secrets checked into the repo, or other sensitive artifacts without clear user-facing disclosure or per-run consent.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The version-publishing path sends repository contents to a third-party service under a changelog-management skill whose stated scope is git/OpenClaw history and rollback operations. Because the capability is not clearly justified by that scope, users may invoke or install the skill expecting local version control features while unintentionally enabling outbound transfer of repository data to an external API.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
hooks/agent-changelog-commit/handler.ts:14

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/pl-init.js:47

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/pl-pull.js:54

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/pl-push.js:26

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/pl-init.js:9

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/pl-pull.js:9

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/pl-push.js:9