-
Notifications
You must be signed in to change notification settings - Fork 0
fix(security): prevent XSS in proxy endpoint via json.dumps() #3170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
07b7b4b
135c88a
359b8e1
7deb69c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -221,7 +221,7 @@ export function FilterBar({ | |||
| const timer = setTimeout(() => { | ||||
| onTrackEvent('search_no_results', { query }); | ||||
| lastTrackedQueryRef.current = query; | ||||
| }, 500); | ||||
| }, 200); | ||||
| return () => clearTimeout(timer); | ||||
| } | ||||
| }, [searchQuery, searchResults.length, onTrackEvent]); | ||||
|
|
@@ -378,7 +378,11 @@ export function FilterBar({ | |||
| {/* Grid size toggle */} | ||||
| <Tooltip title={imageSize === 'normal' ? 'compact view' : 'normal view'}> | ||||
| <Box | ||||
| onClick={() => onImageSizeChange(imageSize === 'normal' ? 'compact' : 'normal')} | ||||
| onClick={() => { | ||||
| const newSize = imageSize === 'normal' ? 'compact' : 'normal'; | ||||
| onImageSizeChange(newSize); | ||||
| onTrackEvent('toggle_grid_size', { size: newSize }); | ||||
|
||||
| onTrackEvent('toggle_grid_size', { size: newSize }); |
Copilot
AI
Jan 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change adds toggle_grid_size event tracking. While this appears to be a useful analytics improvement, it's unrelated to the XSS security fix in proxy.py. Analytics enhancements should be in a separate PR, not bundled with a security fix.
| onTrackEvent('toggle_grid_size', { size: newSize }); |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -89,7 +89,7 @@ export const ImageCard = memo(function ImageCard({ | |||||
| if (code) { | ||||||
| await navigator.clipboard.writeText(code); | ||||||
| setCopyState('copied'); | ||||||
| onTrackEvent?.('copy_code', { spec: image.spec_id, library: image.library, method: 'card' }); | ||||||
| onTrackEvent?.('copy_code', { spec: image.spec_id, library: image.library, method: 'card', page: 'home' }); | ||||||
|
||||||
| onTrackEvent?.('copy_code', { spec: image.spec_id, library: image.library, method: 'card', page: 'home' }); | |
| onTrackEvent?.('copy_code', { spec: image.spec_id, library: image.library, method: 'card' }); |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -164,7 +164,7 @@ export function SpecTabs({ | |||||
| try { | ||||||
| await navigator.clipboard.writeText(code); | ||||||
| setCopied(true); | ||||||
| onTrackEvent?.('copy_code', { library: libraryId, method: 'tab' }); | ||||||
| onTrackEvent?.('copy_code', { spec: specId, library: libraryId, method: 'tab', page: 'spec_detail' }); | ||||||
|
||||||
| onTrackEvent?.('copy_code', { spec: specId, library: libraryId, method: 'tab', page: 'spec_detail' }); | |
| onTrackEvent?.('copy_code', { library: libraryId, method: 'tab' }); |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -64,7 +64,7 @@ export function useAnalytics() { | |||||
| [isProduction] | ||||||
| ); | ||||||
|
|
||||||
| const trackPageview = useMemo(() => debounce(sendPageview, 300), [sendPageview]); | ||||||
| const trackPageview = useMemo(() => debounce(sendPageview, 150), [sendPageview]); | ||||||
|
||||||
| const trackPageview = useMemo(() => debounce(sendPageview, 150), [sendPageview]); | |
| const trackPageview = useMemo(() => debounce(sendPageview, 300), [sendPageview]); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -210,24 +210,32 @@ export function useFilterState({ | |
|
|
||
| // Remove a filter value from a specific group | ||
| const handleRemoveFilter = useCallback((groupIndex: number, value: string) => { | ||
| const group = activeFiltersRef.current[groupIndex]; | ||
| if (group) { | ||
| onTrackEvent('filter_remove', { category: group.category, value }); | ||
| } | ||
| setActiveFilters((prev) => { | ||
| const newFilters = [...prev]; | ||
| const group = newFilters[groupIndex]; | ||
| if (!group) return prev; | ||
| const grp = newFilters[groupIndex]; | ||
| if (!grp) return prev; | ||
|
|
||
| const updatedValues = group.values.filter((v) => v !== value); | ||
| const updatedValues = grp.values.filter((v) => v !== value); | ||
| if (updatedValues.length === 0) { | ||
| return newFilters.filter((_, i) => i !== groupIndex); | ||
| } | ||
| newFilters[groupIndex] = { ...group, values: updatedValues }; | ||
| newFilters[groupIndex] = { ...grp, values: updatedValues }; | ||
| return newFilters; | ||
| }); | ||
| }, []); | ||
| }, [onTrackEvent]); | ||
|
Comment on lines
+213
to
+229
|
||
|
|
||
| // Remove entire group by index | ||
| const handleRemoveGroup = useCallback((groupIndex: number) => { | ||
| const group = activeFiltersRef.current[groupIndex]; | ||
| if (group) { | ||
| onTrackEvent('filter_remove', { category: group.category, value: group.values.join(',') }); | ||
| } | ||
| setActiveFilters((prev) => prev.filter((_, i) => i !== groupIndex)); | ||
| }, []); | ||
| }, [onTrackEvent]); | ||
|
Comment on lines
+233
to
+238
|
||
|
|
||
| // Random filter - replaces last filter slot (or adds first one) | ||
| const handleRandom = useCallback( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -156,9 +156,9 @@ export function SpecPage() { | |
| link.href = impl.preview_url; | ||
| link.download = `${specId}-${impl.library_id}.png`; | ||
| link.click(); | ||
| trackEvent('download_image', { spec: specId, library: impl.library_id }); | ||
| trackEvent('download_image', { spec: specId, library: impl.library_id, page: isOverviewMode ? 'spec_overview' : 'spec_detail' }); | ||
| }, | ||
| [specId, trackEvent] | ||
| [specId, trackEvent, isOverviewMode] | ||
|
Comment on lines
+159
to
+161
|
||
| ); | ||
|
|
||
| // Handle copy code (works for both overview and detail mode) | ||
|
|
@@ -168,13 +168,13 @@ export function SpecPage() { | |
| try { | ||
| await navigator.clipboard.writeText(impl.code); | ||
| setCodeCopied(impl.library_id); | ||
| trackEvent('copy_code', { spec: specId, library: impl.library_id, method: 'image' }); | ||
| trackEvent('copy_code', { spec: specId, library: impl.library_id, method: 'image', page: isOverviewMode ? 'spec_overview' : 'spec_detail' }); | ||
| setTimeout(() => setCodeCopied(null), 2000); | ||
| } catch (err) { | ||
| console.error('Copy failed:', err); | ||
| } | ||
| }, | ||
| [specId, trackEvent] | ||
| [specId, trackEvent, isOverviewMode] | ||
|
Comment on lines
+171
to
+177
|
||
| ); | ||
|
|
||
| // Track page view | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change reduces the debounce time from 500ms to 200ms for tracking search queries with no results. While this may improve analytics responsiveness, it's unrelated to the XSS security fix in proxy.py. Analytics tuning should be in a separate PR, not bundled with a security fix.