fix: address review findings (#2703)

This commit is contained in:
Jinjing
2026-05-23 13:11:08 -07:00
committed by GitHub
parent ace241c43f
commit 3f6940a746
18 changed files with 615 additions and 50 deletions
+49
View File
@@ -0,0 +1,49 @@
import { describe, expect, it } from 'vitest'
import { sanitizeRepoIcon } from './repo-icon'
describe('sanitizeRepoIcon', () => {
it('accepts lucide, emoji, and supported image icons', () => {
expect(sanitizeRepoIcon({ type: 'lucide', name: 'Folder' })).toEqual({
type: 'lucide',
name: 'Folder'
})
expect(sanitizeRepoIcon({ type: 'emoji', emoji: '🚀' })).toEqual({
type: 'emoji',
emoji: '🚀'
})
expect(
sanitizeRepoIcon({
type: 'image',
src: 'https://github.com/stablyai.png?size=64',
source: 'github',
label: 'stablyai/orca'
})
).toEqual({
type: 'image',
src: 'https://github.com/stablyai.png?size=64',
source: 'github',
label: 'stablyai/orca'
})
})
it('keeps null as an explicit reset', () => {
expect(sanitizeRepoIcon(null)).toBeNull()
})
it('rejects unsupported image urls and oversized payloads', () => {
expect(
sanitizeRepoIcon({
type: 'image',
src: 'javascript:alert(1)',
source: 'favicon'
})
).toBeUndefined()
expect(
sanitizeRepoIcon({
type: 'image',
src: `data:image/png;base64,${'a'.repeat(401 * 1024)}`,
source: 'upload'
})
).toBeUndefined()
})
})
+68
View File
@@ -0,0 +1,68 @@
export type RepoIconImageSource = 'upload' | 'favicon' | 'github'
export type RepoIcon =
| { type: 'lucide'; name: string }
| { type: 'emoji'; emoji: string }
| { type: 'image'; src: string; source: RepoIconImageSource; label?: string }
export const MAX_REPO_ICON_UPLOAD_BYTES = 256 * 1024
export const MAX_REPO_ICON_DATA_URL_LENGTH = 400 * 1024
const LUCIDE_ICON_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9]*$/
const IMAGE_SOURCE_IDS = new Set(['upload', 'favicon', 'github'])
function isSupportedImageSrc(src: string): boolean {
return (
/^https:\/\/[^\s]+$/i.test(src) ||
/^data:image\/(?:png|svg\+xml);base64,[A-Za-z0-9+/=\s]+$/i.test(src)
)
}
export function sanitizeRepoIcon(value: unknown): RepoIcon | null | undefined {
if (value === undefined) {
return undefined
}
if (value === null) {
return null
}
if (!value || typeof value !== 'object') {
return undefined
}
const candidate = value as Record<string, unknown>
if (candidate.type === 'lucide') {
const name = typeof candidate.name === 'string' ? candidate.name.trim() : ''
if (!LUCIDE_ICON_NAME_PATTERN.test(name) || name.length > 40) {
return undefined
}
return { type: 'lucide', name }
}
if (candidate.type === 'emoji') {
const emoji = typeof candidate.emoji === 'string' ? candidate.emoji.trim() : ''
if (!emoji || emoji.length > 16) {
return undefined
}
return { type: 'emoji', emoji }
}
if (candidate.type === 'image') {
const src = typeof candidate.src === 'string' ? candidate.src.trim() : ''
const source = typeof candidate.source === 'string' ? candidate.source : ''
if (!IMAGE_SOURCE_IDS.has(source) || src.length > MAX_REPO_ICON_DATA_URL_LENGTH) {
return undefined
}
if (!isSupportedImageSrc(src)) {
return undefined
}
const label = typeof candidate.label === 'string' ? candidate.label.trim().slice(0, 80) : ''
return {
type: 'image',
src,
source: source as RepoIconImageSource,
...(label ? { label } : {})
}
}
return undefined
}
+2
View File
@@ -15,6 +15,7 @@ import type { TaskProvider } from './task-providers'
import type { FeatureTipId } from './feature-tips'
import type { GitBranchChangeStatus } from './git-status-types'
import type { KeybindingOverrides, TerminalShortcutPolicy } from './keybindings'
import type { RepoIcon } from './repo-icon'
// Re-exported for backward compat with renderer call sites that import
// `WorkspaceCreateTelemetrySource` from '../../../shared/types'.
@@ -75,6 +76,7 @@ export type Repo = {
path: string
displayName: string
badgeColor: string
repoIcon?: RepoIcon | null
addedAt: number
kind?: RepoKind
gitUsername?: string