Skip to content

Conversation

@pedroccastro
Copy link

What does this PR do?

This PR fixes two bugs in API v2 user creation endpoint POST /v2/organizations/{orgId}/users:

Bug 1 - avatarValidator rejecting URLs:

  • The validator only accepted base64 encoded images
  • API documentation and all e2e tests show URL examples
  • Users received empty avatarValidator: "" constraint error

Bug 2 - User creation failing when username provided:

  • createNewUsersConnectToOrgIfExists requires a valid email
  • Service was passing username instead of email when username was provided
  • Error: "Invite failed because {username} is not a valid email address"

Key Changes:

  • avatarValidator.ts: Accept both URLs (http/https) and base64 images
  • organizations-users-service.ts: Always use email for user creation; username is applied via updateOrganizationUser after creation

Visual Demo

Before - Avatar URL rejected

avatar_error

Before - Username causing TRPCError

trpc_error

After - Full payload working

working

How should this be tested?

Test Scenario 1 - Avatar URL accepted:

  1. POST /v2/organizations/{orgId}/users
  2. Body: { "email": "test@example.com", "avatarUrl": "https://avatars.githubusercontent.com/u/583231?v=4" }
  3. Expected: User created successfully with avatarUrl

Test Scenario 2 - Username with avatar:

  1. POST /v2/organizations/{orgId}/users
  2. Body:
{
  "email": "test@example.com",
  "username": "testuser",
  "avatarUrl": "https://avatars.githubusercontent.com/u/583231?v=4",
  "locale": "pt",
  "timeZone": "America/Sao_Paulo",
  "timeFormat": 24
}
  1. Expected: User created with correct username and avatarUrl

Test Scenario 3 - Minimal payload (regression):

  1. POST /v2/organizations/{orgId}/users
  2. Body: { "email": "minimal@example.com" }
  3. Expected: User created successfully (existing behavior preserved)

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code
  • N/A I have updated the developer docs in /docs if this PR makes changes that would require a documentation change.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

- avatarValidator: accept URLs in addition to base64 images
- organizations-users-service: use email for user creation instead of username

The avatarValidator was incorrectly rejecting valid URLs even though the API documentation shows URL examples and all e2e tests use URLs.

The user creation was failing when username was provided because createNewUsersConnectToOrgIfExists requires a valid email, not username. The username is correctly applied via updateOrganizationUser after creation.

Fixes user creation endpoint POST /v2/organizations/{orgId}/users
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 2 files

Copy link
Contributor

@keithwillcode keithwillcode left a comment

Choose a reason for hiding this comment

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

Since logic is changing, we should have automated tests to verify the changes

@github-actions github-actions bot marked this pull request as draft December 5, 2025 07:53
Copy link
Contributor

@supalarry supalarry left a comment

Choose a reason for hiding this comment

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

Review:

  Add comprehensive test coverage for avatarValidator changes:

  - Unit tests: 18 scenarios covering valid/invalid URLs and base64 images
  - E2E tests: user creation with username + avatarUrl (URL and base64)
  - Security validation: reject unsafe protocols and malformed data
  - Error message verification for validation failures

  Addresses testing requirements from code review
@pull-request-size pull-request-size bot added size/L and removed size/M labels Dec 5, 2025
@vercel
Copy link

vercel bot commented Dec 5, 2025

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

Project Deployment Preview Comments Updated (UTC)
cal-companion Ready Ready Preview Comment Dec 5, 2025 4:26pm
2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal Ignored Ignored Dec 5, 2025 4:26pm
cal-eu Ignored Ignored Dec 5, 2025 4:26pm

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 4 files

hariombalhara
hariombalhara previously approved these changes Dec 5, 2025
Copy link
Member

@hariombalhara hariombalhara left a comment

Choose a reason for hiding this comment

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

LGTM !!

  - Reject HTTP URLs, accept only HTTPS for security and browser compatibility
  - Reject empty strings and whitespace (use null to reset avatar)
  - Update validation message to clarify HTTPS requirement
  - Add test coverage for new security restrictions

 Addresses security feedback from code review regarding mixed content vulnerabilities and clearer API semantics for avatar reset
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 3 files (changes from recent commits).

Prompt for AI agents (all 1 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/api/v2/src/modules/users/validators/avatarValidator.ts">

<violation number="1" location="apps/api/v2/src/modules/users/validators/avatarValidator.ts:19">
P1: This change removes HTTP URL support, contradicting the PR description which states &quot;Accept both URLs (http/https)&quot;. If the original endpoint accepted HTTP URLs, this is a breaking change for existing API consumers. Either update the code to accept HTTP or clarify in the PR description that HTTP is intentionally being removed for security reasons.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

private isValidUrl(value: string): boolean {
try {
const url = new URL(value);
return url.protocol === "https:";
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 5, 2025

Choose a reason for hiding this comment

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

P1: This change removes HTTP URL support, contradicting the PR description which states "Accept both URLs (http/https)". If the original endpoint accepted HTTP URLs, this is a breaking change for existing API consumers. Either update the code to accept HTTP or clarify in the PR description that HTTP is intentionally being removed for security reasons.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/v2/src/modules/users/validators/avatarValidator.ts, line 19:

<comment>This change removes HTTP URL support, contradicting the PR description which states &quot;Accept both URLs (http/https)&quot;. If the original endpoint accepted HTTP URLs, this is a breaking change for existing API consumers. Either update the code to accept HTTP or clarify in the PR description that HTTP is intentionally being removed for security reasons.</comment>

<file context>
@@ -7,15 +7,16 @@ const BASE64_IMAGE_REGEX =
     try {
       const url = new URL(value);
-      return url.protocol === &quot;http:&quot; || url.protocol === &quot;https:&quot;;
+      return url.protocol === &quot;https:&quot;;
     } catch {
       return false;
</file context>
Suggested change
return url.protocol === "https:";
return url.protocol === "http:" || url.protocol === "https:";
Fix with Cubic

const regex = /^data:image\/[^;]+;base64,(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
return regex.test(avatarString);
validate(avatarString: string): boolean {
if (avatarString === null || avatarString === undefined) return true;
Copy link
Member

Choose a reason for hiding this comment

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

nit: TS confirms at line 9 that avatarString can be string only.
So, we just need
if (!avatarString?.trim()) return false;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants