Skip to content

Extend User Profile with Bio, Social Links, and Expertise Tags#151

Open
pheobeayo wants to merge 2 commits intoInferara:mainfrom
pheobeayo:feat/extend-user-profile
Open

Extend User Profile with Bio, Social Links, and Expertise Tags#151
pheobeayo wants to merge 2 commits intoInferara:mainfrom
pheobeayo:feat/extend-user-profile

Conversation

@pheobeayo
Copy link

@pheobeayo pheobeayo commented Feb 24, 2026

Extended User Profile with Bio, Social Links, and Expertise Tags

Demo video: https://www.loom.com/share/70a29c953d36405e80e8f3db0c61ced1

fixes and closes #70

@SurfingBowser
Copy link
Collaborator

hi @pheobeayo please make sure you meet all the requirements listed in #70 when submitting a PR. Most importantly is the note on a short demo walkthrough of changes. No audio or personal identifying info is required, just a simple video showing the changes.

@pheobeayo
Copy link
Author

hi @pheobeayo please make sure you meet all the requirements listed in #70 when submitting a PR. Most importantly is the note on a short demo walkthrough of changes. No audio or personal identifying info is required, just a simple video showing the changes.

hi @pheobeayo please make sure you meet all the requirements listed in #70 when submitting a PR. Most importantly is the note on a short demo walkthrough of changes. No audio or personal identifying info is required, just a simple video showing the changes.

Demo video added

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Extends the regular user profile UX to support richer profile metadata (social links + expertise tags) and adds shared validation/constants for the new profile fields.

Changes:

  • Added social link fields (website, Twitter/X, GitHub, Discord) to the edit form and rendered social icon links on the profile page.
  • Added expertise tags with predefined autocomplete + free-form entry, and displayed tags as chips on the profile.
  • Introduced shared constants/validators (MAX_BIO_LENGTH, tag limits, URL validation helpers) and small UI helpers (character counter, tags input).

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
UI/src/features/pages/regular/profile/profile.tsx Renders social icon links and expertise tag chips on the user profile page.
UI/src/features/pages/regular/profile/hooks/edit-profile.hook.ts Sends newly added profile fields to the self-edit API payload.
UI/src/features/pages/regular/profile/edit-profile.tsx Adds Social Links + Expertise Tags sections and client-side validation/counter UI.
UI/src/features/components/Expertisetagsinput.tsx New autocomplete + freeSolo multi-tag input with tag limits/deduplication.
UI/src/features/components/CharacterCounter.tsx New reusable character counter component for text inputs/editors.
UI/src/api/soroban-security-portal/models/user.ts Extends user models with new fields and adds constants + URL validators.
UI/package-lock.json Lockfile updates reflecting dependency graph metadata changes.
Files not reviewed (1)
  • UI/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@SurfingBowser
Copy link
Collaborator

@pheobeayo please take a look at the comments

Copy link
Contributor

@0xGeorgii 0xGeorgii left a comment

Choose a reason for hiding this comment

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

@pheobeayo thank you for contributing, please address the following comments:

Critical Issues (Must Fix)

  1. Frontend-Backend Architecture Mismatch

This is the biggest problem. The PR adds bio, website, twitter, github, discord, and expertiseTags fields to SelfEditUserItem (which is sent via the user self-edit endpoint), but the backend already has a separate UserProfile system with its own API:

  • Backend UserProfile API: PUT /api/v1/profiles/self — supports bio, website, expertiseTags, location
  • Backend explicitly removed twitter_handle and github_handle columns in migration 20260127054059_RefactorUserProfileEntity.cs — these are now managed via ConnectedAccounts JSONB on LoginModel

The frontend should be calling the UserProfile API endpoints, not adding these fields to the user self-edit payload. As written, the backend will either ignore these fields or error, since SelfEditUserItem on the backend doesn't have website, twitter, github, discord, or expertiseTags properties.

Action: Refactor to use GET/PUT /api/v1/profiles/self for bio, website, and expertiseTags. For Twitter/GitHub/Discord, use the existing ConnectedAccounts system.

  1. XSS Vulnerability in isValidWebsiteUrl

File: UI/src/api/soroban-security-portal/models/user.ts

export function isValidWebsiteUrl(url: string): boolean {
if (!url) return true;
try { new URL(url); return true; } catch { return false; }
}

The URL constructor accepts javascript:, data:, vbscript: schemes. Since the validated URL is rendered into href on the profile page (), a user could set their website to
javascript:alert(document.cookie) — stored XSS.

Fix:
export function isValidWebsiteUrl(url: string): boolean {
if (!url) return true;
try {
const parsed = new URL(url.trim());
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
} catch { return false; }
}

  1. bio: '' Hardcoded — Data Loss on Every Save

File: UI/src/features/pages/regular/profile/hooks/edit-profile.hook.ts

const selfEditUserItem: SelfEditUserItem = {
// ...
bio: '' // Always empty — overwrites any existing bio
};

The bio field is added to the model but never populated from state. Every profile save sends bio: '', erasing existing data. Additionally, MAX_BIO_LENGTH is validated against aboutYou/personalInfo, suggesting confusion about whether bio and personalInfo are the same field.

  1. Discord Links Are Broken

File: UI/src/features/pages/regular/profile/profile.tsx

url: user?.discord ? https://discord.com/users/${user.discord} : undefined

The edit form collects a Discord username (placeholder: "your username"), but https://discord.com/users/ requires a numeric user ID. This produces broken links for every user. Render Discord as non-clickable text, or use the ConnectedAccounts data which has the actual OAuth account ID.


High Priority Issues

  1. File Naming Convention Violation

Expertisetagsinput.tsx uses inconsistent casing. Existing files in features/components/ use kebab-case (custom-toolbar.tsx, multimode-input.tsx). Should be expertise-tags-input.tsx, or move to src/components/ with PascalCase (ExpertiseTagsInput.tsx) since it's a reusable component.

  1. MUI v7 Deprecated InputProps

All four social link TextField components use the deprecated InputProps prop. MUI v7 (which this project uses) requires slotProps.input:

// Deprecated:
InputProps={{ startAdornment: ... }}

// MUI v7 correct:
slotProps={{ input: { startAdornment: ... } }}

  1. onBlur={validateSocials} Validates All Fields Simultaneously

Every social link field calls validateSocials on blur, which validates all three URL fields at once. Tabbing from Website to Twitter triggers validation on the still-empty GitHub field. Use per-field validation
instead.

  1. URL Regex Is Case-Sensitive

return /^https?://(www.)?(twitter.com|x.com)/[A-Za-z0-9_]{1,15}/?$/.test(url);

HTTPS://Twitter.com/username fails validation. Add the i flag to both isValidTwitterUrl and isValidGitHubUrl.


Medium Priority Issues

  1. Missing Trailing Newlines

All 6 modified/new files are missing trailing newlines (\ No newline at end of file).

  1. Duplicated Chip Styling

The expertise tag chip sx is duplicated verbatim between Expertisetagsinput.tsx and profile.tsx. Extract into a shared constant or small component.

  1. package-lock.json Contains Unrelated Churn

88 additions / 43 deletions are entirely peer: true flag toggling — no new dependencies. This suggests the lockfile was regenerated with a different npm version. Consider reverting: git checkout origin/main -- UI/package-lock.json.

  1. No Unit Tests

The URL validators are security-relevant code with no tests. ExpertiseTagsInput has non-trivial logic (deduplication, limits, Add "..." pattern matching) that should be tested.

@pheobeayo
Copy link
Author

@pheobeayo thank you for contributing, please address the following comments:

Critical Issues (Must Fix)

  1. Frontend-Backend Architecture Mismatch

This is the biggest problem. The PR adds bio, website, twitter, github, discord, and expertiseTags fields to SelfEditUserItem (which is sent via the user self-edit endpoint), but the backend already has a separate UserProfile system with its own API:

  • Backend UserProfile API: PUT /api/v1/profiles/self — supports bio, website, expertiseTags, location
  • Backend explicitly removed twitter_handle and github_handle columns in migration 20260127054059_RefactorUserProfileEntity.cs — these are now managed via ConnectedAccounts JSONB on LoginModel

The frontend should be calling the UserProfile API endpoints, not adding these fields to the user self-edit payload. As written, the backend will either ignore these fields or error, since SelfEditUserItem on the backend doesn't have website, twitter, github, discord, or expertiseTags properties.

Action: Refactor to use GET/PUT /api/v1/profiles/self for bio, website, and expertiseTags. For Twitter/GitHub/Discord, use the existing ConnectedAccounts system.

  1. XSS Vulnerability in isValidWebsiteUrl

File: UI/src/api/soroban-security-portal/models/user.ts

export function isValidWebsiteUrl(url: string): boolean { if (!url) return true; try { new URL(url); return true; } catch { return false; } }

The URL constructor accepts javascript:, data:, vbscript: schemes. Since the validated URL is rendered into href on the profile page (), a user could set their website to javascript:alert(document.cookie) — stored XSS.

Fix: export function isValidWebsiteUrl(url: string): boolean { if (!url) return true; try { const parsed = new URL(url.trim()); return parsed.protocol === 'http:' || parsed.protocol === 'https:'; } catch { return false; } }

  1. bio: '' Hardcoded — Data Loss on Every Save

File: UI/src/features/pages/regular/profile/hooks/edit-profile.hook.ts

const selfEditUserItem: SelfEditUserItem = { // ... bio: '' // Always empty — overwrites any existing bio };

The bio field is added to the model but never populated from state. Every profile save sends bio: '', erasing existing data. Additionally, MAX_BIO_LENGTH is validated against aboutYou/personalInfo, suggesting confusion about whether bio and personalInfo are the same field.

  1. Discord Links Are Broken

File: UI/src/features/pages/regular/profile/profile.tsx

url: user?.discord ? https://discord.com/users/${user.discord} : undefined

The edit form collects a Discord username (placeholder: "your username"), but https://discord.com/users/ requires a numeric user ID. This produces broken links for every user. Render Discord as non-clickable text, or use the ConnectedAccounts data which has the actual OAuth account ID.

High Priority Issues

  1. File Naming Convention Violation

Expertisetagsinput.tsx uses inconsistent casing. Existing files in features/components/ use kebab-case (custom-toolbar.tsx, multimode-input.tsx). Should be expertise-tags-input.tsx, or move to src/components/ with PascalCase (ExpertiseTagsInput.tsx) since it's a reusable component.

  1. MUI v7 Deprecated InputProps

All four social link TextField components use the deprecated InputProps prop. MUI v7 (which this project uses) requires slotProps.input:

// Deprecated: InputProps={{ startAdornment: ... }}

// MUI v7 correct: slotProps={{ input: { startAdornment: ... } }}

  1. onBlur={validateSocials} Validates All Fields Simultaneously

Every social link field calls validateSocials on blur, which validates all three URL fields at once. Tabbing from Website to Twitter triggers validation on the still-empty GitHub field. Use per-field validation instead.

  1. URL Regex Is Case-Sensitive

return /^https?://(www.)?(twitter.com|x.com)/[A-Za-z0-9_]{1,15}/?$/.test(url);

HTTPS://Twitter.com/username fails validation. Add the i flag to both isValidTwitterUrl and isValidGitHubUrl.

Medium Priority Issues

  1. Missing Trailing Newlines

All 6 modified/new files are missing trailing newlines (\ No newline at end of file).

  1. Duplicated Chip Styling

The expertise tag chip sx is duplicated verbatim between Expertisetagsinput.tsx and profile.tsx. Extract into a shared constant or small component.

  1. package-lock.json Contains Unrelated Churn

88 additions / 43 deletions are entirely peer: true flag toggling — no new dependencies. This suggests the lockfile was regenerated with a different npm version. Consider reverting: git checkout origin/main -- UI/package-lock.json.

  1. No Unit Tests

The URL validators are security-relevant code with no tests. ExpertiseTagsInput has non-trivial logic (deduplication, limits, Add "..." pattern matching) that should be tested.

Okay, I have updated the PR

@pheobeayo
Copy link
Author

@pheobeayo please take a look at the comments

Done

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.

Extend User Profile with Bio, Social Links, and Expertise Tags

4 participants