Skip to content
cccskills
FreePublished on GitHub

data-isolation-and-lifecycle

Use when the diff stores, caches, indexes, exports, imports, migrates, soft-deletes or revokes access-controlled or multi-tenant data - policy vs query disagreement, bypass clients, tenant-less cache and object keys, signed links, derived copies (search, cache, logs, analytics), export/restore scope, migration defaults, and stale access after delete or revoke

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md8.2 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Data Isolation and Lifecycle

Overview

Endpoint checks protect one door. Data has many copies — a cache entry, a search document, a thumbnail, an export file, a log line, a backup, a queued job — and each copy has its own reader and its own retention. Isolation fails where a copy forgets who owns the data, or where a deleted or revoked record keeps working.

Core principle: pick one protected record the diff touches and follow it through every write, read, copy and state change. At each edge, name the code that enforces its owner. A tenant column is not that code.

1. A Tenant Field Is Not Isolation (CWE-639, CWE-863)

The owner column exists; the question is which query, policy or key enforces it on this path.

  • Bypass clients: Supabase's service-role key or a Postgres role with BYPASSRLS used in a request handler; a "system" repository that skips the tenant filter, called from a user-facing path.
  • Unscoped escapes: GORM Unscoped() (it also drops the soft-delete filter), Rails unscoped, a Django manager that is not the filtered default, Hibernate filters that do not apply to native queries, Prisma middleware that a raw query skips.
  • Policy and query disagree: row-level security on the table but the new view, function (SECURITY DEFINER), materialized view or aggregate reads around it; a join that brings in rows of another tenant through an unscoped relation.
-- ❌ SECURITY DEFINER runs as the function's owner; a table owner bypasses RLS
--    (unless FORCE ROW LEVEL SECURITY is set), so the caller's policy never applies
CREATE FUNCTION project_stats(p uuid) RETURNS TABLE(...) LANGUAGE sql SECURITY DEFINER
AS $$ SELECT ... FROM tasks WHERE project_id = p $$;
-- ✅ SECURITY INVOKER (the default), or filter on the caller's tenant inside the function

2. Keys and Namespaces Without the Tenant

Two principals collide on one logical key even though their records have separate owners:

  • cache keys built from a user-chosen name (report: + name) or from an id that is only unique per tenant;
  • object-storage keys from the uploaded filename (uploads/avatar.png) — one tenant overwrites or reads another's;
  • search document ids, deduplication keys, idempotency keys and lock names without the tenant;
  • globally unique constraints that let one tenant squat or probe another tenant's slug, email or domain.
// ❌ every org shares one key space
key := fmt.Sprintf("dashboard:%s", req.Name)
// ✅
key := fmt.Sprintf("org:%s:dashboard:%s", auth.OrgID(ctx), req.Name)

3. Signed URLs and Shared Links

  • A presigned URL that allows more than the issuer could do: PUT where GET was meant, a key prefix instead of one object, a content type the client chooses, a week of validity for a one-time download.
  • A share token that unlocks a folder when it was issued for one file, or keeps working after the item was unshared or its ACL changed.
  • A link id that is the object id, so any authenticated user with the id gets the object (access-control-and-idor).

4. Derived Copies That Forget the ACL

  • Search and vector indexes: the filter is applied at ingestion but not at query time, or permission changes never reach the index (llm-and-agent-security §6 for embeddings and context caches).
  • Caches: a private response cached under a key without the user; an ACL change that does not invalidate the cached decision or object.
  • Previews and thumbnails: generated into a public bucket or behind a guessable path while the original is private.
  • Logs, traces, analytics, crash reports: private content or credentials sent to a store with a broader audience or tenant mixing (secrets-and-crypto-review for the credential case).
  • Notifications and emails: a digest built from the list of everything, not the list the recipient may see.

5. Export, Backup, Import, Restore

  • Export scope: each item is authorized after selection, not only the request; deleted, draft and private fields are excluded; the download of the finished file is authorized again (the export id is not a bearer token).
  • Import and restore: archive contents are untrusted input. Owner ids, roles, statuses and foreign keys from the file are rewritten to the importer's scope; an import that can overwrite existing records checks write access to each one.
  • Restore and undo: restoring a snapshot can bring back a removed membership, a revoked key or a permission that current policy no longer allows — re-authorize restored state.

6. Migrations and Backfills

-- ❌ the new column makes every existing document public
ALTER TABLE documents ADD COLUMN is_public boolean NOT NULL DEFAULT true;
-- ✅ existing rows keep their current exposure; new rows opt in
ALTER TABLE documents ADD COLUMN is_public boolean NOT NULL DEFAULT false;

Also: a backfill that assigns orphaned rows to a default organization; a nullable new tenant_id read with OR tenant_id IS NULL (every tenant sees the legacy rows); dual-read code where the old and the new path apply different defaults during rollout; an id format change that lets old and new ids collide.

7. Deletion and Revocation

  • Soft-delete bypass: the default scope hides deleted rows, but a direct lookup, a relation, a search, a background job or an Unscoped() path returns or acts on them.
  • Re-registration: a deleted username, email or domain can be claimed by someone else while old references (shares, invites, webhooks, SSO mappings) still point at it.
  • Stale authority: removing a member, downgrading a role or revoking a key does not end existing sessions, cached permission decisions, WebSocket subscriptions, scheduled jobs or issued signed URLs.
  • Queued work after delete: a retry or a queued export recreates or re-sends data that was deleted after it was enqueued.

Severity Guide

Reading or changing another tenant's data through any copy → HIGH; CRITICAL when unauthenticated or across every tenant. Revoked access that keeps working → HIGH when it grants writes or secrets, MEDIUM for reads of non-sensitive data. A migration that exposes existing private records → HIGH. Retention longer than a privacy preference, with no unauthorized reader → not a finding.

Common Mistakes

  • Reporting a missing tenant_id column on a table every path reaches through a scoped repository — read the repository.
  • Treating a privacy preference or retention wish as an access boundary.
  • Reporting cache keys without the tenant when the cached value is public or the id is globally unique and the ACL is checked after the read.
  • Asserting what an external cache, CDN, replica or backup does — when the policy is not in the repository and it decides the finding, it is a needs-validation note (source-visibility-needs-validation).

Red Flags

  • Unscoped(), unscoped, BYPASSRLS, a service-role key or a "system" repository reached from a request handler.
  • SECURITY DEFINER on a function that reads tenant data.
  • A cache, object or lock key built without the tenant from a client-chosen value.
  • An import that inserts the file's owner_id, org_id, role or status as given.
  • A delete or revoke handler that updates one table and touches no session, cache, index or job.
  • A new boolean visibility column defaulting to true.

References (names and links only)

OWASP Top 10:2025 A01 Broken Access Control · ASVS 5.0 V8 Authorization, V14 Data Protection · CWE-212, 359, 613, 639, 863 · PostgreSQL row security policies · Cloudflare security-audit-skill

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases

The English summary is being prepared. Showing the original description.

makifbaysal/tasktrooper117Updated Oct 20, 2026

Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping

The English summary is being prepared. Showing the original description.

makifbaysal/tasktrooper117Updated Oct 20, 2026

Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal

The English summary is being prepared. Showing the original description.

makifbaysal/tasktrooper117Updated Oct 20, 2026

Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task

The English summary is being prepared. Showing the original description.

makifbaysal/tasktrooper117Updated Oct 20, 2026

How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.

The English summary is being prepared. Showing the original description.

makifbaysal/tasktrooper117Updated Oct 20, 2026

Use when the repository keeps its alerting in code, or a person asks for alerts outside TaskTrooper - PrometheusRule YAML, a Cloud Monitoring alert policy in Terraform, a Datadog monitor, each routed to TaskTrooper's alert ingest URL so it opens an incident here

The English summary is being prepared. Showing the original description.

makifbaysal/tasktrooper117Updated Oct 20, 2026

See all skills by makifbaysal

Report a problem with this skill