Skip to content

[DRAFT] feat: Add blob storage foundation #3824

Draft
0vertake wants to merge 1 commit intosuperplanehq:mainfrom
0vertake:feat/blob-storage-foundation
Draft

[DRAFT] feat: Add blob storage foundation #3824
0vertake wants to merge 1 commit intosuperplanehq:mainfrom
0vertake:feat/blob-storage-foundation

Conversation

@0vertake
Copy link
Copy Markdown
Collaborator

  • Define BlobStorage interface with in-memory and filesystem backends
  • Add blob metadata model and DB migration
  • Add StoreBlob, ListBlobs, DescribeBlob, DeleteBlob API endpoints
  • Wire blob authorization rules
  • Expose BlobContext to component execution context
  • Add blob UI panel across org, canvas, node, and execution scopes
  • Regenerate proto, OpenAPI spec, and TypeScript/Go SDK clients

- Define BlobStorage interface with in-memory and filesystem backends
- Add blob metadata model and DB migration
- Add StoreBlob, ListBlobs, DescribeBlob, DeleteBlob API endpoints
- Wire blob authorization rules
- Expose BlobContext to component execution context
- Add blob UI panel across org, canvas, node, and execution scopes
- Regenerate proto, OpenAPI spec, and TypeScript/Go SDK clients

Signed-off-by: Milos Jovanovic <milosjovanovic519@gmail.com>
Made-with: Cursor
@superplanehq-integration
Copy link
Copy Markdown

👋 Commands for maintainers:

  • /sp start - Start an ephemeral machine (takes ~30s)
  • /sp stop - Stop a running machine (auto-executed on pr close)

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

default:
return "", status.Errorf(codes.InvalidArgument, "invalid blob scope type: %s", scopeType)
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Organization scope object key missing org ID causes cross-tenant collision

High Severity

scopeObjectKey generates blobs/organization/{path} for organization scope without including the organization ID. Since object_key has a global unique constraint (idx_blobs_object_key), two different organizations storing a blob at the same path will collide — the second org gets an AlreadyExists error, and worse, the first org's blob content gets silently overwritten in storage. The function doesn't accept an organization ID parameter, and the caller in StoreBlob doesn't pass one. Other scopes avoid this because they embed globally-unique UUIDs (canvas, execution).

Additional Locations (1)
Fix in Cursor Fix in Web

}
_ = store.Delete(ctx, objectKey)
return nil, status.Errorf(codes.Internal, "failed to store blob metadata: %v", err)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

StoreBlob overwrites existing content before DB duplicate check

High Severity

store.Put at line 95 writes new content to blob storage, then models.CreateBlob at line 126 attempts the DB insert. If the insert fails with a duplicate object_key constraint, the handler returns AlreadyExists at line 128 without cleanup. At that point the original blob's content in storage has already been silently overwritten, while the original DB record still exists pointing to the now-corrupted data. The store.Delete cleanup on line 130 only runs for non-duplicate errors.

Fix in Cursor Fix in Web

@0vertake 0vertake marked this pull request as draft April 15, 2026 13:02
Sign up for free to 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