Back to skill

Security audit

Supabase LangGraph Checkpointer

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims, but it asks users to persist potentially sensitive agent state to Supabase using broad database authority without enough access-control guidance.

Review this carefully before installing. Use it only from trusted server-side code, store Supabase credentials in a secrets manager or environment variable, avoid exposing service role keys to clients, consider a dedicated least-privilege role or RPC layer, add RLS or other tenant controls, and avoid checkpointing secrets or regulated data unless your Supabase retention and access controls are acceptable.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/create_tables.sql:4
Finding

Checkpoint Tables Are Created Without Row-Level Access Controls

Content
View full analysis

Vulnerability Details

File Location: scripts/create_tables.sql:4-24
Vulnerability Type: Missing row-level authorization controls
Risk Level: High

Vulnerable Code:

sql
CREATE TABLE IF NOT EXISTS langgraph_checkpoints (
    thread_id TEXT NOT NULL,
    checkpoint_ns TEXT NOT NULL DEFAULT '',
    checkpoint_id TEXT NOT NULL,
    parent_checkpoint_id TEXT,
    type TEXT,
    checkpoint JSONB NOT NULL,
    metadata JSONB DEFAULT '{}',
    created_at TIMESTAMPTZ DEFAULT now(),
    PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id)
);

CREATE TABLE IF NOT EXISTS langgraph_writes (
    thread_id TEXT NOT NULL,
    checkpoint_ns TEXT NOT NULL DEFAULT '',
    checkpoint_id TEXT NOT NULL,
    task_id TEXT NOT NULL,
    idx INTEGER NOT NULL,
    channel TEXT NOT NULL,
    type TEXT,
    blob JSONB,
    PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id, task_id, idx)
);

Technical Analysis

The installation script creates tables containing complete serialized LangGraph checkpoints, metadata, and pending writes, but it does not enable row-level security, define authorization policies, revoke broad role privileges, or associate rows with a server-validated tenant identity.

The thread_id and checkpoint_ns columns provide logical identifiers, not authorization boundaries. If Supabase API roles have privileges on these tables, PostgREST can expose the data without enforcing ownership of the supplied thread identifier. Application code that accepts a caller-selected thread_id would likewise have no database-level protection against cross-thread access.

Attack Path

  1. An attacker obtains a valid Supabase API key for a role that can access tables in the exposed schema, such as a publicly distributed project key where table grants remain available.
  2. The attacker calls the PostgREST endpoints for langgraph_checkpoints or langgraph_writes.
  3. Because the insta ...[truncated 975 chars]
Remediation
View remediation

Remediation Suggestions

  1. Enable and force row-level security on both tables:
    sql
    ALTER TABLE langgraph_checkpoints ENABLE ROW LEVEL SECURITY;
    ALTER TABLE langgraph_checkpoints FORCE ROW LEVEL SECURITY;
    ALTER TABLE langgraph_writes ENABLE ROW LEVEL SECURITY;
    ALTER TABLE langgraph_writes FORCE ROW LEVEL SECURITY;
    
  2. Revoke default access from broad API roles unless direct table access is explicitly required:
    sql
    REVOKE ALL ON langgraph_checkpoints FROM anon, authenticated;
    REVOKE ALL ON langgraph_writes FROM anon, authenticated;
    
  3. Add a non-null tenant or owner identifier derived from authenticated server-side context. Do not treat a caller-provided thread_id as proof of ownership.
  4. Create narrowly scoped SELECT, INSERT, UPDATE, and DELETE policies that compare the row owner with a trusted authenticated identity.
  5. Prefer security-reviewed RPC functions that expose only the minimum checkpoint operations and validate tenant ownership internally.
  6. Test access with every Supabase role to confirm that cross-tenant reads and writes are rejected.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
SKILL.md:1
Finding

Documentation Recommends Use of a Broadly Privileged Supabase Service-Role Key

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:1-4, 48-52
Vulnerability Type: Excessive credential privilege and RLS bypass
Risk Level: High

Vulnerable Documentation:

markdown
---
name: supabase-checkpointer
description: Durable LangGraph checkpointing via Supabase REST API (PostgREST) — no direct Postgres connection needed. Use when deploying LangGraph graphs to ephemeral platforms (Railway, Fly, Cloud Run) where in-memory state is lost on redeploy, and you need interrupt/resume to survive process death. Works with any Supabase project using the existing service role key.
---
python
from supabase import create_client
from supabase_checkpointer import SupabaseCheckpointer

client = create_client(url, key)
checkpointer = SupabaseCheckpointer(client)

Technical Analysis

The documentation explicitly recommends using the existing Supabase service-role key. Supabase service-role credentials normally bypass row-level security and possess substantially more authority than the two-table checkpointing task requires.

The implementation accepts the supplied client without applying an independent tenant-authorization check. Consequently, compromise or misuse of the application process gives the attacker the authority represented by the service-role credential, rather than authority limited to checkpoint creation and retrieval.

This is a least-privilege violation. Even if row-level policies are later added, a service-role client can generally bypass them, so those policies cannot compensate for exposing or misusing this credential.

Attack Path

  1. The application is configured according to the documentation with a Supabase service-role key.
  2. An attacker compromises the deployment environment, reads environment variables, exploits an application endpoint that exposes arbitrary database behavior, or otherwise obtains control of the initialized client.
  3. The attacker extracts or u ...[truncated 777 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace the service-role recommendation with a dedicated least-privilege backend identity authorized only for required checkpoint operations.
  2. Prefer narrowly scoped database RPC functions over direct unrestricted table access.
  3. Isolate checkpoint storage in a dedicated schema or Supabase project when practical.
  4. Keep privileged keys exclusively in a server-side secret manager; never embed them in source code, logs, browser bundles, or mobile clients.
  5. Add explicit documentation warning that service-role credentials bypass RLS and must never be exposed to untrusted clients.
  6. Implement application-side tenant validation using an identity derived from authenticated server context rather than accepting arbitrary thread_id values.
  7. Rotate any service-role key suspected of disclosure and review audit logs for unauthorized database operations.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/supabase_checkpointer.py:53
Finding

Checkpoint Persistence Failures Are Suppressed While Success Is Reported

Content
View full analysis

Vulnerability Details

File Location: scripts/supabase_checkpointer.py:53-64, 88-90
Vulnerability Type: Fail-open error handling and silent state loss
Risk Level: Medium

Vulnerable Code:

python
        try:
            self.client.table("langgraph_checkpoints").upsert(row).execute()
        except Exception as e:
            log.error(f"Failed to save checkpoint: {e}")

        return {
            "configurable": {
                "thread_id": thread_id,
                "checkpoint_ns": checkpoint_ns,
                "checkpoint_id": checkpoint_id,
            }
        }
python
        if rows:
            try:
                self.client.table("langgraph_writes").upsert(rows).execute()
            except Exception as e:
                log.error(f"Failed to save writes: {e}")

Technical Analysis

Both checkpoint and pending-write persistence errors are caught and only logged. In put(), the method returns a successful-looking configuration containing the new checkpoint identifier even when the database operation failed. In put_writes(), the caller receives no indication that pending writes were lost.

This behavior violates the durability contract expected from a checkpointer. It can produce partial state where a checkpoint exists without its writes, writes fail silently, or neither operation succeeds while the graph continues under the assumption that durable state was committed.

The broad except Exception also conflates transient availability failures, authentication failures, schema errors, serialization problems, and permanent authorization failures. There is no retry policy, transaction boundary, health signal, or explicit degraded-mode decision.

Attack Path

  1. An attacker or infrastructure failure disrupts access to Supabase, exhausts a relevant resource, causes authorization failures, or otherwise makes the upsert fail.
  2. The exception is ...[truncated 932 chars]
Remediation
View remediation

Remediation Suggestions

  1. Propagate persistence exceptions by default so the graph cannot interpret a failed commit as successful.
  2. Return a checkpoint configuration only after confirming that the database accepted the write.
  3. Implement bounded retries with exponential backoff for explicitly identified transient failures.
  4. Distinguish transient network errors from permanent schema, authentication, authorization, and validation failures.
  5. Make any degraded or best-effort persistence mode explicit and opt-in rather than silently enabled.
  6. Add metrics and alerts for failed checkpoint and pending-write operations; avoid relying only on log messages.
  7. Where consistency requires it, commit the checkpoint and associated writes through a transactional database RPC.
  8. Make downstream side effects idempotent so recovery from a failed checkpoint cannot repeat sensitive operations.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (4)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill states that it works with the existing Supabase service role key but does not emphasize that this key is highly privileged and must be handled as a secret. In practice, users may embed the key in insecure environments, logs, client-side code, or broader agent configurations, which could enable full database access if exposed.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill encourages durable checkpointing to Supabase but does not clearly warn that graph state is transmitted over HTTP to a remote Supabase service and stored persistently. LangGraph checkpoints can contain prompts, user inputs, tool outputs, or other sensitive workflow state, so users may unintentionally externalize sensitive data without realizing it.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The put method sends serialized checkpoint and metadata content to Supabase via a REST API call. This is a network transmission of potentially sensitive user or system state, but the file contains no confirmation prompt, user-facing log/print, or warning comment/docstring disclosing that data is being sent to a remote service.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The put_writes method serializes writes entries and upserts them to the langgraph_writes table through the Supabase client. These payloads may contain user or execution data, yet the code provides no visible warning, confirmation, or explanatory comment indicating that this information is transmitted to an external backend.

Content

No source excerpt is available for this finding.