Skip to content

Adds custom avatar URL for custom remotes#5155

Open
sergeibbb wants to merge 9 commits into
mainfrom
feature/custom-avatar-gk
Open

Adds custom avatar URL for custom remotes#5155
sergeibbb wants to merge 9 commits into
mainfrom
feature/custom-avatar-gk

Conversation

@sergeibbb
Copy link
Copy Markdown
Member

@sergeibbb sergeibbb commented Apr 20, 2026

Rebases and integrates #1636 (by @tmkx) on top of the current main after the @gitlens/git package split.

Summary

Adds an optional avatar URL template to gitlens.remotes[].urls, enabling corporate / self-hosted setups to resolve commit-author avatars through a custom URL.

 "gitlens.remotes": [
   {
     "domain": "gitlab.intranet.com",
     "type": "Custom",
     "urls": {
       "repository": "https://gitlab.intranet.com/${repo}",
       ...
+      "avatar": "https://avatar.intranet.com/employee?username=${emailName}&size=${size}"
     }
   }
 ]

Available tokens: ${email}, ${emailName} (email local-part), ${domain}, ${size}.

When no hosting integration resolves the commit author, getAvatarUriFromRemoteProvider now falls back to the best remote-with-provider; if that provider is a CustomRemoteProvider with avatar configured, its interpolated URL is cached and returned.

Differences vs. original PR #1636

  • Rebased onto the @gitlens/git package splitCustomRemote was renamed to CustomRemoteProvider and moved to packages/git/src/remotes/custom.ts; the RemotesUrlsConfig interface in the package needed the same avatar?: string addition as src/config.ts.
  • ${name} token renamed to ${emailName} — disambiguates from the commit-author display name. The schema description already labels it as "email local-part", but the variable name itself was potentially confusing.
  • Hardened against email-driven URL injection. Commit emails are attacker-controllable, and the original interpolation ran through encodeUrl (= encodeURI), which leaves /, ?, #, @, and : intact — so a crafted email could bend the resulting avatar URL's path, query, or fragment. getUrlForAvatar now component-encodes ${email}, ${emailName}, and ${domain} before interpolation, splits the email on the last @ (so RFC 5322 local-parts containing @ aren't truncated), and drops the now-redundant outer encodeUrl.
  • CHANGELOG entry added — credits @tmkx for the original PR.
  • Grammar fix in the markdownDescription for the avatar field ("a avatar url""an avatar URL").
  • Scope of closed issues narrowed. The original PR description had close #302 and close #1036. This PR closes Get GitLab avatar by email #302 only. Issue Get avatar from user #1036 asks for a standalone gitlens.avatars setting (map of email → file URI, or a folder of images) so that users without a custom remote — e.g., regular github.com users who just want local employee photos — can configure avatars. That specific mechanism is not delivered here: this PR requires a gitlens.remotes entry with "type": "Custom" and an avatar template, so Get avatar from user #1036 should remain open.

Refs #1036 · Closes #302

Test plan

  • With a gitlens.remotes entry of "type": "Custom" and an avatar template, commit-author avatars in the blame hover, commit details, and views resolve to the templated URL.
  • Without an avatar template, behavior is unchanged (Gravatar / hosting-integration path).
  • No regression on repos with only standard remotes (github/gitlab.com).
  • Verify ${emailName}, ${email}, ${domain}, ${size} tokens all interpolate.
  • Settings IntelliSense shows the avatar field with the correct description.
  • Email with URL-structural characters (e.g., a/b@host) produces a URL that preserves the email verbatim via component-encoding, without bending the template's path/query.

@sergeibbb sergeibbb force-pushed the feature/custom-avatar-gk branch from 5de1d12 to 7505314 Compare April 20, 2026 13:02
sergeibbb pushed a commit that referenced this pull request Apr 20, 2026
@augmentcode
Copy link
Copy Markdown

augmentcode Bot commented Apr 20, 2026

🤖 Augment PR Summary

Summary: Adds support for resolving commit-author avatars via a user-configured URL template for “Custom” remotes.

Changes:

  • Adds an optional avatar template under gitlens.remotes[].urls (schema + types) with tokens ${email}, ${emailName}, ${domain}, and ${size}
  • Implements CustomRemoteProvider.getUrlForAvatar() to interpolate those tokens and component-encode email-derived values to prevent URL-structure injection
  • Extends avatar resolution to fall back to the best remote provider when no hosting integration returns an avatar, using the custom template when present
  • Adds a trusted-workspace + per-template approval prompt and persists approvals in global storage (sync-enabled)
  • Introduces a reset command to clear approved avatar URL templates
  • Adds unit tests covering interpolation, RFC-5322-style parsing, and encoding edge cases

Technical Notes: Custom avatar URLs are only honored in trusted workspaces and require explicit user approval; resolved URIs are cached alongside existing avatar sources.

🤖 Was this summary useful? React with 👍 or 👎

Copy link
Copy Markdown

@augmentcode augmentcode Bot left a comment

Choose a reason for hiding this comment

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

Review completed. 3 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread package.json Outdated
Comment thread packages/git/src/remotes/custom.ts Outdated
Comment thread src/avatars.ts
sergeibbb added a commit that referenced this pull request Apr 20, 2026
sergeibbb added a commit that referenced this pull request Apr 20, 2026
Commit emails are attacker-controllable, so identity values interpolated
into a `gitlens.remotes` `avatar` template must not be allowed to inject
URL-structural characters. `encodeUrl` uses `encodeURI`, which preserves
`/`, `?`, `#`, `@`, and `:`, so a crafted email could previously bend
the resulting avatar URL's path, query, or fragment.

- Component-encodes `${email}`, `${emailName}`, and `${domain}` before
  interpolation
- Splits on the last `@` so RFC 5322 local-parts containing `@` are
  preserved (and `domain` can't be truncated by a multi-`@` email)
- Drops `getContext(...)` and the outer `encodeUrl(...)` — only the four
  documented tokens are relevant for an avatar URL, and every
  substituted value is now pre-encoded

(#5155)
@michaelblyons michaelblyons mentioned this pull request Apr 20, 2026
7 tasks
@sergeibbb sergeibbb marked this pull request as draft April 20, 2026 16:11
sergeibbb pushed a commit that referenced this pull request Apr 21, 2026
sergeibbb added a commit that referenced this pull request Apr 21, 2026
@sergeibbb sergeibbb force-pushed the feature/custom-avatar-gk branch from 905f7b6 to 1167eb8 Compare April 21, 2026 17:03
sergeibbb added a commit that referenced this pull request Apr 21, 2026
Commit emails are attacker-controllable, so identity values interpolated
into a `gitlens.remotes` `avatar` template must not be allowed to inject
URL-structural characters. `encodeUrl` uses `encodeURI`, which preserves
`/`, `?`, `#`, `@`, and `:`, so a crafted email could previously bend
the resulting avatar URL's path, query, or fragment.

- Component-encodes `${email}`, `${emailName}`, and `${domain}` before
  interpolation
- Splits on the last `@` so RFC 5322 local-parts containing `@` are
  preserved (and `domain` can't be truncated by a multi-`@` email)
- Drops `getContext(...)` and the outer `encodeUrl(...)` — only the four
  documented tokens are relevant for an avatar URL, and every
  substituted value is now pre-encoded

(#5155)
sergeibbb added a commit that referenced this pull request Apr 21, 2026
…ate for custom remotes, detailing security measures and user approval process.

(#5155)
sergeibbb added a commit that referenced this pull request Apr 30, 2026
…ate for custom remotes, detailing security measures and user approval process.

(#5155)
@sergeibbb sergeibbb force-pushed the feature/custom-avatar-gk branch from 1167eb8 to 26d5395 Compare April 30, 2026 17:41
sergeibbb added a commit that referenced this pull request Apr 30, 2026
…ate for custom remotes, detailing security measures and user approval process.

(#5155)
@sergeibbb sergeibbb force-pushed the feature/custom-avatar-gk branch from 26d5395 to 7c8578c Compare April 30, 2026 18:21
@sergeibbb
Copy link
Copy Markdown
Member Author

augment review

Copy link
Copy Markdown

@augmentcode augmentcode Bot left a comment

Choose a reason for hiding this comment

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

Review completed. 1 suggestion posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread src/avatars.ts
sergeibbb added a commit that referenced this pull request Apr 30, 2026
…ate for custom remotes, detailing security measures and user approval process.

(#5155)
@sergeibbb sergeibbb force-pushed the feature/custom-avatar-gk branch from 7c8578c to 8c4d40a Compare April 30, 2026 18:35
@sergeibbb
Copy link
Copy Markdown
Member Author

augment review

Copy link
Copy Markdown

@augmentcode augmentcode Bot left a comment

Choose a reason for hiding this comment

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

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread src/avatars.ts
Comment thread src/avatars.ts
sergeibbb pushed a commit that referenced this pull request May 28, 2026
Copy link
Copy Markdown

@augmentcode augmentcode Bot left a comment

Choose a reason for hiding this comment

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

Review completed. 1 suggestion posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread src/avatars.ts
if (hasAvatarSource) {
// A provider was consulted but returned no avatar — permanently cache "no result"
avatar.uri = undefined;
avatar.timestamp = Infinity;
Copy link
Copy Markdown

@augmentcode augmentcode Bot May 28, 2026

Choose a reason for hiding this comment

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

hasAvatarSource can become true for a CustomRemoteProvider even when getApprovedCustomRemoteAvatarUrl(...) returns undefined due to an untrusted workspace, missing template, or not-yet-approved template. In that case this avatar.timestamp = Infinity permanently caches “no result” and can prevent avatars from starting to resolve later in the same session if trust/config/approval changes without an explicit cache reset.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

sergeibbb added a commit that referenced this pull request May 28, 2026
Covers token interpolation, email parsing edge cases, and URI encoding
to ensure special characters cannot break URL structure or enable injection.

(#5155)
@sergeibbb sergeibbb force-pushed the feature/custom-avatar-gk branch from 28363a0 to bd91240 Compare May 28, 2026 16:48
sergeibbb added a commit that referenced this pull request May 28, 2026
Only permanently caches "no avatar" when a provider was actually consulted,
preventing fallback sources from being skipped on retry when no integration
or custom remote was available.

(#5155)
sergeibbb added a commit that referenced this pull request May 28, 2026
Covers token interpolation, email parsing edge cases, and URI encoding
to ensure special characters cannot break URL structure or enable injection.

(#5155)
@sergeibbb sergeibbb force-pushed the feature/custom-avatar-gk branch from bd91240 to 13f4921 Compare May 28, 2026 17:17
@gitkraken-services
Copy link
Copy Markdown

gitkraken-services Bot commented May 28, 2026

Rebase Failed

Error Details

Error: No merge base found between HEAD and REBASE_HEAD. Repository is a shallow clone — try running "git fetch --unshallow"

Summary

Metric Value
Target main
Source feature/custom-avatar-gk

Generated by Merge Mate at 2026-06-06 06:42:56 UTC

sergeibbb pushed a commit that referenced this pull request May 29, 2026
sergeibbb added a commit that referenced this pull request May 29, 2026
sergeibbb added a commit that referenced this pull request May 29, 2026
Commit emails are attacker-controllable, so identity values interpolated
into a `gitlens.remotes` `avatar` template must not be allowed to inject
URL-structural characters. `encodeUrl` uses `encodeURI`, which preserves
`/`, `?`, `#`, `@`, and `:`, so a crafted email could previously bend
the resulting avatar URL's path, query, or fragment.

- Component-encodes `${email}`, `${emailName}`, and `${domain}` before
  interpolation
- Splits on the last `@` so RFC 5322 local-parts containing `@` are
  preserved (and `domain` can't be truncated by a multi-`@` email)
- Drops `getContext(...)` and the outer `encodeUrl(...)` — only the four
  documented tokens are relevant for an avatar URL, and every
  substituted value is now pre-encoded

(#5155)
sergeibbb added a commit that referenced this pull request May 29, 2026
…ate for custom remotes, detailing security measures and user approval process.

(#5155)
@sergeibbb sergeibbb force-pushed the feature/custom-avatar-gk branch from 13f4921 to 2590992 Compare May 29, 2026 00:26
sergeibbb added a commit that referenced this pull request May 29, 2026
The V8 smi optimization (2^30) was applied to the avatar cache timestamp
in 2975af4, replacing Number.MAX_SAFE_INTEGER. Unlike the annotation
providers where the value is compared against line/column numbers, here
it is compared against Date.now() (~1.75 trillion ms), so the "never
expire" sentinel (~1.07 billion) always appears expired. This caused
repeated async re-lookups for repos with remotes but no integration.

Uses Infinity as the sentinel — makes hasAvatarExpired return false
without any arithmetic, and communicates the "never expire" intent
directly.

(#5155)
sergeibbb added a commit that referenced this pull request May 29, 2026
Only permanently caches "no avatar" when a provider was actually consulted,
preventing fallback sources from being skipped on retry when no integration
or custom remote was available.

(#5155)
sergeibbb added a commit that referenced this pull request May 29, 2026
Covers token interpolation, email parsing edge cases, and URI encoding
to ensure special characters cannot break URL structure or enable injection.

(#5155)
@sergeibbb sergeibbb added this to the 18.1 milestone May 29, 2026
@sergeibbb sergeibbb self-assigned this May 29, 2026
tmkx and others added 9 commits June 3, 2026 13:00
Commit emails are attacker-controllable, so identity values interpolated
into a `gitlens.remotes` `avatar` template must not be allowed to inject
URL-structural characters. `encodeUrl` uses `encodeURI`, which preserves
`/`, `?`, `#`, `@`, and `:`, so a crafted email could previously bend
the resulting avatar URL's path, query, or fragment.

- Component-encodes `${email}`, `${emailName}`, and `${domain}` before
  interpolation
- Splits on the last `@` so RFC 5322 local-parts containing `@` are
  preserved (and `domain` can't be truncated by a multi-`@` email)
- Drops `getContext(...)` and the outer `encodeUrl(...)` — only the four
  documented tokens are relevant for an avatar URL, and every
  substituted value is now pre-encoded

(#5155)
…ate for custom remotes, detailing security measures and user approval process.

(#5155)
The V8 smi optimization (2^30) was applied to the avatar cache timestamp
in 2975af4, replacing Number.MAX_SAFE_INTEGER. Unlike the annotation
providers where the value is compared against line/column numbers, here
it is compared against Date.now() (~1.75 trillion ms), so the "never
expire" sentinel (~1.07 billion) always appears expired. This caused
repeated async re-lookups for repos with remotes but no integration.

Uses Infinity as the sentinel — makes hasAvatarExpired return false
without any arithmetic, and communicates the "never expire" intent
directly.

(#5155)
Only permanently caches "no avatar" when a provider was actually consulted,
preventing fallback sources from being skipped on retry when no integration
or custom remote was available.

(#5155)
Covers token interpolation, email parsing edge cases, and URI encoding
to ensure special characters cannot break URL structure or enable injection.

(#5155)
@sergeibbb sergeibbb force-pushed the feature/custom-avatar-gk branch from 2590992 to 747cec7 Compare June 3, 2026 11:09
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.

Get GitLab avatar by email

4 participants