feat: Enhance domain management and configuration in the extension

- Introduce default configuration settings for API key, domains database URL, update interval, and warning message template in background.js and options.js.
- Update domains database URL to a new source for improved reliability.
- Refactor domain management UI in domains_management.js to support adding, editing, and removing trusted and blocked domains with enhanced user experience.
- Implement search functionality for filtering domains in the management interface.
- Improve status messaging for user feedback during domain operations.
This commit is contained in:
becarta
2025-05-10 03:52:10 +02:00
parent f757b4585a
commit 87506ae85a
6 changed files with 260 additions and 63 deletions

View File

@@ -134,27 +134,59 @@ const healthCheck = {
} }
}; };
// Default configuration
const DEFAULT_CONFIG = {
apiKey: '',
domainsDBURL: 'https://git.365devnet.eu/365DevNet/EnterpriseAppProtection/raw/branch/main/domains.json',
updateInterval: 24, // hours
warningTemplate: 'Warning: This link claims to be {app} but goes to an unofficial domain.',
lastUpdate: null,
suspiciousCount: 0,
suspiciousLinks: []
};
// Function to get configuration
async function getConfig() {
return new Promise((resolve) => {
chrome.storage.local.get([
"safeBrowsingApiKey",
"domainsDBURL",
"updateInterval",
"warningTemplate"
], (result) => {
resolve({
apiKey: result.safeBrowsingApiKey || DEFAULT_CONFIG.apiKey,
domainsDBURL: result.domainsDBURL || DEFAULT_CONFIG.domainsDBURL,
updateInterval: result.updateInterval || DEFAULT_CONFIG.updateInterval,
warningTemplate: result.warningTemplate || DEFAULT_CONFIG.warningTemplate
});
});
});
}
// Function to update last update timestamp
async function updateLastUpdate() {
await chrome.storage.local.set({ lastUpdate: Date.now() });
}
// Function to update domains database
async function updateDomainsDB() { async function updateDomainsDB() {
chrome.storage.local.get(["domainsDBURL"], async function (result) {
const domainsDBURL = result.domainsDBURL || "https://raw.githubusercontent.com/rrpbergsma/EnterpriseAppProtection/refs/heads/main/domains.json";
console.log("updateDomainsDB: Starting update from URL:", domainsDBURL);
try { try {
const response = await fetch(domainsDBURL); const config = await getConfig();
if (!response.ok) { console.log("Updating domains database from:", config.domainsDBURL);
console.error("updateDomainsDB: Fetch failed with status", response.status, response.statusText); const response = await fetch(config.domainsDBURL);
return; if (!response.ok) {
} throw new Error(`HTTP error! status: ${response.status}`);
domainsDB = await response.json(); }
chrome.storage.local.set({ const data = await response.json();
domainsDB: domainsDB, await chrome.storage.local.set({ domainsDB: data });
lastUpdate: Date.now() await updateLastUpdate();
}, () => { console.log('Domains database updated successfully');
console.log("updateDomainsDB: Database updated successfully at", new Date().toLocaleString()); return true;
});
} catch (error) { } catch (error) {
console.error("updateDomainsDB: Failed to update domains database:", error); console.error('Failed to update domains database:', error);
return false;
} }
});
} }
// Run updates when the extension is installed or started // Run updates when the extension is installed or started

View File

@@ -1,7 +1,7 @@
// config.js // config.js
const CONFIG = { const CONFIG = {
// URL to fetch the approved enterprise domains list // URL to fetch the approved enterprise domains list
DOMAINS_DB_URL: 'https://raw.githubusercontent.com/rrpbergsma/EnterpriseAppProtection/refs/heads/main/domains.json', DOMAINS_DB_URL: 'https://git.365devnet.eu/365DevNet/EnterpriseAppProtection/raw/branch/main/domains.json',
// Update interval in hours // Update interval in hours
UPDATE_INTERVAL: 24, UPDATE_INTERVAL: 24,
// Warning message template for suspicious links // Warning message template for suspicious links

View File

@@ -1,39 +1,204 @@
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
const saveButton = document.getElementById("save"); // Get UI elements
const trustedInput = document.getElementById("trusted"); const addTrustedBtn = document.getElementById("addTrustedDomain");
const blockedInput = document.getElementById("blocked"); const addBlockedBtn = document.getElementById("addBlockedDomain");
const domainModal = document.getElementById("domainModal");
const modalTitle = document.getElementById("modalTitle");
const domainNameInput = document.getElementById("domainName");
const domainTypeSelect = document.getElementById("domainType");
const saveDomainBtn = document.getElementById("saveDomain");
const cancelDomainBtn = document.getElementById("cancelDomain");
const trustedSearch = document.getElementById("trustedSearch");
const blockedSearch = document.getElementById("blockedSearch");
const trustedDomainsList = document.getElementById("trustedDomainsList");
const blockedDomainsList = document.getElementById("blockedDomainsList");
const statusMessage = document.getElementById("statusMessage");
let currentDomainType = 'trusted';
let editingDomain = null;
// Load saved domains // Load saved domains
chrome.storage.local.get(["trustedDomains", "blockedDomains"], function (data) { loadDomains();
if (data.trustedDomains) trustedInput.value = data.trustedDomains.join("\n");
if (data.blockedDomains) blockedInput.value = data.blockedDomains.join("\n");
});
// Save button event // Add domain buttons
saveButton.addEventListener("click", function () { if (addTrustedBtn) {
// Retrieve domains from input fields addTrustedBtn.addEventListener("click", () => openAddModal('trusted'));
const trustedDomains = trustedInput.value.split("\n").map(d => d.trim()).filter(d => d); }
const blockedDomains = blockedInput.value.split("\n").map(d => d.trim()).filter(d => d); if (addBlockedBtn) {
addBlockedBtn.addEventListener("click", () => openAddModal('blocked'));
}
// Save to Chrome storage // Modal buttons
chrome.storage.local.set({ trustedDomains, blockedDomains }, function () { if (saveDomainBtn) {
showPopup("✅ Changes saved successfully!"); saveDomainBtn.addEventListener("click", saveDomain);
}); }
}); if (cancelDomainBtn) {
cancelDomainBtn.addEventListener("click", closeModal);
}
// Function to show styled popup message // Search functionality
function showPopup(message) { if (trustedSearch) {
const popup = document.getElementById("popupMessage"); trustedSearch.addEventListener("input", (e) => filterDomains('trusted', e.target.value));
const popupText = document.getElementById("popupText"); }
const popupClose = document.getElementById("popupClose"); if (blockedSearch) {
blockedSearch.addEventListener("input", (e) => filterDomains('blocked', e.target.value));
}
popupText.innerText = message; // Functions
popup.classList.remove("hidden"); function loadDomains() {
popup.classList.add("visible"); chrome.storage.local.get(["trustedDomains", "blockedDomains"], function (data) {
renderDomainList('trusted', data.trustedDomains || []);
popupClose.addEventListener("click", function () { renderDomainList('blocked', data.blockedDomains || []);
popup.classList.remove("visible");
popup.classList.add("hidden");
}); });
} }
function renderDomainList(type, domains) {
const list = type === 'trusted' ? trustedDomainsList : blockedDomainsList;
if (!list) return;
list.innerHTML = '';
domains.forEach(domain => {
const item = createDomainItem(domain, type);
list.appendChild(item);
});
}
function createDomainItem(domain, type) {
const div = document.createElement('div');
div.className = 'domain-item';
div.innerHTML = `
<div class="domain-info">
<span class="domain-name">${domain}</span>
<span class="domain-type">${type}</span>
</div>
<div class="domain-actions">
<button class="edit-btn" onclick="editDomain('${domain}', '${type}')">
<span>✏️</span>
<span>Edit</span>
</button>
<button class="remove-btn" onclick="removeDomain('${domain}', '${type}')">
<span>🗑️</span>
<span>Remove</span>
</button>
</div>
`;
return div;
}
function openAddModal(type) {
currentDomainType = type;
editingDomain = null;
modalTitle.textContent = `Add ${type === 'trusted' ? 'Trusted' : 'Blocked'} Domain`;
domainNameInput.value = '';
domainTypeSelect.value = type;
domainModal.classList.add('active');
}
function closeModal() {
domainModal.classList.remove('active');
editingDomain = null;
}
function saveDomain() {
const domain = domainNameInput.value.trim();
if (!domain) {
showStatus('Please enter a domain name', 'error');
return;
}
chrome.storage.local.get(["trustedDomains", "blockedDomains"], function (data) {
const trustedDomains = data.trustedDomains || [];
const blockedDomains = data.blockedDomains || [];
const type = domainTypeSelect.value;
// Remove from both lists if editing
if (editingDomain) {
const oldType = currentDomainType;
if (oldType === 'trusted') {
const index = trustedDomains.indexOf(editingDomain);
if (index > -1) trustedDomains.splice(index, 1);
} else {
const index = blockedDomains.indexOf(editingDomain);
if (index > -1) blockedDomains.splice(index, 1);
}
}
// Add to appropriate list
if (type === 'trusted' && !trustedDomains.includes(domain)) {
trustedDomains.push(domain);
} else if (type === 'blocked' && !blockedDomains.includes(domain)) {
blockedDomains.push(domain);
}
// Save to storage
chrome.storage.local.set({
trustedDomains: trustedDomains,
blockedDomains: blockedDomains
}, function () {
loadDomains();
closeModal();
showStatus(`Domain ${editingDomain ? 'updated' : 'added'} successfully`, 'success');
});
});
}
function editDomain(domain, type) {
currentDomainType = type;
editingDomain = domain;
modalTitle.textContent = `Edit ${type === 'trusted' ? 'Trusted' : 'Blocked'} Domain`;
domainNameInput.value = domain;
domainTypeSelect.value = type;
domainModal.classList.add('active');
}
function removeDomain(domain, type) {
if (confirm(`Are you sure you want to remove ${domain}?`)) {
chrome.storage.local.get(["trustedDomains", "blockedDomains"], function (data) {
const trustedDomains = data.trustedDomains || [];
const blockedDomains = data.blockedDomains || [];
if (type === 'trusted') {
const index = trustedDomains.indexOf(domain);
if (index > -1) trustedDomains.splice(index, 1);
} else {
const index = blockedDomains.indexOf(domain);
if (index > -1) blockedDomains.splice(index, 1);
}
chrome.storage.local.set({
trustedDomains: trustedDomains,
blockedDomains: blockedDomains
}, function () {
loadDomains();
showStatus('Domain removed successfully', 'success');
});
});
}
}
function filterDomains(type, searchTerm) {
const list = type === 'trusted' ? trustedDomainsList : blockedDomainsList;
if (!list) return;
const items = list.getElementsByClassName('domain-item');
searchTerm = searchTerm.toLowerCase();
Array.from(items).forEach(item => {
const domainName = item.querySelector('.domain-name').textContent.toLowerCase();
item.style.display = domainName.includes(searchTerm) ? '' : 'none';
});
}
function showStatus(message, type) {
statusMessage.textContent = message;
statusMessage.className = `status-message ${type}`;
statusMessage.style.display = 'block';
setTimeout(() => {
statusMessage.style.display = 'none';
}, 3000);
}
// Make functions available globally for onclick handlers
window.editDomain = editDomain;
window.removeDomain = removeDomain;
}); });

View File

@@ -43,7 +43,7 @@
}, },
"options_page": "options.html", "options_page": "options.html",
"content_security_policy": { "content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'; connect-src 'self' https://raw.githubusercontent.com/ https://safebrowsing.googleapis.com/", "extension_pages": "script-src 'self'; object-src 'self'; connect-src 'self' https://git.365devnet.eu/ https://safebrowsing.googleapis.com/",
"sandbox": "sandbox allow-scripts allow-forms allow-popups allow-modals; script-src 'self' 'unsafe-inline' 'unsafe-eval'; child-src 'self'" "sandbox": "sandbox allow-scripts allow-forms allow-popups allow-modals; script-src 'self' 'unsafe-inline' 'unsafe-eval'; child-src 'self'"
}, },
"web_accessible_resources": [{ "web_accessible_resources": [{

View File

@@ -225,7 +225,7 @@
<div class="form-group"> <div class="form-group">
<label for="domainsDBURL">Database URL</label> <label for="domainsDBURL">Database URL</label>
<input type="text" id="domainsDBURL" placeholder="Enter the URL for your domains database" <input type="text" id="domainsDBURL" placeholder="Enter the URL for your domains database"
value="https://raw.githubusercontent.com/rrpbergsma/EnterpriseAppProtection/refs/heads/main/domains.json" /> value="https://git.365devnet.eu/365DevNet/EnterpriseAppProtection/raw/branch/main/domains.json" />
<p class="help-text">URL must point to a valid JSON file containing domain information</p> <p class="help-text">URL must point to a valid JSON file containing domain information</p>
</div> </div>

View File

@@ -11,35 +11,35 @@ document.addEventListener("DOMContentLoaded", function () {
console.error("⚠️ Element with ID 'warningTemplate' not found in options.html!"); console.error("⚠️ Element with ID 'warningTemplate' not found in options.html!");
} }
// Default values // Default configuration
const defaultValues = { const DEFAULT_CONFIG = {
safeBrowsingApiKey: "", apiKey: '',
domainsDBURL: "https://raw.githubusercontent.com/rrpbergsma/EnterpriseAppProtection/refs/heads/main/domains.json", domainsDBURL: 'https://git.365devnet.eu/365DevNet/EnterpriseAppProtection/raw/branch/main/domains.json',
updateInterval: 24, updateInterval: 24,
warningTemplate: "Warning: This link claims to be {app} but goes to an unofficial domain." warningTemplate: 'Warning: This link claims to be {app} but goes to an unofficial domain.'
}; };
// Load stored settings and apply defaults where needed // Load stored settings and apply defaults where needed
chrome.storage.local.get(["safeBrowsingApiKey", "domainsDBURL", "updateInterval", "warningTemplate"], function (data) { chrome.storage.local.get(["safeBrowsingApiKey", "domainsDBURL", "updateInterval", "warningTemplate"], function (data) {
if (apiKeyElement) apiKeyElement.value = data.safeBrowsingApiKey || defaultValues.safeBrowsingApiKey; if (apiKeyElement) apiKeyElement.value = data.safeBrowsingApiKey || DEFAULT_CONFIG.apiKey;
if (domainsDBURLElement) domainsDBURLElement.value = data.domainsDBURL || defaultValues.domainsDBURL; if (domainsDBURLElement) domainsDBURLElement.value = data.domainsDBURL || DEFAULT_CONFIG.domainsDBURL;
if (updateIntervalElement) updateIntervalElement.value = data.updateInterval || defaultValues.updateInterval; if (updateIntervalElement) updateIntervalElement.value = data.updateInterval || DEFAULT_CONFIG.updateInterval;
if (warningTemplateElement) { if (warningTemplateElement) {
warningTemplateElement.value = data.warningTemplate || defaultValues.warningTemplate; warningTemplateElement.value = data.warningTemplate || DEFAULT_CONFIG.warningTemplate;
} }
}); });
// Save settings when clicking "Save" // Save settings when clicking "Save"
if (saveButton) { if (saveButton) {
saveButton.addEventListener("click", function () { saveButton.addEventListener("click", function () {
const apiKey = apiKeyElement && apiKeyElement.value.trim() ? apiKeyElement.value.trim() : defaultValues.safeBrowsingApiKey; const apiKey = apiKeyElement && apiKeyElement.value.trim() ? apiKeyElement.value.trim() : DEFAULT_CONFIG.apiKey;
const domainsDBURL = domainsDBURLElement && domainsDBURLElement.value.trim() ? domainsDBURLElement.value.trim() : defaultValues.domainsDBURL; const domainsDBURL = domainsDBURLElement && domainsDBURLElement.value.trim() ? domainsDBURLElement.value.trim() : DEFAULT_CONFIG.domainsDBURL;
const updateInterval = updateIntervalElement && updateIntervalElement.value.trim() const updateInterval = updateIntervalElement && updateIntervalElement.value.trim()
? parseInt(updateIntervalElement.value.trim()) || defaultValues.updateInterval ? parseInt(updateIntervalElement.value.trim()) || DEFAULT_CONFIG.updateInterval
: defaultValues.updateInterval; : DEFAULT_CONFIG.updateInterval;
const warningTemplate = warningTemplateElement && warningTemplateElement.value.trim() const warningTemplate = warningTemplateElement && warningTemplateElement.value.trim()
? warningTemplateElement.value.trim() ? warningTemplateElement.value.trim()
: defaultValues.warningTemplate; : DEFAULT_CONFIG.warningTemplate;
chrome.storage.local.set({ chrome.storage.local.set({
safeBrowsingApiKey: apiKey, safeBrowsingApiKey: apiKey,