Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Security review and update for chrome extension #3

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,20 @@
- Restart the browser to ensure that all pages are refreshed successfully
- Dify chatbot floating bar can be loaded normally on any page in Chrome, if you need to change the chatbot, just change the ChatBot URL

![img-4.png](images/img-4.png)
![img-4.png](images/img-4.png)

### Security Considerations and Best Practices

When using the Dify Chatbot Chrome extension, it is important to follow these security considerations and best practices:

1. **Validate and Sanitize URLs**: Ensure that the ChatBot URL is validated and sanitized before embedding it in an iframe. This helps prevent potential security vulnerabilities such as XSS attacks.

2. **Use HTTPS**: Always use HTTPS for the ChatBot URL to ensure secure communication between the extension and the chatbot server.

3. **Content Security Policy (CSP)**: The extension includes a content security policy to enhance security. Make sure to review and update the CSP as needed to protect against potential threats.

4. **Security Headers**: The extension includes security headers such as X-Content-Type-Options, X-Frame-Options, and X-XSS-Protection. These headers help protect against common security vulnerabilities.

5. **Regular Updates**: Keep the extension and its dependencies up to date to ensure that you have the latest security patches and improvements.

By following these security considerations and best practices, you can help ensure the safe and secure use of the Dify Chatbot Chrome extension.
18 changes: 17 additions & 1 deletion README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,20 @@
- 保险起见重启浏览器确保所有分页刷新成功
- Chrome打开任意页面均可正常加载DIfy机器人浮动栏,后续如需更换机器人只需要变更ChatBot Url即可

![img-4.png](images/img-4.png)
![img-4.png](images/img-4.png)

### 安全注意事项和最佳实践

在使用Dify Chatbot Chrome扩展程序时,遵循以下安全注意事项和最佳实践非常重要:

1. **验证和清理URL**:确保在将ChatBot URL嵌入iframe之前对其进行验证和清理。这有助于防止潜在的安全漏洞,例如XSS攻击。

2. **使用HTTPS**:始终使用HTTPS作为ChatBot URL,以确保扩展程序与聊天机器人服务器之间的安全通信。

3. **内容安全策略(CSP)**:扩展程序包括内容安全策略以增强安全性。请确保根据需要审查和更新CSP以防范潜在威胁。

4. **安全标头**:扩展程序包括X-Content-Type-Options、X-Frame-Options和X-XSS-Protection等安全标头。这些标头有助于防止常见的安全漏洞。

5. **定期更新**:保持扩展程序及其依赖项的最新状态,以确保您拥有最新的安全补丁和改进。

通过遵循这些安全注意事项和最佳实践,您可以帮助确保安全使用Dify Chatbot Chrome扩展程序。
54 changes: 54 additions & 0 deletions background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'sendData') {
const formattedData = formatData(message.data);
sendDataToLLM(formattedData);
}
});

function formatData(data) {
// Sanitize and format the extracted data
return {
headings: data.headings.map(sanitizeText),
paragraphs: data.paragraphs.map(sanitizeText),
links: data.links.map(sanitizeUrl),
images: data.images.map(sanitizeUrl),
metadata: {
title: sanitizeText(data.metadata.title),
description: sanitizeText(data.metadata.description),
keywords: sanitizeText(data.metadata.keywords)
}
};
}

function sanitizeText(text) {
// Implement text sanitization logic
return text.replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

function sanitizeUrl(url) {
// Implement URL sanitization logic
const parser = document.createElement('a');
parser.href = url;
if (parser.protocol !== 'https:') {
return '';
}
return url;
}

function sendDataToLLM(data) {
// Implement the logic to send data to the LLM
fetch('https://your-llm-endpoint.com/api', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(result => {
console.log('Data sent to LLM:', result);
})
.catch(error => {
console.error('Error sending data to LLM:', error);
});
}
50 changes: 48 additions & 2 deletions content.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
const storage = chrome.storage.sync;
chrome.storage.sync.get(['chatbotUrl'], function(result) {
window.difyChatbotConfig = {
chatbotUrl: result.chatbotUrl,
chatbotUrl: sanitizeUrl(result.chatbotUrl),
};
});

document.body.onload = embedChatbot;

function sanitizeUrl(url) {
const parser = document.createElement('a');
parser.href = url;

// Check if the URL is valid
if (!parser.protocol || !parser.host) {
console.warn('Invalid URL');
return '';
}

// Check if the URL uses HTTPS
if (parser.protocol !== 'https:') {
console.warn('URL must use HTTPS');
return '';
}

// Check for malicious scripts
if (url.includes('javascript:') || url.includes('<script>')) {
console.warn('URL contains malicious scripts');
return '';
}

return url;
}

async function embedChatbot() {
const difyChatbotConfig = window.difyChatbotConfig;
if (!difyChatbotConfig) {
Expand Down Expand Up @@ -165,4 +190,25 @@ async function embedChatbot() {
// add any drag and drop to the floating icon
handleElementDrag(targetButton);
}
}
}

// Function to extract key data points from the webpage
function extractData() {
const data = {
headings: Array.from(document.querySelectorAll('h1, h2, h3')).map(h => h.innerText),
paragraphs: Array.from(document.querySelectorAll('p')).map(p => p.innerText),
links: Array.from(document.querySelectorAll('a')).map(a => a.href),
images: Array.from(document.querySelectorAll('img')).map(img => img.src),
metadata: {
title: document.title,
description: document.querySelector('meta[name="description"]')?.content || '',
keywords: document.querySelector('meta[name="keywords"]')?.content || ''
}
};

// Send the extracted data to the background script
chrome.runtime.sendMessage({ action: 'sendData', data: data });
}

// Call the extractData function when the content script is loaded
extractData();
10 changes: 8 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
"js": ["content.js"]
}
],
"permissions": ["webRequest", "storage"],
"permissions": ["webRequest", "storage", "activeTab", "scripting"],
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "options.html",
"default_icon": {
Expand All @@ -25,5 +28,8 @@
"32": "images/32.png",
"48": "images/48.png",
"128": "images/128.png"
},
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
}
}
}
29 changes: 28 additions & 1 deletion options.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,31 @@ label {
input[type="text"] {
width: 280px;
padding: 6px;
}
}

input[type="checkbox"] {
margin-right: 10px;
}

select {
width: 280px;
padding: 6px;
}

#error-tip {
color: red;
font-size: 14px;
}

button {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
}

button:hover {
background-color: #45a049;
}
30 changes: 29 additions & 1 deletion options.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
<head>
<title>Dify Chatbot Extension</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self';">
<meta http-equiv="X-Content-Type-Options" content="nosniff">
<meta http-equiv="X-Frame-Options" content="DENY">
<meta http-equiv="X-XSS-Protection" content="1; mode=block">
<link href="./tailwind.css" rel="stylesheet">
</head>

Expand All @@ -22,6 +26,30 @@ <h2 class="text-2xl font-semibold mb-4">Dify Chatbot Extension</h2>
</div>
</div>

<div class="mb-4 flex items-center">
<div class="w-1/4">
<label for="data-extraction" class="block font-semibold text-gray-700">Enable Data Extraction</label>
</div>
<div class="w-3/4">
<input type="checkbox" id="data-extraction" name="data-extraction" class="mr-2">
</div>
</div>

<div class="mb-4 flex items-center">
<div class="w-1/4">
<label for="data-types" class="block font-semibold text-gray-700">Data Types to Extract</label>
</div>
<div class="w-3/4">
<select id="data-types" name="data-types" multiple class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:border-blue-400">
<option value="headings">Headings</option>
<option value="paragraphs">Paragraphs</option>
<option value="links">Links</option>
<option value="images">Images</option>
<option value="metadata">Metadata</option>
</select>
</div>
</div>

<div class="mb-4 flex items-center">
<div class="w-1/4"></div>
<div class="w-3/4">
Expand All @@ -42,4 +70,4 @@ <h2 class="text-2xl font-semibold mb-4">Dify Chatbot Extension</h2>
<script src="options.js"></script>
</body>

</html>
</html>
43 changes: 39 additions & 4 deletions options.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,63 @@

document.getElementById('save-button').addEventListener('click', function (e) {
e.preventDefault();
const chatbotUrl = document.getElementById('chatbot-url').value;
const errorTip = document.getElementById('error-tip');
const dataExtraction = document.getElementById('data-extraction').checked;
const dataTypes = Array.from(document.getElementById('data-types').selectedOptions).map(option => option.value);

if (chatbotUrl.trim() === "") {
errorTip.textContent = "Dify ChatBot URL cannot be empty.";
errorTip.textContent = "Dify ChatBot URL cannot be empty.";
} else if (!isValidUrl(chatbotUrl)) {
errorTip.textContent = "Invalid URL format.";
} else if (!isHttpsUrl(chatbotUrl)) {
errorTip.textContent = "URL must use HTTPS.";
} else {
errorTip.textContent = "";

chrome.storage.sync.set({
'chatbotUrl': chatbotUrl,
'dataExtraction': dataExtraction,
'dataTypes': dataTypes
}, function () {
alert('Save Success!');
});
}
});

// Load parameters from chrome.storage when the page loads
chrome.storage.sync.get(['chatbotUrl'], function (result) {
chrome.storage.sync.get(['chatbotUrl', 'dataExtraction', 'dataTypes'], function (result) {
const chatbotUrlInput = document.getElementById('chatbot-url');
const dataExtractionInput = document.getElementById('data-extraction');
const dataTypesInput = document.getElementById('data-types');

if (result.chatbotUrl) {
chatbotUrlInput.value = result.chatbotUrl;
}

});
if (result.dataExtraction !== undefined) {
dataExtractionInput.checked = result.dataExtraction;
}

if (result.dataTypes) {
Array.from(dataTypesInput.options).forEach(option => {
if (result.dataTypes.includes(option.value)) {
option.selected = true;
}
});
}
});

function isValidUrl(url) {
try {
new URL(url);
return true;
} catch (e) {
return false;
}
}

function isHttpsUrl(url) {
const parser = document.createElement('a');
parser.href = url;
return parser.protocol === 'https:';
}