From 01894635fe26eb8314d0c0aa5ef3e828425df889 Mon Sep 17 00:00:00 2001 From: Eugene Olonov Date: Sun, 18 Oct 2026 11:37:37 -0700 Subject: [PATCH 1/2] Don't render videos with relative or invalid URLs, show the error in development builds --- CHANGELOG.md | 1 + __tests__/html2/attachment/video.url.html | 218 ++++++++++++++++++ .../attachment/video.url.html.snap-1.png | Bin 0 -> 28977 bytes .../src/Attachment/VideoContent.spec.tsx | 34 +++ .../component/src/Attachment/VideoContent.tsx | 62 ++--- 5 files changed, 278 insertions(+), 37 deletions(-) create mode 100644 __tests__/html2/attachment/video.url.html create mode 100644 __tests__/html2/attachment/video.url.html.snap-1.png create mode 100644 packages/component/src/Attachment/VideoContent.spec.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b1ccf6b2d..4709838353 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ Legends: - Fixed GIFs in animation cards looping with no way to stop them: they now have a pause button, and do not play when the user prefers reduced motion or the card has `autostart: false`; the icons of the Copy and View code buttons are now visible in forced colors, fixes [#5734](https://github.com/microsoft/BotFramework-WebChat/issues/5734), in PR [#5897](https://github.com/microsoft/BotFramework-WebChat/pull/5897), by [@OEvgeny](https://github.com/OEvgeny) - Fixed the "Positive" card action button having less than 4.5:1 color contrast, fixes [#5713](https://github.com/microsoft/BotFramework-WebChat/issues/5713), in PR [#5905](https://github.com/microsoft/BotFramework-WebChat/pull/5905), by [@OEvgeny](https://github.com/OEvgeny) - Fixed screen readers announcing a single audio, video, or animation in a card as a list item, such as "1 of 1", fixes [#5722](https://github.com/microsoft/BotFramework-WebChat/issues/5722), in PR [#5903](https://github.com/microsoft/BotFramework-WebChat/pull/5903), by [@OEvgeny](https://github.com/OEvgeny) +- Fixed video and animation cards disappearing in production builds when the video URL is relative or invalid, in PR [#5889](https://github.com/microsoft/BotFramework-WebChat/pull/5889), by [@OEvgeny](https://github.com/OEvgeny) ## [4.19.2] - 2026-10-08 diff --git a/__tests__/html2/attachment/video.url.html b/__tests__/html2/attachment/video.url.html new file mode 100644 index 0000000000..c81d738a29 --- /dev/null +++ b/__tests__/html2/attachment/video.url.html @@ -0,0 +1,218 @@ + + + + + + + + + + + + +
+ + + diff --git a/__tests__/html2/attachment/video.url.html.snap-1.png b/__tests__/html2/attachment/video.url.html.snap-1.png new file mode 100644 index 0000000000000000000000000000000000000000..fb44bcf756e901515a58270bb934e1ae1ac23ca8 GIT binary patch literal 28977 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 + +import { render } from '@testing-library/react'; +import React from 'react'; + +import VideoContent from './VideoContent'; + +// Production builds of `validateProps()` return props as-is, the URL is not validated. +jest.mock('@msinternal/botframework-webchat-react-valibot', () => ({ + ...jest.requireActual('@msinternal/botframework-webchat-react-valibot'), + validateProps: (_: unknown, props: unknown) => props +})); + +// The video player needs the Web Chat context, it is not under test. +jest.mock('../hooks/useStyleSet', () => () => [{ videoContent: '' }]); + +test.each([ + ['root-relative', '/videos/intro.mp4'], + ['path-relative', 'intro.mp4'], + ['protocol-relative', '//example.com/intro.mp4'], + ['without a host', 'https://'], + ['with an invalid host', 'http://[invalid'] +])('when the URL is %s in production build, should render nothing', (_, src) => { + const { container } = render(); + + expect(container.innerHTML).toBe(''); +}); + +test('when the URL is absolute in production build, should render a video player', () => { + const { container } = render(); + + expect(container.querySelector('video')?.getAttribute('src')).toBe('https://example.com/intro.mp4'); +}); diff --git a/packages/component/src/Attachment/VideoContent.tsx b/packages/component/src/Attachment/VideoContent.tsx index 35b9c7caff..31d85f3053 100644 --- a/packages/component/src/Attachment/VideoContent.tsx +++ b/packages/component/src/Attachment/VideoContent.tsx @@ -1,6 +1,7 @@ import { validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import { onErrorResumeNext } from 'botframework-webchat-core'; import React, { memo } from 'react'; -import { boolean, object, optional, pipe, readonly, string, type InferInput } from 'valibot'; +import { boolean, object, optional, pipe, readonly, string, url, type InferInput } from 'valibot'; import HTMLVideoContent from './HTMLVideoContent'; import VimeoContent from './VimeoContent'; @@ -13,31 +14,14 @@ const YOUTUBE_WWW_SHORT_DOMAIN = 'www.youtu.be'; const VIMEO_DOMAIN = 'vimeo.com'; const VIMEO_WWW_DOMAIN = 'www.vimeo.com'; -// This is a workaround -// - Today, there is no good URL polyfill for older browser -// - Instead of writing a URL parser, for older browser, we will use this trick to parse the URL -function parseURL(url) { - let urlLike; - - if (typeof URL === 'function') { - urlLike = new URL(url); - } else { - urlLike = document.createElement('a'); - urlLike.setAttribute('href', url); - } - - const { hostname, pathname, search } = urlLike; - - return { hostname, pathname, search }; -} - const videoContentPropsSchema = pipe( object({ alt: optional(string()), autoPlay: optional(boolean()), loop: optional(boolean()), poster: optional(string()), - src: string() + // Relative URLs are not supported, they would point to a different server in each chat client. + src: pipe(string(), url()) }), readonly() ); @@ -47,26 +31,30 @@ type VideoContentProps = InferInput; function VideoContent(props: VideoContentProps) { const { alt, autoPlay = false, loop = false, poster, src } = validateProps(videoContentPropsSchema, props); - const { hostname, pathname, search } = parseURL(src); - const lastSegment = pathname.split('/').pop(); - const searchParams = new URLSearchParams(search); - - switch (hostname) { - case VIMEO_DOMAIN: - case VIMEO_WWW_DOMAIN: - return ; + // Production builds do not validate props, leave out the video if the URL is relative or cannot be parsed. + const parsedURL = onErrorResumeNext(() => new URL(src)); - case YOUTUBE_DOMAIN: - case YOUTUBE_WWW_DOMAIN: - return ; + if (!parsedURL) { + return null; + } - case YOUTUBE_SHORT_DOMAIN: - case YOUTUBE_WWW_SHORT_DOMAIN: - return ; + const { hostname, pathname, searchParams } = parsedURL; + const lastSegment = pathname.split('/').pop(); - default: - return ; - } + // Embed YouTube and Vimeo videos only when the URL has a video ID, otherwise, play the URL in