Back to skill

Security audit

Agentfinobs

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent financial observability package, but it can expose sensitive transaction and budget data over unauthenticated network endpoints by default.

Review this before installing in any shared, cloud, container, or LAN-reachable environment. Do not enable the built-in dashboard or Prometheus exporter unless you can bind them to localhost or protect them with network controls and authentication. Treat JSONL files, webhook payloads, dashboard responses, and metrics as sensitive financial records because they can include agent IDs, task IDs, counterparties, descriptions, tags, amounts, revenue, PnL, and timing data.

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

T09 · Insecure Skill Coding Practices

Error
Location
agentfinobs/dashboard.py:44
Finding
Unauthenticated Dashboard Exposes Financial Data on All Network Interfaces<![CDATA[ ## Vulnerability Details **File Location**: `agentfinobs/dashboard.py:44-83`, `agentfinobs/dashboard.py:132-134` **Related Data Definition**: `agentfinobs/types.py:89-106` **Vulnerability Type**: Unauthenticated network exposure and sensitive information disclosure **Risk Level**: High ### Vulnerable Code ```python def start(self, host: str = "0.0.0.0", port: int = 9400): """Start dashboard in a background daemon thread.""" dashboard = self # capture for handler closure class Handler(BaseHTTPRequestHandler): def do_GET(self): path = self.path.rstrip("/") routes = { "": dashboard._index, "/healthz": dashboard._healthz, "/metrics": dashboard._metrics_all, "/metrics/1h": dashboard._metrics_1h, "/metrics/24h": dashboard._metrics_24h, "/budget": dashboard._budget_status, "/alerts": dashboard._alerts, "/txs/recent": dashboard._recent_txs, "/anomaly/stats": dashboard._anomaly_stats, } handler_fn = routes.get(path) if handler_fn is None: self.send_error(404, "Not found") return self._json_response(handler_fn()) def _json_response(self, data): body = json.dumps(data, indent=2).encode() self.send_response(200) self.send_header("Content-Type", "application/json") self.send_header("Access-Control-Allow-Origin", "*") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) self._server = HTTPServer((host, port), Handler) ``` The transaction endpoint returns complete transaction dictionaries: ```python def _recent_txs(self) -> dict: txs = self.tracker.recent(50) return {"transactions": [tx.to_dict() for tx in txs]} ``` Those dictionaries include sensitive co ...[truncated 3098 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Change the default bind address to loopback: ```python def start(self, host: str = "127.0.0.1", port: int = 9400): ``` 2. Add an explicit `dashboard_host` parameter to `ObservabilityStack.create()` and require deliberate opt-in before permitting a non-loopback address. 3. Add authentication and authorization controls, such as a bearer token validated before route dispatch. Use constant-time token comparison. 4. Remove wildcard CORS. Disable CORS by default or allow only explicitly configured trusted origins. 5. Redact sensitive transaction fields from dashboard responses by default. In particular, make descriptions, tags, task IDs, and counterparties configurable or omitted unless explicitly requested. 6. Recommend TLS and authenticated reverse-proxy deployment when the dashboard must be remotely reachable. 7. Document that the dashboard contains sensitive financial data and must not be exposed directly to public or untrusted networks. 8. Add automated tests confirming that the default listener is loopback-only and that protected endpoints reject unauthenticated requests. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
agentfinobs/prometheus.py:130
Finding
Prometheus Exporter Publicly Exposes Agent Financial Metrics<![CDATA[ ## Vulnerability Details **File Location**: `agentfinobs/prometheus.py:130-132` **Vulnerability Type**: Unauthenticated metrics endpoint bound to all interfaces **Risk Level**: Medium ### Vulnerable Code ```python # Start HTTP server prom.start_http_server(port) logger.info(f"Prometheus metrics at http://0.0.0.0:{port}/metrics") ``` The exposed metrics include financial and operational labels and values: ```python self.tx_total = prom.Counter( "agentfinobs_tx_total", "Total transactions recorded", ["agent_id", "rail", "status"], ) self.tx_amount_total = prom.Counter( "agentfinobs_tx_amount_total", "Total USD spent", ["agent_id", "rail"], ) self.tx_revenue_total = prom.Counter( "agentfinobs_tx_revenue_total", "Total USD revenue", ["agent_id"], ) ``` ### Technical Analysis Constructing `PrometheusExporter` immediately starts an HTTP metrics service. The exporter does not provide a host parameter, authentication, authorization, or transport-security configuration. The default behavior of the invoked Prometheus server is represented by the project itself as an all-interface listener at `0.0.0.0`. The endpoint exposes agent identifiers, payment rails, transaction status counts, spending, revenue, PnL, budget headroom, burn rate, ROI, and alert counts. Although the module's usage documentation describes the endpoint as available through `localhost`, the actual listener is not restricted to loopback. This mismatch can cause users to underestimate the exposure. ### Attack Path 1. An application constructs `PrometheusExporter(port=9401)`. 2. Its constructor immediately starts the metrics HTTP server. 3. The server listens on all network interfaces. 4. An attacker with access to the relevant network requests `http://target:9401/metrics`. 5. The endpoint returns financial totals and identifying labels without authentication. 6. The attacker monitors repeated requests over time to infer agent activity, spending veloci ...[truncated 517 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add a bind-address parameter that defaults to loopback: ```python def __init__( self, port: int = 9401, agent_id: str = "default", host: str = "127.0.0.1", ): ... prom.start_http_server(port, addr=host) ``` 2. Require explicit configuration before allowing `0.0.0.0` or another non-loopback address. 3. Clearly document that the Prometheus endpoint exposes sensitive financial metrics. 4. For remote collection, recommend an authenticated and TLS-enabled reverse proxy or a network policy that permits only the authorized Prometheus collector. 5. Consider minimizing label exposure. Avoid agent IDs or other internal identifiers where aggregate metrics are sufficient. 6. Add tests that verify loopback-only default binding and confirm that public binding requires explicit opt-in. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
agentfinobs/tracker.py:189
Finding
Transaction Records Are Persisted in Plaintext with Ambient File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `agentfinobs/tracker.py:68-71`, `agentfinobs/tracker.py:189-192` **Additional Location**: `agentfinobs/exporters.py:63-84` **Vulnerability Type**: Plaintext storage of potentially sensitive financial records **Risk Level**: Low ### Vulnerable Code The tracker constructs its persistence path without applying restrictive file permissions: ```python self._persist_path: Path | None = None if persist_dir: p = Path(persist_dir) p.mkdir(parents=True, exist_ok=True) self._persist_path = p / f"txs_{agent_id}.jsonl" ``` Complete transaction objects are then appended in plaintext: ```python def _append_to_disk(self, tx: AgentTx): try: with open(self._persist_path, "a") as f: f.write(json.dumps(tx.to_dict()) + "\n") except Exception as e: logger.warning(f"Persist failed: {e}") ``` The separate JSONL exporter has equivalent behavior: ```python def __init__(self, path: str | Path): self._path = Path(path) self._path.parent.mkdir(parents=True, exist_ok=True) self._lock = threading.Lock() def export_tx(self, tx: "AgentTx") -> None: with self._lock: try: with open(self._path, "a") as f: f.write(json.dumps(tx.to_dict()) + "\n") except Exception as e: logger.warning(f"JsonlExporter write failed: {e}") def export_snapshot(self, snapshot: "Snapshot") -> None: snap_path = self._path.with_suffix(".snapshots.jsonl") with self._lock: try: with open(snap_path, "a") as f: f.write(json.dumps(snapshot.to_dict()) + "\n") except Exception as e: logger.warning(f"JsonlExporter snapshot write failed: {e}") ``` ### Technical Analysis Persistence is opt-in, but when enabled, complete transaction dictionaries are written as unencrypted JSON lines. File permissions are inherited from the process umask and surrounding directory configuration. The implem ...[truncated 1721 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create new persistence files with owner-only permissions such as `0600`, and create dedicated directories with mode `0700`. 2. Validate that persistence paths are owned by the expected user and are not group- or world-readable before writing sensitive records. 3. Reject symbolic links and unexpected non-regular files where the threat model includes hostile local users. 4. Offer configurable field redaction so descriptions, tags, task IDs, counterparties, and other sensitive context can be omitted from persisted output. 5. Document that descriptions and tags must not contain credentials, API keys, access tokens, personal data, or other secrets. 6. Support application-managed encryption at rest or document integration with encrypted storage for sensitive deployments. 7. Apply equivalent permission controls to both `SpendTracker` persistence and `JsonlExporter` snapshot and transaction files. 8. Add tests that inspect resulting file modes and verify safe behavior for existing files, symlinks, and shared directories. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description promises a full-featured financial observability capability spanning monitoring, budgeting, anomaly detection, and payment-rail integrations. The supplied code chunk only contains a module docstring for an integrations package and mentions a LangChain/LangGraph callback handler. There is no visible implementation of the described financial features or payment integrations in this chunk. Because the actual code shown serves a different, much narrower purpose and does not substantiate the declared functionality, this is a mismatch.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The README advertises a built-in HTTP dashboard and webhook export capability but does not warn that exposing these interfaces can leak transaction history, spending metrics, budget state, or other operationally sensitive financial telemetry. In an agent-finance observability tool, this omission can lead users to deploy the dashboard or exporters on reachable interfaces without authentication, network restrictions, or data minimization.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The documented dashboard endpoints include full metrics, alerts, budget status, and recent transactions, all of which may reveal sensitive business and financial information if the dashboard is exposed. Because the skill is specifically for monitoring agent spending across payment rails, the listed endpoints materially increase the sensitivity of the exposed data and make undocumented open access more dangerous.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill declares no explicit tool scope or permissions despite advertising features that imply network access, file output, and possibly shell execution. In agent environments, missing scope declarations can cause overbroad default privileges, making it easier for the skill to write files, send data externally, or invoke commands without transparent user approval.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The dashboard starts an HTTP server bound to 0.0.0.0 and exposes sensitive financial observability data such as recent transactions, budget status, alerts, and anomaly statistics without any authentication or authorization checks. In the context of an AI financial monitoring tool, this can leak operational and potentially payment-related metadata to anyone with network access, and the wildcard CORS header further increases the chance of unintended cross-origin access from browsers.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This code file defines a built-in exporter that POSTs transaction batches to any configured endpoint, which is a safety-relevant network transmission of user/system data. While the docstring explains usage, it does not warn that enabling this exporter transmits transaction data off-host or advise users to verify the destination and contents.

Missing User Warnings

Medium
Confidence
82% confidence
Finding
This code automatically records every LLM/chat call as a financial transaction and captures metadata including model name, token counts, and latency. Although the module docstrings describe the behavior, there is no runtime disclosure, confirmation, or visible user-facing warning for this automatic tracking, which affects observability data about user/system activity.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
The tracker pushes every recorded transaction to all registered exporters via `exp.export_tx(tx)`, which may transmit transaction details to external systems. Although the class docstring shows a webhook exporter example, this code file does not include a confirmation prompt or explicit user-facing warning about that outbound data sharing behavior.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
class TestCLI(unittest.TestCase):
    def test_version(self):
        import subprocess
        result = subprocess.run(
            ["python", "-m", "agentfinobs", "version"],
            capture_output=True, text=True,
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def test_demo(self):
        import subprocess
        result = subprocess.run(
            ["python", "-m", "agentfinobs", "demo"],
            capture_output=True, text=True,
            timeout=10,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
f.write(json.dumps(tx) + "\n")

            import subprocess
            result = subprocess.run(
                ["python", "-m", "agentfinobs", "status", path, "--budget", "100"],
                capture_output=True, text=True,
            )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def test_status_missing_file(self):
        import subprocess
        result = subprocess.run(
            ["python", "-m", "agentfinobs", "status", "/nonexistent.jsonl"],
            capture_output=True, text=True,
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Low
Confidence
79% confidence
Finding
When `persist_dir` is provided, the tracker creates a directory and appends transaction records to a JSONL file on disk. While the module docstring mentions periodic persistence, the code path performing the write lacks a direct user-facing disclosure near the file creation and append operations.

Unverifiable Dependency: setuptools has 10 known advisory(ies) (CVE-2013-1633 (Setuptools vulnerable to Man-in-the-middle attacks); CVE-2025-47273 (setuptools has a path traversal vulnerability in PackageIndex.download that lead); CVE-2024-6345 (setuptools vulnerable to Command Injection via package URL) +7 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unverifiable Dependency: wheel has 4 known advisory(ies) (CVE-2026-24049 (Wheel Affected by Arbitrary File Permission Modification via Path Traversal in w); CVE-2022-40898 (pypa/wheel vulnerable to Regular Expression denial of service (ReDoS)); CVE-2022-40898 (An issue discovered in Python Packaging Authority (PyPA) Wheel 0.37.1 and earlie) +1 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unverifiable Dependency: httpx has 2 known advisory(ies) (CVE-2021-41945 (Improper Input Validation in httpx); CVE-2021-41945 (Encode OSS httpx <=1.0.0.beta0 is affected by improper input validation in `http)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unverifiable Dependency: pytest has 2 known advisory(ies) (CVE-2025-71176 (pytest has vulnerable tmpdir handling); CVE-2025-71176 (pytest has vulnerable tmpdir handling)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Missing User Warnings

Low
Confidence
78% confidence
Finding
This test invokes JsonlExporter to write transaction data to a filesystem path, but the file contains no comment, docstring, or other disclosure near the operation indicating that transaction data is persisted to disk. Under the code-file criteria, file writes can warrant a finding when there is no visible warning or explanatory disclosure in the code being reviewed.

Static analysis

No suspicious patterns detected.