Skip to content

feat(avatar): validate user and org avatar fields#1734

Open
AmanGIT07 wants to merge 1 commit into
mainfrom
feat/avatar-validation
Open

feat(avatar): validate user and org avatar fields#1734
AmanGIT07 wants to merge 1 commit into
mainfrom
feat/avatar-validation

Conversation

@AmanGIT07

Copy link
Copy Markdown
Contributor

Summary

The avatar field on users and organizations accepted any string with no format or size checks. This PR validates it on all create and update paths.

Changes

  • New core/avatar package: avatar.Validate accepts only data:image/jpeg;base64, / data:image/png;base64, values, checks the size cap before decoding, decodes the base64, and confirms the bytes start with the matching JPEG/PNG file signature. Empty values stay allowed.
  • user.Service Create/Update and organization.Service Create/Update/AdminCreate run the validation before persisting.
  • New config app.avatar.max_size_bytes (default 1MB, measured on the encoded string); wired through NewService in buildAPIDependencies.
  • The six user/org create/update Connect handlers map avatar.ErrInvalid to CodeInvalidArgument.
  • The admin UI server sets Content-Security-Policy: img-src 'self' data: https: on its responses.

Technical Details

  • avatar.Config lives in core/avatar and is embedded in server.Config; a zero or negative limit falls back to avatar.DefaultMaxBytes.
  • pkg/server/security_headers.go adds a header map wrapper; the UI server passes uiSecurityHeaders to it.

Test Plan

  • Manual testing completed
  • Build and type checking passes
  • Unit tests: core/avatar validator table tests, service rejection tests for user/org, handler mapping tests, security header test.
  • make lint (0 issues) and make test (race detector) pass.

SQL Safety (if your PR touches *_repository.go or goqu.*)

Not applicable — no repository or query changes.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview, Comment Jul 3, 2026 10:22am

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 14182108-ae00-4053-8867-c0125d62297a

📥 Commits

Reviewing files that changed from the base of the PR and between 4a3b4c5 and a677dbe.

📒 Files selected for processing (15)
  • cmd/serve.go
  • core/avatar/avatar.go
  • core/avatar/avatar_test.go
  • core/organization/service.go
  • core/organization/service_test.go
  • core/user/service.go
  • core/user/service_test.go
  • internal/api/v1beta1connect/organization.go
  • internal/api/v1beta1connect/organization_test.go
  • internal/api/v1beta1connect/user.go
  • internal/api/v1beta1connect/user_test.go
  • pkg/server/config.go
  • pkg/server/security_headers.go
  • pkg/server/security_headers_test.go
  • pkg/server/server.go
📝 Walkthrough

Walkthrough

This PR adds an avatar validation package for base64 JPEG/PNG data URLs, integrates validation into organization and user services and their Connect API handlers (mapping validation failures to CodeInvalidArgument), threads avatar configuration through server config and wiring, and adds UI security headers middleware.

Changes

Avatar Validation Feature

Layer / File(s) Summary
Avatar validation package
core/avatar/avatar.go, core/avatar/avatar_test.go
New Validate function checks base64 JPEG/PNG data URLs against size limits and magic byte signatures, returning ErrInvalid on failure; includes Config, DefaultMaxBytes, and unit tests.
Organization service integration
core/organization/service.go, core/organization/service_test.go
Service gains avatarConfig; Create, Update, and AdminCreate validate Avatar before proceeding; constructor and tests updated accordingly.
User service integration
core/user/service.go, core/user/service_test.go
Service gains avatarConfig; Create and Update validate Avatar; tests updated with new config wiring, rejection cases, and base64 data URL expectations.
Connect API error mapping
internal/api/v1beta1connect/organization.go, internal/api/v1beta1connect/organization_test.go, internal/api/v1beta1connect/user.go, internal/api/v1beta1connect/user_test.go
Handlers for create/update organization and user map avatar.ErrInvalid to connect.CodeInvalidArgument, with new tests verifying this behavior.
Config and wiring
pkg/server/config.go, cmd/serve.go
Config struct gains an Avatar field; userService construction passes cfg.App.Avatar.

UI Security Headers

Layer / File(s) Summary
Security headers middleware
pkg/server/security_headers.go, pkg/server/security_headers_test.go, pkg/server/server.go
Adds withSecurityHeaders middleware setting a Content-Security-Policy header, wired into ServeUI's ListenAndServe call, with a corresponding test.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • raystack/frontier#1471: Both PRs modify core/organization/service.go/NewService and its wiring in cmd/serve.go, adjusting constructor parameters and dependencies.

Suggested reviewers: rsbh, whoAbhishekSah, rohilsurana

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Avatars must now be base64 JPEG or PNG data URLs within a configurable
size cap (app.avatar.max_size_bytes, default 1MB). Validation runs in
the user and organization services, and the admin UI server now sets a
Content-Security-Policy header for images.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
pkg/server/security_headers.go (1)

7-9: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Consider expanding beyond img-src for defense-in-depth.

Only img-src is constrained; other CSP directives fall back to permissive defaults, so scripts/frames/etc. remain unrestricted. Since the map-based design already makes this trivial to extend, consider also adding common hardening headers like X-Content-Type-Options: nosniff and Referrer-Policy.

♻️ Optional expansion
 var uiSecurityHeaders = map[string]string{
 	"Content-Security-Policy": "img-src 'self' data: https:",
+	"X-Content-Type-Options":  "nosniff",
+	"Referrer-Policy":         "strict-origin-when-cross-origin",
 }
internal/api/v1beta1connect/user_test.go (1)

127-141: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add matching coverage for UpdateUser/UpdateCurrentUser avatar mapping.

Only CreateUser gets a test for the new avatar.ErrInvalidCodeInvalidArgument mapping, even though the same case errors.Is(err, avatar.ErrInvalid) branch was also added to UpdateUser and UpdateCurrentUser in this file. A regression in either of those switches wouldn't be caught by this file's tests.

Do you want me to generate the two additional test cases mirroring this one for UpdateUser and UpdateCurrentUser?


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 384eacbe-427f-4ccb-ae1f-0263bac38e32

📥 Commits

Reviewing files that changed from the base of the PR and between 854c122 and 4a3b4c5.

📒 Files selected for processing (15)
  • cmd/serve.go
  • core/avatar/avatar.go
  • core/avatar/avatar_test.go
  • core/organization/service.go
  • core/organization/service_test.go
  • core/user/service.go
  • core/user/service_test.go
  • internal/api/v1beta1connect/organization.go
  • internal/api/v1beta1connect/organization_test.go
  • internal/api/v1beta1connect/user.go
  • internal/api/v1beta1connect/user_test.go
  • pkg/server/config.go
  • pkg/server/security_headers.go
  • pkg/server/security_headers_test.go
  • pkg/server/server.go

Comment on lines +128 to +130
mockUserSrv := new(mocks.UserService)
mockUserSrv.EXPECT().Create(mock.Anything, mock.Anything).Return(user.User{}, avatar.ErrInvalid)
h := &ConnectHandler{userService: mockUserSrv}

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.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm whether mocks.NewUserService(t) exists and is the prevailing pattern in this package
rg -n 'func NewUserService' internal/api/v1beta1connect/mocks/user_service.go
rg -n 'mocks\.NewUserService\(t\)|new\(mocks\.UserService\)' internal/api/v1beta1connect

Repository: raystack/frontier

Length of output: 2032


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '744,790p' internal/api/v1beta1connect/mocks/user_service.go
printf '\n----\n'
sed -n '100,150p' internal/api/v1beta1connect/user_test.go

Repository: raystack/frontier

Length of output: 1864


Use mocks.NewUserService(t) here. new(mocks.UserService) skips the t.Cleanup(func() { mock.AssertExpectations(t) }) wiring, so a missing Create call can let this test pass.

@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 28654358183

Coverage increased (+0.08%) to 44.943%

Details

  • Coverage increased (+0.08%) from the base build.
  • Patch coverage: 14 uncovered changes across 5 files (57 of 71 lines covered, 80.28%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
internal/api/v1beta1connect/organization.go 6 2 33.33%
internal/api/v1beta1connect/user.go 6 2 33.33%
core/organization/service.go 11 8 72.73%
cmd/serve.go 2 0 0.0%
pkg/server/server.go 1 0 0.0%
Total (8 files) 71 57 80.28%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 37672
Covered Lines: 16931
Line Coverage: 44.94%
Coverage Strength: 12.49 hits per line

💛 - Coveralls

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

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.

2 participants