Back to skill

Security audit

SafeLink

Security checks for vulnerabilities and agentic risk

Overview

SafeLink has a coherent payments-and-agent-hiring purpose, but the reviewed artifacts expose high-impact wallet, payment, and deployment risks that need human review before installation.

Review this before installing, especially if you would use real funds or mainnet. Use only testnet funds, avoid storing long-lived secrets in the generated .env, do not run the documented curl-to-bash command, inspect or patch the deployment script, and treat Coinbase wallet export output as sensitive secret material. Do not use the x402 or escrow flows with valuable wallets until recipient/amount validation, allowance limits, and credential handling are hardened.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (8)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/deploy-contracts.ts:6
Finding
Unpinned Remote Script Piped Directly into a Shell<![CDATA[ ## Vulnerability Details **File Location**: `scripts/deploy-contracts.ts:6-6` and `scripts/deploy-contracts.ts:44-50` **Vulnerability Type**: Remote payload retrieval and execution **Risk Level**: Critical ### Vulnerable Code ```ts /** * Prerequisites: * 1. Install Foundry: curl -L https://foundry.paradigm.xyz | bash && foundryup */ ``` ```ts try { execSync("forge --version", { stdio: "pipe" }); } catch { console.error( "Foundry not found.\n" + " Install: curl -L https://foundry.paradigm.xyz | bash && foundryup" ); process.exit(1); } ``` ### Technical Analysis The project instructs operators to download an HTTP response and pass it directly to `bash`. The downloaded content is neither pinned to a reviewed version nor verified using a cryptographic checksum or signature. Although the referenced domain appears associated with Foundry, the effective code executed by this instruction can change after the Skill has been reviewed. Compromise of the upstream server, distribution infrastructure, DNS/TLS trust chain, or installation endpoint could turn this instruction into arbitrary local code execution. The command is presented both as a prerequisite and as the error recovery instruction when Foundry is absent, making it likely that an operator will execute it. ### Attack Path 1. An attacker compromises the remote installation endpoint or its delivery infrastructure. 2. The attacker replaces the response with a malicious shell script. 3. An operator runs the installation command shown by SafeLink. 4. `curl` retrieves the attacker-controlled response. 5. The pipe sends the response directly to `bash`. 6. The malicious script executes with the operator's account privileges and can access project files, wallet credentials, deployment keys, and other local resources. ### Impact Assessment Successful exploitation permits arbitrary command execution with the installing user's privileges. Potential consequences include: - Theft of ...[truncated 303 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove all `curl | bash` installation instructions. - Direct users to a versioned official release or trusted package manager. - If a standalone artifact is required: 1. Download it without executing it. 2. Pin an exact release version. 3. Verify a publisher-provided SHA-256 checksum and, where available, a cryptographic signature. 4. Inspect the downloaded artifact before execution. - Document the expected version and checksum in the repository. - Fail safely when Foundry is unavailable rather than suggesting direct remote-shell execution. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/deploy-contracts.ts:78
Finding
Shell Command Injection Through BASE_RPC_URL<![CDATA[ ## Vulnerability Details **File Location**: `scripts/deploy-contracts.ts:78-86` **Vulnerability Type**: OS command injection **Risk Level**: Critical ### Vulnerable Code The setup wizard accepts the RPC URL without security validation: ```ts const rpc = await ask( ` RPC URL ${env["BASE_RPC_URL"] ? `(Enter to keep: ${env["BASE_RPC_URL"]})` : `(Enter for default: ${defaultRpc})`}: ` ); if (rpc.trim()) env["BASE_RPC_URL"] = rpc.trim(); else if (!env["BASE_RPC_URL"]) env["BASE_RPC_URL"] = defaultRpc; ``` The value is subsequently concatenated into a shell command: ```ts const deployOutput = execSync( [ "forge script script/Deploy.s.sol:Deploy", "--rpc-url", process.env["BASE_RPC_URL"]!, "--broadcast", "--slow", // avoid nonce issues "-vvv", ].join(" "), { cwd: CONTRACTS_DIR, stdio: ["inherit", "pipe", "inherit"] } ).toString(); ``` ### Technical Analysis `execSync()` receives a single command string, so Node.js invokes a shell to interpret it. `BASE_RPC_URL` is inserted without quoting or escaping. Consequently, shell metacharacters such as semicolons, command substitutions, redirections, and pipes are interpreted as shell syntax rather than as part of one RPC argument. A malicious value such as the following would add another command to the deployment operation: ```text https://rpc.example; attacker-command ``` The vulnerable command runs during deployment, when `.env` is expected to contain a funded deployment private key and other sensitive provider credentials. ### Attack Path 1. An attacker persuades the operator to use a malicious “recommended” Base RPC URL, or modifies the local `.env`. 2. The value contains shell metacharacters followed by an attacker-selected command. 3. The operator runs `npm run deploy:contracts`. 4. The script joins the value into a command string. 5. `execSync()` invokes a shell. 6. The shell executes both the intended Forge command and the injected command. 7. The injected command read ...[truncated 480 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace shell-string execution with an argument-safe API: ```ts execFileSync( "forge", [ "script", "script/Deploy.s.sol:Deploy", "--rpc-url", validatedRpcUrl, "--broadcast", "--slow", "-vvv", ], { cwd: CONTRACTS_DIR, stdio: ["inherit", "pipe", "inherit"], shell: false, } ); ``` - Parse the value with `new URL()` before storing or using it. - Permit only expected protocols, preferably `https:`. - Reject embedded credentials, control characters, and unsupported URL schemes. - Consider an explicit allowlist for production RPC providers. - Do not interpolate environment-controlled values into shell command strings. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/payments/x402.ts:124
Finding
Facilitator-Controlled USDC Recipient and Amount Are Signed Without Validation<![CDATA[ ## Vulnerability Details **File Location**: `src/payments/x402.ts:91-97` and `src/payments/x402.ts:124-174` **Vulnerability Type**: Insufficient validation of externally supplied transaction authorization **Risk Level**: Critical ### Vulnerable Code ```ts const requirements = (await requirementsRes.json()) as { payTo: `0x${string}`; amount: string; token: `0x${string}`; nonce: string; deadline: string; chainId: number; }; ``` ```ts const typedData = { domain: { name: "USDC", version: "2", chainId: requirements.chainId, verifyingContract: requirements.token, }, types: { TransferWithAuthorization: [ { name: "from", type: "address" }, { name: "to", type: "address" }, { name: "value", type: "uint256" }, { name: "validAfter", type: "uint256" }, { name: "validBefore", type: "uint256" }, { name: "nonce", type: "bytes32" }, ], }, primaryType: "TransferWithAuthorization" as const, message: { from: wallet.address, to: requirements.payTo, value: BigInt(requirements.amount), validAfter: 0n, validBefore: BigInt(requirements.deadline), nonce: requirements.nonce, }, }; const signature = await wallet.signTypedData(typedData); const paymentRes = await fetch(`${config.X402_FACILITATOR_URL}/pay`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...typedData.message, value: typedData.message.value.toString(), validBefore: typedData.message.validBefore.toString(), signature, network: usdcNetwork, }), signal: AbortSignal.timeout(20_000), }); ``` ### Technical Analysis The facilitator supplies `payTo` and `amount`, and both fields are incorporated directly into an EIP-712 `TransferWithAuthorization` payload. The implementation verifies the chain ID, token address, and deadline, but it does not verify: - `requirements.payTo === req.agentId` - `BigInt(requirements.amount) === req.amount` As ...[truncated 1524 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Validate the entire facilitator response with a strict schema before constructing typed data. - Require exact equality: ```ts if (requirements.payTo.toLowerCase() !== req.agentId.toLowerCase()) { throw new PaymentError("Facilitator returned an unexpected payment recipient"); } const authorizedAmount = BigInt(requirements.amount); if (authorizedAmount !== req.amount) { throw new PaymentError("Facilitator returned an unexpected payment amount"); } ``` - Validate that `payTo` is a nonzero EVM address. - Validate `amount` as a positive integer within configured payment limits. - Validate the nonce as exactly 32 bytes. - Bind the requirements response to the original request using a signed or authenticated request identifier. - Return the validated, actually authorized amount instead of `req.amount`. - Consider requiring a trusted facilitator allowlist rather than accepting an arbitrary configured URL. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/payments/escrow.ts:196
Finding
Unlimited USDC Allowance Exceeds the Required Escrow Privilege<![CDATA[ ## Vulnerability Details **File Location**: `src/payments/escrow.ts:37-38` and `src/payments/escrow.ts:196-209` **Vulnerability Type**: Excessive token approval **Risk Level**: High ### Vulnerable Code ```ts // MaxUint256 for single-approval pattern (avoids repeated approve txs and race conditions) const MAX_UINT256 = 2n ** 256n - 1n; ``` ```ts if (currentAllowance < amountAtomicUSDC) { logger.info({ event: "usdc_approve_needed", currentAllowance: currentAllowance.toString(), required: amountAtomicUSDC.toString(), }); const approveTx = { to: usdcAddress, // HIGH-05: approve MaxUint256 once — eliminates repeated approvals // and prevents the race condition where two concurrent deposits both // read allowance=0, both approve, and one deposit fails. data: buildApproveCalldata(escrowAddress, MAX_UINT256), value: 0n, }; ``` ### Technical Analysis When the existing allowance is insufficient for one deposit, SafeLink grants the escrow contract an allowance of `2^256 - 1`. This is effectively unlimited and remains available after the requested deposit completes. The privilege is broader in amount and duration than necessary. The artifact does not include the escrow contract source, so the audit cannot verify whether the configured contract is immutable, audited, non-upgradeable, or protected from administrative compromise. A mutex prevents concurrent local approval races but does not mitigate the security consequences of a persistent unlimited allowance. ### Attack Path 1. The user performs an escrow deposit. 2. SafeLink grants the configured escrow address an unlimited USDC allowance. 3. The allowance remains after the deposit. 4. The escrow address is malicious, incorrectly configured, compromised, or upgraded to hostile logic. 5. The contract invokes `transferFrom` against the wallet. 6. Current and future USDC can be transferred without another wallet approval. ### Impact Assessment A compromised or h ...[truncated 369 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Approve only `amountAtomicUSDC` for each deposit. - Prefer an atomic permit or permit-and-deposit operation that cannot leave residual allowance. - If token behavior requires allowance changes, reset the allowance to zero after the deposit. - Verify the configured escrow address against: - The expected chain. - Pinned deployed bytecode. - A reviewed contract release. - Require explicit confirmation before approving an unlimited amount if unlimited approval remains an optional feature. - Display the exact spender, token, allowance, chain, and persistence implications to the operator. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate-env.ts:260
Finding
Sensitive Credentials Stored in .env Without Enforced Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate-env.ts:260-270` **Vulnerability Type**: Insecure plaintext credential storage **Risk Level**: High ### Vulnerable Code ```ts const envLines = Object.entries(env) .filter(([k]) => k && !k.startsWith("#")) .map(([k, v]) => `${k}=${v}`); writeFileSync(ENV_FILE, envLines.join("\n") + "\n"); ok(`.env written (${envLines.length} variables)`); ``` The values collected in `env` include sensitive entries such as: ```ts env["ANTHROPIC_API_KEY"] = key.trim(); env["COINBASE_CDP_API_KEY_PRIVATE_KEY"] = priv.trim(); env["PRIVY_APP_SECRET"] = secret.trim(); env["TENDERLY_ACCESS_KEY"] = tenderlyKey.trim(); ``` ### Technical Analysis The setup wizard serializes all credentials into a plaintext `.env` file. `writeFileSync()` is called without an explicit restrictive mode, and the script does not correct the permissions of an existing file. The effective permissions therefore depend on the process umask and prior file state. In environments with permissive defaults or a pre-existing broadly readable file, other local principals may be able to read the credentials. Sensitive prompts also use ordinary `readline.question()`, so entered secrets are displayed on the terminal rather than hidden. ### Attack Path 1. The operator runs `npm run setup`. 2. The operator enters LLM and wallet-provider credentials. 3. The script writes all values to plaintext `.env`. 4. The file inherits permissions that may permit access by other local users or services. 5. Another local principal, backup agent, log collector, or accidentally committed repository obtains the file. 6. The attacker reuses the credentials against the relevant providers. ### Impact Assessment Exposed credentials may permit: - Unauthorized LLM API usage and associated billing. - Privy or Coinbase API access and wallet signing operations, depending on provider policy. - Access to Tenderly or Redis services. - Disclosure of deployment configurat ...[truncated 120 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create and maintain `.env` with mode `0o600`: ```ts writeFileSync(ENV_FILE, envLines.join("\n") + "\n", { mode: 0o600 }); chmodSync(ENV_FILE, 0o600); ``` - Use hidden-input prompting for every secret. - Ensure `.env` is excluded through `.gitignore`. - Refuse to continue if `.env` is a symbolic link. - Prefer an operating-system keychain, managed secret store, or MCP host secret facility over plaintext files. - Separate deploy-only credentials from runtime credentials. - Warn operators not to use the setup wizard in recorded terminals or CI logs. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/wallet/coinbase.ts:168
Finding
Coinbase Wallet Export Material Is Printed and Partially Logged<![CDATA[ ## Vulnerability Details **File Location**: `src/wallet/coinbase.ts:168-181` **Vulnerability Type**: Sensitive wallet material exposure through logs **Risk Level**: High ### Vulnerable Code ```ts if (!existingWalletData) { // First-time wallet creation — export data so user can persist it try { const exported = await walletProvider.exportWallet(); const walletDataStr = JSON.stringify(exported); logger.info({ event: "coinbase_wallet_created", address, note: "IMPORTANT: Save COINBASE_WALLET_DATA to your .env to reuse this wallet", wallet_data_hint: walletDataStr.slice(0, 60) + "...", }); // Print to stderr so operator sees it even in MCP mode process.stderr.write( `\n[SafeLink] New Coinbase wallet created: ${address}\n` + `[SafeLink] Add to your .env:\n` + `COINBASE_WALLET_DATA='${walletDataStr}'\n\n` ); } catch { logger.warn({ event: "coinbase_wallet_export_failed" }); } } ``` ### Technical Analysis The complete wallet export is written to stderr, and the first 60 characters are copied into structured logs. The code explicitly performs this output even in MCP mode. MCP hosts, desktop applications, container platforms, CI systems, process supervisors, and centralized logging agents commonly capture stderr. Consequently, wallet persistence data can be retained in systems with a broader readership and longer retention period than the intended secret store. The exact sensitivity of the exported data depends on Coinbase AgentKit's format, but the application itself treats it as wallet persistence material and instructs the operator to store it in an environment variable. It must therefore be handled as a secret. ### Attack Path 1. A Coinbase wallet is created without existing `COINBASE_WALLET_DATA`. 2. SafeLink exports the wallet data. 3. The complete export is printed to stderr. 4. The MCP host or process supervisor captures stderr. 5. Logs are forwarded to a share ...[truncated 608 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Never print wallet exports to stdout, stderr, or structured logs. - Remove `wallet_data_hint`; partial secrets should also be treated as sensitive. - Persist wallet data directly into an approved secret manager. - If automatic persistence is unavailable, write it to a newly created mode-`0o600` file and display only the file path. - Apply centralized log redaction for wallet data and all provider credentials. - Document the sensitivity and rotation/revocation procedure for exported wallet material. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/deploy-contracts.ts:55
Finding
Unpinned Forge and npm Dependencies Create Mutable Supply-Chain Inputs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/deploy-contracts.ts:55-62` and `package.json:31-57` **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Medium ### Vulnerable Code ```ts console.log("Installing Foundry dependencies..."); try { execSync("forge install foundry-rs/forge-std --no-commit 2>/dev/null || true", { cwd: CONTRACTS_DIR, stdio: "inherit", }); } catch { // Already installed — fine } ``` Security-sensitive npm dependencies also use mutable ranges: ```json "dependencies": { "@coinbase/agentkit": "^0.4.0", "@privy-io/server-auth": "^1.14.0", "viem": "^2.21.54", "x402": "^1.1.0" } ``` No npm lockfile was present in the supplied artifact. ### Technical Analysis `forge install foundry-rs/forge-std --no-commit` retrieves an upstream repository without pinning an exact reviewed commit. The command also suppresses errors using shell redirection and `|| true`, allowing builds to continue in an uncertain dependency state. The npm manifest uses caret ranges, and the supplied artifact contains no lockfile. A future installation can therefore resolve versions different from those reviewed. This is particularly sensitive because the dependencies handle wallets, signatures, blockchain transactions, and payment protocols. This finding does not establish that the named packages are malicious. The issue is that installation inputs are mutable and insufficiently reproducible. ### Attack Path 1. An upstream repository, maintainer account, or later compatible package release is compromised. 2. The operator installs the project after the compromise. 3. Forge resolves the current repository state, or npm resolves a newer compatible dependency. 4. The modified dependency is compiled or executed with project privileges. 5. Malicious dependency code accesses runtime credentials, modifies transaction data, or affects deployment artifacts. ### Impact Assessment A compromised dependency can potentially ...[truncated 302 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin Forge dependencies to reviewed commit hashes or immutable release tags. - Commit an npm lockfile and use `npm ci` for reproducible installation. - Consider exact versions for security-critical wallet and payment libraries. - Remove `2>/dev/null || true`; dependency installation failures should stop the deployment. - Verify package integrity and review dependency provenance. - Enable automated dependency vulnerability and provenance scanning. - Rebuild and retest before accepting any dependency update. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/security/input-gate.ts:190
Finding
DNS Rebinding Gap Between Endpoint Validation and Subsequent Connection<![CDATA[ ## Vulnerability Details **File Location**: `src/security/input-gate.ts:190-225` **Vulnerability Type**: SSRF protection time-of-check/time-of-use weakness **Risk Level**: Medium ### Vulnerable Code ```ts export async function validateEndpointUrlStrict( rawUrl: string, resolver: typeof dns.lookup = dns.lookup ): Promise<URL> { const parsed = validateEndpointUrl(rawUrl); const hostname = parsed.hostname.toLowerCase(); if (isIP(hostname)) { assertHostNotBlocked(hostname); return parsed; } let resolved: Array<{ address: string }> = []; try { const out = await resolver(hostname, { all: true }); resolved = out as Array<{ address: string }>; } catch { throw new ValidationError(`Could not resolve endpoint hostname "${hostname}"`); } if (resolved.length === 0) { throw new ValidationError(`Endpoint hostname "${hostname}" did not resolve to any address`); } for (const entry of resolved) { assertHostNotBlocked(entry.address.toLowerCase()); } return parsed; } ``` ### Technical Analysis The function checks the IP addresses returned by one DNS lookup, but it returns the original hostname-bearing URL. A later HTTP request will normally perform another DNS lookup. An attacker controlling DNS can return a public address during validation and then return a loopback, private, link-local, or metadata-service address during the actual connection. This creates a time-of-check/time-of-use gap. The explicit appearances of `metadata.google.internal` and `169.254.169.254` elsewhere in this file are defensive denylist entries; the code does not directly access those endpoints. The issue is that rebinding may bypass the intended denylist at connection time. ### Attack Path 1. An attacker registers or controls a domain used as an agent endpoint. 2. During `validateEndpointUrlStrict()`, DNS returns a public IP address. 3. Validation succeeds and returns the original URL. 4. The attacker changes the DNS response ...[truncated 747 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Enforce IP validation in the HTTP transport's actual DNS lookup callback, not only before the request. - Validate every address returned at connection time. - Pin the connection to a previously validated IP while preserving the original hostname for TLS certificate verification and the HTTP `Host` header. - Disable redirects or revalidate the destination and DNS result for every redirect. - Block all loopback, private, link-local, multicast, reserved, and documentation ranges for IPv4 and IPv6. - Apply outbound network firewall rules so the process cannot reach cloud metadata and internal management networks. - Prefer an explicit endpoint-domain allowlist in production. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (82)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The declared description presents a production A2A operational skill centered on secure hiring/execution flows and runtime protections. This code chunk instead serves as a DevOps/deployment script for smart contracts. While it does relate loosely to ERC-8004 registry and escrow contracts mentioned in the description, its primary behavior is contract build/test/deploy automation on Base and local config updates. It also uses a deployer private key directly, which differs from the declared emphasis on MPC wallet signing for operations. The advanced runtime capabilities in the description—x402 payments, replay protection, DNS-safe validation, policy gating, and risk scoring—are not implemented or evidenced here. Therefore the description does not accurately represent what this code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description presents a runtime security/transaction skill for agent-to-agent hiring and settlement workflows. The supplied code does not implement escrow, hiring/execution logic, x402 payments, ERC-8004 checks, DNS-safe endpoint validation, MPC signing, or replay protection enforcement. Instead, it is a developer setup utility whose primary purpose is environment configuration: collecting API keys and settings, testing connectivity to external services, warning about mainnet usage, and writing a .env file. While some configured variables relate to the broader system described (e.g., x402 facilitator URL, Redis for replay protection, Privy wallet provider), this code chunk itself only performs setup and validation, not the declared secure A2A workflow behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The code substantially supports part of the description: escrowed settlement, proof-before-settlement via proofCommitment/proofHash, risk-scored transactions, approval/policy gating, and MPC wallet signing. However, the declared description presents a broader production A2A hiring/execution skill with several specific security and payment capabilities that are not implemented in this chunk. The code is narrowly focused on escrow payment lifecycle management for a SafeEscrow contract using USDC, including allowance handling, simulation, risk gating, release/refund, and escrow state reads. There is no evidence here of x402 facilitator payments, ERC-8004 identity/reputation checks, replay protection, DNS-safe endpoint validation, or general A2A hiring/execution workflow behavior. Because those are prominent declared capabilities rather than minor implementation details, the description overstates what this code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The description presents a broad production A2A hiring/execution skill with multiple security and trust features. The supplied code chunk is much narrower: it handles x402 payment requirement retrieval, validates facilitator-returned chainId/token/deadline, signs a USDC TransferWithAuthorization payload via MPC wallet, submits it to a facilitator, and verifies receipts. MPC wallet signing and x402 facilitator payments do align with the description, and there is some defensive validation. However, most of the declared core capabilities are not represented in this code chunk, especially escrow, identity/reputation checks, DNS-safe endpoint validation, policy gating, and risk scoring. Because the declared purpose materially overstates the implemented behavior of the provided code, this is a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The supplied code is narrowly focused on MPC wallet operations: selecting a wallet provider, creating/loading a wallet, sending EVM transactions, signing typed data and messages, and exposing the agent address. While MPC wallet signing is indeed present and aligns with one part of the description, the majority of the declared purpose is not represented in this code chunk. There is no evidence of escrow logic, hiring/execution workflow handling, x402 payments, ERC-8004 identity or reputation validation, DNS-safe endpoint checks, explicit replay-protection mechanisms beyond ordinary transaction nonce usage, policy gating, or risk scoring. Because the declared description presents a much broader secure A2A execution/hiring skill and this code only implements wallet functionality, the description does not accurately represent what this specific code chunk actually does.

YARA rule 'agent_skill_prompt_injection_hidden_instructions': Prompt injection or hidden instructions embedded in AI agent skill text [agent_skills]

High
Category
YARA Match
Content
Base RPC endpoint), ERC8004_REGISTRY_ADDRESS (after deploy), SAFE_ESCROW_ADDRESS (after deploy), X402_FACILITATOR_URL (default: https://x402.org/facilitator)"
  required_env_wallet: "One of: COINBASE_CDP_API_KEY_NAME + COINBASE_CDP_API_KEY_PRIVATE_KEY (Coinbase AgentKit) OR PRIVY_APP_ID + PRIVY_APP_SECRET (Privy MPC). Both are MPC providers — private keys never enter app memory."
  required_env_llm: "ANTHROPIC_API_KEY (when LLM_PROVIDER=anthropic, default) OR LLM_BASE_URL + LLM_API_KEY (when LLM_PROVIDER=openai_compatible)"
  optional_env: "REDIS_URL (multi-instance replay store), TENDERLY_ACCESS_KEY (simulation), BASESCAN_API_KEY (explorer), TASK_AUTH_SHARED_SECRET (>=32 chars, when TASK_AUTH_REQUIRED=true), SIWX_VERIFIER_URL (when SIWX_REQUIRED=true), AUTONOMYS_RPC_URL (memory checkpoints)"
  deploy_only_env: "DEPLOYER_PRIVATE_KEY — used once by scripts/deploy-contracts.ts to deploy on-chain contracts. NOT loaded at MCP runtime. Use a throwaway funded key; discard after deploymen
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Ae1

High
Category
analysis-evasion
Content
runtime_files: "scripts/generate-env.ts writes .env interactively on first setup. scripts/deploy-contracts.ts writes deployed contract addresses back to .env af
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
runtime_files: "scripts/generate-env.ts writes .env interactively on first setup. scripts/deploy-contracts.ts writes deployed contract addresses back to .env af
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Credential Access

High
Category
Privilege Escalation
Content
optional_env: "REDIS_URL (multi-instance replay store), TENDERLY_ACCESS_KEY (simulation), BASESCAN_API_KEY (explorer), TASK_AUTH_SHARED_SECRET (>=32 chars, when TASK_AUTH_REQUIRED=true), SIWX_VERIFIER_URL (when SIWX_REQUIRED=true), AUTONOMYS_RPC_URL (memory checkpoints)"
  deploy_only_env: "DEPLOYER_PRIVATE_KEY — used once by scripts/deploy-contracts.ts to deploy on-chain contracts. NOT loaded at MCP runtime. Use a throwaway funded key; discard after deployment."
  runtime_network: "Opens HTTP server on TASK_SERVER_PORT (default 3402, bound to 127.0.0.1) only when safe_listen_for_hire tool is called."
  runtime_files: "scripts/generate-env.ts writes .env interactively on first setup. scripts/deploy-contracts.ts writes deployed contract addresses back to .env after one-time deployment. Neither runs automatically."
  security_test_note: "tests/stress/ files contain literal prompt-injection strings (e.g. Ignore all previous instructions) as adversarial test fixtures to verify the input-gate blocks them. These are not instructions."
  homepage: "https://github.com/charliebot8888/SafeLink"
  repository: "https://github.com/charliebot8888/SafeLink"
Confidence
88% confidence
Finding
The skill requires and documents handling of multiple secrets in .env, including API credentials and a deployer private key for one-time contract deployment. Even though the file says the deployer key is not used at runtime, combining secret collection, local file writes, and agent-accessible environment usage materially increases the blast radius if the host, setup scripts, or logs are compromised.

Instruction Override

High
Category
Prompt Injection
Content
deploy_only_env: "DEPLOYER_PRIVATE_KEY — used once by scripts/deploy-contracts.ts to deploy on-chain contracts. NOT loaded at MCP runtime. Use a throwaway funded key; discard after deployment."
  runtime_network: "Opens HTTP server on TASK_SERVER_PORT (default 3402, bound to 127.0.0.1) only when safe_listen_for_hire tool is called."
  runtime_files: "scripts/generate-env.ts writes .env interactively on first setup. scripts/deploy-contracts.ts writes deployed contract addresses back to .env after one-time deployment. Neither runs automatically."
  security_test_note: "tests/stress/ files contain literal prompt-injection strings (e.g. Ignore all previous instructions) as adversarial test fixtures to verify the input-gate blocks them. These are not instructions."
  homepage: "https://github.com/charliebot8888/SafeLink"
  repository: "https://github.com/charliebot8888/SafeLink"
---
Confidence
80% confidence
Finding
This pattern attempts to override system instructions or ignore safety constraints. Without LLM analysis, manual review is recommended.

Instruction Override

High
Category
Prompt Injection
Content
deploy_only_env: "DEPLOYER_PRIVATE_KEY — used once by scripts/deploy-contracts.ts to deploy on-chain contracts. NOT loaded at MCP runtime. Use a throwaway funded key; discard after deployment."
  runtime_network: "Opens HTTP server on TASK_SERVER_PORT (default 3402, bound to 127.0.0.1) only when safe_listen_for_hire tool is called."
  runtime_files: "scripts/generate-env.ts writes .env interactively on first setup. scripts/deploy-contracts.ts writes deployed contract addresses back to .env after one-time deployment. Neither runs automatically."
  security_test_note: "tests/stress/ files contain literal prompt-injection strings (e.g. Ignore all previous instructions) as adversarial test fixtures to verify the input-gate blocks them. These are not instructions."
  homepage: "https://github.com/charliebot8888/SafeLink"
  repository: "https://github.com/charliebot8888/SafeLink"
---
Confidence
80% confidence
Finding
This pattern attempts to override system instructions or ignore safety constraints. Without LLM analysis, manual review is recommended.

Instruction Override

High
Category
Prompt Injection
Content
deploy_only_env: "DEPLOYER_PRIVATE_KEY — used once by scripts/deploy-contracts.ts to deploy on-chain contracts. NOT loaded at MCP runtime. Use a throwaway funded key; discard after deployment."
  runtime_network: "Opens HTTP server on TASK_SERVER_PORT (default 3402, bound to 127.0.0.1) only when safe_listen_for_hire tool is called."
  runtime_files: "scripts/generate-env.ts writes .env interactively on first setup. scripts/deploy-contracts.ts writes deployed contract addresses back to .env after one-time deployment. Neither runs automatically."
  security_test_note: "tests/stress/ files contain literal prompt-injection strings (e.g. Ignore all previous instructions) as adversarial test fixtures to verify the input-gate blocks them. These are not instructions."
  homepage: "https://github.com/charliebot8888/SafeLink"
  repository: "https://github.com/charliebot8888/SafeLink"
---
Confidence
80% confidence
Finding
This pattern attempts to override system instructions or ignore safety constraints. Without LLM analysis, manual review is recommended.

YARA rule 'agent_skill_prompt_injection_hidden_instructions': Prompt injection or hidden instructions embedded in AI agent skill text [agent_skills]

High
Category
YARA Match
Content
rst MCP server for agent-to-agent hiring with on-chain escrow, x402 USDC micropayments, MPC wallets, ERC-8004 identity, and policy-gated transactions on Base.",
  "tags": ["security", "web3", "a2a", "payments", "escrow", "x402", "erc-8004", "agentic-wallet", "mcp", "production"],
  "requiredEnv": [
    {
      "name": "ANTHROPIC_API_KEY",
      "description": "Claude Haiku key for task execution (LLM_PROVIDER=anthropic)",
      "sensitive": true,
      "required": "when LLM_PROVIDER=anthropic"
    },
    {
      "name": "BASE_RPC_URL",
      "description": "Base network RPC endpoint (default: https://sepolia.base.org)",
      "sensitive": false,
      "required": "always"
    },
    {
      "name": "PRIVY_APP_ID",
      "description": "Privy application ID for MPC wallet (WALLET_PROVIDER=privy)",
      "sensitive": false,
      "required": "when WALLET_PROVIDER=privy"
    },
    {
      "name": "PRIVY_APP_SECRET",
      "description": "Privy application secret for MPC wallet signing",
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

External Script Fetching

High
Category
Supply Chain
Content
* Deploy ERC8004Registry and SafeEscrow to Base (Sepolia or Mainnet based on BASE_RPC_URL).
 *
 * Prerequisites:
 *   1. Install Foundry: curl -L https://foundry.paradigm.xyz | bash && foundryup
 *   2. Set BASE_RPC_URL and DEPLOYER_PRIVATE_KEY in .env
 *   3. Fund the deployer address with ETH on the target Base network
 *
Confidence
96% confidence
Finding
The documented prerequisite `curl -L https://foundry.paradigm.xyz | bash` is a classic pipe-to-shell pattern that executes remote content without integrity verification. If the remote endpoint, DNS, TLS trust chain, or distribution path is compromised, users could run arbitrary code on their machine, which is especially dangerous in a deployment workflow that also handles private keys.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
process.exit(1);
}

if (!process.env["DEPLOYER_PRIVATE_KEY"]) {
  console.error(
    "闂?DEPLOYER_PRIVATE_KEY not set in .env\n" +
    "   This is a ONE-TIME deploy key 闂?you can use a throwaway.\n" +
Confidence
70% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

External Script Fetching

High
Category
Supply Chain
Content
} catch {
  console.error(
    "闂?Foundry not found.\n" +
    "   Install: curl -L https://foundry.paradigm.xyz | bash && foundryup"
  );
  process.exit(1);
}
Confidence
96% confidence
Finding
Repeating the same pipe-to-shell install command in an error message reinforces unsafe operator behavior and normalizes arbitrary remote script execution. Because this skill is for production A2A and contract deployment, the context increases risk: a compromised installer could steal wallet material, alter artifacts, or tamper with deployments.

Credential Access

High
Category
Privilege Escalation
Content
#!/usr/bin/env tsx
/**
 * SafeLink interactive .env setup wizard.
 *
 * Usage: npm run setup
 */
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
#!/usr/bin/env tsx
/**
 * SafeLink interactive .env setup wizard.
 *
 * Usage: npm run setup
 */
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
#!/usr/bin/env tsx
/**
 * SafeLink interactive .env setup wizard.
 *
 * Usage: npm run setup
 */
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
#!/usr/bin/env tsx
/**
 * SafeLink interactive .env setup wizard.
 *
 * Usage: npm run setup
 */
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
#!/usr/bin/env tsx
/**
 * SafeLink interactive .env setup wizard.
 *
 * Usage: npm run setup
 */
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
#!/usr/bin/env tsx
/**
 * SafeLink interactive .env setup wizard.
 *
 * Usage: npm run setup
 */
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
#!/usr/bin/env tsx
/**
 * SafeLink interactive .env setup wizard.
 *
 * Usage: npm run setup
 */
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 { fileURLToPath } from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const ENV_FILE = join(__dirname, "..", ".env");
const MAINNET_CONFIRM_PHRASE = "I_UNDERSTAND_MAINNET_RISK";

const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
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 { fileURLToPath } from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const ENV_FILE = join(__dirname, "..", ".env");
const MAINNET_CONFIRM_PHRASE = "I_UNDERSTAND_MAINNET_RISK";

const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.prompt_injection_instructions

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/deploy-contracts.ts:44

Prompt-injection style instruction pattern detected.

Warn
Code
suspicious.prompt_injection_instructions
Location
SKILL.md:16