Back to skill

Security audit

Supabase Vault

Security checks for vulnerabilities and agentic risk

Overview

This skill is a real Supabase-based secrets integration, but it handles very sensitive credentials and secrets with several under-scoped or overstated safeguards that should be reviewed before install.

Install only if you are comfortable giving this skill persistent access to a Supabase service_role key and letting it rewrite OpenClaw secret configuration. Use a dedicated Supabase project, pin and verify dependencies, validate the Supabase URL yourself, rotate the service_role key if exposed, and manually decide what to do with the original ~/.openclaw/secrets.json after migration.

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

T09 · Insecure Skill Coding Practices

Error
Location
assets/rpc-handler.ts:69
Finding

Service-role credential exposed through child-process arguments

Content
View full analysis
{ await execFileAsync("node", [ "-e", `const k=require(${JSON.stringify(KEYCHAIN_JS)});k.store(${JSON.stringify(url)},${JSON.stringify(serviceRoleKey)});`, ]); } ``` ### Technical Analysis The Supabase service-role key and project URL are serialized directly into the argument passed to `node -e`. Although `execFileAsync` avoids shell interpretation, it does not protect argument confidentiality. The resulting command line can be visible to local process-inspection utilities, endpoint monitoring software, audit systems, crash collectors, or other processes with sufficient access. The service-role key is particularly sensitive because it bypasses Supabase Row Level Security. This subprocess is unnecessary for confidential data transport. If CommonJS interoperability requires a child process, the credentials should be supplied over a protected standard-input pipe rather than placed in `argv`. ### Attack Path 1. A user connects the Skill to Supabase through the dashboard. 2. The gateway calls `keychainStore`. 3. A transient `node -e` process starts with the complete service-role key embedded in its command-line argument. 4. A local process monitor, telemetry agent, or sufficiently privileged local process records or reads that argument. 5. The captured key is used to invoke Supabase APIs with service-role privileges. ### Impact Assessment An attacker who captures this credential may obtain broad access to the associated Supabase project. The service-role credential can bypass RLS and may permit reading or modifying data beyond the Vault functions, depending on the project configuration. The Skill recommends a dedicated project, which reduces collat ...[truncated 67 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/crypto-local.js:46
Finding

Machine-derived fallback encryption lacks secret key material

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/fetch-secrets.js:73
Finding

Empty secret request retrieves and prints the entire Vault

Content
View full analysis
(typeof row === "string" ? row : row.name)); } // Fetch each secret await Promise.all( namesToFetch.map(async (name) => { const refKey = addLeadingSlash(name); try { const { data, error } = await supabase.rpc("read_secret", { secret_name: name }); if (error) { errors[refKey] = { message: error.message }; } else if (data === null || data === undefined) { errors[refKey] = { message: `Secret "${name}" not found in Vault` }; } else { values[refKey] = String(data); } } catch (err) { errors[refKey] = { message: String(err.message || err) }; } }) ); ``` The results are subsequently returned through standard output: ```javascript clearTimeout(timeoutHandle); output(values, errors); ``` ### Technical Analysis The script is intended to resolve specifically requested OpenClaw SecretRefs. The minimum privilege necessary is therefore access only to the named secrets required by the current request. Instead, invoking the script without arguments lists every secret and then decrypts and prints every value. This fail-open default turns an empty or malformed request into a bulk Vault export. The configured `trustedDirs` restriction may protect the exec provider's script path, but it does not prevent another process running as the same user from directly invoking the script. It also does not mitigate accidental invoca ...[truncated 807 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/migrate.js:196
Finding

Migration leaves the original plaintext secret vault intact

Content
View full analysis
`/${k}`)); patchRefsInObject(config, migratedSet); addSupabaseProvider(config); writeConfig(config); ok("openclaw.json updated — SecretRefs now point to Supabase provider"); } catch (e) { err(`Failed to update openclaw.json: ${e.message}`); } ``` No subsequent operation deletes, truncates, encrypts, or otherwise protects `~/.openclaw/secrets.json`. The behavior is acknowledged in `SKILL.md:140-141`: ```markdown Migration moves all keys from `secrets.json` to Supabase Vault and updates all SecretRefs in `openclaw.json` from `file` → `exec/supabase`. The local `secrets.json` is left in place as a safety backup. ``` This conflicts with the threat-model claim in `references/architecture.md:34-36` that plaintext-on-disk exposure is eliminated. ### Technical Analysis Migration copies secrets to Supabase and rewrites configuration references, but the original plaintext source remains on disk indefinitely. Consequently, migration does not remove the original disclosure path. Keeping a safety backup can be a legitimate operational choice, but retaining it in plaintext contradicts the stated security objective and should require explicit informed consent. A secure migration should verify remote writes and then offer deletion or encrypted archival of the original file. ### Attack Path 1. A user completes migration to Supabase Vault. 2. The configuration starts referencing the Supabase exec provider. 3. The original `~/.openclaw/secrets.json` remains unchanged. 4. An attacker who later gains read access to that file obtains all migrated values directly. 5. The attacker does not need to compromise Supabase Vault or ...[truncated 318 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:27
Finding

Secret-handling dependency is installed without version or integrity pinning

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
assets/rpc-handler.ts:143
Finding

Unrestricted Supabase endpoint can receive privileged credentials and migrated secrets

Content
View full analysis
{ try { const { url, serviceRoleKey } = p as { url?: string; serviceRoleKey?: string }; if (!url?.trim() || !serviceRoleKey?.trim()) { respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "url and serviceRoleKey required")); return; } // Test connection const supabase = makeClient(url.trim(), serviceRoleKey.trim()); const { error } = await supabase.rpc("list_secret_names"); if (error) { respond(false, undefined, errorShape( ErrorCodes.UNAVAILABLE, `Connection test failed: ${error.message}. Make sure you ran setup.sql in your Supabase project.` )); return; } // Store credentials await keychainStore(url.trim(), serviceRoleKey.trim()); ``` The supplied destination is passed directly to the client: ```typescript function makeClient(url: string, serviceRoleKey: string) { const { createClient } = requireSupabase(); return createClient(url, serviceRoleKey, { auth: { persistSession: false, autoRefreshToken: false }, }); } ``` ### Technical Analysis The gateway validates only that the URL and key are non-empty. It does not enforce HTTPS, validate the origin against an expected Supabase host or approved self-hosted deployment, or restrict loopback and link-local addresses. Because the service-role key is supplied to the client for the connection test, an attacker-controlled or mistakenly entered endpoint can receive the credential. If the endpoint emulates the expected RPC response, it can be persisted as the Vault destination. A later migration will then send all local secret values to that endpoint. Network transmission is required for the Skill's declared functionality, but unrestricted des ...[truncated 1142 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (58)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 43)May include surrounding context.

md
Copy `assets/rpc-handler.ts` to `src/gateway/server-methods/supabase-vault.ts` in the OpenClaw source, then register it in the server-methods index:

Credential Access

High
Category
Privilege Escalation
Confidence
86% confidence
Finding

The documented design requires retrieving the Supabase SERVICE_ROLE_KEY at runtime to read secrets. A service-role credential is highly privileged and bypasses RLS; if the host, exec provider, or local credential store is compromised, an attacker can access or manipulate all vault secrets for the project.

Content

Scanner excerpt · SKILL.md (reported line 115)May include surrounding context.

text
Gateway starts
  → exec provider triggers fetch-secrets.js
      → keychain.js retrieves SUPABASE_URL + SERVICE_ROLE_KEY
          (macOS: Keychain Access / Linux: GNOME Keyring / fallback: AES-256-GCM file)
      → @supabase/supabase-js createClient(url, key)
      → supabase.rpc('read_secret', { secret_name }) for each requested key

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 23)May include surrounding context.

ts
const execFileAsync = promisify(execFile);

const SKILL_DIR    = path.join(os.homedir(), ".openclaw", "skills", "supabase-vault");
const KEYCHAIN_JS  = path.join(SKILL_DIR, "scripts", "keychain.js");
const MIGRATE_JS   = path.join(SKILL_DIR, "scripts", "migrate.js");
const SECRETS_FILE = path.join(os.homedir(), ".openclaw", "secrets.json");
const CONFIG_FILE  = path.join(os.homedir(), ".openclaw", "openclaw.json");

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 52)May include surrounding context.

ts
const execFileAsync = promisify(execFile);

const SKILL_DIR    = path.join(os.homedir(), ".openclaw", "skills", "supabase-vault");
const KEYCHAIN_JS  = path.join(SKILL_DIR, "scripts", "keychain.js");
const MIGRATE_JS   = path.join(SKILL_DIR, "scripts", "migrate.js");
const SECRETS_FILE = path.join(os.homedir(), ".openclaw", "secrets.json");
const CONFIG_FILE  = path.join(os.homedir(), ".openclaw", "openclaw.json");

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 60)May include surrounding context.

ts
const execFileAsync = promisify(execFile);

const SKILL_DIR    = path.join(os.homedir(), ".openclaw", "skills", "supabase-vault");
const KEYCHAIN_JS  = path.join(SKILL_DIR, "scripts", "keychain.js");
const MIGRATE_JS   = path.join(SKILL_DIR, "scripts", "migrate.js");
const SECRETS_FILE = path.join(os.homedir(), ".openclaw", "secrets.json");
const CONFIG_FILE  = path.join(os.homedir(), ".openclaw", "openclaw.json");

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 70)May include surrounding context.

ts
const execFileAsync = promisify(execFile);

const SKILL_DIR    = path.join(os.homedir(), ".openclaw", "skills", "supabase-vault");
const KEYCHAIN_JS  = path.join(SKILL_DIR, "scripts", "keychain.js");
const MIGRATE_JS   = path.join(SKILL_DIR, "scripts", "migrate.js");
const SECRETS_FILE = path.join(os.homedir(), ".openclaw", "secrets.json");
const CONFIG_FILE  = path.join(os.homedir(), ".openclaw", "openclaw.json");

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 77)May include surrounding context.

ts
const execFileAsync = promisify(execFile);

const SKILL_DIR    = path.join(os.homedir(), ".openclaw", "skills", "supabase-vault");
const KEYCHAIN_JS  = path.join(SKILL_DIR, "scripts", "keychain.js");
const MIGRATE_JS   = path.join(SKILL_DIR, "scripts", "migrate.js");
const SECRETS_FILE = path.join(os.homedir(), ".openclaw", "secrets.json");
const CONFIG_FILE  = path.join(os.homedir(), ".openclaw", "openclaw.json");

Credential Access

High
Category
Privilege Escalation
Confidence
72% confidence
Finding

This handler retrieves high-privilege Supabase credentials into process memory and over a child-process stdout boundary, which increases the attack surface for secrets exposure if the helper script, process environment, or error handling is compromised. In this skill context that access is expected, but using a service-role key makes the consequences of accidental exposure materially higher.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 57)May include surrounding context.

ts
return JSON.parse(stdout.trim());
}

async function keychainRetrieve(): Promise<{ url: string; serviceRoleKey: string }> {
  const { stdout } = await execFileAsync("node", [
    "-e",
    `const k=require(${JSON.stringify(KEYCHAIN_JS)});try{console.log(JSON.stringify(k.retrieve()));}catch(e){console.log(JSON.stringify({error:e.message}));}`,

Credential Access

High
Category
Privilege Escalation
Confidence
76% confidence
Finding

The code sends the Supabase URL and service-role key as arguments embedded in a 'node -e' command string to a child process. Even though 'execFile' avoids shell injection, transporting secrets to another process can expose them to process inspection, debugging tools, crash dumps, or less controlled helper code, which is sensitive given the service-role key's broad privileges.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 67)May include surrounding context.

ts
return result;
}

async function keychainStore(url: string, serviceRoleKey: string): Promise<void> {
  await execFileAsync("node", [
    "-e",
    `const k=require(${JSON.stringify(KEYCHAIN_JS)});k.store(${JSON.stringify(url)},${JSON.stringify(serviceRoleKey)});`,

Credential Access

High
Category
Privilege Escalation
Confidence
71% confidence
Finding

The status endpoint retrieves stored backend credentials and actively tests them against Supabase, even though it only returns masked metadata. That broadens the set of code paths that access sensitive service-role credentials, increasing exposure if the RPC surface is reachable by less-trusted callers or if child-process retrieval is compromised.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 135)May include surrounding context.

ts
let keyCount = 0;

        try {
          const creds = await keychainRetrieve();
          urlMasked = maskUrl(creds.url);

          // Test connection by listing secrets

Credential Access

High
Category
Privilege Escalation
Confidence
83% confidence
Finding

This line stores a Supabase service-role key for later automated use, giving the local application persistent access to a highly privileged backend credential. In a secrets-management skill this is functionally necessary, but if the keychain helper or host is compromised, an attacker gains broad ability to read, write, and delete stored secrets remotely.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 182)May include surrounding context.

ts
}

        // Store credentials
        await keychainStore(url.trim(), serviceRoleKey.trim());

        // Add exec provider to config
        const config = readConfig();

Credential Access

High
Category
Privilege Escalation
Confidence
78% confidence
Finding

The list endpoint retrieves stored service-role credentials and uses them to enumerate secrets from the remote vault. While expected for functionality, this means any unauthorized access to this RPC could disclose secret inventory and confirms live access to a privileged secrets backend, which is especially sensitive in a vault-management context.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 211)May include surrounding context.

ts
"supabase-vault.list": async ({ respond }) => {
      try {
        const creds = await keychainRetrieve();
        const supabase = makeClient(creds.url, creds.serviceRoleKey);
        const { data, error } = await supabase.rpc("list_secret_names");
        if (error) throw new Error(error.message);

Credential Access

High
Category
Privilege Escalation
Confidence
80% confidence
Finding

The write endpoint retrieves stored service-role credentials and uses them to insert secrets into the remote vault. In context this is intended behavior, but compromise of this RPC or of the stored credential path would let an attacker alter the application's secret store, potentially planting malicious API keys, tokens, or configuration values.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 233)May include surrounding context.

ts
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "name and value required"));
          return;
        }
        const creds = await keychainRetrieve();
        const supabase = makeClient(creds.url, creds.serviceRoleKey);
        const { data, error } = await supabase.rpc("insert_secret", {
          name: name.trim(),

Credential Access

High
Category
Privilege Escalation
Confidence
80% confidence
Finding

The delete endpoint retrieves privileged backend credentials and can remove secrets from the vault. In a secrets-management skill that is expected, but if invoked by an unauthorized party it could cause denial of service, break integrations, or force secret rotation across dependent systems.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 255)May include surrounding context.

ts
respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "name required"));
          return;
        }
        const creds = await keychainRetrieve();
        const supabase = makeClient(creds.url, creds.serviceRoleKey);
        const { error } = await supabase.rpc("delete_secret", { secret_name: name.trim() });
        if (error) throw new Error(error.message);

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 116)May include surrounding context.

md
│ OPENAI   │  ◄────────┤
                                    │ (encr.)  │           │ ┌────────────────┐
                                    └──────────┘           └►│ macOS Keychain │
                                                             │ GNOME Keyring  │
                                                             │ AES-256-GCM    │
                                                             │ (machine key)  │
                                                             └────────────────┘

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/architecture.md (reported line 25)May include surrounding context.

md
│ OPENAI   │  ◄────────┤
                                    │ (encr.)  │           │ ┌────────────────┐
                                    └──────────┘           └►│ macOS Keychain │
                                                             │ GNOME Keyring  │
                                                             │ AES-256-GCM    │
                                                             │ (machine key)  │
                                                             └────────────────┘

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 23)May include surrounding context.

ts
/**
 * keychain.js — Platform-aware bootstrap credential storage
 *
 * Priority order:
 *   1. macOS Keychain    (security CLI)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 49)May include surrounding context.

ts
/**
 * keychain.js — Platform-aware bootstrap credential storage
 *
 * Priority order:
 *   1. macOS Keychain    (security CLI)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 74)May include surrounding context.

ts
/**
 * keychain.js — Platform-aware bootstrap credential storage
 *
 * Priority order:
 *   1. macOS Keychain    (security CLI)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 129)May include surrounding context.

ts
/**
 * keychain.js — Platform-aware bootstrap credential storage
 *
 * Priority order:
 *   1. macOS Keychain    (security CLI)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 189)May include surrounding context.

ts
/**
 * keychain.js — Platform-aware bootstrap credential storage
 *
 * Priority order:
 *   1. macOS Keychain    (security CLI)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · assets/rpc-handler.ts (reported line 200)May include surrounding context.

ts
/**
 * keychain.js — Platform-aware bootstrap credential storage
 *
 * Priority order:
 *   1. macOS Keychain    (security CLI)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/architecture.md (reported line 21)May include surrounding context.

md
/**
 * keychain.js — Platform-aware bootstrap credential storage
 *
 * Priority order:
 *   1. macOS Keychain    (security CLI)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/fetch-secrets.js (reported line 22)May include surrounding context.

js
/**
 * keychain.js — Platform-aware bootstrap credential storage
 *
 * Priority order:
 *   1. macOS Keychain    (security CLI)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/fetch-secrets.js (reported line 23)May include surrounding context.

js
/**
 * keychain.js — Platform-aware bootstrap credential storage
 *
 * Priority order:
 *   1. macOS Keychain    (security CLI)

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/keychain.js:33