Back to skill

Security audit

second-hand-trading

Security checks for vulnerabilities and agentic risk

Overview

The skill fits its trading purpose, but it needs review because it sends account tokens and trading content over plain HTTP and stores sensitive local history and credentials.

Review this before installing. Use it only if you trust the AgentNego service and can accept that messages, personas, contract terms, bearer tokens, and relay tokens may be sent to the configured endpoint. Prefer an HTTPS endpoint, avoid sharing personal or payment details, keep the skill directory private, clear memory when no longer needed, and rotate credentials if it has already been used over HTTP or from a shared machine.

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
scripts/core.py:10
Finding
Authentication Credentials and Trading Data Are Transmitted over Plaintext HTTP<![CDATA[ ## Vulnerability Details **File Location**: `scripts/core.py:10-11, 115-131, 145-146, 173-178, 311-329`; `scripts/cli.py:11`; `SKILL.md:137, 238` **Vulnerability Type**: Plaintext transmission of sensitive information and insufficient endpoint trust enforcement **Risk Level**: High ### Vulnerable Code ```python class PlazaClientCore: """Core functionality for the Plaza client""" def __init__(self, api_base_url: str = "http://115.190.255.55:80/api/v1", config_file: str = None): self.api_base_url = api_base_url.rstrip("/") ``` ```python def _call_api(self, endpoint: str, method: str = "POST", data: Optional[dict] = None, headers: Optional[dict] = None) -> dict: url = f"{self.api_base_url}/{endpoint}" request_headers = {"Content-Type": "application/json"} if headers: request_headers.update(headers) if self.agent_token: request_headers["Authorization"] = f"Bearer {self.agent_token}" try: if method == "POST": response = requests.post( url, json=data, headers=request_headers, timeout=10 ) elif method == "GET": response = requests.get( url, params=data, headers=request_headers, timeout=10 ) else: raise ValueError(f"Unsupported HTTP method: {method}") response.raise_for_status() return response.json() ``` ```python def enter_plaza(self, agent_name: str, owner_persona: str, target_persona: str, metadata: Optional[dict] = None) -> dict: """Enter the plaza and get agent credentials.""" payload = { "agent_name": agent_name, "owner_persona": owner_persona, "target_persona": target_persona, "metadata": metadata or {} } response = self._call_api("enter_plaza", data=payload) ``` ```python parser.add_argument( "--api-base-url", default="http://115.190.255.55:80/api/v1", help="B ...[truncated 2813 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the HTTP endpoint with an authenticated HTTPS hostname: ```python DEFAULT_API_BASE_URL = "https://api.agentnego.example/api/v1" ``` 2. Reject non-HTTPS endpoints before any credentials are loaded or transmitted: ```python from urllib.parse import urlparse parsed = urlparse(api_base_url) if parsed.scheme != "https": raise ValueError("The API endpoint must use HTTPS") ``` 3. Maintain an allowlist of trusted API hostnames. Do not rely on a bare IP unless certificate identity is securely configured. 4. Bind stored credentials to the exact trusted origin that issued them. Refuse to attach a stored token when the scheme, hostname, or port changes. 5. Never disable TLS certificate verification. Consider certificate or public-key pinning where the deployment model supports secure pin rotation. 6. Separate initial registration from authenticated requests so that existing credentials cannot be sent to a newly supplied endpoint automatically. 7. Avoid placing sensitive data in error messages or logs, and document exactly which owner information is transmitted. 8. Rotate all tokens that may previously have been transmitted over plaintext HTTP after secure transport is deployed. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/memory_logger.py:34
Finding
Complete Messages and Contract Terms Are Retained Indefinitely in Plaintext<![CDATA[ ## Vulnerability Details **File Location**: `scripts/memory_logger.py:16-26, 34-61, 94-120, 160-172`; call sites in `scripts/core.py:173-178, 189-193, 267-280, 297-306, 348-358` **Vulnerability Type**: Excessive plaintext retention of sensitive communications **Risk Level**: Medium ### Vulnerable Code ```python def __init__(self, log_file: str = None): if log_file is None: current_dir = os.path.dirname(os.path.abspath(__file__)) log_dir = os.path.join(current_dir, "..", "memory") os.makedirs(log_dir, exist_ok=True) self.log_file = os.path.join(log_dir, "agent_memory.jsonl") else: self.log_file = log_file self._ensure_log_dir() ``` ```python def log_interaction(self, agent_id: str, other_agent_id: str, interaction_type: str, content: str, topics: Optional[List[str]] = None, keywords: Optional[List[str]] = None, metadata: Optional[Dict[str, Any]] = None): log_entry = { "timestamp": datetime.now().isoformat(), "type": "interaction", "agent_id": agent_id, "other_agent_id": other_agent_id, "interaction_type": interaction_type, "content": content, "topics": topics or [], "keywords": keywords or [], "metadata": metadata or {} } self._write_entry(log_entry) ``` ```python def log_contract(self, agent_id: str, other_agent_id: str, contract_id: str, contract_type: str, terms: str, status: str, metadata: Optional[Dict[str, Any]] = None): log_entry = { "timestamp": datetime.now().isoformat(), "type": "contract", "agent_id": agent_id, "other_agent_id": other_agent_id, "contract_id": contract_id, "contract_type": contract_type, "terms": terms, "status": status, "meta ...[truncated 2751 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make metadata-only logging the default. Do not store complete content or contract terms unless the user explicitly enables it. 2. Store only fields required for summaries, such as event type, timestamp, counterparty pseudonym, and a user-approved short summary. 3. Redact phone numbers, email addresses, physical addresses, payment identifiers, authentication material, and other sensitive fields before persistence. 4. Encrypt sensitive memory using a key held in an OS keyring or external secret manager rather than in the same directory. 5. Create the log atomically with owner-only permissions, such as mode `0600` on POSIX systems. 6. Add configurable time-based and size-based retention, followed by automatic deletion or secure compaction. 7. Separate each agent's records to reduce cross-agent exposure. 8. Provide controls to disable memory logging and to delete specific conversations or contracts. 9. Document what information is retained, where it is stored, and how long it remains available. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/core.py:38
Finding
Credential Ciphertext and Its Decryption Key Are Stored Together<![CDATA[ ## Vulnerability Details **File Location**: `scripts/core.py:38-57, 59-76, 78-104` **Vulnerability Type**: Ineffective local secret protection and insufficient credential-file permissions **Risk Level**: Medium ### Vulnerable Code ```python def _init_encryption(self): """Initialize encryption system automatically without user input.""" config_dir = os.path.dirname(self.config_file) key_file = os.path.join(config_dir, ".config_key") if os.path.exists(key_file): with open(key_file, "rb") as f: self._key = base64.urlsafe_b64decode(f.read()) else: self._key = Fernet.generate_key() with open(key_file, "wb") as f: f.write(base64.urlsafe_b64encode(self._key)) if os.name == 'posix': os.chmod(key_file, 0o600) self._fernet = Fernet(self._key) ``` ```python def _load_config(self): """Load and decrypt agent credentials from the configuration file.""" if os.path.exists(self.config_file): try: with open(self.config_file, "rb") as f: encrypted_data = f.read() decrypted_data = self._fernet.decrypt(encrypted_data) config = json.loads(decrypted_data) self.agent_id = config.get("agent_id") self.agent_token = config.get("agent_token") self.target_agent_ids = config.get("target_agent_ids", []) self.relays = config.get("relays", {}) ``` ```python def _save_config(self): """Encrypt and save agent credentials to the configuration file.""" config = { "agent_id": self.agent_id, "agent_token": self.agent_token, "target_agent_ids": self.target_agent_ids, "relays": self.relays } try: data = json.dumps(config, ensure_ascii=False, indent=2).encode() encrypted_data = self._fernet.encrypt(data) with open(self.config_file, "wb") as f: f.write(encrypted_data) ``` ### Technical Analysis ...[truncated 2017 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store bearer and relay tokens in an operating-system credential store, hardware-backed keystore, or external secret manager. 2. If file encryption is unavoidable, derive the encryption key from a user-held secret or retrieve it from a store outside the project and backup domain. 3. Never store the usable decryption key beside the ciphertext. 4. Create both key and configuration files atomically with owner-only permissions. On POSIX systems, use secure creation flags and mode `0600` rather than applying permissions only afterward. 5. Validate permissions and ownership whenever existing files are loaded; refuse to load credentials from insecure files. 6. Apply equivalent access controls on non-POSIX platforms. 7. Exclude runtime keys, credentials, memory files, and encrypted configurations from source archives, version control, and shared backups. 8. Implement token expiry, rotation, and revocation, and rotate credentials following suspected filesystem disclosure. 9. Minimize persisted state by avoiding storage of target identifiers or relay credentials when they are no longer needed. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
Findings (24)

Hidden Instructions

High
Category
Prompt Injection
Content
---
name: second-hand-trading
description: A skill for an AI agent to represent its owner in AgentNego's Hub Plaza for second-hand trading, including initial communication, price inquiries, information verification, risk screening, and preliminary negotiation, with the ability to establish secure relay connections at appropriate times.
---
Confidence
60% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Memory Manipulation

High
Category
Memory Poisoning
Content
parser_memory_get_agents = memory_subparsers.add_parser("get-agents", help="Get all interacted agents")
    parser_memory_get_agents.add_argument("agent_id", help="Agent ID")

    parser_memory_clear = memory_subparsers.add_parser("clear", help="Clear memory")
    parser_memory_clear.add_argument("agent_id", help="Agent ID")
    parser_memory_clear.add_argument("--all", action="store_true", help="Clear all memory")
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Memory Manipulation

High
Category
Memory Poisoning
Content
parser_memory_get_agents = memory_subparsers.add_parser("get-agents", help="Get all interacted agents")
    parser_memory_get_agents.add_argument("agent_id", help="Agent ID")

    parser_memory_clear = memory_subparsers.add_parser("clear", help="Clear memory")
    parser_memory_clear.add_argument("agent_id", help="Agent ID")
    parser_memory_clear.add_argument("--all", action="store_true", help="Clear all memory")
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Memory Manipulation

High
Category
Memory Poisoning
Content
parser_memory_get_agents = memory_subparsers.add_parser("get-agents", help="Get all interacted agents")
    parser_memory_get_agents.add_argument("agent_id", help="Agent ID")

    parser_memory_clear = memory_subparsers.add_parser("clear", help="Clear memory")
    parser_memory_clear.add_argument("agent_id", help="Agent ID")
    parser_memory_clear.add_argument("--all", action="store_true", help="Clear all memory")
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Memory Manipulation

High
Category
Memory Poisoning
Content
Clear memory

        Args:
            agent_id: Agent ID for which to clear memory. If None and clear_all=True, all memory will be cleared
            clear_all: Whether to clear all memory
        """
        if os.path.exists(self.log_file):
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Memory Manipulation

High
Category
Memory Poisoning
Content
Clear memory

        Args:
            agent_id: Agent ID for which to clear memory. If None and clear_all=True, all memory will be cleared
            clear_all: Whether to clear all memory
        """
        if os.path.exists(self.log_file):
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill advertises automatic credential storage and persistent interaction logging without warning the user that authentication material and trading data will be written locally. In an agent context, silent persistence of tokens and conversation history increases the risk of credential theft, unauthorized reuse, and unintended retention of sensitive user or counterparty information.

Ssd 3

Medium
Confidence
91% confidence
Finding
The skill mandates comprehensive logging of messages, events, errors, and contracts into JSONL memory files, which is likely to capture sensitive trading content in plain language. Even if the skill says not to exchange personal information, real conversations may still include identifiers, negotiation details, or other private data that become persistently accessible on disk.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The workflow tells the agent to save `agent_id` and `agent_token` to a local configuration file without warning about the sensitivity of those values or the consequences of local persistence. If the local environment is shared, backed up, or otherwise accessible, those credentials could be reused to impersonate the agent or access communications.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The credential management section normalizes persistent storage and automatic updating of authentication data but omits any security warning, retention policy, or guidance on protecting those files. This can lead users to leave long-lived tokens on disk without understanding the exposure, especially when relay state updates expand the scope of retained session data.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The skill stores agent credentials and relay tokens locally using an encryption key that is also generated and stored on the same host in a predictable adjacent file (.config_key). That means compromise of the local filesystem, backups, or the skill directory can expose both the ciphertext and the key, reducing the protection to simple obfuscation and enabling unauthorized reuse of agent identities and relay access.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
This skill provides broad capabilities to register with, authenticate to, and interact extensively with an external service, including direct messaging, broadcast messaging, relay communications, contract proposals, and blocking. In an untrusted or unknown-purpose skill, this creates a significant exfiltration and command-and-control surface because arbitrary content and metadata can be transmitted to a remote host, and the hardcoded API endpoint points to an external IP over plain HTTP.

External Transmission

Medium
Category
Data Exfiltration
Content
try:
            if method == "POST":
                response = requests.post(url, json=data, headers=request_headers, timeout=10)
            elif method == "GET":
                response = requests.get(url, params=data, headers=request_headers, timeout=10)
            else:
Confidence
97% confidence
Finding
The code transmits data to an external service using requests, and the default base URL is an HTTP endpoint rather than HTTPS. Because Authorization bearer tokens, agent identifiers, message contents, relay tokens, and contract data may be sent over plaintext transport, a network attacker can intercept or modify traffic, leading to credential theft, message tampering, impersonation, and session hijacking.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This code persistently writes conversation content, topics, keywords, and arbitrary metadata to disk in a JSONL file without any notice, consent, minimization, retention control, or protection. In an agent skill context, these fields can contain prompts, secrets, personal data, or sensitive operational context, creating privacy and data-exposure risk if the file is accessed by other components or users.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The logger stores agent persona, contract terms, status, names, tags, and free-form metadata directly to disk. These records may contain confidential business logic, relationship details, or sensitive instructions, and the skill provides no disclosure, classification, or access control around that storage.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The clear_memory method can erase all stored memory by truncating the log file, and can also rewrite the file to remove selected entries. This is a destructive operation with no confirmation prompt or prominent warning in the method documentation/comments indicating the permanence of the action.

Intent-Code Divergence

Low
Confidence
96% confidence
Finding
The file states at L125 and L131 that credentials are managed through an encrypted file `agent_config.enc`, but later at L187 and L277-L280 it says credentials are automatically saved to `agent_config.json`. These statements actively contradict each other about a security-relevant behavior: whether credential storage is encrypted or plaintext JSON.

Intent-Code Divergence

Low
Confidence
91% confidence
Finding
In the workflow section, the skill says plaza credentials will be automatically saved to `agent_config.json`, while the scripts overview names `agent_config.enc` as the credential store. This is an intent/documentation divergence because the operational instructions tell the user to expect a different artifact than the one described elsewhere.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
cryptography>=42.0.0
Confidence
95% confidence
Finding
The dependency is specified with a lower-bound only (requests>=2.31.0), which allows future installs to resolve to different versions over time. This weakens reproducibility and can inadvertently introduce vulnerable or incompatible releases from the supply chain, especially when the package also has known security advisories across versions.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
84% confidence
Finding
Requests has multiple known advisories, and because the manifest does not pin the resolved version, it is impossible to determine from this file alone whether deployment will select an affected release. The danger is primarily uncertainty and exposure to vulnerable versions through non-reproducible dependency resolution rather than proof that a vulnerable version is definitely installed.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
cryptography>=42.0.0
Confidence
95% confidence
Finding
The dependency is specified as cryptography>=42.0.0 instead of an exact version, so installations are not deterministic and may pull in later releases without review. For security-sensitive libraries like cryptography, this increases supply-chain risk and makes it hard to verify whether a deployment is affected by known issues.

Unverifiable Dependency: cryptography has 16 known advisory(ies) (GHSA-39hc-v87j-747x (Vulnerable OpenSSL included in cryptography wheels); CVE-2023-50782 (Python Cryptography package vulnerable to Bleichenbacher timing oracle attack); GHSA-537c-gmf6-5ccf (Vulnerable OpenSSL included in cryptography wheels) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
84% confidence
Finding
Cryptography is a high-value security dependency with known advisories, but the unpinned requirement prevents verification of the actual installed version. This creates supply-chain uncertainty and may allow an affected version to be installed in some environments, which is particularly concerning for cryptographic components.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The broadcast command hard-codes Chinese default topics and keywords ("二手交易", "二手", "交易") as the default behavior. For a general-purpose CLI, this imposes a specific language/locale choice on users without any opt-in or documented regional justification, which matches the language/locale policy concern.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
This code file contains developer-facing natural language in Chinese comments such as '确保配置文件路径是相对于技能目录的', with similar Chinese-only comments elsewhere. Under the stated policy, forcing a specific language without user opt-in is a natural-language locale violation when no alternative or opt-in is provided.

Static analysis

No suspicious patterns detected.