Back to skill

Security audit

Bot-to-Bot Arbitrage Framework: Multi-Bot Coordination with Trust Verification

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed educational arbitrage guide, but its sample framework handles financial settlement and sensitive trading signals with material confidentiality and verification gaps.

Review this skill carefully before installing or using it for real trading. Treat the code as an educational draft only: add recipient-scoped or encrypted event delivery, verify reporter identities and signatures before settlement, avoid raw exported signing keys, and test only in paper-trading or sandbox environments until the financial controls are independently reviewed.

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:884
Finding

Unsigned and Unauthorized Fill Reports Can Be Accepted as Verified Execution

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 884-927
Vulnerability Type: Missing cryptographic verification and authorization
Risk Level: High

Vulnerable Code

python
for event in events.get("events", []):
    report = event["payload"]
    exchange = report["exchange"]

    if exchange in verified_legs:
        continue  # already verified this leg

    # Find the expected leg for this exchange
    expected = next(
        (l for l in expected_legs if l["exchange"] == exchange),
        None
    )
    if not expected:
        continue

    # Validate fill against expectations
    validation = self._validate_fill(report, expected)
    if validation["valid"]:
        verified_legs[exchange] = {
            "report": report,
            "validation": validation
        }
    else:
        failures.append({
            "exchange": exchange,
            "reason": validation["reason"],
            "report": report
        })

if len(verified_legs) < len(expected_legs):
    time.sleep(0.5)

all_verified = len(verified_legs) == len(expected_legs)

verification = {
    "opportunity_id": opportunity_id,
    "all_verified": all_verified,
    "verified_legs": verified_legs,
    "failures": failures,
    "missing_legs": [
        l["exchange"] for l in expected_legs
        if l["exchange"] not in verified_legs
    ],
    "verified_at": datetime.utcnow().isoformat()
}

Technical Analysis

The verifier accepts an event based only on its claimed exchange and whether its reported price and quantity fall within expected tolerances. It does not:

  • Verify the Ed25519 signature embedded in the report.
  • Confirm that reporter_id is the bot authorized by the negotiated deal.
  • Bind the reporting agent to the claimed exchange.
  • Confirm that the event originated from the authenticated reporting agent.
  • Reject replayed fill reports or d ...[truncated 1707 chars]
Remediation
View remediation

Remediation Suggestions

  • Obtain the authorized bot identity and public key from the negotiated deal rather than trusting fields supplied by the report.
  • Reconstruct the exact canonical unsigned payload and verify its Ed25519 signature before processing the report.
  • Require reporter_id to match the bot assigned to the expected exchange and opportunity.
  • Verify that the authenticated event publisher matches reporter_id.
  • Bind each expected leg to a unique deal ID, bot ID, exchange, order nonce, and opportunity ID.
  • Maintain a replay cache for signatures, nonces, event IDs, and exchange order IDs.
  • Reject malformed, duplicate, expired, or out-of-window reports.
  • Where supported, validate exchange-generated execution receipts through an independent exchange API or signed exchange attestation.
  • Do not build settlement evidence or release escrow unless every leg passes authentication, authorization, freshness, and semantic validation.
  • Add negative tests covering forged signatures, mismatched reporters, replayed reports, and reports from unassigned exchanges.

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:347
Finding

Execution Signals Are Published Without Explicit Recipient Restrictions

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 347-351
Vulnerability Type: Sensitive trading-data exposure through insufficient event access control
Risk Level: High

Vulnerable Code

python
execute("publish_event", {
    "agent_id": self.agent_id,
    "event_type": "arbitrage.execution_signal",
    "payload": signal
})

The transmitted signal contains sensitive execution information:

python
signal = {
    "opportunity_id": opportunity_id,
    "exchange": leg["exchange"],
    "side": leg["side"],
    "symbol": leg["symbol"],
    "quantity": leg["quantity"],
    "limit_price": leg["limit_price"],
    "execution_deadline_ms": leg.get("deadline_ms", 500),
    "escrow_id": self.pending_escrows.get(opportunity_id),
    "timestamp": datetime.utcnow().isoformat()
}
signal["signature"] = self.sign_payload(signal)

Technical Analysis

The principal dispatch_execution() implementation publishes each order signal without a visibility allowlist, recipient identifier, or encrypted payload. The signal discloses the target exchange, side, asset, quantity, price, deadline, active opportunity ID, and escrow ID.

A later section of the guide demonstrates scoped events, but that safeguard is not applied to the main coordinator implementation. If the event service defaults to public, tenant-wide, or otherwise broad visibility, parties other than the selected exchange bot can observe imminent trades.

Signing the signal provides authenticity but no confidentiality. Any event consumer that can read the payload can obtain the trading intent.

Attack Path

  1. An attacker gains legitimate or unintended read access to the event stream.
  2. The coordinator publishes an execution signal without a recipient allowlist.
  3. The attacker extracts the exchange, side, symbol, quantity, limit price, and deadline.
  4. The attacker submits a competing order before the intended ...[truncated 815 chars]
Remediation
View remediation

Remediation Suggestions

  • Require an explicit recipient allowlist for every execution signal, limited to the bot assigned to that specific leg.
  • Make signal dispatch fail closed when recipient scoping is absent or unsupported.
  • Encrypt each payload with authenticated public-key encryption for the selected bot.
  • Include the recipient identity, deal ID, opportunity ID, leg ID, expiration time, and nonce in the signed and encrypted data.
  • Send each bot only its own leg; never disclose the complete arbitrage opportunity.
  • Exclude escrow identifiers unless they are strictly required by the executor.
  • Use short expiration windows and replay protection.
  • Confirm the event service’s default visibility and tenant-isolation behavior instead of assuming privacy.
  • Add integration tests proving that unrelated agents cannot query or subscribe to execution signals.
  • Document metadata and event payloads that are visible to the GreenHelix service operator.

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:1483
Finding

Counterparty Reveal Publishes the Full Opportunity in Plaintext

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 1483-1513
Vulnerability Type: Misleading confidentiality control and plaintext sensitive-data transmission
Risk Level: High

Vulnerable Code

python
def reveal_to_counterparty(
    self,
    opportunity_id: str,
    counterparty_public_key_b64: str
) -> dict:
    """Reveal opportunity details to a specific counterparty.

    In production, this would encrypt the details with the counterparty's
    public key. For this example, we publish a reveal event that references
    the original commitment.
    """
    stored = self.commitments.get(opportunity_id)
    if not stored:
        raise ValueError(f"No commitment found for {opportunity_id}")

    reveal_payload = {
        "opportunity_id": opportunity_id,
        "preimage": stored["preimage"],
        "commitment": stored["commitment"],
        "revealed_to": counterparty_public_key_b64,
        "revealed_at": datetime.utcnow().isoformat()
    }

    # Counterparty can verify: sha256(preimage) == commitment
    execute("publish_event", {
        "agent_id": self.agent_id,
        "event_type": "arbitrage.opportunity_reveal",
        "payload": reveal_payload,
        "signature": self.sign_payload(reveal_payload)
    })

    return reveal_payload

Technical Analysis

The method accepts a counterparty public key but does not use it for encryption. Instead, it places the key in the revealed_to metadata field and publishes the complete commitment preimage as plaintext.

The preimage includes:

  • Symbol.
  • Buy and sell exchanges.
  • Buy and sell prices.
  • Quantity.
  • Opportunity identifier.
  • Commitment nonce.

The publication call also omits an explicit event visibility restriction. Therefore, the implementation does not provide the confidentiality implied by reveal_to_counterparty() or by the surrounding MEV-protection discussion.

The co ...[truncated 1317 chars]

Remediation
View remediation

Remediation Suggestions

  • Do not publish the plaintext preimage.
  • Use an authenticated key-agreement and encryption scheme appropriate to the counterparty’s registered encryption key.
  • Do not assume an Ed25519 signing key can directly serve as an encryption key; register and validate a suitable encryption key or use a documented conversion protocol with expert review.
  • Encrypt the preimage locally and publish only ciphertext, nonce, algorithm identifiers, recipient ID, and necessary authenticated metadata.
  • Apply an explicit recipient allowlist in addition to encryption.
  • Authenticate the counterparty key against its registered agent identity and negotiated deal.
  • Include deal ID, recipient ID, expiration, and opportunity ID as authenticated associated data.
  • Rotate or expire reveals rapidly and reject replayed ciphertexts.
  • Rename or remove the method until genuine recipient confidentiality is implemented.
  • Add tests demonstrating that unauthorized subscribers and the event transport cannot recover plaintext opportunity details.

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:427
Finding

Coordinator Private Key Is Exported as an Unencrypted Base64 String

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 427-440
Vulnerability Type: Unsafe cryptographic key handling
Risk Level: Medium

Vulnerable Code

python
private_key = Ed25519PrivateKey.generate()
public_key = private_key.public_key()

private_bytes = private_key.private_bytes(
    encoding=serialization.Encoding.Raw,
    format=serialization.PrivateFormat.Raw,
    encryption_algorithm=serialization.NoEncryption()
)
public_bytes = public_key.public_bytes(
    encoding=serialization.Encoding.Raw,
    format=serialization.PublicFormat.Raw
)

COORDINATOR_PRIVATE_KEY = base64.b64encode(private_bytes).decode()

Technical Analysis

The coordinator private key is explicitly exported in raw form with NoEncryption() and converted into an ordinary immutable Python string. Base64 is an encoding, not encryption, and offers no protection against disclosure.

The example retains the key in several runtime objects: the cryptographic key object, private_bytes, and COORDINATOR_PRIVATE_KEY. A string is particularly prone to accidental exposure through debug output, exception reporting, configuration serialization, process inspection, notebook state, or logging.

The audited code does not print or transmit COORDINATOR_PRIVATE_KEY. Therefore, the pre-scan warning about direct key exfiltration is not confirmed. Likewise, base64.b64decode() is used to reconstruct an Ed25519 key object and does not execute decoded code. The confirmed issue is unsafe secret representation and lifecycle management.

Attack Path

  1. The example generates and exports the raw private key without encryption.
  2. The key is retained as a reusable base64 string and passed among several classes.
  3. Application logging, debugging, state serialization, a crash report, or runtime inspection exposes the string.
  4. An attacker decodes the base64 value to recover the raw Ed25519 private key.
  5. The attacker sign ...[truncated 782 chars]
Remediation
View remediation

Remediation Suggestions

  • Keep the private key in a non-exportable HSM, KMS, operating-system key store, or dedicated secrets manager where possible.
  • If export is unavoidable, store it in an encrypted private-key format protected by a separately managed secret.
  • Avoid global base64 private-key strings and minimize the number of in-memory copies.
  • Load AGENT_SIGNING_KEY through a secure secret-injection mechanism rather than embedding or serializing it in application state.
  • Pass a signer interface or protected key object to components instead of raw private-key material.
  • Never include private keys in logs, exceptions, metrics, event payloads, notebooks, or compliance reports.
  • Restrict process and secret-store access to the minimum runtime identity.
  • Implement key rotation and immediate revocation procedures.
  • Zero mutable key buffers where the language and cryptographic library permit it.
  • Add automated secret-scanning and logging tests to prevent accidental output of key values.
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 (9)

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

md
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization

API_BASE = "https://api.greenhelix.net/v1"
API_KEY = "your-api-key"

session = requests.Session()

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

md
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization

API_BASE = "https://api.greenhelix.net/v1"
API_KEY = "your-api-key"

session = requests.Session()

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The coordinator documentation says each bot should receive only its own leg, but dispatch_execution publishes raw execution signals without any visibility scoping. In a multi-bot arbitrage setting, exposing leg details broadly defeats the stated information-barrier design and can leak actionable trade intent that enables copy-trading or front-running.

Content

No source excerpt is available for this finding.

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

}

text

The equivalent curl for bot discovery:

```bash
# Discover Binance specialist bots

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

print(f"Failures: {verification['failures']}")

text

The curl equivalent for building evidence:

```bash
# Build claim chain for execution proof

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

}

text

### Settlement via curl

```bash
# Release escrow after verified execution

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The guide claims reveals should be private or encrypted, but the sample reveal_to_counterparty implementation publishes the full opportunity preimage to the event bus. That leaks both legs, prices, quantity, and timing data, enabling counterparties or observers to reconstruct and potentially front-run the arbitrage despite the surrounding MEV-protection claims.

Content

No source excerpt is available for this finding.

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

text

```bash
curl -X POST https://sandbox.greenhelix.net/v1 \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{

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

text

```bash
curl -X POST https://sandbox.greenhelix.net/v1 \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{