Back to skill

Security audit

structs-onboarding

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its Structs onboarding purpose, but it handles wallet recovery phrases and remote signup endpoints in ways that could expose account control if logs or endpoints are unsafe.

Review carefully before installing. Use this only if you are comfortable with the agent handling wallet seed phrases and signing onboarding actions. Avoid passing mnemonics on the command line, avoid logging script output, prefer HTTPS guild endpoints you have verified independently, and treat any mnemonic exposed in command history or agent transcripts as compromised.

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
scripts/create-player.mjs:285
Finding

Unvalidated Remote Endpoints Permit Plaintext Disclosure and Server-Side Request Forgery

Content
View full analysis

Vulnerability Details

File Location: scripts/create-player.mjs:285-286, 371-375; insecure HTTP endpoint example in SKILL.md:99-105, 123-124
Vulnerability Type: Unrestricted user-controlled network destinations and plaintext transmission
Risk Level: High

Vulnerable code:

js
const reactorApi = args.reactorApi.replace(/\/+$/, '');
const guildApi = args.guildApi.replace(/\/+$/, '');

signupResponse = await fetch(`${guildApi}/auth/signup`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(signupPayload)
});

The documentation also presents a plaintext endpoint:

json
{
  "guild": {
    "id": "0-1",
    "name": "Orbital Hydro",
    "tag": "OH",
    "services": {
      "guild_api": "http://crew.oh.energy/api/",
      "reactor_api": "https://public.testnet.structs.network/",
      "client_websocket": "wss://public.testnet.structs.network:26657/websocket"
    }
  }
}

Technical Analysis

The script accepts --guild-api and --reactor-api as arbitrary strings and uses them as network destinations after only removing trailing slashes. It does not enforce HTTPS, validate hostnames against an allowlist or trusted on-chain guild record, reject embedded credentials, restrict redirects, or prevent access to loopback, link-local, private-network, and cloud metadata addresses.

The guild signup request transmits the wallet address, public key, join signature, guild identifier, username, profile image reference, and optional rendering attributes. Although it does not transmit the mnemonic or private key, the signature is credential-like proof generated with the private key. Sending this information over HTTP allows a network-positioned attacker to observe or alter the request and response.

The unrestricted URL behavior also creates a server-side request forgery primitive in environments where the Agent can reach resources unavaila ...[truncated 1958 chars]

Remediation
View remediation

Remediation Suggestions

  1. Parse endpoints with new URL() and permit only https: in normal operation. Remove plaintext HTTP examples from SKILL.md.
  2. Verify the guild endpoint against the authoritative on-chain guild record before submitting any signed data.
  3. Require explicit user approval for destinations outside a narrowly defined trusted-host allowlist.
  4. Resolve destination hostnames and reject loopback, link-local, private, multicast, reserved, and cloud metadata address ranges for both IPv4 and IPv6. Repeat validation after DNS resolution and on every redirect.
  5. Disable redirects or limit them to validated HTTPS destinations on the same trusted host.
  6. Add bounded connection and response timeouts, response-size limits, and strict JSON schema validation.
  7. Display the final normalized destination and the exact categories of data being submitted before signing or sending the request.
  8. Consider binding the intended endpoint, guild identifier, nonce, and expiration into the signed protocol so signatures cannot be replayed outside their intended context.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/create-player.mjs:42
Finding

Wallet Mnemonics Are Exposed Through Process Arguments and Structured Output

Content
View full analysis

Vulnerability Details

File Location: scripts/create-player.mjs:42, 379-383, 405-410, 441-447, 451-454; documented usage in SKILL.md:157, 188-190, 326
Vulnerability Type: Plaintext secret exposure through command-line arguments, stdout, and error output
Risk Level: High

Vulnerable command-line parsing:

js
function parseArgs(argv) {
  const args = {};
  for (let i = 2; i < argv.length; i++) {
    if (argv[i] === '--mnemonic' && argv[i + 1]) args.mnemonic = argv[++i];
    else if (argv[i] === '--guild-id' && argv[i + 1]) args.guildId = argv[++i];
    else if (argv[i] === '--guild-api' && argv[i + 1]) args.guildApi = argv[++i];
    else if (argv[i] === '--reactor-api' && argv[i + 1]) args.reactorApi = argv[++i];
    else if (argv[i] === '--username' && argv[i + 1]) args.username = argv[++i];
    else if (argv[i] === '--pfp' && argv[i + 1]) args.pfp = argv[++i];
    else if (argv[i] === '--pfp-client-render-attributes' && argv[i + 1]) args.pfpClientRenderAttributes = argv[++i];
    else if (argv[i] === '--timeout' && argv[i + 1]) args.timeout = parseInt(argv[++i], 10);
  }
  return args;
}

Mnemonic disclosure on successful completion:

js
console.log(JSON.stringify({
  success: true,
  mnemonic: generated ? mnemonic : undefined,
  address,
  pubkey: pubkeyHex,
  player_id: playerId,
  guild_id: args.guildId,
  username,
  pfp,
  pfp_client_render_attributes: pfpClientRenderAttributes,
  created: true,
  next_step: `structsd tx structs planet-explore --from [key-name] --gas auto --gas-adjustment 1.5 -- ${playerId}`
}));

Mnemonic disclosure on timeout:

js
if (!playerId) {
  fail({
    error: `Player creation timed out after ${timeout}s. The signup was submitted but the player has not appeared yet. You can re-run with the same --mnemonic to resume polling.`,
    mnemonic: 
...[truncated 2870 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove --mnemonic support. Read recovery seeds from an interactive protected TTY, a dedicated file descriptor, or an operating-system secret manager.
  2. If file input is required, enforce restrictive ownership and permissions and avoid copying the secret into logs or exception messages.
  3. Do not include a mnemonic in the normal JSON result or in any error object.
  4. For newly generated wallets, store the mnemonic directly in an explicitly selected secret store or permission-restricted file using atomic creation and mode 0600.
  5. Return only a non-secret confirmation, wallet address, public key, and secret-storage reference through stdout.
  6. Ensure logging and telemetry layers redact mnemonic-shaped values and never serialize wallet objects or private-key material.
  7. Zero or release in-memory references as soon as practical, while recognizing that JavaScript cannot guarantee complete memory erasure.
  8. Update SKILL.md to prohibit command-line seed submission and provide secure recovery and generation examples.
  9. Warn users that any mnemonic previously passed on the command line or captured in logs should be treated as compromised and rotated.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (9)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 32)May include surrounding context.

md
If using **Path B** (guild signup) below, the `create-player.mjs` script can generate a mnemonic automatically — you can skip ahead to Step 1 and let the script

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 96)May include surrounding context.

md
If using **Path B** (guild signup) below, the `create-player.mjs` script can generate a mnemonic automatically — you can skip ahead to Step 1 and let the script

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 355)May include surrounding context.

md
If using **Path B** (guild signup) below, the `create-player.mjs` script can generate a mnemonic automatically — you can skip ahead to Step 1 and let the script

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 357)May include surrounding context.

md
If using **Path B** (guild signup) below, the `create-player.mjs` script can generate a mnemonic automatically — you can skip ahead to Step 1 and let the script

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

When a new wallet is generated, the script includes the mnemonic in its JSON stdout output. In agent/tooling environments, stdout is commonly logged, captured by orchestration systems, or exposed to downstream steps, so this can leak the private recovery phrase and allow full wallet takeover.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill instructs the agent to make outbound network requests to public nodes, guild endpoints, and external APIs (curl, guild config fetches, POST signup flow) but does not declare an explicit tool scope such as permissions or allowed-tools. That mismatch weakens least-privilege controls: an operator may invoke the skill without realizing it requires network access and the skill may interact with untrusted remote services, including HTTP endpoints, during onboarding.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

The script sends address data to the reactor API and later posts address, public key, signature, username, and optional profile fields to the guild API. While these network calls are part of the tool's purpose, the file lacks a clear user-facing disclosure at execution time that account-linked data will be transmitted to remote services specified by the provided URLs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script accepts a mnemonic via command-line argument, which is a sensitive secret, and uses it to derive private keys. The code contains no runtime disclosure warning that passing secrets on the command line may expose them through shell history, process listings, or orchestration logs.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

The dependency uses a caret range (^0.34.0), which allows installation of newer compatible versions rather than a single fixed version. In an agent skill that handles key creation and player onboarding, dependency drift increases supply-chain risk because a future published package version could introduce malicious code or breaking behavior without any change to this repository.

Content

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

json
"type": "module",
  "description": "Unified player creation tool for Structs AI agents",
  "dependencies": {
    "@cosmjs/proto-signing": "^0.34.0"
  }
}

Static analysis

No suspicious patterns detected.