Back to skill

Security audit

Agent Onchain Watch

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its on-chain monitoring purpose, but it can silently return fake blockchain results and mislabel chain data, so it should be reviewed before use.

Review before installing. Do not rely on this for real wallet, compliance, or incident monitoring until the mock fallback is removed or explicitly labeled, unsupported chains are rejected or correctly mapped, outbound LLM data sharing is documented, and dependencies are audited.

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
src/fetcher.ts:23
Finding
Fabricated On-Chain Data Returned as Genuine Results When the Etherscan Key Is Missing## Vulnerability Details **File Location**: `src/fetcher.ts:23-25, 62, 78-103, 126-139` **Vulnerability Type**: Silent production fallback to fabricated financial data **Risk Level**: High **Complete Code Snippet**: ```ts async function callEtherscan(params: Record<string, string>): Promise<unknown> { // API 키 없을 경우 Mock 반환 (테스트용) if (!process.env.ETHERSCAN_API_KEY) { console.warn('[Etherscan] API 키 없음. Mock 데이터 반환.'); return null; // Mock 데이터는 개별 함수에서 처리하거나 여기서 분기 가능 } ``` ```ts export async function fetchBalance(address: string): Promise<number | null> { console.log(`[Fetcher] 잔액 조회: ${address}`); if (!process.env.ETHERSCAN_API_KEY) return 1.2345; // Mock Balance } ``` ```ts export async function fetchTransactions(address: string): Promise<TxRecord[]> { console.log(`[Fetcher] 트랜잭션 조회: ${address}`); if (!process.env.ETHERSCAN_API_KEY) { // Mock Transactions return [ { hash: '0xmockhash1', from: '0xSender', to: address, value_eth: 5.0, timestamp: new Date().toISOString(), type: 'in' }, { hash: '0xmockhash2', from: address, to: '0xReceiver', value_eth: 12.5, timestamp: new Date(Date.now() - 60000).toISOString(), type: 'out' } ]; } ``` ```ts export async function fetchTokenTransfers(address: string): Promise<TokenTransferRecord[]> { console.log(`[Fetcher] 토큰 전송 조회: ${address}`); if (!process.env.ETHERSCAN_API_KEY) { // Mock Token Transfers return [ { token_name: 'USDC', token_symbol: 'USDC', from: '0xSender', to: address, value: '1000000000', timestamp: new Date().toISOString() } ]; } } ``` ### Technical Analysis The production data-access functions silently return fixed ...[truncated 1941 chars]
Remediation
## Remediation Suggestions - Fail closed when `ETHERSCAN_API_KEY` is unavailable and return an explicit configuration error. - Remove mock responses from production data-access functions. - Place mock implementations in a separate test adapter that can only be selected through an explicit test configuration such as `NODE_ENV === "test"`. - Add a startup configuration check so the process cannot advertise readiness without required credentials. - If demonstration mode is required, require an explicit opt-in flag and include an unambiguous `data_source: "mock"` marker in every response. - Add automated tests verifying that missing production credentials result in an error rather than a successful report.

T09 · Insecure Skill Coding Practices

Warning
Location
src/handler.ts:25
Finding
Caller-Supplied Chain Is Echoed While All Queries Are Hard-Coded to Ethereum Mainnet## Vulnerability Details **File Location**: `src/handler.ts:25-28`; `src/fetcher.ts:28-30` **Vulnerability Type**: Insufficient input validation and cross-chain result mislabeling **Risk Level**: Medium **Complete Code Snippet**: ```ts const chain = input.chain ?? 'ethereum'; const eventTypes = input.event_types ?? ['tx', 'token_transfer']; console.log(`[Handler] 시작 | address: ${address} | chain: ${chain}`); ``` ```ts const url = new URL('https://api.etherscan.io/v2/api'); url.searchParams.set('chainid', '1'); // Ethereum Mainnet url.searchParams.set('apikey', process.env.ETHERSCAN_API_KEY!); ``` The supplied value is subsequently returned unchanged: ```ts const output: OnchainWatchOutput = { address, chain, balance_eth: balance, transactions: txList, token_transfers: tokenList, risk_flags: riskFlags, summary, }; ``` ### Technical Analysis The handler accepts an arbitrary `chain` value and preserves it in the output, but the Etherscan client always uses `chainid=1`, corresponding to Ethereum mainnet. No allowlist, normalization, or consistency check links the requested chain to the chain that is actually queried. Ethereum-compatible networks share the same address format, so the address validator does not prevent this mismatch. Consequently, valid Ethereum-mainnet data can be labeled as belonging to another requested network. This is an output-integrity and validation flaw rather than a privilege-escalation issue. It is exploitable through the normal job payload without authentication bypass or control over the Etherscan endpoint. ### Attack Path 1. A caller submits a valid 42-character hexadecimal address and a non-Ethereum chain name. 2. The handler accepts and records the supplied chain because no supported-chain validation is performed. 3. The fetcher sends all balance and transaction requests with `chainid=1`. 4. Ethereum-mainnet records are returned and analyzed ...[truncated 593 chars]
Remediation
## Remediation Suggestions - If only Ethereum is supported, reject any normalized chain value other than `ethereum`. - If multiple networks are intended, maintain an explicit allowlist mapping each supported chain name to its authoritative chain ID and endpoint. - Pass the validated chain ID into every fetch operation instead of hard-coding `1`. - Return the normalized chain that was actually queried, not the unvalidated caller-supplied value. - Add tests for unsupported chains, aliases, casing, and consistency between requested chain, queried chain ID, and response metadata.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (29)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Providing only a generic LLM-call interface while advertising specialized on-chain monitoring is a deceptive capability mismatch. This is especially risky in a security-analysis context because users may act on unsupported summaries or alerts, and may disclose addresses, incidents, or investigative notes to external services unintentionally.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
Providing only a generic LLM-call interface while advertising specialized on-chain monitoring is a deceptive capability mismatch. This is especially risky in a security-analysis context because users may act on unsupported summaries or alerts, and may disclose addresses, incidents, or investigative notes to external services unintentionally.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Providing only a generic LLM-call interface while advertising specialized on-chain monitoring is a deceptive capability mismatch. This is especially risky in a security-analysis context because users may act on unsupported summaries or alerts, and may disclose addresses, incidents, or investigative notes to external services unintentionally.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Providing only a generic LLM-call interface while advertising specialized on-chain monitoring is a deceptive capability mismatch. This is especially risky in a security-analysis context because users may act on unsupported summaries or alerts, and may disclose addresses, incidents, or investigative notes to external services unintentionally.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Providing only a generic LLM-call interface while advertising specialized on-chain monitoring is a deceptive capability mismatch. This is especially risky in a security-analysis context because users may act on unsupported summaries or alerts, and may disclose addresses, incidents, or investigative notes to external services unintentionally.

Known Vulnerable Dependency: form-data==4.0.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
93% confidence
Finding
The lockfile includes form-data 4.0.5, which is flagged with a high-severity advisory for CRLF injection via unescaped multipart field names. Because this package is pulled in transitively through @types/node-fetch, the issue is present in the dependency graph even though this file alone does not prove active exploitation; if any runtime code constructs multipart requests with attacker-controlled field names, it could enable request smuggling or header/body manipulation against downstream services.

Credential Access

High
Category
Privilege Escalation
Content
import * as path from 'path';
import { TxRecord, TokenTransferRecord } from './types';

// .env 직접 로드
dotenv.config({ path: path.resolve(__dirname, '../.env') });

console.log('[Fetcher] API Key Check:', process.env.ETHERSCAN_API_KEY ? 'FOUND' : 'MISSING');
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import * as path from 'path';
import { TxRecord, TokenTransferRecord } from './types';

// .env 직접 로드
dotenv.config({ path: path.resolve(__dirname, '../.env') });

console.log('[Fetcher] API Key Check:', process.env.ETHERSCAN_API_KEY ? 'FOUND' : 'MISSING');
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { TxRecord, TokenTransferRecord } from './types';

// .env 직접 로드
dotenv.config({ path: path.resolve(__dirname, '../.env') });

console.log('[Fetcher] API Key Check:', process.env.ETHERSCAN_API_KEY ? 'FOUND' : 'MISSING');
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
import { TxRecord, TokenTransferRecord } from './types';

// .env 직접 로드
dotenv.config({ path: path.resolve(__dirname, '../.env') });

console.log('[Fetcher] API Key Check:', process.env.ETHERSCAN_API_KEY ? 'FOUND' : 'MISSING');
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The primary skill description is written only in Korean, which imposes a specific language on users without any opt-in or alternative language option. This matches the policy category for language or locale constraints that are not documented as optional or justified.

Lp3

Medium
Category
MCP Least Privilege
Confidence
73% confidence
Finding
The skill declares network and environment-backed capabilities implicitly but does not scope or disclose them via explicit permissions or allowed-tools metadata. This weakens reviewability and policy enforcement, making it easier for a skill to access secrets or make outbound requests beyond what users expect.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The system prompt explicitly instructs the model to produce the summary in Korean. Under the policy rule, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is clearly justified, which is not indicated here.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
This TypeScript file contains natural-language comments and user-visible console messages entirely in Korean, including operational status and error output. Under the policy, forcing a specific language without user opt-in is a locale/language policy violation unless the constraint is clearly justified, which it is not here.

External Transmission

Medium
Category
Data Exfiltration
Content
return null; // Mock 데이터는 개별 함수에서 처리하거나 여기서 분기 가능
  }

  const url = new URL('https://api.etherscan.io/v2/api');
  url.searchParams.set('chainid', '1'); // Ethereum Mainnet
  url.searchParams.set('apikey', process.env.ETHERSCAN_API_KEY!);
  for (const [k, v] of Object.entries(params)) {
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This TypeScript file contains natural-language comments and error messages exclusively in Korean, including the returned user-facing validation errors. The policy for this category flags language or locale constraints when a skill forces a specific language without user opt-in, and this file provides no indication of a language choice or region-specific justification.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This file contains natural-language comments and console messages exclusively in Korean, including operational status outputs shown to users or operators. The provided policy requires flagging language or locale constraints when the skill forces a specific language without opt-in, and no alternative language choice or justification is present here.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The adapter sends the full combined system prompt and user message to Google's external Gemini API, which can expose sensitive wallet, contract, or user-provided monitoring data to a third party without any visible user consent or disclosure in this code path. In an on-chain monitoring skill, prompts may contain addresses, internal heuristics, or operational context, so silent transmission increases privacy, compliance, and data-handling risk.

External Transmission

Medium
Category
Data Exfiltration
Content
}
    this.client = new OpenAI({
      apiKey: config.apiKey,
      baseURL: 'https://api.groq.com/openai/v1',
    });
    this.model = config.model || 'llama-3.3-70b-versatile';
  }
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
The manifest description and usage text are presented in Korean only, which imposes a specific language on users without indicating any opt-in or alternative locale support. The policy explicitly calls for flagging language or locale constraints when they are forced without user choice.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
The manifest description is written only in Korean ("지갑 및 컨트랙트 온체인 활동 모니터링 및 요약 에이전트"), which signals a fixed language choice in the skill metadata. Under the policy for natural-language violations, a language constraint should either offer user opt-in/choice or be clearly documented as region-justified.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"author": "KimCode",
  "license": "ISC",
  "dependencies": {
    "@anthropic-ai/sdk": "^0.33.1",
    "dotenv": "^16.4.7",
    "openai": "^6.27.0"
  },
Confidence
81% confidence
Finding
Using a caret range for @anthropic-ai/sdk allows automatic adoption of future minor/patch releases, which can introduce supply-chain risk if an upstream release is compromised or contains a breaking security regression. In an agent that processes on-chain monitoring data and likely handles API credentials, unexpected dependency changes can affect runtime behavior and trust boundaries.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"license": "ISC",
  "dependencies": {
    "@anthropic-ai/sdk": "^0.33.1",
    "dotenv": "^16.4.7",
    "openai": "^6.27.0"
  },
  "devDependencies": {
Confidence
80% confidence
Finding
Using a caret range for dotenv permits unreviewed upstream updates during install, which is a classic software supply-chain exposure. While the direct risk is low, this package influences environment variable handling, so a compromised release could affect secret loading or application startup behavior.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
    "@anthropic-ai/sdk": "^0.33.1",
    "dotenv": "^16.4.7",
    "openai": "^6.27.0"
  },
  "devDependencies": {
    "@types/node": "^22.10.2",
Confidence
82% confidence
Finding
Using a caret range for openai means future minor/patch versions may be pulled in without review, creating avoidable supply-chain risk. Because this skill likely interacts with external AI APIs and may handle prompts, outputs, and credentials, an unvetted SDK update could alter request handling or introduce insecure behavior.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"openai": "^6.27.0"
  },
  "devDependencies": {
    "@types/node": "^22.10.2",
    "ts-node": "^10.9.2",
    "typescript": "^5.7.2"
  }
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
src/fetcher.ts:9