T09 · Insecure Skill Coding Practices
- Location
skill.py:61- Finding
API Trading Credentials Are Stored Without Restrictive File Permissions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is a disclosed Polymarket trading CLI, but live trading and credential handling need Review before installation.
Install only if you are comfortable giving this skill a dedicated, limited-funds Polymarket wallet and allowing it to submit trades. Review every trade manually outside the skill, avoid using a main wallet, restrict credential-file permissions, and treat the unpinned trading dependency as a supply-chain risk until the publisher adds confirmation, least-privilege read-only paths, and stronger secret storage.
skill.py:61API Trading Credentials Are Stored Without Restrictive File Permissions
skill.py:338Public Price Queries Unnecessarily Access the Wallet Private Key and Generate Persistent Credentials
requirements.txt:2Security-Critical Dependencies Are Unbounded and the Trading Client Import Namespace Is Inconsistent
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
- **Strongly recommended**: Use a **dedicated wallet with limited funds**, NOT your main wallet.
- The private key is **only used locally** for signing transactions via `py-clob-client`. It is **never transmitted** to any endpoint other than `clob.polymarket.com` (Polymarket's official CLOB API).
- If using config file method: `chmod 600 ~/.openclaw/credentials/polymarket.json`
## Commands
The skill declares capabilities that require access to environment variables, file writes, and networked trading endpoints, but it does not declare any explicit tool scope or permission boundaries. In a trading skill that handles private keys and can place orders, this increases the chance of over-broad execution and unintended access to sensitive data or state-changing actions.
The skill instructs users to persist a wallet private key in a local JSON file under ~/.openclaw/credentials and also caches generated API credentials locally. Persistent storage of high-value secrets materially increases the blast radius of local compromise, accidental backup leakage, or misuse by other tools with filesystem access.
Method 2: Via config file (Legacy)
Create ~/.openclaw/credentials/polymarket.json:
{
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
- **Strongly recommended**: Use a **dedicated wallet with limited funds**, NOT your main wallet.
- The private key is **only used locally** for signing transactions via `py-clob-client`. It is **never transmitted** to any endpoint other than `clob.polymarket.com` (Polymarket's official CLOB API).
- If using config file method: `chmod 600 ~/.openclaw/credentials/polymarket.json`
## Commands
The trading triggers are broad phrases like '买 2 美元', '下注 5 美元', and 'Long [market]', which can overlap with normal conversational intent and may cause the skill to activate for real-money actions without sufficiently precise user direction. In a financial trading context, ambiguous activation is dangerous because it can lead to unintended order placement or wallet usage.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
}
os.makedirs(os.path.dirname(API_CREDS_FILE), exist_ok=True)
with open(API_CREDS_FILE, 'w') as f:
json.dump(creds_dict, f, indent=2)
def get_default_wallet():
The helper returns Chinese-only labels, and those labels are injected into normal output regardless of user preference. This creates a locale/language policy issue because the skill forces a specific language without opt-in or documented regional scope.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
# Get odds
try:
d = requests.get(f"https://gamma-api.polymarket.com/events/slug/{slug}", timeout=5).json()
markets = d.get('markets', [])
opts = []
for m in markets[:3]:
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
slug = slug.replace('event/', '').replace('https://polymarket.com/event/', '')
try:
resp = requests.get(f"https://gamma-api.polymarket.com/events/slug/{slug}", timeout=10)
if resp.status_code != 200:
print(f"❌ Not found: {resp.status_code}")
return
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
slug = slug.replace('event/', '').replace('https://polymarket.com/event/', '')
try:
resp = requests.get(f"https://gamma-api.polymarket.com/events/slug/{slug}", timeout=10)
if resp.status_code != 200:
print(f"❌ Not found: {resp.status_code}")
return
The skill can place live market orders immediately on a buy/sell command with no confirmation prompt, preview, slippage warning, or dry-run safeguard. In an agent or automation setting, a mistaken command, prompt injection, or parameter mix-up could directly trigger irreversible financial transactions using the user's configured private key.
Several printed labels in portfolio and balance views are in Chinese, and the file provides no mechanism for selecting language or opting into this locale. That constitutes a natural-language policy concern because the skill imposes a specific language on all users.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def _balance_of(contract, decimals):
data = f"0x70a08231000000000000000000000000{wallet[2:]}"
try:
resp = requests.post(POLYGON_RPC, json={"jsonrpc":"2.0","method":"eth_call","params":[{"to":contract,"data":data},"latest"],"id":1}, timeout=10)
return int(resp.json().get("result", "0x0"), 16) / 10**decimals
except:
return None
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def _balance_of(contract, decimals):
data = f"0x70a08231000000000000000000000000{wallet[2:]}"
try:
resp = requests.post(POLYGON_RPC, json={"jsonrpc":"2.0","method":"eth_call","params":[{"to":contract,"data":data},"latest"],"id":1}, timeout=10)
return int(resp.json().get("result", "0x0"), 16) / 10**decimals
except:
return None
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
def _balance_of(contract, decimals):
data = f"0x70a08231000000000000000000000000{wallet[2:]}"
try:
resp = requests.post(POLYGON_RPC, json={"jsonrpc":"2.0","method":"eth_call","params":[{"to":contract,"data":data},"latest"],"id":1}, timeout=10)
return int(resp.json().get("result", "0x0"), 16) / 10**decimals
except:
return None
The natural-language trigger section provides only Chinese and English examples, which can imply a language constraint in activation behavior. If the skill expects or prioritizes only those languages, that should be documented as an explicit choice rather than an unstated limitation.
Examples like "这个市场什么意思", "What are the odds for [outcome]", and "Explain this market" do not clearly limit activation to Polymarket or prediction-market contexts. Without negative examples or stricter scoping, these phrases may collide with general requests about unrelated markets or events.
The dependency specification uses a lower-bound only constraint (requests>=2.31.0) rather than pinning to a known-safe version, so the actual installed version may vary by environment and could resolve to a release affected by published advisories. In a trading skill that likely performs authenticated HTTP requests, an unsafe requests version could expose credentials or weaken transport/session security depending on runtime resolution.
The dependency is specified with a lower bound only (requests>=2.31.0), which makes builds non-reproducible and allows future installs to resolve to unexpected versions. In a trading skill, dependency drift can introduce vulnerable or breaking releases into a network-facing component without code changes in the skill itself.
# Polymarket Skill Dependencies
requests>=2.31.0
py-clob-client>=1.5.0
The manifest references requests without an exact version pin, and the package has multiple historical advisories, so the deployed environment could resolve to an affected release. While >=2.31.0 may avoid some older issues, the absence of a fixed reviewed version means exposure cannot be ruled out and is especially relevant for a CLI that makes authenticated HTTP requests.
py-clob-client>=1.5.0 is also unpinned, so installations may pull different versions over time, including releases with undiscovered security issues or behavioral changes. Because this skill executes market actions, relying on floating versions in a trading client increases supply-chain and operational risk.
# Polymarket Skill Dependencies
requests>=2.31.0
py-clob-client>=1.5.0
No suspicious patterns detected.