Ram2005 commited on
Commit
d29d533
Β·
verified Β·
1 Parent(s): 4f9b1e7

Upload frontend/src/components/ChatWidget.jsx

Browse files
frontend/src/components/ChatWidget.jsx CHANGED
@@ -9,9 +9,59 @@ const SUGGESTIONS = [
9
  "Latest news about Indian startups 2025",
10
  ]
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  export default function ChatWidget({ onClose }) {
13
  const [messages, setMessages] = useState([
14
- { role: 'assistant', content: 'πŸ‘‹ Hi! I\'m Bharat Tech Atlas AI. Ask me about Indian startups, sectors, funding trends, or any company!' }
15
  ])
16
  const [input, setInput] = useState('')
17
  const [loading, setLoading] = useState(false)
@@ -51,9 +101,11 @@ export default function ChatWidget({ onClose }) {
51
  throw new Error(`HTTP ${resp.status}: ${errText}`)
52
  }
53
  const data = await resp.json()
 
 
54
  setMessages(prev => [...prev, {
55
  role: 'assistant',
56
- content: data.content || 'Sorry, I had trouble responding. Try again!'
57
  }])
58
  } catch (err) {
59
  console.error('Chat error:', err)
@@ -77,7 +129,7 @@ export default function ChatWidget({ onClose }) {
77
  <p className="text-[10px] text-atlas-muted">Powered by Qwen2.5-0.5B + Web Search</p>
78
  </div>
79
  </div>
80
- <button onClick={onClose} className="text-atlas-muted hover:text-atlas-text text-lg">βœ•</button>
81
  </div>
82
 
83
  {/* Messages */}
@@ -89,7 +141,11 @@ export default function ChatWidget({ onClose }) {
89
  ? 'bg-brand-500/20 text-brand-300 rounded-br-none'
90
  : 'bg-atlas-surface text-atlas-muted rounded-bl-none'
91
  }`}>
92
- {m.content}
 
 
 
 
93
  </div>
94
  </div>
95
  ))}
@@ -126,6 +182,7 @@ export default function ChatWidget({ onClose }) {
126
  onChange={e => setInput(e.target.value)}
127
  onKeyDown={e => e.key === 'Enter' && sendMessage(input)}
128
  placeholder="Ask about startups, sectors, funding, latest news..."
 
129
  className="flex-1 bg-atlas-surface border border-atlas-border rounded-lg px-3 py-2 text-xs text-atlas-text placeholder:text-atlas-muted/50 focus:outline-none focus:border-brand-500/50"
130
  />
131
  <button onClick={() => sendMessage(input)} disabled={loading || !input.trim()}
 
9
  "Latest news about Indian startups 2025",
10
  ]
11
 
12
+ // ─── XSS Prevention: escape HTML special chars ────────────────────────────────
13
+ function escapeHtml(text) {
14
+ if (!text) return ''
15
+ return text
16
+ .replace(/&/g, '&amp;')
17
+ .replace(/</g, '&lt;')
18
+ .replace(/>/g, '&gt;')
19
+ .replace(/"/g, '&quot;')
20
+ .replace(/'/g, '&#x27;')
21
+ }
22
+
23
+ // ─── XSS Prevention: strip script tags and event handlers ─────────────────────
24
+ function sanitizeChatContent(text) {
25
+ if (!text) return ''
26
+ let t = text
27
+ // Remove script tags
28
+ t = t.replace(/<script[^>]*>.*?<\/script>/gi, '')
29
+ // Remove iframe tags
30
+ t = t.replace(/<iframe[^>]*>.*?<\/iframe>/gi, '')
31
+ // Remove event handlers (onerror, onclick, etc.)
32
+ t = t.replace(/\son\w+\s*=\s*["']?[^"'>]*["']?/gi, '')
33
+ // Remove javascript: and data: URLs
34
+ t = t.replace(/(javascript|data|vbscript):/gi, '')
35
+ // Remove meta refresh
36
+ t = t.replace(/<meta[^>]*http-equiv\s*=\s*["']?refresh["']?[^>]*>/gi, '')
37
+ return t
38
+ }
39
+
40
+ // ─── Render sanitized text with basic Markdown (no raw HTML) ──────────────────
41
+ function SafeMarkdown({ text }) {
42
+ if (!text) return null
43
+ const sanitized = sanitizeChatContent(text)
44
+ // Convert **bold**, *italic*, `code`, and bullet points to HTML safely
45
+ let html = escapeHtml(sanitized)
46
+ // Bold
47
+ .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
48
+ // Italic
49
+ .replace(/\*(.+?)\*/g, '<em>$1</em>')
50
+ // Inline code
51
+ .replace(/`([^`]+)`/g, '<code>$1</code>')
52
+ // Bullet points (simple)
53
+ .replace(/^\s*-\s+/gm, 'β€’ ')
54
+ // Numbered lists
55
+ .replace(/^\s*(\d+)\.\s+/gm, '$1. ')
56
+ // Line breaks
57
+ .replace(/\n/g, '<br>')
58
+
59
+ return <span dangerouslySetInnerHTML={{ __html: html }} />
60
+ }
61
+
62
  export default function ChatWidget({ onClose }) {
63
  const [messages, setMessages] = useState([
64
+ { role: 'assistant', content: "πŸ‘‹ Hi! I'm Bharat Tech Atlas AI. Ask me about Indian startups, sectors, funding trends, or any company!" }
65
  ])
66
  const [input, setInput] = useState('')
67
  const [loading, setLoading] = useState(false)
 
101
  throw new Error(`HTTP ${resp.status}: ${errText}`)
102
  }
103
  const data = await resp.json()
104
+ // Sanitize response before displaying
105
+ const safeContent = sanitizeChatContent(data.content) || 'Sorry, I had trouble responding. Try again!'
106
  setMessages(prev => [...prev, {
107
  role: 'assistant',
108
+ content: safeContent
109
  }])
110
  } catch (err) {
111
  console.error('Chat error:', err)
 
129
  <p className="text-[10px] text-atlas-muted">Powered by Qwen2.5-0.5B + Web Search</p>
130
  </div>
131
  </div>
132
+ <button onClick={onClose} className="text-atlas-muted hover:text-atlas-text text-lg" aria-label="Close chat">βœ•</button>
133
  </div>
134
 
135
  {/* Messages */}
 
141
  ? 'bg-brand-500/20 text-brand-300 rounded-br-none'
142
  : 'bg-atlas-surface text-atlas-muted rounded-bl-none'
143
  }`}>
144
+ {m.role === 'user' ? (
145
+ <span>{m.content}</span>
146
+ ) : (
147
+ <SafeMarkdown text={m.content} />
148
+ )}
149
  </div>
150
  </div>
151
  ))}
 
182
  onChange={e => setInput(e.target.value)}
183
  onKeyDown={e => e.key === 'Enter' && sendMessage(input)}
184
  placeholder="Ask about startups, sectors, funding, latest news..."
185
+ maxLength={2000}
186
  className="flex-1 bg-atlas-surface border border-atlas-border rounded-lg px-3 py-2 text-xs text-atlas-text placeholder:text-atlas-muted/50 focus:outline-none focus:border-brand-500/50"
187
  />
188
  <button onClick={() => sendMessage(input)} disabled={loading || !input.trim()}