Skip to content

Commit 3a9fef9

Browse files
committed
fix(workflows): validate gradient color stops
1 parent fc3126b commit 3a9fef9

2 files changed

Lines changed: 36 additions & 4 deletions

File tree

packages/utils/src/color.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ describe('perceivedBackgroundBrightness', () => {
4848
expect(
4949
perceivedBackgroundBrightness('linear-gradient(45deg, currentColor, transparent)')
5050
).toBeNull()
51+
expect(
52+
perceivedBackgroundBrightness('linear-gradient(45deg, #fff, rebeccapurple, #000)')
53+
).toBeNull()
54+
expect(perceivedBackgroundBrightness('linear-gradient(rebeccapurple, #fff, #000)')).toBeNull()
5155
expect(perceivedBackgroundBrightness('currentColor')).toBeNull()
5256
})
5357
})

packages/utils/src/color.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,49 @@ export function perceivedBackgroundBrightness(background: string): number | null
3434
const solidBrightness = parseSolidBrightness(value)
3535
if (solidBrightness !== null) return solidBrightness
3636

37-
if (!/^(?:repeating-)?(?:linear|radial|conic)-gradient\(/.test(value)) return null
37+
const gradient = value.match(/^(?:repeating-)?(linear|radial|conic)-gradient\((.*)\)$/)
38+
if (!gradient) return null
3839

39-
const colorStops = value.match(/#[0-9a-f]{6}\b|#[0-9a-f]{3}\b|\b(?:white|black)\b/g)
40-
if (!colorStops || colorStops.length < 2) return null
40+
const [, gradientType, contents] = gradient
41+
const parts = contents.split(',').map((part) => part.trim())
42+
const firstStop = parseSupportedColorStop(parts[0])
43+
const colorStops = firstStop === null ? parts.slice(1) : parts
44+
if (
45+
colorStops.length < 2 ||
46+
(firstStop === null && !isSupportedGradientPreamble(gradientType, parts[0]))
47+
) {
48+
return null
49+
}
4150

4251
let totalBrightness = 0
4352
for (const colorStop of colorStops) {
44-
const brightness = parseSolidBrightness(colorStop)
53+
const brightness = parseSupportedColorStop(colorStop)
4554
if (brightness === null) return null
4655
totalBrightness += brightness
4756
}
4857

4958
return totalBrightness / colorStops.length
5059
}
5160

61+
function parseSupportedColorStop(value: string): number | null {
62+
const match = value.match(/^(#[0-9a-f]{6}\b|#[0-9a-f]{3}\b|(?:white|black)\b)(?:\s|$)/)
63+
return match ? parseSolidBrightness(match[1]) : null
64+
}
65+
66+
function isSupportedGradientPreamble(type: string, value: string): boolean {
67+
if (type === 'linear') {
68+
return /^(?:-?(?:\d+(?:\.\d+)?|\.\d+)(?:deg|grad|rad|turn)|to\s+(?:top|right|bottom|left)(?:\s+(?:top|right|bottom|left))?)$/.test(
69+
value
70+
)
71+
}
72+
if (type === 'radial') {
73+
return /^(?:(?:circle|ellipse|closest-side|closest-corner|farthest-side|farthest-corner|at)\b|-?(?:\d|\.\d))/.test(
74+
value
75+
)
76+
}
77+
return /^(?:from|at)\b/.test(value)
78+
}
79+
5280
function parseSolidBrightness(value: string): number | null {
5381
if (value === 'white') return 1
5482
if (value === 'black') return 0

0 commit comments

Comments
 (0)