Specify a webhook-driven task for OpenClaw
Describe event inputs, validation, deduplication, and intended actions before integration work.
5,502,269 AI resources indexedWorking toward the world’s largest AI resource index.
Apply when writing code that touches auth, data access, file handling, or external APIs. Stella handles privileged legal data (attorney-client privilege, litigation holds, personal data).
--- name: conventions-security-22cffa01734e description: "Apply when writing code that touches auth, data access, file handling, or external APIs. Stella handles privileged legal data (attorney-client privilege, litigation holds, personal data)." --- # Security Conventions Apply when writing code that touches auth, data access, file handling, or external APIs. Stella handles privileged legal data (attorney-client privilege, litigation holds, personal data). ## SOC 2 / ISO 27001 Principles - **Least privilege** — minimum permissions needed. No wildcard IAM policies, no admin-by-default. - **Audit trail** — state-changing operations traceable to an actor and timestamp. Never silently mutate data. - **Encryption in transit and at rest** — TLS everywhere, S3 SSE, no plaintext secrets in code or logs. - **Input validation at boundaries** — all external input validated and sanitised before processing. - **Workspace isolation** — data from one workspace must never leak to another. - **Ethical walls** — workspace (matter) boundaries enforce information barriers for the `member` role at the RLS layer (not just the UI): a member has zero visibility into workspaces they are not assigned to via `workspace_members` — no names, no members, no metadata. **Known limitation:** org `owner`/`admin` roles carry a deliberate firm-admin override granting access to every client matter (`client_id IS NOT NULL`) regardless of assignment, and there is no per-matter screening to wall a specific admin out of a specific matter. Do not describe admin access as "absolute confidentiality." Screening a matter from an admin requires a matter-level exclusion consulted by the RLS view (`stella_authorized_workspaces`), which is not yet built. - **Access control** — every endpoint enforces auth and authorisation. No "internal-only" endpoints without guards. - **Dependency hygiene** — minimal, pinned, audited. - **Logging without leaking** — never log secrets, tokens, PII, or document contents. - **Change management** — all changes go through PR review. No direct commits to main. - **Data retention** — when data is deleted, it is actually deleted (not soft-deleted indefinitely). ## Structural Guardrails Prefer solutions that make security bugs **structurally impossible** (compile-time, lint-time) over ones that rely on developer discipline. ### Authorized database boundary Workspace-scoped handlers use `createSafeHandler`, declare permissions beside the route, derive `workspaceId: SafeId<"workspace">` from the authorized context, and access tenant data through `scopedDb`. Never accept ownership IDs from body/query input or fall back to root `db` because a relation is awkward to express. Root/system handlers require a documented non-tenant purpose and tables whose RLS posture denies ordinary application access. Enforced by `require-safe-route-handlers/require-safe-route-handlers` and `no-body-ownership-ids/no-body-ownership-ids`. When an identifier comes from a related row, authorize it in the same query or transaction that uses it. A prior UI filter, cache lookup, or separate read-then-write check is not an access-control boundary. ### Server-bound audit events Actors, workspace/organization ownership, request metadata, and before/after identifiers come from authenticated server context. Clients may supply a user action or reason where the domain requires it, but never the authoritative actor or tenant. Required audit writes participate in the same transaction as the mutation, or use a durable outbox when the sink is external. Enforced by `require-audit-on-mutation/require-audit-on-mutation`. ### Workspace status filtering `resolveAccessibleWorkspaces` returns **all** workspaces (including `deleting`). The auth macro exposes two fields: - `activeWorkspaceIds` — excludes `deleting` workspaces (includes active and archived). Use this for search, chat, MCP, and any query that builds a workspace allowlist. This is the default; reach for it first. - `accessibleWorkspaces` — includes all statuses. Only use in `workspaceAccessMacro` (which needs the status to return appropriate HTTP codes). Never pass these IDs as a search/query allowlist. If you need workspace IDs for a new feature, use `activeWorkspaceIds` unless you have an explicit reason to include deleting workspaces and document that reason in a comment. ### CSV and file exports Use `escapeCSV` from `@/api/lib/csv` for all CSV cell values. Never hand-roll CSV escaping; the shared utility handles both delimiter quoting and spreadsheet formula neutralization (=, +, -, @, tab, CR prefixes). This prevents CSV injection attacks where user-controlled values starting with formula characters execute in Excel/LibreOffice. ### Multi-entry-point validation Validate untrusted input at each entry boundary with the owning shared schema. Generated MCP capabilities validate the live endpoint's body, params, and query schemas in `apps/api/src/mcp/capability-tools.ts`; do not repeat that parsing inside the handler. Native MCP tools and chat registry tools normalize input through `normalizeObjectInputAtBoundary` before dispatch; any other caller that bypasses the HTTP route (cron jobs, workers) must apply the same normalizer and schema before calling the operation. Keep business invariants and related-resource authorization in the owning operation: an ID's valid shape does not establish its ownership. ### Filename sanitization All user-supplied filenames must pass through `sanitizeFilename` (`@/api/lib/sanitize-filename`) before storage or use in file operations (ZIP entries, Content-Disposition headers, S3 keys). The sanitizer strips path separators, traversal sequences, and dangerous characters. Enforced by `security-guards/no-raw-filename-write`. ### Cross-org user ID validation When a handler accepts a `userId` from user input (body, query, or params) and uses it in a query that returns user data (names, emails, images), validate org membership first using `validateOrgUserId` from `@/api/lib/validated-org-user-id`. The returned `ValidatedOrgUserId` proves the check happened at the type level, making cross-org user ID injection structurally impossible. For read paths that resolve userIds stored in the database (not from user input), scope the user query with an `innerJoin` on the `member` table filtered by `session.activeOrganizationId`; enforced by `security-guards/no-unscoped-user-query`. ### CI workflow permissions GitHub Actions workflows must declare the **minimum** permissions needed. Never use `permissions: write-all`. For PR-triggered workflows, scope to `contents: read` + `pull-requests: write`. Pin third-party actions to commit SHAs, not mutable tags. For SBOM/provenance, use the shared `stella/.github` reusable workflows which handle pinning, checksums, and PR-based updates.--- name: conventions-security-22cffa01734e description: "Apply when writing code that touches auth, data access, file handling, or external APIs. Stella handles privileged legal data (attorney-client privilege, litigation holds, personal data)." --- # Security Conventions Apply when writing code that touches auth, data access, file handling, or external APIs. Stella handles privileged legal data (attorney-client privilege, litigation holds, personal data). ## SOC 2 / ISO 27001 Principles - **Least privilege** — minimum permissions needed. No wildcard IAM policies, no admin-by-default. - **Audit trail** — state-changing operations traceable to an actor and timestamp. Never silently mutate data. - **Encryption in transit and at rest** — TLS everywhere, S3 SSE, no plaintext secrets in code or logs. - **Input validation at boundaries** — all external input validated and sanitised before processing. - **Workspace isolation** — data from one workspace must never leak to another. - **Ethical walls** — workspace (matter) boundaries enforce information barriers for the `member` role at the RLS layer (not just the UI): a member has zero visibility into workspaces they are not assigned to via `workspace_members` — no names, no members, no metadata. **Known limitation:** org `owner`/`admin` roles carry a deliberate firm-admin override granting access to every client matter (`client_id IS NOT NULL`) regardless of assignment, and there is no per-matter screening to wall a specific admin out of a specific matter. Do not describe admin access as "absolute confidentiality." Screening a matter from an admin requires a matter-level exclusion consulted by the RLS view (`stella_authorized_workspaces`), which is not yet built. - **Access control** — every endpoint enforces auth and authorisation. No "internal-only" endpoints without guards. - **Dependency hygiene** — minimal, pinned, audited. - **Logging without leaking** — never log secrets, tokens, PII, or document contents. - **Change management** — all changes go through PR review. No direct commits to main. - **Data retention** — when data is deleted, it is actually deleted (not soft-deleted indefinitely). ## Structural Guardrails Prefer solutions that make security bugs **structurally impossible** (compile-time, lint-time) over ones that rely on developer discipline. ### Authorized database boundary Workspace-scoped handlers use `createSafeHandler`, declare permissions beside the route, derive `workspaceId: SafeId<"workspace">` from the authorized context, and access tenant data through `scopedDb`. Never accept ownership IDs from body/query input or fall back to root `db` because a relation is awkward to express. Root/system handlers require a documented non-tenant purpose and tables whose RLS posture denies ordinary application access. Enforced by `require-safe-route-handlers/require-safe-route-handlers` and `no-body-ownership-ids/no-body-ownership-ids`. When an identifier comes from a related row, authorize it in the same query or transaction that uses it. A prior UI filter, cache lookup, or separate read-then-write check is not an access-control boundary. ### Server-bound audit events Actors, workspace/organization ownership, request metadata, and before/after identifiers come from authenticated server context. Clients may supply a user action or reason where the domain requires it, but never the authoritative actor or tenant. Required audit writes participate in the same transaction as the mutation, or use a durable outbox when the sink is external. Enforced by `require-audit-on-mutation/require-audit-on-mutation`. ### Workspace status filtering `resolveAccessibleWorkspaces` returns **all** workspaces (including `deleting`). The auth macro exposes two fields: - `activeWorkspaceIds` — excludes `deleting` workspaces (includes active and archived). Use this for search, chat, MCP, and any query that builds a workspace allowlist. This is the default; reach for it first. - `accessibleWorkspaces` — includes all statuses. Only use in `workspaceAccessMacro` (which needs the status to return appropriate HTTP codes). Never pass these IDs as a search/query allowlist. If you need workspace IDs for a new feature, use `activeWorkspaceIds` unless you have an explicit reason to include deleting workspaces and document that reason in a comment. ### CSV and file exports Use `escapeCSV` from `@/api/lib/csv` for all CSV cell values. Never hand-roll CSV escaping; the shared utility handles both delimiter quoting and spreadsheet formula neutralization (=, +, -, @, tab, CR prefixes). This prevents CSV injection attacks where user-controlled values starting with formula characters execute in Excel/LibreOffice. ### Multi-entry-point validation Validate untrusted input at each entry boundary with the owning shared schema. Generated MCP capabilities validate the live endpoint's body, params, and query schemas in `apps/api/src/mcp/capability-tools.ts`; do not repeat that parsing inside the handler. Native MCP tools and chat registry tools normalize input through `normalizeObjectInputAtBoundary` before dispatch; any other caller that bypasses the HTTP route (cron jobs, workers) must apply the same normalizer and schema before calling the operation. Keep business invariants and related-resource authorization in the owning operation: an ID's valid shape does not establish its ownership. ### Filename sanitization All user-supplied filenames must pass through `sanitizeFilename` (`@/api/lib/sanitize-filename`) before storage or use in file operations (ZIP entries, Content-Disposition headers, S3 keys). The sanitizer strips path separators, traversal sequences, and dangerous characters. Enforced by `security-guards/no-raw-filename-write`. ### Cross-org user ID validation When a handler accepts a `userId` from user input (body, query, or params) and uses it in a query that returns user data (names, emails, images), validate org membership first using `validateOrgUserId` from `@/api/lib/validated-org-user-id`. The returned `ValidatedOrgUserId` proves the check happened at the type level, making cross-org user ID injection structurally impossible. For read paths that resolve userIds stored in the database (not from user input), scope the user query with an `innerJoin` on the `member` table filtered by `session.activeOrganizationId`; enforced by `security-guards/no-unscoped-user-query`. ### CI workflow permissions GitHub Actions workflows must declare the **minimum** permissions needed. Never use `permissions: write-all`. For PR-triggered workflows, scope to `contents: read` + `pull-requests: write`. Pin third-party actions to commit SHAs, not mutable tags. For SBOM/provenance, use the shared `stella/.github` reusable workflows which handle pinning, checksums, and PR-based updates.
Imported from the listed AgentSkill.sh category. Complete supplied package files and notices are retained. Required external tools and credentials are not installed. No bundled code was executed during import. Individual agent task behavior has not been tested.