Skip to content

Commit 3938954

Browse files
huang47shhtheonlypersonchristian-byrne
authored
refactor: remove static v-html sinks (#14583)
## Summary Removes the three `v-html` sinks that render repository-owned static markup: - replaces the mask editor's HTML-string SVG map with a typed Vue icon component - renders the two pricing arrows as normal template text - deletes the now-unused `iconsHtml` constant ## Security rationale These values are currently hard-coded and therefore not directly exploitable. Removing the raw HTML sinks still narrows the XSS attack surface and prevents a future data-flow change from silently turning them into unsafe DOM injection points. ## Verification Local verification on commit `3354fdff0`: - `pnpm test:unit src/components/maskeditor/ToolPanel.test.ts src/platform/cloud/subscription/components/PricingTable.test.ts src/platform/workspace/components/PricingTableWorkspace.test.ts` — 2 files passed, 22 tests passed - `pnpm typecheck` — passed - targeted ESLint, Stylelint, and `oxfmt --check` — passed - scoped `rg` check finds no remaining `v-html` or `iconsHtml` references in the changed code paths The mask-editor test verifies every tool still renders its SVG icon. ## PR split / overlap check This PR is intentionally limited to static application markup. It does **not** modify node search. Live file list for #14574: - `packages/shared-frontend-utils/src/formatUtil.test.ts` - `packages/shared-frontend-utils/src/formatUtil.ts` - `src/components/searchbox/v2/NodeSearchListItem.test.ts` Intersection with this PR: **none**. Related: #14574 ## Split series - #14583 — static repository-owned markup - #14584 — application rich-content boundary - #14585 — website rich-text boundary - #14574 — node-search escaping (owned by the existing PR; excluded here) Co-authored-by: ShihChi Huang <shh@theonlyperson.com> Co-authored-by: Christian Byrne <cbyrne@comfy.org>
1 parent 9d1a491 commit 3938954

4 files changed

Lines changed: 97 additions & 52 deletions

File tree

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<template>
2+
<svg
3+
v-if="tool === Tools.MaskPen"
4+
:data-testid="`icon-${tool}`"
5+
viewBox="0 0 44 44"
6+
fill="currentColor"
7+
>
8+
<path
9+
class="cls-1"
10+
d="M10.97,15.98v14.04c0,.825.675,1.5,1.5,1.5h23.07c.825,0,1.5-.675,1.5-1.5V15.98c0-.825-.675-1.5-1.5-1.5H12.47c-.825,0-1.5.675-1.5,1.5ZM25.79,28.16c-4.365,1.41-8.355-2.58-6.945-6.945.51-1.575,1.785-2.85,3.36-3.36,4.365-1.41,8.355,2.58,6.945,6.945-.51,1.575-1.785,2.85-3.36,3.36Z"
11+
/>
12+
</svg>
13+
<svg
14+
v-else-if="tool === Tools.Eraser"
15+
:data-testid="`icon-${tool}`"
16+
viewBox="0 0 44 44"
17+
fill="currentColor"
18+
>
19+
<g>
20+
<rect
21+
class="cls-2"
22+
x="16.68"
23+
y="10"
24+
width="10.63"
25+
height="24"
26+
rx="1.16"
27+
ry="1.16"
28+
transform="translate(22 -9.11) rotate(45)"
29+
/>
30+
<path
31+
class="cls-1"
32+
d="M17.27,34.27c-.42,0-.85-.16-1.17-.48l-5.88-5.88c-.31-.31-.48-.73-.48-1.17s.17-.86.48-1.17l15.34-15.34c.62-.62,1.72-.62,2.34,0l5.88,5.88c.65.65.65,1.7,0,2.34l-15.34,15.34c-.32.32-.75.48-1.17.48ZM26.73,10.73c-.18,0-.34.07-.46.19l-15.34,15.34c-.12.12-.19.29-.19.46s.07.34.19.46l5.88,5.88c.26.26.67.26.93,0l15.34-15.34c.26-.26.26-.67,0-.93l-5.88-5.88c-.12-.12-.29-.19-.46-.19Z"
33+
/>
34+
</g>
35+
<path
36+
class="cls-3"
37+
d="M20.33,11.03h8.32c.64,0,1.16.52,1.16,1.16v15.79h-10.63v-15.79c0-.64.52-1.16,1.16-1.16Z"
38+
transform="translate(20.97 -11.61) rotate(45)"
39+
/>
40+
</svg>
41+
<svg
42+
v-else-if="tool === Tools.MaskBucket"
43+
:data-testid="`icon-${tool}`"
44+
viewBox="0 0 44 44"
45+
fill="currentColor"
46+
>
47+
<path
48+
class="cls-1"
49+
d="M33.4,21.76l-11.42,11.41-.04.05c-.61.61-1.6.61-2.21,0l-8.91-8.91c-.61-.61-.61-1.6,0-2.21l.04-.05.3-.29h22.24Z"
50+
/>
51+
<path
52+
class="cls-1"
53+
d="M20.83,34.17c-.55,0-1.07-.21-1.46-.6l-8.91-8.91c-.8-.8-.8-2.11,0-2.92l11.31-11.31c.8-.8,2.11-.8,2.92,0l8.91,8.91c.39.39.6.91.6,1.46s-.21,1.07-.6,1.46l-11.31,11.31c-.39.39-.91.6-1.46.6ZM23.24,10.83c-.27,0-.54.1-.75.31l-11.31,11.31c-.41.41-.41,1.09,0,1.5l8.91,8.91c.4.4,1.1.4,1.5,0l11.31-11.31c.2-.2.31-.47.31-.75s-.11-.55-.31-.75l-8.91-8.91c-.21-.21-.48-.31-.75-.31Z"
54+
/>
55+
<path
56+
class="cls-1"
57+
d="M34.28,26.85c0,.84-.68,1.52-1.52,1.52s-1.52-.68-1.52-1.52,1.52-2.86,1.52-2.86c0,0,1.52,2.02,1.52,2.86Z"
58+
/>
59+
</svg>
60+
<svg
61+
v-else-if="tool === Tools.MaskColorFill"
62+
:data-testid="`icon-${tool}`"
63+
viewBox="0 0 44 44"
64+
fill="currentColor"
65+
>
66+
<path
67+
class="cls-1"
68+
d="M30.29,13.72c-1.09-1.1-2.85-1.09-3.94,0l-2.88,2.88-.75-.75c-.2-.19-.51-.19-.71,0-.19.2-.19.51,0,.71l1.4,1.4-9.59,9.59c-.35.36-.54.82-.54,1.32,0,.14,0,.28.05.41-.05.04-.1.08-.15.13-.39.39-.39,1.01,0,1.4.38.39,1.01.39,1.4,0,.04-.04.08-.09.11-.13.14.04.3.06.45.06.5,0,.97-.19,1.32-.55l9.59-9.59,1.38,1.38c.1.09.22.14.35.14s.26-.05.35-.14c.2-.2.2-.52,0-.71l-.71-.72,2.88-2.89c1.08-1.08,1.08-2.85-.01-3.94ZM19.43,25.82h-2.46l7.15-7.15,1.23,1.23-5.92,5.92Z"
69+
/>
70+
</svg>
71+
<svg
72+
v-else-if="tool === Tools.PaintPen"
73+
:data-testid="`icon-${tool}`"
74+
viewBox="0 0 44 44"
75+
fill="currentColor"
76+
>
77+
<path
78+
class="cls-1"
79+
d="M34,13.93c0,.47-.19.94-.55,1.31l-13.02,13.04c-.09.07-.18.15-.27.22-.07-1.39-1.21-2.48-2.61-2.49.07-.12.16-.24.27-.34l13.04-13.04c.72-.72,1.89-.72,2.6,0,.35.35.55.83.55,1.3Z"
80+
/>
81+
<path
82+
class="cls-1"
83+
d="M19.64,29.03c0,4.46-6.46,3.18-9.64,0,3.3-.47,4.75-2.58,7.06-2.58,1.43,0,2.58,1.16,2.58,2.58Z"
84+
/>
85+
</svg>
86+
</template>
87+
88+
<script setup lang="ts">
89+
import { Tools } from '@/extensions/core/maskeditor/types'
90+
91+
defineProps<{ tool: Tools }>()
92+
</script>

src/components/maskeditor/ToolPanel.test.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,6 @@ import { Tools, allTools } from '@/extensions/core/maskeditor/types'
1111

1212
type ToolManager = ReturnType<typeof useToolManager>
1313

14-
vi.mock('@/extensions/core/maskeditor/constants', () => ({
15-
iconsHtml: {
16-
pen: '<svg data-testid="icon-pen" />',
17-
rgbPaint: '<svg data-testid="icon-rgbPaint" />',
18-
eraser: '<svg data-testid="icon-eraser" />',
19-
paintBucket: '<svg data-testid="icon-paintBucket" />',
20-
colorSelect: '<svg data-testid="icon-colorSelect" />'
21-
}
22-
}))
23-
2414
const initialMock = () =>
2515
reactive({
2616
currentTool: Tools.MaskPen as Tools,
@@ -75,7 +65,7 @@ describe('ToolPanel', () => {
7565
expect(screen.getAllByTestId('tool-button')).toHaveLength(allTools.length)
7666
})
7767

78-
it('should render the icon HTML for each tool', () => {
68+
it('should render an SVG icon for each tool', () => {
7969
renderPanel()
8070
for (const tool of allTools) {
8171
expect(screen.getByTestId(`icon-${tool}`)).toBeInTheDocument()

src/components/maskeditor/ToolPanel.vue

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@
1414
"
1515
@click="onToolSelect(tool)"
1616
>
17-
<div
18-
class="flex items-center justify-center"
19-
v-html="iconsHtml[tool]"
20-
></div>
17+
<div class="flex items-center justify-center">
18+
<MaskEditorToolIcon :tool="tool" />
19+
</div>
2120
<div class="maskEditor_toolPanelIndicator"></div>
2221
</div>
2322
</div>
@@ -43,8 +42,8 @@ import { useI18n } from 'vue-i18n'
4342
4443
import { cn } from '@comfyorg/tailwind-utils'
4544
45+
import MaskEditorToolIcon from '@/components/maskeditor/MaskEditorToolIcon.vue'
4646
import type { useToolManager } from '@/composables/maskeditor/useToolManager'
47-
import { iconsHtml } from '@/extensions/core/maskeditor/constants'
4847
import type { Tools } from '@/extensions/core/maskeditor/types'
4948
import { allTools } from '@/extensions/core/maskeditor/types'
5049
import { useMaskEditorStore } from '@/stores/maskEditorStore'

src/extensions/core/maskeditor/constants.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)