Back to skill

Security audit

Oganim Deploy

Security checks for vulnerabilities and agentic risk

Overview

The skill is a disclosed deployment runbook, but it gives agents broad production credentials and includes unsafe production account-testing workflows.

Install only if this is your own project and you intend to let the agent operate production deployment, database, CRM, and Supabase admin workflows. Prefer staging data, pinned dependencies, isolated execution without broad secret-file access, explicit approval before production changes, and remove the customer-password mutation recipe before use.

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

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/playwright-recipes.md:130
Finding
Production customer passwords can be destructively overwritten during testing<![CDATA[ ## Vulnerability Details **File Location**: `references/playwright-recipes.md:130-159` **Vulnerability Type**: Production account mutation using unrestricted administrative credentials **Risk Level**: High ### Vulnerable Code ```js const cred = Object.fromEntries( fs.readFileSync(process.env.HOME + '/.openclaw/credentials/supabase/credentials.env', 'utf8') .split('\n').filter(l => l.includes('=')) .map(l => { const i = l.indexOf('='); return [l.slice(0, i), l.slice(i + 1)]; }) ); const USER_ID = '...'; const TEMP_PASS = 'temp-' + Date.now(); await fetch(`${cred.SUPABASE_URL}/auth/v1/admin/users/${USER_ID}`, { method: 'PUT', headers: { apikey: cred.SUPABASE_SERVICE_ROLE_KEY, Authorization: 'Bearer ' + cred.SUPABASE_SERVICE_ROLE_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ password: TEMP_PASS }), }); // ... do test work ... // IMPORTANT: scramble the password back so the test password isn't usable. await fetch(`${cred.SUPABASE_URL}/auth/v1/admin/users/${USER_ID}`, { method: 'PUT', headers: { apikey: cred.SUPABASE_SERVICE_ROLE_KEY, Authorization: 'Bearer ' + cred.SUPABASE_SERVICE_ROLE_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ password: 'reset-' + Math.random().toString(36) }), }); ``` ### Technical Analysis The recipe uses the production Supabase service-role key to replace the password of an arbitrary user identified by `USER_ID`. A service-role key bypasses normal row-level authorization and can administer the entire Supabase project. The purported cleanup does not restore the original password. It changes the password to another unknown value, permanently invalidating the customer's original credentials. In addition, the initial temporary password is based only on the current timestamp and is not cryptographically random. If execution terminates before the second request, that predictable password can remain active. Mutating a real customer's cr ...[truncated 1172 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the password-mutation recipe from production workflows. - Perform portal testing in a separate staging Supabase project with disposable synthetic users. - Use a dedicated, allowlisted test account rather than an arbitrary `USER_ID`. - Prefer single-use magic links or a purpose-built, audited impersonation mechanism that does not alter user credentials. - Never attempt to “restore” a password by replacing it with another unknown value. - Require explicit human approval for every production administrative operation. - If any temporary mutation remains necessary in a non-production environment, use a cryptographically random secret, wrap cleanup in `try/finally`, impose a short expiration, and verify rollback. - Rotate the production service-role key if this recipe has been exposed to untrusted operators or execution environments. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
references/playwright-recipes.md:26
Finding
Routine deployment and verification workflows consume excessively privileged production credentials<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:38-39, 76, 149-171`; `references/playwright-recipes.md:26-41, 105, 132-148` **Vulnerability Type**: Violation of least privilege through production-wide deployment, database, service-role, and administrator credentials **Risk Level**: High ### Vulnerable Code From `SKILL.md`: ```bash VERCEL_TOKEN=$(cat ~/.openclaw/credentials/vercel/token) \ npx vercel deploy --prod --yes ``` ```text The runner connects to the Supabase pooler as the `postgres` superuser using credentials from `~/.openclaw/credentials/supabase/credentials.env`. ``` ```text - `~/.openclaw/credentials/vercel/token` (chmod 600) - `~/.openclaw/credentials/supabase/credentials.env` — `SUPABASE_URL`, `SUPABASE_PROJECT_REF`, `SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY`, `SUPABASE_DB_PASSWORD` - `~/.openclaw/credentials/supabase/admin_password.txt` — your CRM admin password. Treat as production credential. ``` From `references/playwright-recipes.md`: ```js const cred = Object.fromEntries( fs.readFileSync(process.env.HOME + '/.openclaw/credentials/supabase/credentials.env', 'utf8') .split('\n').filter(l => l.includes('=')) .map(l => { const i = l.indexOf('='); return [l.slice(0, i), l.slice(i + 1)]; }) ); const gen = await fetch(`${cred.SUPABASE_URL}/auth/v1/admin/generate_link`, { method: 'POST', headers: { apikey: cred.SUPABASE_SERVICE_ROLE_KEY, Authorization: 'Bearer ' + cred.SUPABASE_SERVICE_ROLE_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ type: 'magiclink', email }), }).then(r => r.json()); ``` ```js const PASS = fs.readFileSync(process.env.HOME + '/.openclaw/credentials/supabase/admin_password.txt', 'utf8').trim(); ``` ### Technical Analysis The Skill's normal workflow combines several highly privileged production capabilities: - A Vercel token capable of production deployment - PostgreSQL superuser credentials used for migrations - A Supabase service-role key that ...[truncated 1786 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Separate production deployment, migration, authentication, and UI-testing identities. - Use staging infrastructure and synthetic test data for Playwright probes. - Replace long-lived credentials with short-lived, task-scoped tokens. - Restrict the Vercel token to the required project and deployment operation. - Run migrations through a controlled deployment service rather than exposing PostgreSQL superuser credentials to an agent workflow. - Use a dedicated test-only Supabase project or narrowly scoped server endpoint instead of exposing the service-role key. - Store only the precise variables needed by each task in separate credential files. - Require explicit confirmation before production deployment, migration, administrative authentication, or customer-data access. - Run probes in an isolated environment that cannot read unrelated credentials. - Rotate credentials after suspected disclosure and maintain audit logs for all privileged operations. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/setup-playwright.sh:14
Finding
Unpinned npm packages are downloaded and executed in a credential-bearing environment<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup-playwright.sh:14-18`; also `SKILL.md:38-39, 146` and `references/playwright-recipes.md:9` **Vulnerability Type**: Mutable third-party dependency execution without a reviewed version or integrity lock **Risk Level**: Medium ### Vulnerable Code ```bash # Install playwright if not already present if [ ! -d node_modules/playwright ]; then echo "Installing playwright (no-save) ..." npm i playwright --no-save 2>&1 | tail -3 fi ``` The deployment workflow also invokes an unpinned CLI: ```bash VERCEL_TOKEN=$(cat ~/.openclaw/credentials/vercel/token) \ npx vercel deploy --prod --yes ``` The documented setup repeats the unpinned installation: ```bash mkdir -p /tmp/<project>-test && cd /tmp/<project>-test npm init -y >/dev/null && npm i playwright --no-save ``` ### Technical Analysis Neither `playwright` nor the Vercel CLI is pinned to a reviewed version. The Skill does not include a lockfile or integrity metadata for the temporary npm project. As a result, npm resolves mutable package and transitive dependency versions at execution time. `npx vercel` may download and execute the current registry version directly. npm packages and their dependencies can execute lifecycle scripts during installation. Such code inherits the filesystem and environment permissions of the invoking user, which is particularly dangerous because the documented workflow has access to production Vercel and Supabase credentials. No malicious npm package is embedded in the reviewed project. The vulnerability is the uncontrolled supply-chain execution path. ### Attack Path 1. An attacker compromises an npm publisher account, a transitive dependency, or the package-distribution path. 2. The operator runs `npm i playwright --no-save` or `npx vercel deploy`. 3. npm resolves and downloads a version that was not reviewed with this Skill. 4. Package installation code or the downloaded CLI executes with the operator's per ...[truncated 544 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin exact reviewed versions of `playwright` and the Vercel CLI. - Commit a lockfile and install with `npm ci` rather than mutable `npm install`. - Use a locally installed, integrity-locked Vercel CLI instead of unpinned `npx`. - Verify package provenance and registry integrity before installation. - Disable npm lifecycle scripts with `--ignore-scripts` where compatible with the dependency. - Cache approved dependencies in a controlled artifact repository. - Run dependency installation in a sandbox without access to production credentials. - Separate installation from deployment so downloaded package code does not execute while secrets are available. - Use automated dependency scanning and controlled update review. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
references/playwright-recipes.md:45
Finding
Authenticated production probes disable TLS validation and Chromium sandboxing<![CDATA[ ## Vulnerability Details **File Location**: `references/playwright-recipes.md:45-46`; repeated at lines `76` and `108` **Vulnerability Type**: Disabled browser process isolation and server-authentication validation **Risk Level**: Medium ### Vulnerable Code ```js const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] }); const page = await (await browser.newContext({ ignoreHTTPSErrors: true })).newPage(); page.on('pageerror', e => console.log('[PAGEERROR]', e.message)); await page.goto(url, { waitUntil: 'networkidle' }); ``` The same security bypasses are used in other recipes: ```js const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] }); const page = await (await browser.newContext({ viewport: { width: 412, height: 915 }, ignoreHTTPSErrors: true, })).newPage(); ``` ```js const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] }); const page = await (await browser.newContext({ ignoreHTTPSErrors: true })).newPage(); ``` ### Technical Analysis `--no-sandbox` disables an important Chromium process-containment boundary. If a visited page exploits a browser vulnerability, the resulting process has a greater ability to affect the host. `ignoreHTTPSErrors: true` causes the browser to accept expired, mismatched, self-signed, or otherwise invalid certificates. This weakens assurance that credentials, magic-link tokens, and authenticated sessions are being sent to the intended production server. These options are unnecessary for a correctly configured public HTTPS production deployment. Their combined use is particularly risky because the recipes process real authentication tokens and administrative credentials. ### Attack Path 1. An attacker compromises the target website, DNS path, proxy, or network connection. 2. The probe accepts an invalid TLS certificate instead of terminating the connection. 3. The probe sends authentication material or loads attacker-controlled ...[truncated 592 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `ignoreHTTPSErrors: true` and fail closed on all TLS validation errors. - Remove `--no-sandbox` and run Chromium with its normal sandbox protections. - If a constrained CI environment cannot support the browser sandbox, move the probe into an isolated, unprivileged container or virtual machine. - Do not make production credentials available to a browser process with reduced containment. - Restrict outbound network access to explicitly approved HTTPS destinations. - Validate the expected hostname and certificate chain before transmitting authentication material. - Keep Chromium and Playwright pinned and promptly patched. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
references/playwright-recipes.md:53
Finding
Magic-link URLs and authenticated customer content may be retained in logs and shared workspace artifacts<![CDATA[ ## Vulnerability Details **File Location**: `references/playwright-recipes.md:53-58`; related artifact-copying instructions at lines `167-177` **Vulnerability Type**: Sensitive authentication and customer-data exposure through logs, screenshots, and temporary files **Risk Level**: Medium ### Vulnerable Code ```js const stillOnLogin = await page.locator('input[type="password"]').first().isVisible().catch(() => false); console.log('login form visible (= auth FAILED):', stillOnLogin); console.log('current URL:', page.url()); console.log('body excerpt:', (await page.locator('body').innerText()).slice(0, 300)); await page.screenshot({ path: '/tmp/oganim-test/magic-link.png' }); await browser.close(); ``` The Skill then recommends moving screenshots into an Agent-readable workspace: ```bash cp /tmp/oganim-test/foo.png ~/.openclaw/workspace/foo.png # Then: tool call → image(path=~/.openclaw/workspace/foo.png, prompt="...") ``` ```text `/tmp/oganim-test/` is outside the assistant's read boundary, so screenshots must live somewhere under `~/.openclaw/workspace/` to be inspected. ``` ### Technical Analysis The magic-link recipe prints the complete current URL after visiting a URL containing `token_hash`. Depending on application redirect behavior, that sensitive token may remain in the URL and be written to terminal or CI logs. The recipe also logs the first 300 characters of the authenticated page and takes an unrestricted screenshot. These outputs may contain customer names, account information, financial details, or other portal content. The instructions explicitly copy screenshots from a temporary test directory into an Agent-readable workspace, but do not prescribe redaction, restrictive permissions, retention limits, or deletion. ### Attack Path 1. The probe generates a real customer's magic-link token using the service-role key. 2. It opens the authenticated production portal. 3. The current URL, potentially including the authentication token ...[truncated 765 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Use synthetic staging accounts rather than real production customers. - Never log a complete authentication URL; strip query parameters and fragments before output. - Add centralized redaction for token hashes, authorization headers, email addresses, and customer identifiers. - Avoid logging authenticated page text unless a narrowly scoped assertion is required. - Mask sensitive elements before screenshots and crop captures to the component under test. - Create artifacts with restrictive permissions and randomized filenames in a private directory. - Do not copy sensitive screenshots into a shared Agent workspace. - Automatically delete screenshots, traces, videos, and logs immediately after verification. - Configure CI systems to redact secrets and enforce short artifact-retention periods. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (12)

Credential Access

High
Category
Privilege Escalation
Content
node deploy/run-migration.cjs deploy/migration-stageN-<topic>.sql
```

The runner connects to the Supabase pooler as the `postgres` superuser using credentials from `~/.openclaw/credentials/supabase/credentials.env`.

**Always include `reset role;`** at the top of any migration that creates triggers, alters tables, or replaces policies — otherwise the pooler may leave the session as `authenticated` from a previous transaction and ownership checks fail. See [references/migration-template.sql](references/migration-template.sql).
Confidence
95% confidence
Finding
The skill explicitly directs the agent to use a local file containing Supabase credentials to connect as the `postgres` superuser for migrations. Even though this may be operationally legitimate, exposing a path to high-value secrets and normalizing superuser access materially increases the risk of credential misuse, accidental disclosure, or destructive database actions if the skill is invoked in the wrong context.

Ae1

High
Category
analysis-evasion
Content
Then write a probe (see [references/playwright-recipes.md](references/playwright-recipes.md) for templates) that:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Credential Access

High
Category
Privilege Escalation
Content
## Project Secrets Reference

- `~/.openclaw/credentials/vercel/token` (chmod 600)
- `~/.openclaw/credentials/supabase/credentials.env` — `SUPABASE_URL`, `SUPABASE_PROJECT_REF`, `SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY`, `SUPABASE_DB_PASSWORD`
- `~/.openclaw/credentials/supabase/admin_password.txt` — your CRM admin password. Treat as production credential.

## See Also
Confidence
93% confidence
Finding
The skill enumerates a concrete credential file path and names sensitive values including the anon key, service role key, and DB password. This materially aids secret discovery and increases the chance that an automated agent, log pipeline, or downstream tool will access or expose credentials during execution.

Credential Access

High
Category
Privilege Escalation
Content
- `~/.openclaw/credentials/vercel/token` (chmod 600)
- `~/.openclaw/credentials/supabase/credentials.env` — `SUPABASE_URL`, `SUPABASE_PROJECT_REF`, `SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY`, `SUPABASE_DB_PASSWORD`
- `~/.openclaw/credentials/supabase/admin_password.txt` — your CRM admin password. Treat as production credential.

## See Also
Confidence
96% confidence
Finding
The skill reveals the existence and path of a production admin password file for the CRM. In an agentic environment, this creates a strong temptation and clear roadmap for credential access; compromise of this password could expose sensitive admin functionality and customer data.

Credential Access

High
Category
Privilege Escalation
Content
const fs = require('fs');
const { chromium } = require('playwright');
const cred = Object.fromEntries(
  fs.readFileSync(process.env.HOME + '/.openclaw/credentials/supabase/credentials.env', 'utf8')
    .split('\n').filter(l => l.includes('='))
    .map(l => { const i = l.indexOf('='); return [l.slice(0, i), l.slice(i + 1)]; })
);
Confidence
97% confidence
Finding
This recipe reads a Supabase credentials file containing a service-role key and then uses it to call admin auth APIs that generate real magic links. Service-role credentials are highly privileged secrets; embedding instructions to access them from a general verification recipe meaningfully increases the chance of credential misuse, leakage, or privilege abuse.

Credential Access

High
Category
Privilege Escalation
Content
```js
const fs = require('fs');
const { chromium } = require('playwright');
const PASS = fs.readFileSync(process.env.HOME + '/.openclaw/credentials/supabase/admin_password.txt', 'utf8').trim();

(async () => {
  const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] });
Confidence
98% confidence
Finding
The recipe reads an admin password from disk and automates interactive login to the CRM. Storing and using a reusable admin password in this way expands the attack surface for credential theft and enables full administrative access if the secret is exposed through logs, prompts, screenshots, or local compromise.

Credential Access

High
Category
Privilege Escalation
Content
```js
const cred = Object.fromEntries(
  fs.readFileSync(process.env.HOME + '/.openclaw/credentials/supabase/credentials.env', 'utf8')
    .split('\n').filter(l => l.includes('='))
    .map(l => { const i = l.indexOf('='); return [l.slice(0, i), l.slice(i + 1)]; })
);
Confidence
97% confidence
Finding
This recipe again reads the Supabase service-role credentials and uses them to modify customer account passwords via admin APIs. That is highly sensitive because it grants direct control over user authentication state and could lock out users, impersonate them, or permanently weaken account security if mishandled.

Rp1

Medium
Category
MCP Rug Pull
Confidence
88% confidence
Finding
The skill instructs use of `npx vercel deploy --prod` without pinning a specific Vercel CLI version. This can cause execution of an unexpected or newly published package version, creating supply-chain risk and reducing deployment reproducibility for a production workflow.

Session Persistence

Medium
Category
Rogue Agent
Content
When you can't easily reproduce a UI bug, run a Playwright probe. They expect `pg` and `playwright` installed under `/tmp/<project>-test/`:

```bash
mkdir -p /tmp/<project>-test && cd /tmp/<project>-test
npm init -y >/dev/null && npm i playwright --no-save
# If your cached chromium version doesn't match the Playwright npm version:
ln -sfn ~/.cache/ms-playwright/chromium_headless_shell-1217 \
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.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
## Project Secrets Reference

- `~/.openclaw/credentials/vercel/token` (chmod 600)
- `~/.openclaw/credentials/supabase/credentials.env` — `SUPABASE_URL`, `SUPABASE_PROJECT_REF`, `SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY`, `SUPABASE_DB_PASSWORD`
- `~/.openclaw/credentials/supabase/admin_password.txt` — your CRM admin password. Treat as production credential.
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Session Persistence

Medium
Category
Rogue Agent
Content
## Setup (once per server)

```bash
mkdir -p /tmp/oganim-test && cd /tmp/oganim-test
npm init -y >/dev/null && npm i playwright --no-save

# Playwright pins a browser version; symlink existing cache so the install
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
92% confidence
Finding
The recipe explicitly instructs copying screenshots from browser-based verification runs into an assistant-readable workspace, and those screenshots may contain authenticated CRM or customer-portal content. That creates a real confidentiality risk because session-visible PII, admin views, or customer data can be exposed to broader tooling without a clear minimization or redaction requirement.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
references/playwright-recipes.md:38