Back to skill

Security audit

Emoji Today

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it says, but it can spend from a configured wallet through remote payment challenges without strong local limits.

Review before installing. Use only a dedicated wallet with minimal USDC, keep unrelated secrets out of the process environment, do not set EMOJI_TODAY_URL unless you fully trust the endpoint, and expect votes or mints to be public or permanent. The publisher should add strict payment validation, exact dependency locking, and explicit confirmation before signing or spending.

Vulnerability Patterns
  • 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
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/vote-and-post.mjs:68
Finding

Unrestricted x402 Payment Authorization

Content
View full analysis

Vulnerability Details

File Location: scripts/vote-and-post.mjs, lines 68-81
Vulnerability Type: Missing network, asset, recipient, and amount restrictions
Risk Level: High

Vulnerable Code

js
async function fetchWithX402(url, opts = {}) {
  const { wrapFetchWithPaymentFromConfig } = await import("@x402/fetch");
  const { ExactEvmScheme } = await import("@x402/evm");

  const account = getAccount();

  const payFetch = wrapFetchWithPaymentFromConfig(fetch, {
    schemes: [
      {
        network: "eip155:*",
        client: new ExactEvmScheme(account),
      },
    ],
  });

  return payFetch(url, opts);
}

Technical Analysis

The x402 client authorizes payment handling for every EVM network matching eip155:*. The application does not independently validate or restrict:

  • The chain to Base mainnet (eip155:8453)
  • The payment asset to the expected USDC contract
  • The payment recipient to the documented revenue wallet
  • The maximum payment amount
  • The expected price for each operation

Consequently, security-sensitive payment terms are delegated to the remote x402 response and the payment libraries. This does not enforce the narrower behavior documented in SKILL.md, which states that voting costs $0.01 USDC, minting costs $1.00 USDC, and transactions occur on Base.

Attack Path

  1. A user invokes the vote or mint command with a funded EVM wallet.
  2. The configured API endpoint is compromised, malicious, or incorrectly configured.
  3. The endpoint returns x402 payment requirements containing an unexpected network, asset, recipient, or excessive payment amount.
  4. The generic eip155:* payment client processes the payment challenge.
  5. No application-level policy rejects payment terms that differ from the documented transaction.

Impact Assessment

An attacker controlling the payment endpoint may be able to cause the wallet to authorize transactio ...[truncated 354 chars]

Remediation
View remediation

Remediation Suggestions

  • Replace the wildcard network with an exact Base mainnet restriction: eip155:8453.
  • Validate the x402 payment requirements before signing or submitting any transaction.
  • Hard-code or securely configure an allowlist containing:
    • The expected chain ID
    • The official USDC asset contract
    • The authorized payment recipient
    • The permitted payment scheme
  • Apply separate maximum payment limits for each operation:
    • Vote: no more than the documented $0.01 USDC
    • Mint: no more than the documented $1.00 USDC
  • Reject payment requirements containing additional transfers, unknown contracts, or unexpected calldata.
  • Require explicit user confirmation whenever payment terms differ from the locally defined policy.
  • Log validated payment details without exposing the private key or other sensitive wallet material.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/vote-and-post.mjs:34
Finding

Arbitrary API Origin Used by Payment-Enabled Requests

Content
View full analysis

Vulnerability Details

File Location: scripts/vote-and-post.mjs, line 34; payment sinks at lines 91-96 and 118-123
Vulnerability Type: Unvalidated external endpoint configuration
Risk Level: Medium

Vulnerable Code

js
const BASE_URL = process.env.EMOJI_TODAY_URL || "https://emoji.today";

The unvalidated value is used by both payment-enabled operations:

js
const res = await fetchWithX402(`${BASE_URL}/api/vote`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ emoji, fid, signature }),
});
js
const res = await fetchWithX402(`${BASE_URL}/api/vote/mint`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ emoji, voter: fid.toString(), mintTo }),
});

Technical Analysis

EMOJI_TODAY_URL is accepted without URL parsing, HTTPS enforcement, hostname validation, or an origin allowlist. Its value is passed directly to an x402 payment-enabled fetch client.

An alternate origin therefore receives the Farcaster FID, wallet signature, selected emoji, and mint recipient data. It also controls the x402 challenge supplied to the wallet. The risk is amplified by the unrestricted payment policy because no local check constrains the payment network, token, recipient, or amount.

The vote signature does not disclose the private key. However, the signed message is deterministic for a given FID and UTC date and is not bound to the selected emoji, request origin, or a server-provided nonce.

Attack Path

  1. An attacker gains influence over the process environment, deployment configuration, .env file, or command execution context.
  2. The attacker sets EMOJI_TODAY_URL to an attacker-controlled HTTP or HTTPS endpoint.
  3. A user invokes the normal vote or mint command.
  4. The script sends identity metadata, the daily vote signature, or mint recipient data to the attacker-controlled ...[truncated 731 chars]
Remediation
View remediation

Remediation Suggestions

  • Parse EMOJI_TODAY_URL using the standard URL API before use.
  • Require the https: scheme and reject embedded credentials, fragments, and unexpected ports.
  • Allowlist the exact production origin https://emoji.today.
  • If custom endpoints are necessary for development, require a separate explicit development mode and disable automatic payments by default.
  • Display the destination origin and verified payment terms before authorizing a payment to any non-production endpoint.
  • Combine origin validation with strict chain, asset, recipient, and amount restrictions.
  • Improve vote authentication by binding signatures to the intended origin, selected emoji, operation, expiration time, and a unique server nonce.

T08 · Insecure Dependencies

Warning
Location
scripts/package.json:6
Finding

Mutable Wallet-Facing Dependencies Without a Lockfile

Content
View full analysis

Vulnerability Details

File Location: scripts/package.json, lines 6-10
Vulnerability Type: Non-reproducible dependency resolution for security-sensitive packages
Risk Level: Medium

Vulnerable Code

json
"dependencies": {
  "@x402/fetch": "^2.3.0",
  "@x402/evm": "^2.3.0",
  "viem": "^2.0.0",
  "dotenv": "^16.0.0"
}

The documented installation command is:

bash
npm install

No dependency lockfile is present in the audited project structure.

Technical Analysis

Caret version ranges permit npm to resolve later compatible releases. Without a committed package-lock.json, repeated installations are not guaranteed to install the dependency versions that were reviewed.

This is security-sensitive because the dependencies handle environment loading, the EVM private key, message signing, and x402 payment authorization. Third-party packages and their transitive dependencies can also execute npm lifecycle scripts during installation.

The audit did not identify evidence that the named packages are typosquatted or intentionally malicious. The confirmed weakness is that dependency resolution is mutable and non-reproducible for code with access to wallet credentials and payment operations.

Attack Path

  1. A dependency or transitive dependency publishes a compromised or unexpectedly unsafe release compatible with the declared caret range.
  2. A user follows the documented npm install procedure.
  3. npm resolves the newer compatible release because no lockfile fixes the reviewed dependency graph.
  4. Malicious code executes through an installation lifecycle script or when the application imports the package.
  5. The dependency runs within the Node.js process and can access environment variables, including the EVM private key, or alter signing and payment behavior.

Impact Assessment

Exploitation could expose the EVM private key, manipulate signatures, redirect or modify p ...[truncated 306 chars]

Remediation
View remediation

Remediation Suggestions

  • Pin security-sensitive dependencies to reviewed exact versions instead of caret ranges.
  • Generate, review, and commit a package-lock.json.
  • Change deployment and automation instructions from npm install to npm ci.
  • Review both direct and transitive dependencies whenever the lockfile changes.
  • Use automated vulnerability and provenance checks in CI.
  • Consider npm ci --ignore-scripts where dependency functionality permits lifecycle scripts to be disabled.
  • Run the application under a minimally privileged operating-system account.
  • Keep unrelated credentials out of the process environment.
  • Use a dedicated wallet containing only the minimum funds needed for the intended vote or mint.
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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (12)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/vote-and-post.mjs (reported line 27)May include surrounding context.

js
const __dirname = dirname(fileURLToPath(import.meta.url));

// Load .env from scripts dir, then from skill root, then from cwd
config({ path: join(__dirname, ".env") });
config({ path: join(__dirname, "..", ".env") });
config();

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/vote-and-post.mjs (reported line 37)May include surrounding context.

js
const __dirname = dirname(fileURLToPath(import.meta.url));

// Load .env from scripts dir, then from skill root, then from cwd
config({ path: join(__dirname, ".env") });
config({ path: join(__dirname, "..", ".env") });
config();

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/vote-and-post.mjs (reported line 41)May include surrounding context.

js
const __dirname = dirname(fileURLToPath(import.meta.url));

// Load .env from scripts dir, then from skill root, then from cwd
config({ path: join(__dirname, ".env") });
config({ path: join(__dirname, "..", ".env") });
config();

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/vote-and-post.mjs (reported line 28)May include surrounding context.

js
const __dirname = dirname(fileURLToPath(import.meta.url));

// Load .env from scripts dir, then from skill root, then from cwd
config({ path: join(__dirname, ".env") });
config({ path: join(__dirname, "..", ".env") });
config();

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/vote-and-post.mjs (reported line 29)May include surrounding context.

js
const __dirname = dirname(fileURLToPath(import.meta.url));

// Load .env from scripts dir, then from skill root, then from cwd
config({ path: join(__dirname, ".env") });
config({ path: join(__dirname, "..", ".env") });
config();

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill requires access to sensitive environment variables and performs network actions, but it does not explicitly declare tool scope or permissions. This weakens isolation and reviewability because an agent runner may grant broader capabilities than intended, increasing the chance of unintended secret exposure or unapproved external requests.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The vote path signs an identity-linked message and submits it together with a paid x402 request without a runtime warning or consent checkpoint. This exposes both wallet-backed authorization and Farcaster-linked identity data to a remote service, making the skill more dangerous in autonomous or delegated-agent contexts than a normal local CLI utility.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The mint path performs a paid network action via x402 and can spend funds from the configured wallet without any execution-time confirmation or explicit disclosure in the command flow. In an agent setting, this is risky because a caller may trigger a real onchain payment and NFT mint using the agent's wallet simply by invoking the command, especially since the skill description normalizes paid use.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
93% confidence
Finding

The dependency uses a caret version range, which allows npm to install newer minor and patch releases than the version originally tested. If an upstream package is compromised or introduces a breaking security regression, installs may silently pick up the affected version and expose a supply-chain risk. In this skill, the risk is somewhat elevated because the packages interact with EVM wallets and payment flows.

Content

Scanner excerpt · scripts/package.json (reported line 7)May include surrounding context.

json
"private": true,
  "type": "module",
  "dependencies": {
    "@x402/fetch": "^2.3.0",
    "@x402/evm": "^2.3.0",
    "viem": "^2.0.0",
    "dotenv": "^16.0.0"

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
93% confidence
Finding

The dependency uses a caret version range, which allows npm to install newer minor and patch releases than the version originally tested. If an upstream package is compromised or introduces a breaking security regression, installs may silently pick up the affected version and expose a supply-chain risk. Because this skill handles onchain payment-related functionality, dependency drift is more sensitive than in a non-financial utility.

Content

Scanner excerpt · scripts/package.json (reported line 8)May include surrounding context.

json
"type": "module",
  "dependencies": {
    "@x402/fetch": "^2.3.0",
    "@x402/evm": "^2.3.0",
    "viem": "^2.0.0",
    "dotenv": "^16.0.0"
  }

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
92% confidence
Finding

Using a non-exact version for viem permits automatic resolution to future compatible releases that may not have been audited with this skill. A malicious or vulnerable upstream release could affect transaction construction, signing, or chain interactions, creating a software supply-chain exposure. The onchain wallet context makes even low-probability dependency compromise more concerning.

Content

Scanner excerpt · scripts/package.json (reported line 9)May include surrounding context.

json
"dependencies": {
    "@x402/fetch": "^2.3.0",
    "@x402/evm": "^2.3.0",
    "viem": "^2.0.0",
    "dotenv": "^16.0.0"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
91% confidence
Finding

The dotenv dependency is also unpinned, so installations may resolve to newer releases without explicit review. While dotenv is less sensitive than blockchain libraries, a compromised or buggy release could still affect environment variable handling or be used as a foothold in a broader supply-chain attack.

Content

Scanner excerpt · scripts/package.json (reported line 10)May include surrounding context.

json
"@x402/fetch": "^2.3.0",
    "@x402/evm": "^2.3.0",
    "viem": "^2.0.0",
    "dotenv": "^16.0.0"
  }
}

Static analysis

No suspicious patterns detected.