mirror of
https://github.com/langgenius/dify.git
synced 2025-12-23 15:57:29 +00:00
Compare commits
1 Commits
refactor-t
...
fix/share-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d337e5d78 |
@@ -10,6 +10,7 @@ import { cleanup, render } from '@testing-library/react'
|
|||||||
import '@testing-library/jest-dom'
|
import '@testing-library/jest-dom'
|
||||||
import BlockInput from '../app/components/base/block-input'
|
import BlockInput from '../app/components/base/block-input'
|
||||||
import SupportVarInput from '../app/components/workflow/nodes/_base/components/support-var-input'
|
import SupportVarInput from '../app/components/workflow/nodes/_base/components/support-var-input'
|
||||||
|
import { sanitizeMarkdownContent } from '../app/components/base/markdown'
|
||||||
|
|
||||||
// Mock styles
|
// Mock styles
|
||||||
jest.mock('../app/components/app/configuration/base/var-highlight/style.module.css', () => ({
|
jest.mock('../app/components/app/configuration/base/var-highlight/style.module.css', () => ({
|
||||||
@@ -71,6 +72,18 @@ describe('XSS Prevention - Block Input and Support Var Input Security', () => {
|
|||||||
expect(scriptElements).toHaveLength(0)
|
expect(scriptElements).toHaveLength(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Markdown Sanitization', () => {
|
||||||
|
it('strips dangerous attributes and protocols from raw HTML blocks', () => {
|
||||||
|
const jsProtocol = 'java' + 'script:alert(1)'
|
||||||
|
const malicious = `<img src="x" onerror="alert(1)"><a href="${jsProtocol}">click</a><script>alert(1)</script>`
|
||||||
|
const sanitized = sanitizeMarkdownContent(malicious)
|
||||||
|
expect(sanitized).not.toContain('onerror')
|
||||||
|
expect(sanitized).not.toContain('<script')
|
||||||
|
const protoLower = jsProtocol.toLowerCase().split('alert')[0] // java + script:
|
||||||
|
expect(sanitized.toLowerCase()).not.toContain(protoLower)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
export {}
|
export {}
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
|
'use client'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import 'katex/dist/katex.min.css'
|
import 'katex/dist/katex.min.css'
|
||||||
import { flow } from 'lodash-es'
|
import { flow } from 'lodash-es'
|
||||||
|
import DOMPurify from 'dompurify'
|
||||||
|
import { useMemo } from 'react'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import { preprocessLaTeX, preprocessThinkTag } from './markdown-utils'
|
import { preprocessLaTeX, preprocessThinkTag } from './markdown-utils'
|
||||||
import type { ReactMarkdownWrapperProps, SimplePluginInfo } from './react-markdown-wrapper'
|
import type { ReactMarkdownWrapperProps, SimplePluginInfo } from './react-markdown-wrapper'
|
||||||
|
|
||||||
const ReactMarkdown = dynamic(() => import('./react-markdown-wrapper').then(mod => mod.ReactMarkdownWrapper), { ssr: false })
|
const ReactMarkdown = dynamic(() => import('./react-markdown-wrapper').then(mod => mod.ReactMarkdownWrapper), { ssr: false })
|
||||||
|
|
||||||
|
const SANITIZE_OPTIONS: DOMPurify.Config = {
|
||||||
|
USE_PROFILES: { html: true },
|
||||||
|
ADD_TAGS: ['details', 'summary'],
|
||||||
|
FORBID_TAGS: ['style', 'script'],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sanitizeMarkdownContent = (content: string) => DOMPurify.sanitize(content, SANITIZE_OPTIONS)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview Main Markdown rendering component.
|
* @fileoverview Main Markdown rendering component.
|
||||||
* This file was refactored to extract individual block renderers and utility functions
|
* This file was refactored to extract individual block renderers and utility functions
|
||||||
@@ -26,10 +37,11 @@ export const Markdown = (props: MarkdownProps) => {
|
|||||||
preprocessThinkTag,
|
preprocessThinkTag,
|
||||||
preprocessLaTeX,
|
preprocessLaTeX,
|
||||||
])(props.content)
|
])(props.content)
|
||||||
|
const sanitizedContent = useMemo(() => sanitizeMarkdownContent(latexContent), [latexContent])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn('markdown-body', '!text-text-primary', props.className)}>
|
<div className={cn('markdown-body', '!text-text-primary', props.className)}>
|
||||||
<ReactMarkdown pluginInfo={pluginInfo} latexContent={latexContent} customComponents={customComponents} customDisallowedElements={props.customDisallowedElements} />
|
<ReactMarkdown pluginInfo={pluginInfo} latexContent={sanitizedContent} customComponents={customComponents} customDisallowedElements={props.customDisallowedElements} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { Markdown } from '@/app/components/base/markdown'
|
||||||
import Header from './header'
|
import Header from './header'
|
||||||
import type { FeedbackType } from '@/app/components/base/chat/chat/type'
|
import type { FeedbackType } from '@/app/components/base/chat/chat/type'
|
||||||
import { format } from '@/service/base'
|
|
||||||
|
|
||||||
export type IResultProps = {
|
export type IResultProps = {
|
||||||
content: string
|
content: string
|
||||||
@@ -24,10 +25,9 @@ const Result: FC<IResultProps> = ({
|
|||||||
style={{
|
style={{
|
||||||
maxHeight: '70vh',
|
maxHeight: '70vh',
|
||||||
}}
|
}}
|
||||||
dangerouslySetInnerHTML={{
|
>
|
||||||
__html: format(content),
|
<Markdown content={content} className='w-full' />
|
||||||
}}
|
</div>
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user