Back to skill

Security audit

Supabase Security Audit

Security checks for vulnerabilities and agentic risk

Overview

This security-audit skill is mostly purpose-aligned, but it asks for powerful Supabase secrets and runs them through unsafe dependency loading, disabled database TLS verification, and unvalidated SQL input.

Install only after narrowing when the skill runs, removing the unused service-role key requirement, using pinned project-local dependencies, enabling PostgreSQL certificate verification, validating probe IDs as UUIDs or parameterizing claims, and preferably using a dedicated least-privileged audit database role instead of production administrator credentials.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/audit.js:207
Finding
SQL Injection Through Unvalidated Probe UID<![CDATA[ ## Vulnerability Details **File Location**: `scripts/audit.js`, lines 207 and 247 **Vulnerability Type**: SQL injection through direct string interpolation **Risk Level**: Critical ### Vulnerable Code ```js await pg.query(`set local request.jwt.claims to '{"sub":"${probeUid}","role":"authenticated"}'`); ``` The same vulnerable statement occurs in both `checkPrivilegeEscalationLive()` and `checkCustomerDataLeak()`. ### Technical Analysis The user-controlled `--probe-uid` argument is inserted directly into a SQL statement. It is neither validated as a UUID nor passed through a query parameter. An attacker can supply a value containing a quote and SQL syntax to terminate the JSON/SQL string and append arbitrary statements. Although the code previously executes `SET LOCAL ROLE authenticated`, the underlying connection authenticates as `postgres.<project-ref>`. Injected SQL could attempt to reset the role or otherwise abuse privileges available to the session. The surrounding transaction and final rollback are not reliable security boundaries. Injected statements could manipulate transaction state, invoke functions with external effects, read protected information, or execute operations whose consequences are not fully neutralized by rollback. ### Attack Path 1. An attacker controls or influences the `--probe-uid` argument supplied to the audit. 2. The malicious value is interpolated into the `SET LOCAL request.jwt.claims` SQL statement. 3. The value terminates the expected string literal and appends attacker-selected SQL. 4. The injected statements execute through the privileged PostgreSQL connection. 5. Depending on database permissions and installed functionality, the attacker may read or modify project data, alter policies or roles, or compromise database integrity. ### Impact Assessment Successful exploitation can provide unauthorized database access within the privileges of the connection. Potential scope includes: - Reading data protect ...[truncated 272 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate `probeUid` and `targetUid` against a strict UUID parser before any database operation. 2. Never interpolate claims into SQL. Use a parameterized call to `set_config`: ```js const claims = JSON.stringify({ sub: probeUid, role: 'authenticated', }); await pg.query( `select set_config('request.jwt.claims', $1, true)`, [claims] ); ``` 3. Reject unexpected CLI argument formats and duplicate arguments. 4. Use a dedicated, least-privileged audit database role rather than the database owner or administrative `postgres` identity. 5. Restrict the audit role to the exact schemas, tables, and metadata views required by the checks. 6. Add automated tests using quotes, semicolons, comments, and malformed UUIDs to verify that SQL injection is impossible. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/audit.js:106
Finding
PostgreSQL TLS Certificate Verification Is Disabled<![CDATA[ ## Vulnerability Details **File Location**: `scripts/audit.js`, lines 106–115 **Vulnerability Type**: Improper TLS certificate validation **Risk Level**: High ### Vulnerable Code ```js const [host, port] = pooler.split(':'); const pg = new Client({ host, port: Number(port || 6543), user: `postgres.${cred.SUPABASE_PROJECT_REF}`, password: cred.SUPABASE_DB_PASSWORD, database: 'postgres', ssl: { rejectUnauthorized: false }, connectionTimeoutMillis: 15000, }); ``` ### Technical Analysis Setting `rejectUnauthorized: false` encrypts the connection but disables authentication of the PostgreSQL server certificate. The client will accept an invalid, self-signed, expired, or attacker-controlled certificate. The risk is amplified because the `--pooler` option permits the destination hostname to be changed. The database password and all subsequent database traffic are sent through the resulting connection. ### Attack Path 1. An attacker gains a network interception position, compromises DNS resolution, or convinces a user to provide a malicious `--pooler` host. 2. The attacker presents an untrusted TLS certificate. 3. The client accepts the certificate because server verification is disabled. 4. The client sends the Supabase database username and password to the impersonated endpoint. 5. The attacker captures credentials and may proxy, inspect, or alter audit traffic. 6. The stolen credentials can then be used for direct database access, subject to network controls and credential privileges. ### Impact Assessment Potential impact includes: - Disclosure of the Supabase database password. - Exposure of database schema information and query results. - Modification of queries or responses in transit. - Subsequent unauthorized access to project data. - Database-wide compromise if the captured account has administrative privileges. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enable certificate verification: ```js ssl: { rejectUnauthorized: true, } ``` 2. Use the appropriate trusted CA certificate if the platform requires an explicit CA bundle. 3. Restrict pooler destinations to approved Supabase hostnames associated with the configured project. 4. Require explicit confirmation before accepting a custom `--pooler` value. 5. Do not silently fall back to insecure TLS behavior. 6. Use a dedicated database credential with read-only and metadata-only privileges. 7. Rotate the database password if the script has previously been used over an untrusted network or against an unverified endpoint. ]]>

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/audit.js:30
Finding
Local Code Execution Through Predictable Temporary Module Loading<![CDATA[ ## Vulnerability Details **File Location**: `scripts/audit.js`, lines 30–39 **Vulnerability Type**: Local dependency or tool hijacking **Risk Level**: High ### Vulnerable Code ```js let Client; const pgCandidates = [ '/tmp/sb-tools/node_modules/pg', process.env.PG_PATH, 'pg', ].filter(Boolean); for (const p of pgCandidates) { try { ({ Client } = require(p)); break; } catch {} } ``` ### Technical Analysis The script preferentially loads executable JavaScript from the predictable shared path `/tmp/sb-tools/node_modules/pg`. It does not verify ownership, permissions, package identity, version, or integrity before calling `require()`. Node.js executes a module's top-level code immediately when it is loaded. A local attacker who can create or replace the package at this path can therefore execute arbitrary code as the user running the audit. The `PG_PATH` environment variable creates an additional module-substitution mechanism. While environment overrides can be legitimate, accepting an arbitrary executable module path without explicit trust checks expands the attack surface. ### Attack Path 1. A local attacker or another process creates a malicious package at `/tmp/sb-tools/node_modules/pg`, or influences `PG_PATH`. 2. A victim runs `scripts/audit.js`. 3. The script loads the attacker-controlled module before attempting to load the legitimate project dependency. 4. The module's initialization code executes with the victim's operating-system privileges. 5. The malicious code can read the credentials file, inspect environment variables, alter audit output, or perform arbitrary filesystem and network operations. ### Impact Assessment Successful exploitation grants arbitrary local code execution with the privileges of the audit process. The attacker may gain access to: - The Supabase database password. - The anonymous and service-role keys loaded by the process. - Other files readable by the current operating-system account. - Database que ...[truncated 99 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Declare `pg` as a pinned project dependency and load it normally: ```js const { Client } = require('pg'); ``` 2. Store dependencies in the project directory rather than a shared temporary directory. 3. Commit a lockfile and use a reproducible installation process. 4. Remove automatic lookup under `/tmp`. 5. Remove `PG_PATH`, or require an explicit command-line opt-in and verify that the path: - Is absolute. - Is owned by the current user or a trusted administrator. - Is not group- or world-writable. - Resolves outside shared temporary directories. 6. Run the audit in a restricted environment with minimal filesystem and network privileges. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:34
Finding
Unpinned Runtime Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 34–39; `scripts/audit.js`, lines 40–42 **Vulnerability Type**: Unsafe third-party dependency installation **Risk Level**: Medium ### Vulnerable Code ```bash mkdir -p /tmp/sb-tools && (cd /tmp/sb-tools && npm i pg) ``` The script repeats this installation instruction when the package cannot be found: ```js if (!Client) { console.error('ERR: the `pg` package is required. Install it once with:'); console.error(' mkdir -p /tmp/sb-tools && (cd /tmp/sb-tools && npm i pg)'); process.exit(2); } ``` ### Technical Analysis The installation command retrieves the current package version and transitive dependency graph at execution time. No reviewed version, lockfile, or integrity pin is supplied. npm lifecycle scripts are not disabled. Consequently, the effective code executed by the Skill can change independently of the reviewed repository. A compromised package release, registry account, or transitive dependency could execute malicious code during installation or when the package is subsequently loaded. Installing into `/tmp` also combines this supply-chain issue with the predictable module-loading weakness. ### Attack Path 1. A user follows the documented dependency installation command. 2. npm resolves the latest available `pg` release and its transitive dependencies. 3. A compromised release or dependency is downloaded. 4. Malicious lifecycle code may execute during installation. 5. The audit subsequently loads the installed module. 6. Malicious module code gains access to the process and Supabase credentials. ### Impact Assessment Potential consequences include: - Arbitrary local code execution. - Theft of Supabase credentials. - Unauthorized database access. - Modification or suppression of audit output. - Compromise of other files and credentials accessible to the user. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add a project-local `package.json` and lockfile containing a reviewed, pinned `pg` version. 2. Install with a reproducible command such as: ```bash npm ci --ignore-scripts ``` 3. Verify that disabling lifecycle scripts is compatible with all required packages. 4. Use package integrity information from the lockfile. 5. Keep dependencies in the project directory rather than `/tmp`. 6. Perform dependency vulnerability and provenance checks during releases. 7. Review and deliberately update dependency versions rather than resolving the latest release at audit runtime. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/audit.js:53
Finding
Unnecessary Collection of Supabase Service-Role Credentials<![CDATA[ ## Vulnerability Details **File Location**: `scripts/audit.js`, lines 53–67; `SKILL.md`, lines 23–31 **Vulnerability Type**: Excessive credential requirements and violation of least privilege **Risk Level**: Medium ### Vulnerable Code ```js const credPath = args.cred || path.join(process.env.HOME, '.openclaw/credentials/supabase/credentials.env'); if (!fs.existsSync(credPath)) { console.error('ERR: credentials file not found:', credPath); process.exit(2); } const cred = Object.fromEntries( fs.readFileSync(credPath, 'utf8').split('\n') .filter(l => l.includes('=') && !l.trim().startsWith('#')) .map(l => { const i = l.indexOf('='); return [l.slice(0, i).trim(), l.slice(i + 1).trim()]; }) ); for (const k of ['SUPABASE_URL', 'SUPABASE_PROJECT_REF', 'SUPABASE_ANON_KEY', 'SUPABASE_SERVICE_ROLE_KEY', 'SUPABASE_DB_PASSWORD']) { if (!cred[k]) { console.error('ERR: missing', k, 'in', credPath); process.exit(2); } } ``` The documentation requires the same unused secret: ```text SUPABASE_SERVICE_ROLE_KEY=... ``` ### Technical Analysis The script requires and loads `SUPABASE_SERVICE_ROLE_KEY`, but the reviewed implementation never uses that value. The service-role key normally bypasses Supabase row-level security and is therefore a highly privileged secret. Requiring this credential exceeds the minimum privileges necessary for the declared audit functions. It also unnecessarily exposes the key to all executable modules loaded by the process, including any module obtained through the unsafe `/tmp` dependency path. The generic environment-file parser additionally imports every assignment from the selected file rather than limiting parsing to required keys. No direct transmission of the service-role key by the reviewed code was found. The risk arises from unnecessary collection and expanded exposure. ### Attack Path 1. A user follows the documentation and places the service-role key in the credentials file. 2. The audit reads the complet ...[truncated 785 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `SUPABASE_SERVICE_ROLE_KEY` from the required credential list and documentation. 2. Parse only the exact values needed by the audit: ```js const requiredKeys = [ 'SUPABASE_URL', 'SUPABASE_PROJECT_REF', 'SUPABASE_ANON_KEY', 'SUPABASE_DB_PASSWORD', ]; ``` 3. Prefer a dedicated, read-only audit database credential rather than a broad administrative password. 4. Separate credentials by function so anonymous REST checks do not share a file with privileged database credentials. 5. Verify restrictive credential-file permissions before reading it, such as owner-only access. 6. Avoid retaining unnecessary secrets in process memory. 7. Rotate the service-role key if it may have been exposed through an untrusted local dependency. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (16)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code substantially matches several declared audit areas: RLS coverage, anonymous exposure, privilege escalation, cross-customer data leaks, and HTTP security headers. However, there are material omissions relative to the declared purpose. First, the description says the skill can 'apply hotfix templates when issues are found,' but the script only reports findings and never modifies code/configuration or emits fix templates. Second, the description includes 'magic-link flow correctness,' but no logic examines authentication flows, redirects, tokens, or email link behavior. Third, the description frames this as a 'Supabase + Vercel project' audit, yet the code only interacts with Supabase credentials, Supabase REST, Postgres, and a generic site URL for headers; it contains no Vercel-specific inspection. These are meaningful description-versus-behavior gaps, so this should be flagged as a mismatch.

Credential Access

High
Category
Privilege Escalation
Content
## Prerequisites

Credentials file at `~/.openclaw/credentials/supabase/credentials.env` (override path with `--cred`) containing:

```
SUPABASE_URL=https://<ref>.supabase.co
Confidence
97% confidence
Finding
The skill explicitly instructs the use of a credentials file containing `SUPABASE_SERVICE_ROLE_KEY` and database password, which are highly sensitive secrets capable of bypassing normal client restrictions. In an agent setting, centralizing and referencing these credentials increases the chance of accidental disclosure, misuse by an over-privileged skill, or execution of powerful operations against production data.

Ae1

High
Category
analysis-evasion
Content
Then re-run `audit.js` to confirm the finding flipped to ✅.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Then re-run `audit.js` to confirm the finding flipped to ✅.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Self-Modification

High
Category
Rogue Agent
Content
**Pattern**

```sql
create policy profiles_self_update
  on public.profiles
  for update
  using (auth.uid() = id);   -- no WITH CHECK
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
**Pattern**

```sql
create policy profiles_self_update
  on public.profiles
  for update
  using (auth.uid() = id);   -- no WITH CHECK
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
**Pattern**

```sql
create policy profiles_self_update
  on public.profiles
  for update
  using (auth.uid() = id);   -- no WITH CHECK
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Credential Access

High
Category
Privilege Escalation
Content
* Supabase project and prints a human-readable report.
 *
 * USAGE:
 *   node audit.js [--cred ~/.openclaw/credentials/supabase/credentials.env]
 *                 [--pooler aws-1-eu-central-1.pooler.supabase.com:6543]
 *                 [--public-tables profiles,deals,documents,...]
 *                 [--probe-uid <uuid>]   # an existing customer-role user id
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
* Supabase project and prints a human-readable report.
 *
 * USAGE:
 *   node audit.js [--cred ~/.openclaw/credentials/supabase/credentials.env]
 *                 [--pooler aws-1-eu-central-1.pooler.supabase.com:6543]
 *                 [--public-tables profiles,deals,documents,...]
 *                 [--probe-uid <uuid>]   # an existing customer-role user id
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Self-Modification

High
Category
Rogue Agent
Content
reset role;

-- 1. Replace the existing self-update policy with one that locks role + email.
drop policy if exists profiles_self_update on public.profiles;

create policy profiles_self_update
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill instructs use of environment-backed secrets and a credentials file containing highly privileged Supabase keys, but it does not declare any explicit tool scope or permission boundary. In an agent ecosystem, missing scope declarations can let a broadly-invoked skill access sensitive credentials or environment data without clear operator intent or review.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The skill description defines broad activation criteria around general 'security' or 'audit' requests, increasing the chance the agent invokes a credentialed, security-sensitive skill in situations that do not require it. Over-broad routing raises the risk of unnecessary secret exposure, unintended probing of production systems, or execution of privileged audit workflows from loosely related prompts.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The 'When to Run' triggers include ambiguous phrases like 'are we secure?' and 'before exposing a new surface,' which lack clear scope boundaries for a privileged audit skill. In context, this can cause the agent to overuse a skill that expects access to service-role secrets and live environment details, expanding operational risk beyond justified cases.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
This code reads high-sensitivity values including `SUPABASE_SERVICE_ROLE_KEY` and `SUPABASE_DB_PASSWORD` from a credentials file and later uses them for database and API access. While the header documents required variables, there is no runtime confirmation, warning, or visible notice when the script accesses these credentials, which fits the missing-warning criterion for sensitive credential access in code.

Missing User Warnings

Medium
Confidence
79% confidence
Finding
The script performs HTTPS requests to the Supabase REST API and sends authentication material in the `apikey` and `Authorization` headers. Although the overall audit purpose is described in comments, the code does not provide a visible runtime warning that authenticated requests will be sent to the configured Supabase project, which is a safety-relevant network action.

Natural-Language Policy Violations

Low
Confidence
79% confidence
Finding
This markdown file includes a natural-language/code example that restricts allowed origins to specific domains, including a country-specific `.co.il` domain, without explicitly stating that these values are only illustrative and must be replaced for each deployment. That can be read as prescribing a fixed locale/domain policy rather than offering a user or project-specific choice.

Static analysis

Detected: suspicious.exposed_secret_literal, suspicious.insecure_tls_verification

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/audit.js:108

HTTPS certificate verification is disabled.

Warn
Code
suspicious.insecure_tls_verification
Location
scripts/audit.js:110