feat(ai): implement secure image attachments for PostHog AI chat#27
Draft
0xluffyb wants to merge 1 commit into
Draft
feat(ai): implement secure image attachments for PostHog AI chat#270xluffyb wants to merge 1 commit into
0xluffyb wants to merge 1 commit into
Conversation
…e storage and multimodal model Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
| conversation_id=uuid.UUID(conversation_id), | ||
| ) | ||
| except InvalidConversationAttachment as error: | ||
| raise exceptions.ValidationError({"image": str(error)}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR implements end-to-end PNG/JPEG image attachments for PostHog AI chat, featuring secure private storage, multimodal model input, and comprehensive guardrails.
Backend
products/posthog_ai/backend/models/assistant.py): Tenant-scoped storage with team, creator, and conversation UUID; fail-closed manager prevents unscoped queries; private unguessable object paths never use client filenamesproducts/posthog_ai/backend/attachments.py): Pillow-verified PNG/JPEG formats; 4 MiB/file and 4 files/message caps; 25M decoded-pixel limit; server-side re-encoding strips EXIF/metadata; sanitized display names; random object paths prevent enumerationee/api/conversation.py): Authenticated upload, content, and delete actions on ConversationViewSet; content returnsprivate, max-age=3600cache andX-Content-Type-Options: nosniff; every attachment ID is server-resolved against authenticated user, team, and conversation UUIDee/api/conversation.py:_get_scoped_attachment): Fail-closed queryset never trusts client-provided IDs; rejects mismatches with 404posthog/schema.py,ee/hogai/core/agent_modes/executables.py): Extended HumanMessage schema with HumanMessageAttachment; Temporal/checkpoints carry only IDs; transient Anthropic base64 blocks hydrated at final model boundaryee/api/conversation.py): Explicitly rejects attachments for Research (agent_mode=research), Sandbox (is_sandbox=true), and queue endpoints with 400 errors; no silent dropsFrontend
frontend/src/scenes/max/maxThreadLogic.tsx):pendingAttachmentsreducer/listeners tracks upload state;addPendingAttachmentsaction filters PNG/JPEG, enforces limits, revokes Blob URLs on unmount; guards double-submit during uploadfrontend/src/scenes/max/components/QuestionInput.tsx): Accessible file picker (accept="image/png,image/jpeg",multiple); drag/drop affordance with visual feedback; paste handler distinguishes images from text; removable previews with size/format validation; image-only submit allowedfrontend/src/scenes/max/Thread.tsx): Displays attached images via authenticated content endpoint; derivesconversationIdfrom maxThreadLogicfrontend/src/lib/api.ts):api.conversations.attachments.upload/delete/contenttyped against backend serializersSecurity Measures
Testing
ee/api/test/test_conversation.py): Valid PNG/JPEG upload/re-encoding; spoofed MIME and invalid SVG rejected; size and decoded-pixel caps enforced; team/user/conversation IDOR returns 404; authenticated content validatesCache-Controlandnosniff; rejected for Research, Sandbox, and queueproducts/posthog_ai/backend/tests/test_attachments.py): Transient base64 blocks load from storage; user/conversation mismatches raiseInvalidConversationAttachmentee/hogai/core/agent_modes/test/test_executables.py): Verifies Anthropic blocks at final execution layerfrontend/src/scenes/max/components/QuestionInput.test.tsx,maxThreadLogic.test.ts): Picker, drop, and paste workflows; preview rendering/removal; size/type rejections; image-only submit; double-submit guard; four-file limit; optimistic attachment preservationGenerated Artifacts
posthog.schema.pydefines HumanMessageAttachment;schema_enums.pyadds ContentType enum; OpenAPI generatesConversationAttachmentinproducts/conversations/frontend/generated/api.schemas.tsand MCP toolsmaxThreadLogicandmaxLogicCREATE TABLE conversation_attachmentswith FKs, uniqueobject_path, and scoped indexesType Safety
@extend_schemaannotations withOpenApiParameterfor path variables; correct MIME types on binary responseConversationAttachmentadded to.semgrep/rules/idor-team-scoped-models.yamlNeed help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.