Back to skill

Security audit

AIresearchOS

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims, but it needs Review because it can use API credentials and spend crypto funds through under-scoped commands and mutable dependencies.

Review before installing. Use a dedicated low-balance wallet and a limited API key, avoid setting a custom base URL unless you fully trust that endpoint, do not submit secrets or sensitive business data in research prompts, and prefer pinned/reviewed dependencies before enabling x402 payments.

Vulnerability Patterns
  • 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
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/check-status.mjs:29
Finding
Bearer Credential Disclosure Through an Unrestricted Custom API Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/check-status.mjs:29-58, 76`; related invocation guidance in `SKILL.md:78-81, 139, 199-234` and `SETUP.md:117-130` **Vulnerability Type**: Bearer credential exfiltration through insufficient destination validation **Risk Level**: High ### Vulnerable Code ```javascript const requestId = args.id const baseUrl = (args['base-url'] || '').replace(/\/+$/, '') const apiPath = args['api-path'] || '/api/v1' if (!requestId || !baseUrl) { process.stdout.write(JSON.stringify({ action: 'error', error: 'missing_args', message: '--id and --base-url are required', }) + '\n') process.exit(1) } const statusUrl = `${baseUrl}${apiPath}/research/${requestId}` const outputUrl = `${baseUrl}${apiPath}/research/${requestId}/output` async function main() { const headers = {} // Read API key from environment (injected by OpenClaw, never from CLI args) const apiKey = process.env.AIRESEARCHOS_API_KEY if (apiKey && apiPath === '/api/v1') { headers['Authorization'] = `Bearer ${apiKey}` } // Check status const statusResponse = await fetch(statusUrl, { headers }) ``` The same authorization headers are also reused for the report request: ```javascript const outputResponse = await fetch(outputUrl, { headers }) ``` The Skill instructions apply the same configurable origin to authenticated operations: ```bash curl -s -X POST "${AIRESEARCHOS_BASE_URL:-https://airesearchos.com}/api/v1/research" \ -H "Authorization: Bearer $AIRESEARCHOS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query":"<USER_QUERY>","mode":"<MODE>","reportLength":"standard","skipClarifyingQuestions":false}' ``` ### Technical Analysis The status checker accepts an arbitrary `--base-url`, constructs request URLs from it, and attaches `AIRESEARCHOS_API_KEY` whenever `--api-path` equals `/api/v1`. It does not enforce HTTPS, validate the hostname, reject embedded URL credentials, or restrict the destination to A ...[truncated 1676 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Default to an exact allowlist containing only `https://airesearchos.com`. 2. Parse destinations with `new URL()` and reject: - Protocols other than HTTPS. - Embedded usernames or passwords. - Unexpected ports. - Unapproved hostnames. - Malformed or ambiguous URLs. 3. Do not forward the primary AIresearchOS key to custom origins. Require a separate, explicitly named credential for each custom endpoint. 4. Require explicit user approval before enabling a custom authenticated origin and display the normalized destination without displaying the credential. 5. Disable automatic redirects or validate every redirect target and prohibit cross-origin redirects when authorization headers are present. 6. Restrict `apiPath` to an explicit enumeration such as `/api/v1` or `/api/x402`; do not accept arbitrary path strings. 7. Apply the same endpoint policy to every authenticated `curl` command documented in `SKILL.md`. 8. Add automated tests confirming that credentials are never transmitted to HTTP URLs, unapproved origins, or redirect targets. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:78
Finding
Shell Command Injection Through Unescaped Research Queries and Clarification Answers<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:78-81, 109-112, 199-202` **Vulnerability Type**: Shell command injection caused by direct interpolation into shell commands **Risk Level**: High ### Vulnerable Code API-key research submission: ```bash curl -s -X POST "${AIRESEARCHOS_BASE_URL:-https://airesearchos.com}/api/v1/research" \ -H "Authorization: Bearer $AIRESEARCHOS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query":"<USER_QUERY>","mode":"<MODE>","reportLength":"standard","skipClarifyingQuestions":false}' ``` x402 research submission: ```bash node {baseDir}/scripts/x402-request.mjs \ --url "${AIRESEARCHOS_BASE_URL:-https://airesearchos.com}/api/x402/research/<ENDPOINT_SLUG>" \ --method POST \ --body '{"query":"<USER_QUERY>","reportLength":"standard"}' \ --max-payment <MAX_USDC> ``` Clarification submission: ```bash curl -s -X POST "${AIRESEARCHOS_BASE_URL:-https://airesearchos.com}/api/v1/research/<ID>/clarify" \ -H "Authorization: Bearer $AIRESEARCHOS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"answers":["<ANSWER_1>","<ANSWER_2>","<ANSWER_3>"]}' ``` ### Technical Analysis The Skill tells the Agent to substitute user-controlled research queries and clarification answers into single-quoted shell arguments. A single quote in user input terminates the shell string. Subsequent characters can then be interpreted as shell syntax rather than request data. The documented input checks only constrain type, length, and allowed enumerations. They do not provide shell escaping or JSON serialization. JSON escaping by itself would also be insufficient if the resulting data were still interpolated unsafely into a shell command. Because these commands run in the Agent environment, successful command injection executes with the same operating-system privileges and environment access as the Agent. The x402 command is especially sensitive because the process environment may contain `AIRESEARCHOS_WALLET_KEY ...[truncated 1384 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not construct network requests by interpolating user text into shell commands. 2. Implement submission and clarification in a Node.js helper that: - Receives values as separately parsed arguments or through standard input. - Builds request objects in memory. - Uses `JSON.stringify()` to serialize request bodies. - Calls `fetch()` directly without a shell. 3. Prefer standard input for large or sensitive free-form text so it is not exposed in process listings. 4. If a shell command is unavoidable, pass data through positional parameters to a fixed script and never evaluate or concatenate it into command text. 5. Validate research mode, report length, API path, endpoint slug, and UUID values using strict executable-code allowlists. 6. Add tests containing single quotes, double quotes, command substitutions, newlines, backticks, semicolons, Unicode control characters, and malformed JSON. 7. Ensure cron payloads contain only generated identifiers that have passed strict UUID validation. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/x402-request.mjs:29
Finding
Insufficient Validation of x402 Payment Destination and Spending Bounds<![CDATA[ ## Vulnerability Details **File Location**: `scripts/x402-request.mjs:29-34, 63, 95-129, 150` **Vulnerability Type**: Unsafe payment signing based on untrusted server requirements **Risk Level**: High ### Vulnerable Code ```javascript const url = args.url const method = args.method || 'POST' const body = args.body || '' const maxPaymentUsd = parseFloat(args['max-payment'] || '1.00') ``` ```javascript const initialResponse = await fetch(url, fetchOpts) ``` ```javascript const req = paymentRequired.accepts[0] const version = paymentRequired.x402Version const requiredUsd = parseInt(req.amount) / 1_000_000 process.stderr.write(JSON.stringify({ step: 'payment_required', price_usdc: requiredUsd, pay_to: req.payTo, network: req.network, }) + '\n') // Step 3: Check max payment safety if (requiredUsd > maxPaymentUsd) { process.stderr.write(JSON.stringify({ error: 'payment_exceeds_max', required: req.amount, required_usdc: requiredUsd, max: String(Math.round(maxPaymentUsd * 1_000_000)), max_usdc: maxPaymentUsd, }) + '\n') process.exit(1) } // Step 4: Sign payment process.stderr.write(JSON.stringify({ step: 'signing_payment' }) + '\n') let encodedPayment try { const { x402Version, payload: innerPayload } = await evmScheme.createPaymentPayload(version, req) const paymentPayload = { x402Version, accepted: req, payload: innerPayload, } encodedPayment = encodePaymentSignatureHeader(paymentPayload) } catch (err) { ``` The signed payment is returned to the same unrestricted URL: ```javascript const paidResponse = await fetch(url, paidOpts) ``` ### Technical Analysis The helper accepts any URL and trusts the first payment option supplied by a remote HTTP 402 response. Before requesting a wallet signature, it does not independently enforce: - HTTPS transport. - The expected AIresearchOS origin. - Base mainnet or chain ID 8453. - The canonical USDC asset contract. - An approved facilitator or payment recip ...[truncated 2187 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse the destination with `new URL()` and require HTTPS. 2. Restrict payment-capable requests to an explicit allowlist of approved origins. 3. Reject redirects or validate every redirect target before sending a payment header. 4. Validate `--max-payment` with a strict decimal expression and require a finite, positive value within an application-defined ceiling. 5. Validate `req.amount` as a digit-only, positive integer and compare integer base units using `BigInt`; avoid floating-point arithmetic for payment authorization. 6. Enforce the exact expected: - Base mainnet chain identifier. - Canonical USDC contract address. - Token decimals. - x402 version and payment scheme. - Facilitator policy. 7. Require the recipient to match a configured or user-approved payee. 8. Examine all payment options and select only one that satisfies policy; do not automatically trust `accepts[0]`. 9. Before signing, present the normalized chain, asset, amount, and recipient and obtain explicit user confirmation for every x402 payment, as required by the Skill's declared behavior. 10. Apply an immutable application-level maximum that cannot be raised solely by untrusted or generated command arguments. 11. Add tests for `NaN`, infinity, exponent notation, negative values, oversized integers, incorrect token decimals, wrong chains, wrong assets, and unexpected recipients. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/package.json:7
Finding
Mutable Third-Party Dependencies Installed Without an Integrity-Pinned Lockfile<![CDATA[ ## Vulnerability Details **File Location**: `scripts/package.json:7-10`; installation instructions in `SKILL.md:93-97` and `SETUP.md:86-94` **Vulnerability Type**: Unpinned dependency resolution and lifecycle-capable first-use installation **Risk Level**: Medium ### Vulnerable Code ```json "dependencies": { "@x402/core": "^2.3.0", "@x402/evm": "^2.3.0", "viem": "^2.45.2" } ``` The Skill instructs the Agent to perform a mutable installation at runtime: ```bash if [ ! -d "{baseDir}/scripts/node_modules" ]; then cd {baseDir}/scripts && npm install fi ``` The setup guide repeats the operation: ```bash cd {baseDir}/scripts && npm install ``` ### Technical Analysis The repository does not include a lockfile, and all three direct dependencies use caret ranges. Consequently, `npm install` can resolve package and transitive dependency versions that differ from those present when the Skill was reviewed. The installation is performed on first use rather than as a controlled build step. By default, npm installation can execute package lifecycle scripts. This creates a supply-chain execution path before the payment helper handles the wallet private key. The identified package names do not, based on the available project evidence, establish typosquatting or deliberate malicious dependencies. The issue is the absence of reproducible, integrity-pinned resolution and the execution of newly resolved dependency code in a sensitive wallet context. ### Attack Path 1. A direct or transitive dependency account, release process, or registry distribution path is compromised, or a future compatible version introduces malicious behavior. 2. The Skill is used on a system where `scripts/node_modules` does not exist. 3. Following `SKILL.md`, the Agent runs `npm install`. 4. npm resolves versions allowed by the caret ranges rather than versions fixed during audit. 5. Malicious lifecycle code may execute during installation, or malicious runtime code loads when `x ...[truncated 564 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin direct dependencies to exact reviewed versions rather than caret ranges. 2. Generate, review, and commit `package-lock.json` with integrity hashes. 3. Replace first-use `npm install` with `npm ci` so installation fails if the manifest and lockfile disagree. 4. Use `npm ci --ignore-scripts` where package compatibility permits. 5. If lifecycle scripts are required, document and audit each required script explicitly. 6. Install dependencies during a controlled deployment or build stage rather than automatically when a wallet operation is requested. 7. Use automated dependency review, vulnerability scanning, provenance verification, and controlled update pull requests. 8. Run the payment helper in a restricted environment with minimal filesystem and network access. 9. Keep only limited funds in the operational x402 wallet and separate it from wallets holding unrelated assets. ]]>
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 (10)

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README explicitly instructs users to place a blockchain wallet private key in local skill configuration, but it does not clearly warn that this value is highly sensitive and can enable irreversible theft of on-chain funds if the host, config file, backups, or repo are exposed. In the context of a payment-enabled skill using USDC on Base, normalizing private-key placement in config increases the chance of unsafe storage, accidental commit, or overbroad file access by other local tools.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill performs privileged actions involving environment secrets and outbound network requests, but it does not declare an explicit tool scope or allowed-tools policy. That increases the blast radius because a host agent may grant broader execution capability than necessary, making accidental misuse or abuse of API keys and network access more likely.

External Transmission

Medium
Category
Data Exfiltration
Content
### Submit Research (API Key)

```bash
curl -s -X POST "${AIRESEARCHOS_BASE_URL:-https://airesearchos.com}/api/v1/research" \
  -H "Authorization: Bearer $AIRESEARCHOS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"<USER_QUERY>","mode":"<MODE>","reportLength":"standard","skipClarifyingQuestions":false}'
Confidence
86% confidence
Finding
This command sends user-supplied research queries to an external service together with authenticated API access. While external transmission is expected for this skill's purpose, it is still a real data-exfiltration surface because sensitive prompts or business information may be transmitted off-platform to a third party.

External Transmission

Medium
Category
Data Exfiltration
Content
### After Submission: Schedule Background Check via Cron

**CRITICAL: Do NOT poll inline. Do NOT loop. Do NOT run poll-research.mjs. Do NOT run repeated curl commands. Use the `cron` tool.**

After the POST request returns with the research ID, do TWO things:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The script loads a raw EVM private key from an environment variable and uses it to sign payment payloads for server-provided 402 payment requirements, but provides no strong safety controls or warning about the signing and spending implications. In this payment-enabled skill, that increases risk of misuse if a user points the script at an untrusted endpoint, because the tool can be induced to sign payment data with valuable credentials.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script sends an arbitrary caller-supplied request body to any caller-supplied URL via fetch, with no destination allowlist, confirmation step, or warning that sensitive research data may be disclosed to a third party. In this skill context, the helper is explicitly designed to submit research jobs and may handle proprietary prompts, API outputs, or user data, so silent exfiltration risk is real even if the behavior is intended.

Missing User Warnings

Low
Confidence
90% confidence
Finding
The setup guide states that on first use the agent will run `npm install`, which executes package installation logic and modifies the local filesystem by creating `node_modules` and downloading code. Although this behavior is disclosed in the x402 dependency section, the warning is not especially prominent and users may not appreciate that enabling the skill can trigger code retrieval and execution-adjacent package lifecycle activity from the network in a local skill directory.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"type": "module",
  "description": "Helper scripts for the AIresearchOS OpenClaw skill",
  "dependencies": {
    "@x402/core": "^2.3.0",
    "@x402/evm": "^2.3.0",
    "viem": "^2.45.2"
  }
Confidence
94% confidence
Finding
The dependency uses a caret range (^2.3.0), which allows future non-breaking releases to be installed automatically. This can introduce supply-chain risk if a newly published upstream version is compromised, malicious, or unexpectedly changes behavior, and the risk is somewhat heightened here because the package set includes payment and blockchain-related libraries.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"description": "Helper scripts for the AIresearchOS OpenClaw skill",
  "dependencies": {
    "@x402/core": "^2.3.0",
    "@x402/evm": "^2.3.0",
    "viem": "^2.45.2"
  }
}
Confidence
94% confidence
Finding
The dependency uses a caret range (^2.3.0), allowing automatic resolution to newer minor/patch versions. That creates a real but low-severity supply-chain exposure, especially in a skill that handles x402 payment flows where dependency compromise could affect transaction logic or credential handling.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
    "@x402/core": "^2.3.0",
    "@x402/evm": "^2.3.0",
    "viem": "^2.45.2"
  }
}
Confidence
94% confidence
Finding
Using ^2.45.2 permits unreviewed future releases within the same major version to be installed, which weakens build reproducibility and increases exposure to upstream compromise. In this context, the library is part of a blockchain/payment-oriented helper script set, so unexpected dependency changes may have security-sensitive effects even if the base issue remains low severity.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/x402-request.mjs:41