Skip to content

improvement(kbs): ownership bindings - #4833

Merged
icecrasher321 merged 3 commits into
stagingfrom
fix/kb-file-access-authorization
Jun 1, 2026
Merged

improvement(kbs): ownership bindings#4833
icecrasher321 merged 3 commits into
stagingfrom
fix/kb-file-access-authorization

Conversation

@icecrasher321

@icecrasher321icecrasher321 commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Authorize knowledge-base file access via trusted storage-key→workspace ownership bindings (workspace_files + document.storage_key)

Type of Change

  • Other: Security

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercelBot commented Jun 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
docsReadyReadyPreview, CommentJun 1, 2026 10:37pm

Request Review

@cursor

cursorBot commented Jun 1, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Changes authentication, cross-tenant KB file access, deletes, and upload binding across API routes, document service, migration, and cleanup—security-critical with broad blast radius if misconfigured.

Overview
This PR hardens knowledge-base (kb/) file access so ownership and authorization no longer depend on attacker-controlled document.fileUrl matching.

Read/serve path:verifyKBFileAccess now requires a trusted workspace_files binding (exact key → owning workspace + caller permission) and an active document referencing the same key via persisted document.storageKey (no URL/LIKE lookups). Deletes use binding-only verifyKBFileWriteAccess (write/admin on the owner workspace).

Upload & binding lifecycle: KB presigned (single/batch), multipart completion, and related flows require workspaceId, enforce write/admin, and record workspace_files ownership up front; multipart rolls back the object if binding insert fails. Document create/upsert validates internal kb/ URLs against bindings (KnowledgeBaseFileOwnershipError → 403); storage deletes check binding vs KB workspace. Moving a KB between workspaces re-points bindings only for keys the KB already owned; connector sync sets storageKey and upload metadata; soft-delete cleanup sweeps orphan KB bindings.

Schema: Migration adds document.storage_key, backfills from serve URLs, and backfills conservative workspace_files bindings for legacy objects.

Reviewed by Cursor Bugbot for commit 54fcedf. Configure here.

Comment threadapps/sim/lib/knowledge/connectors/sync-engine.ts
@greptile-apps

greptile-appsBot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces a trusted ownership layer for knowledge-base file authorization. Instead of granting KB file access by parsing document.fileUrl (which an attacker could plant), access is now resolved through a workspace_files binding written at upload time. A new document.storage_key column enables exact-key liveness checks; a migration backfills both columns for existing data. Companion changes tighten delete authorization, add a workspace re-point on KB transfers, and add an orphan-binding sweeper to the cleanup job.

  • authorization.tsverifyKBFileAccess is rewritten to require a non-deleted workspace_files binding (ownership) plus an active document.storageKey match (liveness); verifyKBFileWriteAccess is added for binding-only delete authorization.
  • service.ts (documents) — assertKnowledgeBaseFileUrlsOwnership guards document creation against planted cross-tenant kb/ keys; deleteDocumentStorageFiles now validates binding ownership before deleting storage objects — but the new guard treats null-workspaceId bindings the same as missing bindings, causing storage objects for personal-KB documents to be permanently leaked on deletion (regression from old unconditional-delete behavior).
  • cleanup-soft-deletes.ts — adds cleanupOrphanedKnowledgeBaseBindings to sweep workspace_files rows that have no corresponding document.storageKey reference after a 7-day grace window.

Confidence Score: 3/5

The core ownership-binding logic is well-structured and correct for workspace KBs, but personal-KB documents have a storage leak on deletion and remain inaccessible for reads after this deployment.

Two issues compound: (1) prior review threads confirmed null-workspace KB files lose read access permanently because hasActiveKbDocumentForKey and verifyKBFileAccess both treat null workspaceId as a denial; (2) deleteDocumentStorageFiles now also silently skips storage cleanup for those same files, so every personal-KB document deletion leaves the object in S3/Blob indefinitely with no cleanup path. Together these mean personal-KB users lose both read access and the ability to reclaim storage after deletion.

apps/sim/lib/knowledge/documents/service.ts (deleteDocumentStorageFiles regression for personal-KB files) and apps/sim/app/api/files/authorization.ts (verifyKBFileAccess denial for null-workspace KBs — covered in prior threads but unresolved)

Important Files Changed

FilenameOverview
apps/sim/app/api/files/authorization.tsRewrites verifyKBFileAccess to use a trusted workspace_files binding (ownership-first) plus a liveness check; adds verifyKBFileWriteAccess for deletes. Core flow is correct but null-workspace KBs remain unreadable (flagged in previous threads).
apps/sim/lib/knowledge/documents/service.tsAdds assertKnowledgeBaseFileUrlsOwnership guard and populates document.storageKey at creation; rewrites deleteDocumentStorageFiles to validate binding ownership before deleting. Personal KB files (null workspaceId / no binding) are silently skipped by the new ownership guard, permanently leaking storage objects on deletion.
apps/sim/background/cleanup-soft-deletes.tsAdds cleanupOrphanedKnowledgeBaseBindings to sweep workspace_files rows with no matching document.storageKey after the grace window. Logic is sound; only workspace-scoped bindings are swept (personal-KB bindings with null workspaceId are out of scope by design).
apps/sim/lib/knowledge/service.tsAdds ownership binding re-point when a KB changes workspace; correctly guards against laundering by scoping the UPDATE to the current workspace's bindings and only when a current workspace exists.
apps/sim/lib/uploads/server/metadata.tsAdds insertFileMetadataMany (bulk insert with ON CONFLICT DO NOTHING) and getFileMetadataByKeys; fixes orderBy to prefer active rows when includeDeleted returns both. Changes look correct.
apps/sim/app/api/files/multipart/route.tsCorrectly defers recordKnowledgeBaseOwnership to after completeS3/BlobMultipartUpload; rolls back the object if binding insert fails. Refactors if/else chain cleanly.
packages/db/migrations/0222_kb_document_storage_key.sqlAdds document.storage_key, backfills from file_url in batched transactions, builds index CONCURRENTLY, then backfills workspace_files ownership bindings. Multi-workspace anti-poisoning guard and earliest-uploader tie-break look correct. Personal-KB rows (null workspace_id) intentionally excluded from the binding backfill.
apps/sim/lib/knowledge/connectors/sync-engine.tsThreads KnowledgeBaseOwner through addDocument/updateDocument; writes ownership binding via kbOwnershipMetadata for workspace KBs. Personal KBs (null workspace) produce no binding, meaning storage objects from connector syncs cannot be cleaned up on deletion.

Sequence Diagram

sequenceDiagram
participant Client
participant PresignedRoute as /api/files/presigned
participant MultipartRoute as /api/files/multipart
participant S3 as S3 / Blob
participant WorkspaceFiles as workspace_files (binding)
participant AuthZ as verifyKBFileAccess
participant Document as document.storageKey
Note over Client,Document: Upload Flow (single presign — binding written before PUT)
Client->>PresignedRoute: "POST ?type=knowledge-base&workspaceId=ws-1"
PresignedRoute->>WorkspaceFiles: insertFileMetadata (key, ws-1)
PresignedRoute-->>Client: presigned URL + key
Client->>S3: PUT object
Note over Client,Document: Upload Flow (multipart — binding written AFTER complete)
Client->>MultipartRoute: "POST action=complete"
MultipartRoute->>S3: completeMultipartUpload
S3-->>MultipartRoute: ok
MultipartRoute->>WorkspaceFiles: recordKnowledgeBaseOwnership
MultipartRoute-->>Client: "{key, location}"
Note over Client,Document: Read Authorization
Client->>AuthZ: verifyKBFileAccess(key, userId)
AuthZ->>WorkspaceFiles: getFileMetadataByKey(key)
WorkspaceFiles-->>AuthZ: "binding {workspaceId}"
AuthZ->>AuthZ: getUserEntityPermissions(userId, workspaceId)
AuthZ->>Document: hasActiveKbDocumentForKey(key, workspaceId)
Document-->>AuthZ: "rows.length > 0"
AuthZ-->>Client: true / false
Note over Client,Document: Delete Authorization
Client->>AuthZ: verifyKBFileWriteAccess(key, userId)
AuthZ->>WorkspaceFiles: getFileMetadataByKey(key)
AuthZ->>AuthZ: "permission === write|admin?"
AuthZ-->>Client: true / false
Loading

Reviews (3): Last reviewed commit: "more comments" | Re-trigger Greptile

Comment threadapps/sim/app/api/files/authorization.ts
Comment threadapps/sim/lib/knowledge/documents/service.ts
Comment threadapps/sim/app/api/files/presigned/batch/route.ts
@icecrasher321

Copy link
Copy Markdown
CollaboratorAuthor

bugbot run

@icecrasher321

Copy link
Copy Markdown
CollaboratorAuthor

@greptile

Comment threadapps/sim/app/api/files/upload/route.ts
Comment threadapps/sim/lib/knowledge/documents/service.ts
Comment threadapps/sim/lib/knowledge/documents/service.ts
@icecrasher321

Copy link
Copy Markdown
CollaboratorAuthor

bugbot run

@icecrasher321

Copy link
Copy Markdown
CollaboratorAuthor

@greptile

@icecrasher321icecrasher321 changed the title improvement(kbs): ownership associationimprovement(kbs): ownership bindingsJun 1, 2026

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 54fcedf. Configure here.

Comment threadapps/sim/lib/knowledge/documents/service.ts
@icecrasher321
icecrasher321 merged commit 9bed841 into stagingJun 1, 2026
14 checks passed
@waleedlatif1
waleedlatif1 deleted the fix/kb-file-access-authorization branch June 2, 2026 16:54
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@icecrasher321