@@ -79,8 +79,16 @@ func (s *s3Store) Create(ctx context.Context, document *core.Document) (string,
7979}
8080
8181// CanvasStore implementation for user-owned canvases
82- func (s * s3Store ) getCanvasKey (userID , canvasID string ) string {
83- return path .Join (userID , canvasID )
82+ func (s * s3Store ) getCanvasKey (userID , canvasID string ) (string , error ) {
83+ // Sanitize canvasID to prevent path traversal attacks.
84+ // It should be a simple name, not a path.
85+ if path .Base (canvasID ) != canvasID {
86+ return "" , fmt .Errorf ("invalid canvas id: must not be a path" )
87+ }
88+ if canvasID == "" || canvasID == "." || canvasID == ".." {
89+ return "" , fmt .Errorf ("invalid canvas id: must not be empty or a dot directory" )
90+ }
91+ return path .Join (userID , canvasID ), nil
8492}
8593
8694func (s * s3Store ) List (ctx context.Context , userID string ) ([]* core.Canvas , error ) {
@@ -125,7 +133,10 @@ func (s *s3Store) List(ctx context.Context, userID string) ([]*core.Canvas, erro
125133}
126134
127135func (s * s3Store ) Get (ctx context.Context , userID , id string ) (* core.Canvas , error ) {
128- key := s .getCanvasKey (userID , id )
136+ key , err := s .getCanvasKey (userID , id )
137+ if err != nil {
138+ return nil , err
139+ }
129140 resp , err := s .s3Client .GetObject (ctx , & s3.GetObjectInput {
130141 Bucket : aws .String (s .bucket ),
131142 Key : aws .String (key ),
@@ -154,7 +165,10 @@ func (s *s3Store) Get(ctx context.Context, userID, id string) (*core.Canvas, err
154165}
155166
156167func (s * s3Store ) Save (ctx context.Context , canvas * core.Canvas ) error {
157- key := s .getCanvasKey (canvas .UserID , canvas .ID )
168+ key , err := s .getCanvasKey (canvas .UserID , canvas .ID )
169+ if err != nil {
170+ return err
171+ }
158172
159173 // Preserve CreatedAt on update
160174 if canvas .CreatedAt .IsZero () {
@@ -184,8 +198,11 @@ func (s *s3Store) Save(ctx context.Context, canvas *core.Canvas) error {
184198}
185199
186200func (s * s3Store ) Delete (ctx context.Context , userID , id string ) error {
187- key := s .getCanvasKey (userID , id )
188- _ , err := s .s3Client .DeleteObject (ctx , & s3.DeleteObjectInput {
201+ key , err := s .getCanvasKey (userID , id )
202+ if err != nil {
203+ return err
204+ }
205+ _ , err = s .s3Client .DeleteObject (ctx , & s3.DeleteObjectInput {
189206 Bucket : aws .String (s .bucket ),
190207 Key : aws .String (key ),
191208 })
0 commit comments