T09 · Insecure Skill Coding Practices
- Location
scripts/setup.mjs:52- Finding
Generated Wallet Private Key Is Not Protected by Enforced File Permissions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill mostly matches its prediction-market trading purpose, but it handles real wallet keys and installation provenance in ways users should review carefully.
Install only from a repository and commit you trust, preferably one matching the declared homepage. Use a dedicated low-value wallet, verify ~/.openclaw/.env is owner-only readable, avoid storing a valuable mainnet key there, and treat mainnet commands and token approvals as real financial actions.
scripts/setup.mjs:52Generated Wallet Private Key Is Not Protected by Enforced File Permissions
README.md:17Installation Instructions Reference an Unpinned Repository That Differs from the Declared Homepage
Vitest 1.6.1 is flagged for arbitrary file read and execution when the Vitest UI server is listening. This is a real issue, but it affects test/UI-server usage rather than the production runtime of the trading skill; still, in a wallet- or market-related project, compromise of a developer environment could expose secrets, test keys, or source code.
If the project resolves to vitest 1.6.1, the cited advisory indicates that the Vitest UI server can allow arbitrary file read and code execution. Even though vitest is a devDependency, exploitation in a developer workstation or CI runner could expose the plaintext wallet key described by this skill and potentially compromise signing credentials or build infrastructure.
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).
---
name: precog
description: "Trade on prediction markets. Create a local wallet, list markets, check prices, buy and sell outcome shares. Coming soon: create and fund markets directly from this skill."
homepage: "https://github.com/openclaw/precog-skill"
env:
PRIVATE_KEY:
description: "Secp256k1 private key (0x-prefixed) for signing transactions. Generated locally by running setup.mjs --generate and saved to ~/.openclaw/.env. Never transmitted over the network."
required: true
PRECOG_RPC_URL:
description: "Override the default public RPC endpoints (https://sepolia.base.org for testnet,
The ABI includes broad administrative role-management functions such as addAdmin, grantRole, revokeRole, addCaller, and addMarketOperator that are not justified by a simple trading skill. If an agent or integration can access these methods, it could escalate privileges, alter trusted operators, change allowed assets/oracles/receivers, or permanently compromise control of markets and funds.
The ABI exposes ownedTokenMint, ownedTokenBurn, ownedTokenMove, and ownedTokenTransferOwnership, which are highly privileged token-control operations unrelated to ordinary prediction-market trading. In the context of a skill that can create a local wallet and trade on behalf of users, these functions are especially dangerous because they could enable arbitrary balance manipulation, confiscation, issuance, or transfer of token ownership if misused or surfaced by mistake.
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.
PostCSS 8.5.8 is present and has multiple advisories involving file read, incomplete source map fixes, and output encoding/XSS issues. In this skill it is a transitive dev dependency used by Vite, so the risk is more relevant to development/build environments and any workflow that processes attacker-supplied CSS or source maps.
Vite 5.4.21 is flagged for path traversal and Windows path handling issues, including file access bypasses and possible NTLM hash disclosure. Although it is a dev dependency, these are genuine vulnerabilities that can matter if developers run Vite or related tooling on untrusted networks or with attacker-influenced paths/files.
The ws 8.18.3 package is flagged for memory disclosure and memory exhaustion issues. Because ws is a runtime dependency of viem, this is more relevant than purely dev-only findings: if the skill uses WebSocket RPC connections to blockchain nodes or accepts hostile peer traffic, exploitation could cause denial of service or unintended data exposure.
The skill explicitly specifies that ~/.openclaw/.env stores a blockchain PRIVATE_KEY in plaintext. For a trading skill that can operate on mainnet with real funds, plaintext wallet-key storage materially increases the chance of wallet compromise through local malware, accidental backup/sync exposure, or overly permissive filesystem access.
"PRECOG_NETWORK": "optional — default network: 'sepolia' (testnet, default) or 'mainnet' (Base mainnet, real funds). Can be overridden per-command with --network."
},
"configPaths": {
"~/.openclaw/.env": "created by setup.mjs --generate. Stores PRIVATE_KEY in plaintext. Treat as a wallet key file: restrict permissions and back it up."
},
"networks": {
"sepolia": { "chainId": 84532, "contract": "0x61ec71F1Fd37ecc20d695E83F3D68e82bEfe8443" },
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
// ── Load ~/.openclaw/.env ─────────────────────────────────────────────────────
const ENV_FILE = join(homedir(), ".openclaw", ".env");
if (existsSync(ENV_FILE)) {
for (const line of readFileSync(ENV_FILE, "utf8").split("\n")) {
const m = line.match(/^([A-Z_][A-Z0-9_]*)=(.+)$/);
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
// ── Load ~/.openclaw/.env ─────────────────────────────────────────────────────
const ENV_FILE = join(homedir(), ".openclaw", ".env");
if (existsSync(ENV_FILE)) {
for (const line of readFileSync(ENV_FILE, "utf8").split("\n")) {
const m = line.match(/^([A-Z_][A-Z0-9_]*)=(.+)$/);
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
// ── Load ~/.openclaw/.env ─────────────────────────────────────────────────────
const ENV_FILE = join(homedir(), ".openclaw", ".env");
if (existsSync(ENV_FILE)) {
for (const line of readFileSync(ENV_FILE, "utf8").split("\n")) {
const m = line.match(/^([A-Z_][A-Z0-9_]*)=(.+)$/);
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
// ── Load ~/.openclaw/.env ─────────────────────────────────────────────────────
const ENV_FILE = join(homedir(), ".openclaw", ".env");
if (existsSync(ENV_FILE)) {
for (const line of readFileSync(ENV_FILE, "utf8").split("\n")) {
const m = line.match(/^([A-Z_][A-Z0-9_]*)=(.+)$/);
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
//
// Usage:
// node setup.mjs # check wallet status, show address + balances
// node setup.mjs --generate # generate a new wallet, save to ~/.openclaw/.env
//
// Env: PRECOG_RPC_URL (optional)
import { existsSync, readFileSync, appendFileSync, mkdirSync } from "fs";
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
//
// Usage:
// node setup.mjs # check wallet status, show address + balances
// node setup.mjs --generate # generate a new wallet, save to ~/.openclaw/.env
//
// Env: PRECOG_RPC_URL (optional)
import { existsSync, readFileSync, appendFileSync, mkdirSync } from "fs";
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
//
// Usage:
// node setup.mjs # check wallet status, show address + balances
// node setup.mjs --generate # generate a new wallet, save to ~/.openclaw/.env
//
// Env: PRECOG_RPC_URL (optional)
import { existsSync, readFileSync, appendFileSync, mkdirSync } from "fs";
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
//
// Usage:
// node setup.mjs # check wallet status, show address + balances
// node setup.mjs --generate # generate a new wallet, save to ~/.openclaw/.env
//
// Env: PRECOG_RPC_URL (optional)
import { existsSync, readFileSync, appendFileSync, mkdirSync } from "fs";
The script checks for and manages a PRIVATE_KEY stored in ~/.openclaw/.env, indicating reliance on plaintext local secret storage for a cryptocurrency wallet. If the file is readable by other local users, backups, malware, or logs, compromise of the private key would let an attacker fully control the wallet and steal funds. In this skill context, handling prediction-market trading makes wallet compromise especially sensitive because the key authorizes financial transactions.
const existingKey = _getPrivateKey();
if (existingKey) {
console.error(`Wallet already set: ${privateKeyToAccount(existingKey).address}`);
console.error("Remove PRIVATE_KEY from ~/.openclaw/.env first.");
return { error: "already_exists" };
}
if (_existsSync(_envFile) && _readFileSync(_envFile, "utf8").includes("PRIVATE_KEY=")) {
The skill instructs users to validate they are human by using a passport-based identity system and says identity is kept private, but it does not provide a meaningful privacy warning about sharing identity-derived data with a third-party verifier. In a wallet/trading context, this can cause users to underestimate privacy, compliance, and data-handling risks, especially because identity workflows are high-sensitivity and often involve irreversible disclosure or correlation risks.
The skill declares environment variable requirements, including a private key, but does not declare an explicit tool/permission scope. In an agent setting, missing scope metadata weakens least-privilege boundaries and can let the runtime expose sensitive capabilities more broadly than intended.
The skill is explicitly designed to create and persist a local wallet private key in ~/.openclaw/.env in plaintext across sessions. Persistent storage of signing keys materially increases compromise impact: any local process, backup leak, or misconfigured permissions can lead to theft of funds or unauthorized market actions.
---
name: precog
description: "Trade on prediction markets. Create a local wallet, list markets, check prices, buy and sell outcome shares. Coming soon: create and fund markets directly from this skill."
homepage: "https://github.com/openclaw/precog-skill"
env:
PRIVATE_KEY:
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
## Security and local state
- **`~/.openclaw/.env`** — created by `setup.mjs --generate`. Stores `PRIVATE_KEY` in plaintext. Treat it like a wallet key file: restrict permissions (`chmod 600`) and back it up. Losing it means losing access to any funds in that wallet.
- **No key is ever transmitted.** Transactions are signed locally; only the signed transaction is broadcast to the RPC.
- **Use a throwaway wallet.** The MATE markets use a practice token with no real value — ideal for testing. Do not load a high-value key into this skill.
- **Custom RPC risk.** If you set `PRECOG_RPC_URL`, use only a trusted endpoint. An untrusted RPC can observe signed transaction contents (but cannot extract your private key from them).
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
→ node sell.mjs --market 4 --outcome 1 --shares <n> --min <suggested-min>
→ After trade: suggest checking positions or remaining balance
User: "Create a market about X" / "Can you create a market for Y?"
→ Tell the user to go to https://core.precog.markets/84532/create-market
→ Explain briefly: log in with MetaMask, fill the form, submit for review
→ Remind them: after submission they need to fund the market at the launchpad
The manifest states market creation is 'coming soon', yet the ABI already exposes createMarket and createCustomMarket. This mismatch is dangerous because users and calling agents may trust the narrower description and unknowingly invoke market-creation flows that can commit funds, create on-chain liabilities, or interact with unreviewed code paths outside the advertised scope.