Back to skill

Security audit

mobile app builder with live link, publishes to app store, create ai apps

Security checks for vulnerabilities and agentic risk

Overview

The skill is broadly coherent with LaunchPulse app building, but it also exposes high-impact production, billing, database, publishing, and secret-handling actions with insufficient guardrails.

Review this skill before installing. Use it only with a trusted LaunchPulse account and avoid supplying production payment, GitHub, Fly.io, app-store, or database credentials unless you understand where they are sent and stored. Do not use --api-base or LAUNCHPULSE_API_BASE_URL except for a trusted local backend, and consider removing or permission-hardening the stored auth file after use.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/launchpulse.cjs:126
Finding
Authentication and third-party secrets can be redirected to an arbitrary API endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/launchpulse.cjs`, lines 126-129, 318, 365-368, 717-719, 1458-1477, 2069-2079, and 2160-2184 **Vulnerability Type**: Unrestricted destination for sensitive network requests **Risk Level**: High ### Vulnerable Code ```js function normalizeApiBaseUrl(value) { const fallback = DEFAULT_API_BASE_URL; const raw = value && String(value).trim().length ? String(value).trim() : fallback; return raw.replace(/\/+$/, ''); } ``` ```js let apiBase = process.env.LAUNCHPULSE_API_BASE_URL || DEFAULT_API_BASE_URL; ``` ```js if (a === '--api-base') { apiBase = args[i + 1] || apiBase; i += 1; continue; } ``` ```js function buildAuthHeaders(bearerToken) { return bearerToken ? { Authorization: `Bearer ${bearerToken}` } : {}; } ``` The same configurable backend can receive third-party deployment credentials: ```js const githubUsername = cfg.githubUsername || process.env.GITHUB_USERNAME || null; const githubToken = cfg.githubToken || process.env.GITHUB_TOKEN || null; const flyApiToken = cfg.flyToken || process.env.FLY_API_TOKEN || null; if (!githubUsername || !githubToken || !flyApiToken) { throw new Error('deploy --target fly requires --github-username, --github-token, and --fly-token (or env vars)'); } startResult = await fetchJson( withUserId(`${cfg.apiBase}/project/${encodeURIComponent(projectId)}/deploy`, legacyUserId), { method: 'POST', headers, body: { ...(legacyUserId ? { userId: legacyUserId } : {}), githubUsername, githubToken, flyApiToken, }, timeoutMs: 60_000, }, ); ``` It can also receive environment and payment secrets: ```js const saveResult = await fetchJson( withUserId(`${cfg.apiBase}/project/${encodeURIComponent(projectId)}/env-files/save`, legacyUserId), { method: 'POST', headers, body: { filePath: envPath, variables: generatedVars, }, timeoutMs: 120_000, }, ); ``` ### Technical Analysis The ` ...[truncated 2844 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse the backend value with the standard `URL` class and reject malformed URLs, embedded credentials, fragments, and schemes other than HTTPS. 2. Allow the production endpoint only at an explicit hostname such as `https://api.launchpulse.ai`. 3. If local development must remain supported, permit HTTP only for loopback destinations such as `127.0.0.1`, `[::1]`, and carefully validated `localhost`. 4. Require explicit user confirmation before sending credentials to any non-default HTTPS hostname. 5. Do not forward a stored production PAT automatically to an overridden development backend. Require a separate development credential. 6. Apply a destination policy before constructing any authenticated request, not only during argument parsing. 7. Warn clearly when deployment, payment, environment, store, or domain-registration data will be transmitted to a non-default backend. 8. Prefer narrowly scoped, short-lived service credentials and separate tokens by operation. 9. Add automated tests covering attacker-controlled hosts, non-loopback HTTP addresses, embedded URL credentials, redirects, and unusual URL encodings. 10. Review redirect behavior and prevent authorization headers or sensitive bodies from being forwarded across origins. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/launchpulse.cjs:187
Finding
LaunchPulse personal access token is stored without explicit restrictive permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/launchpulse.cjs`, lines 187-205 **Vulnerability Type**: Insecure plaintext credential storage **Risk Level**: Medium ### Vulnerable Code ```js function saveStoredAuth(pat, userId) { const authPath = getAuthFilePath(); ensureDir(path.dirname(authPath)); fs.writeFileSync( authPath, JSON.stringify( { pat, userId: userId || null, savedAt: nowIso(), }, null, 2, ), 'utf8', ); } ``` ### Technical Analysis The device-login PAT is written in plaintext to the local `auth.json` file. The write operation does not specify a restrictive file mode such as `0600`, and the containing directory is created without an explicit `0700` mode. Consequently, effective access permissions depend on the operating system, process umask, pre-existing directory permissions, and pre-existing file mode. On systems with permissive defaults, the token file may be readable by other local users. If the file already exists with unsafe permissions, rewriting it does not explicitly correct those permissions. Local storage is necessary for the documented persistent login flow, but relying entirely on ambient permission defaults is not appropriate for a reusable bearer credential. ### Attack Path 1. The user completes the LaunchPulse device-login flow. 2. The script receives a PAT and calls `saveStoredAuth`. 3. The PAT is written to `${OPENCLAW_STATE_DIR:-~/.openclaw}/launchpulse/auth.json` without an explicit restrictive mode. 4. A local user or process with permission to traverse the directory checks the file. 5. If the effective filesystem permissions permit access, the local actor reads the plaintext PAT. 6. The actor submits the stolen bearer token to the LaunchPulse API and performs operations allowed by the token. This attack requires local filesystem access and permissive effective permissions; it does not independently provide remote access. ### Impact ...[truncated 554 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create the LaunchPulse state directory with mode `0700`. 2. Create the authentication file atomically with mode `0600`, for example by writing to a securely created temporary file in the same directory and renaming it. 3. Explicitly call `chmod` on existing authentication files to correct unsafe legacy permissions. 4. Refuse to load the token when the file is owned by another user or has group/world-readable permissions, and provide a safe remediation message. 5. Avoid following symbolic links when creating or replacing the credential file. 6. Use the operating system credential store or keychain where available instead of a plaintext JSON file. 7. Ensure logout securely removes the stored credential and report failure rather than silently leaving the token behind. 8. Document the token location, required permissions, scope, and revocation procedure. 9. Prefer short-lived or revocable tokens with the minimum API permissions required. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/launchpulse.cjs:1789
Finding
Fly.io API token is transmitted in a URL query parameter<![CDATA[ ## Vulnerability Details **File Location**: `scripts/launchpulse.cjs`, lines 1789-1792 **Vulnerability Type**: Sensitive credential exposure through URL logging **Risk Level**: Medium ### Vulnerable Code ```js if (cfg.flyToken) url.searchParams.set('flyApiToken', cfg.flyToken); if (cfg.region) url.searchParams.set('region', cfg.region); const result = await fetchJson(url.toString(), { method: 'GET', headers, timeoutMs: 30_000 }); ``` ### Technical Analysis For the domain-status operation, the script adds the Fly.io API token to the request URL as the `flyApiToken` query parameter. HTTPS protects the URL while it is in transit between endpoints, but query strings are routinely retained by systems outside the application's direct control, including: - Backend and reverse-proxy access logs. - Load-balancer and API-gateway logs. - Application performance monitoring and distributed tracing. - Error reports and debugging output. - Browser or intermediary history if the URL is copied or replayed. The token is an authentication secret and should not form part of a URL. Other operations already demonstrate that structured request bodies can be used, so query-string transport is not necessary for the declared domain-management functionality. ### Attack Path 1. The user invokes the domain-status operation with `--fly-token` or a corresponding token source. 2. The script appends the complete Fly.io token to the URL query string. 3. The request passes through the LaunchPulse API infrastructure. 4. A proxy, load balancer, backend server, monitoring platform, or error-reporting system records the complete request URL. 5. An individual or compromised service with access to those logs extracts the token. 6. The token is reused against Fly.io APIs to perform operations allowed by its scope. ### Impact Assessment An exposed Fly.io token may grant control over Fly.io resources authorized to that credential. Depending on its scope, this can include: - Reading ap ...[truncated 473 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `flyApiToken` from the query string. 2. Change the endpoint to accept the token in a protected request header or POST body. 3. Prefer a dedicated authorization header for the Fly.io credential if the backend contract permits it. 4. Ensure application, proxy, gateway, tracing, and error logs redact sensitive headers and request bodies. 5. Rotate any Fly.io token that may already have appeared in URL logs. 6. Review historical logs and telemetry retention systems for the `flyApiToken` parameter and securely remove or redact affected records. 7. Use short-lived, narrowly scoped Fly.io credentials rather than broad, long-lived account tokens. 8. Add tests that fail if fields matching token or secret names are placed in URL query parameters. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (22)

Credential Access

High
Category
Privilege Escalation
Content
The first time you run the skill, it automatically starts a device-login flow:
1. OpenClaw prints a sign-in link
2. You open it, sign in with Google on launchpulse.ai, and confirm
3. OpenClaw receives a personal access token (PAT) and stores it locally

Token storage path: `${OPENCLAW_STATE_DIR:-~/.openclaw}/launchpulse/auth.json`
Confidence
95% confidence
Finding
The skill obtains a personal access token and stores it locally in a predictable path, while also allowing PAT/JWT injection through environment variables and command flags. These practices materially increase exposure risk through local file compromise, weak file permissions, backups, shell history, debug logs, or accidental disclosure, and the token appears sufficient to access the user's LaunchPulse account and projects.

Credential Access

High
Category
Privilege Escalation
Content
- `/launchpulse storage init`
- `/launchpulse storage upload <projectId> --payload-file ./upload.json`
- `/launchpulse env-files list <projectId>`
- `/launchpulse env-files save <projectId> --file-path vitereact/.env --vars-file ./vars.json`
- `/launchpulse payments inject-env <projectId> --project-type vitereact`
- `/launchpulse payments setup <projectId> --project-type expo --stripe-publishable-key pk_test_... --revenuecat-ios-key appl_... --revenuecat-secret-key sk_...`
Confidence
97% confidence
Finding
The skill supports writing .env files and passing payment and platform keys, including sensitive secret material, through CLI arguments and files. This is dangerous because secrets can leak through shell history, process listings, logs, workspace files, or accidental inclusion in generated projects, leading to credential compromise and abuse of payment or backend services.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The CLI’s declared purpose is app generation, but its exposed command surface also includes deployment, domain registration/mapping, billing upgrades, database access, app-store publishing, storage operations, and payment secret handling. This capability expansion materially increases the attack surface and the chance that an agent or user invokes high-impact operations outside the expected trust boundary.

Credential Access

High
Category
Privilege Escalation
Content
--api-base <url>        (default: env LAUNCHPULSE_API_BASE_URL or hosted LaunchPulse API)
  --pat <token>           (default: env LAUNCHPULSE_PAT; or stored device-login token)
  --api-key <token>       alias for --pat (for API-key style workflows)
  --access-token <jwt>    (default: env LAUNCHPULSE_ACCESS_TOKEN; Supabase access token)
  --user-id <uuid>        legacy fallback for older LaunchPulse backends that require userId
  --no-login              fail instead of starting device-login flow when unauthenticated
  --name <project-slug>   preferred project id/slug
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
--api-base <url>        (default: env LAUNCHPULSE_API_BASE_URL or hosted LaunchPulse API)
  --pat <token>           (default: env LAUNCHPULSE_PAT; or stored device-login token)
  --api-key <token>       alias for --pat (for API-key style workflows)
  --access-token <jwt>    (default: env LAUNCHPULSE_ACCESS_TOKEN; Supabase access token)
  --user-id <uuid>        legacy fallback for older LaunchPulse backends that require userId
  --no-login              fail instead of starting device-login flow when unauthenticated
  --name <project-slug>   preferred project id/slug
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
--api-base <url>        (default: env LAUNCHPULSE_API_BASE_URL or hosted LaunchPulse API)
  --pat <token>           (default: env LAUNCHPULSE_PAT; or stored device-login token)
  --api-key <token>       alias for --pat (for API-key style workflows)
  --access-token <jwt>    (default: env LAUNCHPULSE_ACCESS_TOKEN; Supabase access token)
  --user-id <uuid>        legacy fallback for older LaunchPulse backends that require userId
  --no-login              fail instead of starting device-login flow when unauthenticated
  --name <project-slug>   preferred project id/slug
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
--api-base <url>        (default: env LAUNCHPULSE_API_BASE_URL or hosted LaunchPulse API)
  --pat <token>           (default: env LAUNCHPULSE_PAT; or stored device-login token)
  --api-key <token>       alias for --pat (for API-key style workflows)
  --access-token <jwt>    (default: env LAUNCHPULSE_ACCESS_TOKEN; Supabase access token)
  --user-id <uuid>        legacy fallback for older LaunchPulse backends that require userId
  --no-login              fail instead of starting device-login flow when unauthenticated
  --name <project-slug>   preferred project id/slug
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
App-store publishing and 2FA submission are high-trust account actions with irreversible external consequences, including releasing software and handling authentication challenges. Exposing them in a broadly described generation skill makes accidental or induced misuse more likely, especially in agentic contexts where users may not expect publication behavior.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
Domain search, checkout, registration, verification, and mapping allow actions that can incur cost, affect public routing, and modify externally visible infrastructure. These operations exceed the expected scope of a quick-start app builder and could be abused by a prompt-injected agent flow to purchase domains or redirect traffic.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The `db query` action accepts arbitrary SQL and forwards it to a backend project database endpoint, enabling unrestricted reads or mutations if backend authorization is permissive. In an agent skill, this is especially dangerous because natural-language workflows could be steered into exfiltrating data, altering records, or damaging application state.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The payments setup flow collects Stripe and RevenueCat keys, then transmits and stores them through remote API endpoints and env-file save operations. This creates significant secret-handling risk: a compromised agent flow, backend, logs, or mis-scoped permissions could expose payment credentials or inject them into the wrong project.

Credential Access

High
Category
Privilege Escalation
Content
throw new Error('Usage: launchpulse.cjs payments setup <projectId> [--project-type <vitereact|expo>] [keys...] [--vars-file <json>]');
      }
      const projectType = normalizeProjectType(cfg.projectType || args[1]) || 'vitereact';
      const envPath = cfg.filePath || (projectType === 'expo' ? 'expo/.env' : 'vitereact/.env');

      const generatedVars = [];
      if (cfg.stripePublishableKey) {
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The skill explicitly directs the assistant to run a Node script, perform networked API operations, and use environment variables/tokens, but it declares no tool scope or permissions boundary. That omission can cause users or hosting platforms to underestimate the skill's capabilities, reducing informed consent and weakening policy enforcement around network and credential use.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
**What it does:**
1. Creates a new project (web or Expo mobile)
2. Plans MVP features using AI and auto-approves them
3. Starts Quick Start (single-pass background build)
4. Returns immediately with project id/session id so progress can be monitored
5. Iterates on existing projects with change requests
Confidence
87% confidence
Finding
The skill says it 'auto-approves' AI-planned MVP features, meaning an autonomous system can decide what will be built and initiated without a manual review checkpoint. In the context of code generation, deployment, and downstream production operations, this raises the risk of unintended functionality, insecure defaults, cost-incurring actions, or propagation of harmful changes from ambiguous prompts.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill states that a personal access token is obtained and stored locally, and also supports direct token injection via flags/environment variables, but it does not present this as a clear security warning to the user. This is dangerous because users may provide high-value credentials without understanding storage, reuse, exposure, or local compromise risks.

Session Persistence

Medium
Category
Rogue Agent
Content
- `/launchpulse upgrade --tokens 0` (buy 50K token pack)

**Quick Start a web app:**
- `/launchpulse web "Create a landing page for a dog walking business"`

**Quick Start a mobile app (Expo):**
- `/launchpulse mobile "Create an Expo app for habit tracking"`
Confidence
60% confidence
Finding
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.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The documented production operations include deploys, domain changes, database queries, environment-file writes, storage actions, and payment/app-store setup, but there are no user-facing warnings about their security-sensitive or potentially destructive nature. In a user-invocable skill, this increases the chance of accidental deployment changes, credential mishandling, data exposure, or modification of production systems without sufficient confirmation.

External Transmission

Medium
Category
Data Exfiltration
Content
const os = require('node:os');
const path = require('node:path');
const VERSION = '1.1.1';
const DEFAULT_API_BASE_URL = 'https://api.launchpulse.ai/api';

function usage(exitCode) {
  const text = `
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
const os = require('node:os');
const path = require('node:path');
const VERSION = '1.1.1';
const DEFAULT_API_BASE_URL = 'https://api.launchpulse.ai/api';

function usage(exitCode) {
  const text = `
Confidence
50% 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
93% confidence
Finding
The device-login PAT is written to a persistent local auth file without any permission hardening or meaningful warning to the user about credential persistence. On multi-user systems or insecure environments, this increases the risk of token theft and unauthorized reuse.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The manifest focuses on building apps from descriptions and starting builds, but the `upgrade` command implements commerce and account-billing actions. Billing plan management is not an obvious requirement for the core build-from-text workflow and represents an account-management capability outside the stated purpose.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill accepts highly sensitive payment/service keys and sends them to remote API endpoints without prominent safety messaging or secure-entry guidance. In an agent setting, users may not realize they are disclosing production secrets to a hosted backend, increasing the chance of mishandling or over-sharing.

Static analysis

Detected: suspicious.env_credential_access, suspicious.potential_exfiltration

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/launchpulse.cjs:163

Sensitive-looking file read is paired with a network send.

Warn
Code
suspicious.potential_exfiltration
Location
scripts/launchpulse.cjs:176