Back to skill

Security audit

The AI Agent FinOps Playbook: Budget Enforcement, Cost Allocation & Spend Analytics for Multi-Agent Systems

Security checks for vulnerabilities and agentic risk

Overview

This is a non-executing FinOps guide, but its examples can change live billing, webhook, and API-key state while understating production safety requirements.

Review this carefully before installing or following it. Treat the code as illustrative only, use a test GreenHelix account and sandbox endpoint first, do not paste real production API keys into examples, add webhook signature verification and replay protection before any pause/kill action, and verify key rotation with rollback before invalidating old credentials.

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

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:729
Finding

Unauthenticated Webhooks Can Trigger Agent Pause or Termination

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:729-815
Vulnerability Type: Missing authentication and integrity verification for control webhooks
Risk Level: High

Vulnerable Code

python
from flask import Flask, request, jsonify
import logging

app = Flask(__name__)
logger = logging.getLogger("finops_alerts")


@app.route("/alerts/warning", methods=["POST"])
def handle_warning():
    """75% budget consumed -- log and notify Slack."""
    payload = request.json
    agent_id = payload.get("agent_id")
    spent_pct = payload.get("spent_pct")
    remaining = payload.get("remaining_budget")

    logger.warning(
        f"BUDGET WARNING: {agent_id} at {spent_pct}% "
        f"(${remaining} remaining)"
    )
    notify_slack(
        channel="#agent-finops",
        message=f"Agent {agent_id} has consumed {spent_pct}% of its daily budget. "
                f"${remaining} remaining.",
    )
    return jsonify({"status": "acknowledged"})


@app.route("/alerts/critical", methods=["POST"])
def handle_critical():
    """90% budget consumed -- pause the agent."""
    payload = request.json
    agent_id = payload.get("agent_id")

    logger.critical(
        f"BUDGET CRITICAL: {agent_id} at {payload.get('spent_pct')}%. "
        f"Pausing agent."
    )
    pause_agent(agent_id)
    notify_slack(
        channel="#agent-finops",
        message=f"CRITICAL: Agent {agent_id} paused at 90% budget.",
    )
    return jsonify({"status": "agent_paused"})


@app.route("/alerts/exhausted", methods=["POST"])
def handle_exhausted():
    """100% budget consumed -- kill the agent process."""
    payload = request.json
    agent_id = payload.get("agent_id")

    logger.error(
        f"BUDGET EXHAUSTED: {agent_id}. Terminating."
    )
    kill_agent(agent_id)
    notify_slack(
        channel="#agent-finops",
        message=f"EXHAUSTED: Agent {agent_id} terminated. Budget fully consumed.",
    )
    return jsonify({"status": "agent_terminated"})


def pause_agent(
...[truncated 2763 chars]
Remediation
View remediation

Remediation Suggestions

  1. Verify a provider-generated HMAC or Ed25519 signature over the unmodified request body before parsing or acting on it.
  2. Compare signatures using a constant-time operation and reject requests with absent, malformed, or invalid signatures.
  3. Require a signed timestamp and enforce a narrow acceptance window.
  4. Store and reject previously processed event IDs or nonces to prevent replay.
  5. Bind each webhook registration to its expected agent and event type; do not permit an arbitrary body field to select the control target.
  6. Validate the JSON schema and allowlist known agent identifiers and threshold values.
  7. Place the endpoint behind TLS, authentication, network access controls, and rate limiting.
  8. Separate notification endpoints from destructive control actions. Queue authenticated events for policy evaluation rather than directly terminating processes in the HTTP handler.
  9. Record signature-validation results and event IDs in tamper-resistant audit logs.
  10. Add tests covering forged signatures, stale timestamps, duplicate events, unknown agents, malformed payloads, and cross-agent substitution.

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:186
Finding

Outbound HTTP Requests Lack Timeouts

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:186-189 and SKILL.md:795-802
Vulnerability Type: Unbounded outbound network operations
Risk Level: Medium

Vulnerable Code

python
def _execute(self, tool: str, input_data: dict) -> dict:
    """Execute a tool on the GreenHelix gateway."""
    resp = self.session.post(
        f"{self.base_url}/v1",
        json={"tool": tool, "input": input_data},
    )
    if resp.status_code == 402:
        raise BudgetExhaustedError(
            f"Agent {self.agent_id} has exceeded its budget cap. "
            f"Response: {resp.text}"
        )
    resp.raise_for_status()
    return resp.json()
python
def notify_slack(channel: str, message: str):
    """Send a Slack notification. Replace with your Slack webhook."""
    import requests as req
    req.post(os.environ.get("SLACK_WEBHOOK_URL", ""), json={
        "channel": channel,
        "text": message,
    })

Technical Analysis

Both outbound HTTP operations omit the timeout argument. The requests library does not impose a default timeout, so a connection or response can remain blocked indefinitely.

This is especially problematic in the webhook receiver because the synchronous Slack request occurs while processing an alert. An unavailable or deliberately slow downstream endpoint can retain server workers and prevent subsequent alerts from being processed. The same condition in _execute() can block agent workflows and FinOps enforcement logic.

The Slack URL is supplied through an environment variable, while the gateway client also accepts a configurable base URL. A compromised configuration, malfunctioning remote service, or network intermediary could therefore direct calls to an endpoint that intentionally accepts connections without completing responses.

Attack Path

  1. A configured remote endpoint becomes unavailable or behaves as a slow server, or an attacker gains influence over the relevant endpoint configuration or ...[truncated 1003 chars]
Remediation
View remediation

Remediation Suggestions

  1. Configure explicit connect and read timeouts for every outbound request:
python
resp = self.session.post(
    f"{self.base_url}/v1",
    json={"tool": tool, "input": input_data},
    timeout=(3.05, 15),
)
python
req.post(
    slack_url,
    json={"channel": channel, "text": message},
    timeout=(3.05, 10),
)
  1. Catch requests.exceptions.ConnectTimeout, ReadTimeout, and RequestException and use controlled failure behavior.
  2. If retries are appropriate, use exponential backoff, jitter, strict attempt limits, and idempotency keys. Do not retry budget-exhaustion responses.
  3. Move notifications to a bounded asynchronous queue so webhook processing does not depend synchronously on Slack availability.
  4. Apply circuit breakers and concurrency limits to unhealthy downstream services.
  5. Validate configured endpoint schemes and hosts against an allowlist where configuration is not fully trusted.
  6. Monitor timeout rates, queue depth, worker utilization, and alert-delivery failures.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (10)

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The guide states that the sandbox requires no API key, but the immediately adjacent examples require GREENHELIX_API_KEY from the environment and point at live-looking API usage patterns. This mismatch can mislead operators into supplying real credentials and running state-changing examples under false assumptions, increasing the chance of unintended billing or account changes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The quick-start includes wallet creation, deposits, budget cap changes, and webhook registration against a production API base URL without a strong warning that these are live state-changing billing operations. Readers may copy and execute the snippet directly with real credentials, causing unintended charges, configuration drift, or impact to live agents.

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 71)May include surrounding context.

md
finops = AgentFinOps(
    api_key=os.environ["GREENHELIX_API_KEY"],
    agent_id="research-agent-01",
    base_url="https://api.greenhelix.net/v1",
)

# 1. Create an isolated wallet

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 176)May include surrounding context.

md
finops = AgentFinOps(
    api_key=os.environ["GREENHELIX_API_KEY"],
    agent_id="research-agent-01",
    base_url="https://api.greenhelix.net/v1",
)

# 1. Create an isolated wallet

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 2115)May include surrounding context.

md
finops = AgentFinOps(
    api_key=os.environ["GREENHELIX_API_KEY"],
    agent_id="research-agent-01",
    base_url="https://api.greenhelix.net/v1",
)

# 1. Create an isolated wallet

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 540)May include surrounding context.

print(f"Spent this month: ${status['spent_this_month']}")

text

**curl -- set budget cap:**

```bash
curl -s -X POST https://sandbox.greenhelix.net/v1 \

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 835)May include surrounding context.

)

text

**curl -- list webhooks:**

```bash
curl -s -X POST https://sandbox.greenhelix.net/v1 \

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 874)May include surrounding context.

)

text

**curl -- get webhook logs:**

```bash
curl -s -X POST https://sandbox.greenhelix.net/v1 \

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 1447)May include surrounding context.

print(f"Permissions: {key_result.get('permissions')}")

text

**curl -- create API key:**

```bash
curl -s -X POST https://sandbox.greenhelix.net/v1 \

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The prose correctly says to update the agent to the new key and verify it before invalidating the old key, but the sample rotation code omits those safety steps. If copied as-is, it can revoke working credentials before rollout completes, causing outages or emergency lockouts for live agents.

Content

No source excerpt is available for this finding.