Back to skill

Security audit

Agent Contract Lifecycle Management

Security checks for vulnerabilities and agentic risk

Overview

This non-executing guide is openly about contract automation, but its production-style examples can automate escrow, penalties, reputation actions, and termination with insufficient safeguards.

Review carefully before installing or adapting this skill. Treat it as illustrative only, use sandbox credentials, add explicit approval thresholds for any fund movement or termination, require bilateral signature verification before escrow funding, add idempotency and breach deduplication, and validate API destinations before sending bearer tokens.

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
SKILL.md:1770
Finding

Escrow Funding Proceeds Without Verified Bilateral Signatures

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 1770-1790
Vulnerability Type: Missing authorization and signature verification before financial commitment
Risk Level: High

Vulnerable Code

python
def finalize_contract(self, contract: dict) -> dict:
    """Sign and activate a negotiated contract."""
    # Sign the contract
    signature = self.signer.sign_contract(contract)
    contract["parties"]["consumer" if self.agent_id
        == contract["parties"]["consumer"]["agent_id"]
        else "provider"]["claim_chain_id"] = signature["claim_chain_id"]
    contract["parties"]["consumer" if self.agent_id
        == contract["parties"]["consumer"]["agent_id"]
        else "provider"]["signed_at"] = signature["signed_at"]

    # Create escrow
    escrow_result = self.escrow_mgr.create_contract_escrow(contract)
    escrow_id = escrow_result["escrow_id"]
    contract["escrow"]["escrow_id"] = escrow_id

    # Activate all SLA monitors
    activation = self.tracker.activate_all_obligations(contract)

    # Update fingerprint with all runtime IDs
    contract["fingerprint"] = self.signer.fingerprint(contract)

Technical Analysis

The lifecycle manager signs the contract only as the currently authenticated agent and then immediately creates the escrow. It does not invoke verify_counterparty_signature(), even though that method is defined elsewhere in the guide for bilateral signature verification.

Consequently, there is no enforcement that:

  • The counterparty signed the contract.
  • The counterparty signature belongs to the expected agent.
  • Both parties signed the same canonical contract fingerprint.
  • The counterparty signature was created before escrow funding.
  • The contract remained unchanged between signing and activation.

The contract is also mutated after the local signature is generated by adding signature, escrow, and runtime identifiers. This requires a cle ...[truncated 1546 chars]

Remediation
View remediation

Remediation Suggestions

  1. Define an immutable canonical contract payload that excludes mutable runtime fields such as escrow IDs and SLA monitor IDs.
  2. Calculate one canonical fingerprint from that payload.
  3. Require independent signatures from both expected party identities over that exact fingerprint.
  4. Call verify_counterparty_signature() and verify the chain owner, contract ID, fingerprint, signature status, and expected counterparty identity before creating escrow.
  5. Reject activation if either signature is absent, expired, revoked, malformed, or associated with another contract version.
  6. Bind escrow creation to the verified fingerprint and signature-chain identifiers.
  7. Add an explicit contract state machine such as draft → locally_signed → bilaterally_signed → funded → active.
  8. Require a human or policy-engine approval for deposits above a configured financial threshold.
  9. Use an idempotency key based on the contract ID and signed fingerprint to prevent duplicate escrow creation.

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:1863
Finding

Milestone Processing Releases Gross Escrow Funds Before Applying Penalties

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 1863-1900; related release implementation at lines 677-717
Vulnerability Type: Incorrect financial transaction ordering and missing compliance gate
Risk Level: High

Vulnerable Code

python
def process_milestone(self, contract_id: str,
                      milestone: str) -> dict:
    """Process a contract milestone, adjusting release for penalties."""
    contract = self.contracts[contract_id]
    escrow_id = self.escrows[contract_id]

    # Calculate total penalties since last milestone
    relevant_penalties = [
        e for e in self.enforcement_log
        if e.get("obligation_id") in [
            o["obligation_id"] for o in contract["obligations"]
        ]
    ]
    total_penalties = sum(
        e.get("penalty_amount", 0) for e in relevant_penalties
    )

    # Release milestone (amount will be adjusted by penalties)
    result = self.escrow_mgr.release_milestone(
        contract, escrow_id, milestone
    )

    if result["released"]:
        adjusted_amount = max(0, result["amount"] - total_penalties)
        self._audit("milestone_processed", {
            "contract_id": contract_id,
            "milestone": milestone,
            "gross_release": result["amount"],
            "penalty_deductions": total_penalties,
            "net_release": adjusted_amount,
        })

    return {
        "milestone": milestone,
        "result": result,
        "penalties_applied": total_penalties,
    }

The called helper releases the gross scheduled amount:

python
release_amount = round(total * (release_pct / 100.0), 2)

result = self.execute("release_escrow", {
    "escrow_id": escrow_id,
    "agent_id": contract["parties"]["consumer"]["agent_id"],
    "amount": str(release_amount),
})

Technical Analysis

process_milestone() calculates penalties but calls `release_m ...[truncated 2223 chars]

Remediation
View remediation

Remediation Suggestions

  1. Fetch and verify the current compliance state before any milestone release.
  2. Reject or suspend release if required obligations are noncompliant, unavailable, disputed, or still within an unresolved measurement window.
  3. Calculate gross amount, applicable penalties, prior deductions, and net amount before invoking release_escrow.
  4. Send only the final net amount to the escrow API.
  5. Perform compliance verification, penalty consumption, and release as one atomic server-side operation where possible.
  6. Associate every penalty with a contract ID, breach ID, accounting period, status, and target milestone.
  7. Mark penalties as consumed only after a successful release transaction.
  8. Persist milestone states and use idempotency keys to prevent repeated processing.
  9. Reconcile the returned remote transaction amount against the requested net amount before writing the success audit event.
  10. Ensure audit records contain the actual escrow transaction identifier and executed amount rather than a locally calculated value alone.

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:1810
Finding

Recurring Monitoring Reapplies Penalties for the Same Unresolved Breach

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 1810-1843; enforcement implementation at lines 1089-1128
Vulnerability Type: Missing idempotency and breach-event deduplication
Risk Level: High

Vulnerable Code

python
# Check for breaches and enforce
enforcement_actions = []
for obl_status in snapshot["obligations"]:
    if not obl_status["compliant"]:
        breach_data = {
            "actual_value": obl_status.get("current_value"),
            "threshold": obl_status["threshold"],
            "obligation_id": obl_status["obligation_id"],
            "detected_at": datetime.now(timezone.utc).isoformat(),
            "violation_count": 1,
            "breach_days": 1,
        }

        result = self.enforcer.enforce_breach(
            contract, obl_status["obligation_id"],
            breach_data, escrow_id
        )

        if result["enforced"]:
            enforcement_actions.append(result)
            self.enforcement_log.append(result)

Each invocation then executes another penalty and reputation update:

python
enforcement_result = self._execute_penalty(
    contract, penalty_amount, escrow_id, obligation_id, breach_data
)

self._record_enforcement(
    contract, obligation_id, penalty_amount,
    breach_data, enforcement_result
)

Technical Analysis

The monitoring method is intended to run repeatedly, such as every five minutes. However, each observation of a noncompliant state is treated as a new breach with newly generated detected_at, violation_count = 1, and breach_days = 1.

There is no mechanism to distinguish:

  • A newly started breach from an already open breach.
  • Multiple observations belonging to the same measurement window.
  • A continuing breach from a resolved and subsequently recurring breach.
  • A penalty already enforced for the same breach event.
  • A retry after an uncertain network response from a g ...[truncated 1589 chars]
Remediation
View remediation

Remediation Suggestions

  1. Model breaches as persistent events with stable identifiers.
  2. Derive an idempotency key from the contract ID, obligation ID, measurement window, and breach sequence.
  3. Maintain explicit states such as detected, grace_period, enforced, resolved, and reopened.
  4. Enforce only on valid state transitions or once per contract-defined accounting period.
  5. Store remote transaction IDs and reject duplicate enforcement attempts.
  6. Distinguish network retries from new enforcement operations using the same idempotency key.
  7. Apply contract-defined grace periods, caps, and rolling-window rules before enforcement.
  8. Submit reputation updates only after confirmed, deduplicated enforcement.
  9. Prevent duplicate disputes by associating one active dispute with each unresolved breach event.
  10. Add tests covering repeated monitoring of one continuous breach, recovery and recurrence, delayed API responses, and process restarts.

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:125
Finding

Unrestricted API Base URL Can Expose Bearer Credentials

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 125-143
Vulnerability Type: Credential disclosure through attacker-controlled network destination
Risk Level: High

Vulnerable Code

python
def __init__(self, api_key: str, agent_id: str,
             base_url: str = "https://api.greenhelix.net/v1"):
    self.api_key = api_key
    self.agent_id = agent_id
    self.base_url = base_url
    self.session = requests.Session()
    self.session.headers.update({
        "Content-Type": "application/json",
        "Authorization": f"Bearer {api_key}",
    })

def execute(self, tool: str, input_data: dict) -> dict:
    """Execute a GreenHelix tool and return the response."""
    resp = self.session.post(
        f"{self.base_url}/v1",
        json={"tool": tool, "input": input_data},
    )
    resp.raise_for_status()
    return resp.json()

Technical Analysis

The client accepts an arbitrary base_url and attaches the bearer credential to every request made by the session. It does not enforce HTTPS, validate the destination hostname, restrict ports, or bind the credential to an approved origin.

If an attacker can influence configuration, constructor parameters, an agent-generated URL, or an integration wrapper, the client will transmit the authorization header to the selected endpoint. The requests library also follows redirects by default, and the implementation does not define a redirect policy.

The request lacks an explicit timeout. A malicious or unavailable endpoint can therefore stall an autonomous workflow for an extended period, although timeout behavior may also depend on the surrounding runtime.

Attack Path

  1. An attacker gains influence over the base_url supplied to ContractClient or one of its subclasses.
  2. The attacker provides a URL under their control, or a URL that redirects in an unsafe manner.
  3. The client creates a session with the producti ...[truncated 970 chars]
Remediation
View remediation

Remediation Suggestions

  1. Use a fixed production API origin or an explicit allowlist of approved HTTPS hostnames.
  2. Parse and validate the URL before creating the client.
  3. Reject HTTP, embedded user information, unexpected ports, fragments, and unapproved IP-literal destinations.
  4. Resolve and validate destinations against private, loopback, link-local, and metadata-service address ranges where user-controlled URLs are possible.
  5. Disable redirects for authenticated API requests or validate every redirect destination before forwarding credentials.
  6. Attach the authorization header per request only after destination validation rather than placing it in unrestricted session defaults.
  7. Set explicit connection and read timeouts, for example timeout=(5, 30).
  8. Use narrowly scoped, short-lived credentials with financial limits and rotation support.
  9. Keep sandbox and production credentials separate.
  10. Correct and normalize API path construction so a configured /v1 base is not unintentionally combined with another /v1.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (8)

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The guide repeatedly claims escrow release is conditioned on verified SLA compliance, but the implementation releases milestone funds solely based on milestone name and percentage. This creates a dangerous integrity gap: operators may trust the example as enforcing compliance when it can pay out despite breaches, undermining financial controls and enabling loss of escrowed funds.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This markdown file states that agents can transfer deposits, automate penalty enforcement, escrow clawback, termination, and run workflows 'without human intervention.' Although it notes the guide is educational and uses a sandbox, it does not clearly warn readers that the described patterns involve financial commitments, automated settlements, and potentially destructive contract actions if adapted outside the sandbox.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

md
"""Base client for all contract lifecycle operations on GreenHelix."""

    def __init__(self, api_key: str, agent_id: str,
                 base_url: str = "https://api.greenhelix.net/v1"):
        self.api_key = api_key
        self.agent_id = agent_id
        self.base_url = base_url

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

md
"""Base client for all contract lifecycle operations on GreenHelix."""

    def __init__(self, api_key: str, agent_id: str,
                 base_url: str = "https://api.greenhelix.net/v1"):
        self.api_key = api_key
        self.agent_id = agent_id
        self.base_url = base_url

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

md
"""Base client for all contract lifecycle operations on GreenHelix."""

    def __init__(self, api_key: str, agent_id: str,
                 base_url: str = "https://api.greenhelix.net/v1"):
        self.api_key = api_key
        self.agent_id = agent_id
        self.base_url = base_url

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

md
"""Base client for all contract lifecycle operations on GreenHelix."""

    def __init__(self, api_key: str, agent_id: str,
                 base_url: str = "https://api.greenhelix.net/v1"):
        self.api_key = api_key
        self.agent_id = agent_id
        self.base_url = base_url

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The method promises a complete audit trail for a specific contract, but it ignores contract_id and returns generic transaction analytics. In a contract enforcement system, this can mislead users into believing they have contract-specific evidence when they may be viewing unrelated or incomplete records, weakening auditability and dispute support.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The example walks readers through initiating contracts, creating escrow, monitoring obligations, enforcing breaches, and terminating vendors, but it does not place a local warning immediately around the executable-style workflow. For markdown files, omission of warnings around behaviors affecting user funds, counterparties, or system integrity is in scope even when the document is an educational guide.

Content

No source excerpt is available for this finding.