Back to skill

Security audit

Contract Renewal Agent

Security checks for vulnerabilities and agentic risk

Overview

This contract tracker is purpose-aligned, but it needs Review because it stores sensitive contract data in weakly protected local JSON and can permanently delete records without confirmation.

Review before installing if you will store real contract or financial data. Use it only where local user access is trusted, keep backups, and be careful with delete requests because records are removed from the local database immediately.

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 (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/contract_renewal_agent.py:11
Finding

Contract Database Is Created Without Restrictive File Permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/contract_renewal_agent.py, lines 11–21
Vulnerability Type: Insecure local storage permissions and unsafe file replacement
Risk Level: Medium

Vulnerable Code

python
DB_PATH = Path.home() / ".openclaw" / "workspace" / "contract-renewal-agent" / "contracts.json"


def load_db() -> dict:
    if not DB_PATH.exists():
        DB_PATH.parent.mkdir(parents=True, exist_ok=True)
        return {"contracts": {}}
    with open(DB_PATH) as f:
        return json.load(f)


def save_db(db: dict) -> None:
    with open(DB_PATH, "w") as f:
        json.dump(db, f, indent=2, default=str)

Technical Analysis

The application stores contract names, counterparties, annual values, dates, and notes in a plaintext JSON database. It creates the parent directory and database without explicitly enforcing restrictive permissions such as 0700 for the directory and 0600 for the file. Effective permissions therefore depend on the process umask, and existing insecure permissions are not corrected.

The database is also written directly with open(DB_PATH, "w"). This operation follows a pre-existing symbolic link and truncates the target before writing. If another local user can modify the database directory or replace the database path, the save operation may overwrite another file writable by the victim account. The direct write is also non-atomic, so interruption or concurrent writes can leave the database partially written or corrupted.

Exploitation requires local access and sufficient filesystem permissions to read the resulting database or manipulate its path or parent directory.

Attack Path

  1. The victim runs an operation that invokes save_db, such as adding, updating, or deleting a contract.
  2. The program creates or writes contracts.json using permissions derived solely from the environment's umask.
  3. Under a permissive umask or pre-existing unsafe permissions, another local account reads t ...[truncated 933 chars]
Remediation
View remediation

Remediation Suggestions

  • Create the database directory with mode 0700 and verify that it is owned by the current user.
  • Create database files with mode 0600, independently of the process umask.
  • Correct unsafe permissions on an existing database after verifying its ownership.
  • Reject symbolic links for the database path and verify each relevant path component before writing.
  • Serialize into a securely created same-directory temporary file using exclusive creation and mode 0600.
  • Flush buffered data, call fsync, and atomically replace the database with os.replace.
  • Consider applying an advisory lock to prevent concurrent writers from losing updates or corrupting the JSON document.
  • For highly sensitive contract data, add encryption at rest with keys stored separately in an operating-system credential facility.
  • Handle malformed JSON and I/O failures without replacing the last valid database.

A hardened implementation should follow this pattern:

python
import json
import os
import tempfile

def save_db(db: dict) -> None:
    DB_PATH.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
    os.chmod(DB_PATH.parent, 0o700)

    if DB_PATH.is_symlink():
        raise RuntimeError("Refusing to write through a symbolic link")

    fd, temp_name = tempfile.mkstemp(
        prefix=".contracts-",
        suffix=".tmp",
        dir=DB_PATH.parent,
    )

    try:
        os.fchmod(fd, 0o600)
        with os.fdopen(fd, "w") as f:
            json.dump(db, f, indent=2, default=str)
            f.flush()
            os.fsync(f.fileno())

        os.replace(temp_name, DB_PATH)
        os.chmod(DB_PATH, 0o600)
    except Exception:
        try:
            os.unlink(temp_name)
        except FileNotFoundError:
            pass
        raise
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (4)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill advertises local JSON storage and exposes contract-management operations that necessarily write data, but it does not declare any explicit tool scope such as permissions or allowed-tools. That mismatch weakens user visibility and policy enforcement around file-writing behavior, increasing the chance the skill can modify local data in ways the user did not clearly authorize.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill exposes a delete_contract capability, but the user-facing description and usage guidance do not clearly warn that tracked contracts can be permanently removed. In a contract-management context, silent destructive actions can cause loss of business records, missed renewals, and operational disruption if triggered accidentally or through prompt manipulation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This code performs a persistent deletion from the local contracts database and immediately saves the change. Although the command name is descriptive, there is no confirmation prompt or explicit warning to the user before the irreversible action occurs.

Content

No source excerpt is available for this finding.

Scope Creep

Low
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Content

Scanner excerpt · references/renewal_strategies.md (reported line 26)May include surrounding context.

md
### Seller-side leverage
- Switching cost is high (migration, training, data lock-in)
- Performance metrics — show ROI delivered
- Upsell opportunity — renewal is the right moment to expand scope
- Auto-renew clauses — know if the contract auto-renews and at what price

## Pricing Strategy

Static analysis

No suspicious patterns detected.