Skip to content

Commit

Permalink
Docs bot escape user entry (#461)
Browse files Browse the repository at this point in the history
  • Loading branch information
seanparkross committed Jul 5, 2024
1 parent fed7be4 commit fff9d39
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 7 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@mdx-js/react": "^3.0.0",
"autoprefixer": "^10.4.16",
"clsx": "^1.2.1",
"dompurify": "^3.1.5",
"graphiql": "^3.0.10",
"graphql": "^16.8.1",
"graphql-ws": "^5.14.2",
Expand All @@ -46,6 +47,7 @@
"@docusaurus/module-type-aliases": "3.4.0",
"@docusaurus/tsconfig": "3.4.0",
"@docusaurus/types": "3.4.0",
"@types/dompurify": "^3",
"@types/punycode": "^2",
"@types/react-transition-group": "^4",
"dotenv": "^16.3.1",
Expand Down
50 changes: 43 additions & 7 deletions src/components/AiChatBot/AiChatBot.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useEffect, useRef, useState } from 'react';
import Markdown from 'markdown-to-jsx';
import DOMPurify from 'dompurify';
import './styles.css';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import { CloseIcon, RespondingIconGray, SparklesIcon } from '@site/src/components/AiChatBot/icons';
Expand Down Expand Up @@ -80,6 +81,19 @@ export function AiChatBot({ style }) {
DEV_TOKEN: string;
};

const sanitizeInput = (input: string): string => {
const sanitized = DOMPurify.sanitize(input.trim());
return sanitized.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
};

const validateInput = (input: string): boolean => {
return input.length > 0 && input.length <= 1000;
};

const storedUserID = localStorage.getItem('hasuraDocsUserID') as string | 'null';

// Effect to auto-scroll to the bottom if autoScroll is true
Expand Down Expand Up @@ -191,20 +205,41 @@ export function AiChatBot({ style }) {

// Send the query to the websocket when the user submits the form
const handleSubmit = async () => {
// if the input is empty, do nothing
if (!input) {
const sanitizedInput = sanitizeInput(input);

if (!validateInput(sanitizedInput)) {
console.error('Invalid input');
return;
}

if (ws) {
const toSend = JSON.stringify({ previousMessages: messages, currentUserInput: input, messageThreadId });
setCurrentMessage({ userMessage: input, botResponse: '' });
setCurrentMessage({ userMessage: sanitizedInput, botResponse: '' });
setInput('');
ws.send(toSend);
setIsResponding(true);
}
};

const renderMessage = (content: string) => {
return (
<Markdown
options={{
overrides: {
a: {
props: {
target: '_blank',
rel: 'noopener noreferrer'
},
},
},
}}
>
{DOMPurify.sanitize(content)}
</Markdown>
);
};

const isOnOverviewOrIndex =
window.location.href.endsWith('/index') ||
window.location.href.endsWith('/overview') ||
Expand Down Expand Up @@ -250,14 +285,14 @@ export function AiChatBot({ style }) {
{msg.userMessage && (
<div className="user-message-container">
<div className="formatted-text message user-message">
<Markdown>{msg.userMessage}</Markdown>
{renderMessage(msg.userMessage)}
</div>
</div>
)}
{msg.botResponse && (
<div className="bot-message-container">
<div className="formatted-text message bot-message">
<Markdown>{msg.botResponse}</Markdown>
{renderMessage(msg.botResponse)}
</div>
</div>
)}
Expand All @@ -266,15 +301,15 @@ export function AiChatBot({ style }) {
<div className="user-message-container">
{currentMessage.userMessage && (
<div className="formatted-text message user-message">
<Markdown>{currentMessage.userMessage}</Markdown>
{renderMessage(currentMessage.userMessage)}
</div>
)}
</div>
<div>
<div className="bot-message-container">
{currentMessage.botResponse && (
<div className="formatted-text message bot-message">
<Markdown>{currentMessage.botResponse}</Markdown>
{renderMessage(currentMessage.botResponse)}
</div>
)}
</div>
Expand All @@ -295,6 +330,7 @@ export function AiChatBot({ style }) {
className="input-text"
value={input}
onChange={e => setInput(e.target.value)}
maxLength={1000}
/>
<button disabled={isResponding || isConnecting} className="input-button" type="submit">
{isConnecting ? 'Connecting...' : isResponding ? 'Responding...' : 'Send'}
Expand Down
25 changes: 25 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4764,6 +4764,15 @@ __metadata:
languageName: node
linkType: hard

"@types/dompurify@npm:^3":
version: 3.0.5
resolution: "@types/dompurify@npm:3.0.5"
dependencies:
"@types/trusted-types": "npm:*"
checksum: e544b3ce53c41215cabff3d89256ff707c7ee8e0c9a1b5034b22014725d288b16e6942cdcdeeb4221c578c3421a6a4721aa0676431f55d7abd18c07368855c5e
languageName: node
linkType: hard

"@types/eslint-scope@npm:^3.7.3":
version: 3.7.7
resolution: "@types/eslint-scope@npm:3.7.7"
Expand Down Expand Up @@ -5130,6 +5139,13 @@ __metadata:
languageName: node
linkType: hard

"@types/trusted-types@npm:*":
version: 2.0.7
resolution: "@types/trusted-types@npm:2.0.7"
checksum: 8e4202766a65877efcf5d5a41b7dd458480b36195e580a3b1085ad21e948bc417d55d6f8af1fd2a7ad008015d4117d5fdfe432731157da3c68678487174e4ba3
languageName: node
linkType: hard

"@types/unist@npm:*, @types/unist@npm:^3.0.0":
version: 3.0.2
resolution: "@types/unist@npm:3.0.2"
Expand Down Expand Up @@ -7442,10 +7458,12 @@ __metadata:
"@docusaurus/tsconfig": "npm:3.4.0"
"@docusaurus/types": "npm:3.4.0"
"@mdx-js/react": "npm:^3.0.0"
"@types/dompurify": "npm:^3"
"@types/punycode": "npm:^2"
"@types/react-transition-group": "npm:^4"
autoprefixer: "npm:^10.4.16"
clsx: "npm:^1.2.1"
dompurify: "npm:^3.1.5"
dotenv: "npm:^16.3.1"
graphiql: "npm:^3.0.10"
graphql: "npm:^16.8.1"
Expand Down Expand Up @@ -7538,6 +7556,13 @@ __metadata:
languageName: node
linkType: hard

"dompurify@npm:^3.1.5":
version: 3.1.5
resolution: "dompurify@npm:3.1.5"
checksum: 4ea935df48b49a0a76c66b6eee8522ca12783f2643119482b8329867f1e8adb34ff1d2dd56973927be9de5f01079948556907f22e882b06fa7b0c0ba281bf14a
languageName: node
linkType: hard

"domutils@npm:^2.5.2, domutils@npm:^2.8.0":
version: 2.8.0
resolution: "domutils@npm:2.8.0"
Expand Down

0 comments on commit fff9d39

Please sign in to comment.