
What Is Prompt Poisoning and How to Protect Your AI from It
October 15, 2025
Learn about prompt poisoning attacks in AI systems and practical strategies to secure your applications from hidden malicious instructions.
Learn how to build, test, and publish browser extensions for Chrome, Firefox, and Edge with this step-by-step guide.
Share Article
Browser extensions can enhance the browsing experience, improve productivity, and solve specific problems for users. In this comprehensive guide, we’ll explore how to build, test, and publish modern browser extensions that work across multiple browsers.
Browser extensions are software programs that extend the functionality of web browsers. They can modify web content, add new features to websites, or provide utility functions accessible from the browser’s toolbar.
Modern extensions typically consist of:
Before we start coding, let’s set up a development environment:
# Create project directory
mkdir my-extension
cd my-extension
# Initialize package.json
npm init -y
# Install development dependencies
npm install --save-dev webpack webpack-cli copy-webpack-plugin
Next, let’s create a basic directory structure:
my-extension/
├── src/
│ ├── manifest.json
│ ├── background/
│ │ └── index.js
│ ├── content/
│ │ └── index.js
│ ├── popup/
│ │ ├── popup.html
│ │ └── popup.js
│ └── options/
│ ├── options.html
│ └── options.js
├── public/
│ └── icons/
│ ├── icon-16.png
│ ├── icon-48.png
│ └── icon-128.png
└── package.json
The manifest.json file is the blueprint of your extension. Let’s create a basic manifest that works with Manifest V3, the latest standard: Firefox and Edge also support Manifest V3, but some features may vary slightly.
{
"manifest_version": 3,
"name": "My Browser Extension",
"version": "1.0.0",
"description": "A helpful browser extension",
"icons": {
"16": "icons/icon-16.png",
"48": "icons/icon-48.png",
"128": "icons/icon-128.png"
},
"action": {
"default_popup": "popup/popup.html",
"default_title": "My Extension"
},
"background": {
"service_worker": "background/index.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content/index.js"]
}
],
"permissions": ["storage", "activeTab"],
"options_page": "options/options.html"
}
This manifest sets up a basic extension with background services, content scripts, and a popup interface.
Background scripts run separately from web pages and manage the extension’s state. Here’s a simple background script that listens for installation and messages:
// src/background/index.js
// Handle extension installation
chrome.runtime.onInstalled.addListener(({ reason }) => {
if (reason === "install") {
// Initialize extension state
chrome.storage.local.set({
isEnabled: true,
settings: {
theme: "light",
notifications: true
}
});
// Open options page after installation
chrome.runtime.openOptionsPage();
}
});
// Listen for messages from content scripts or popup
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === "GET_STATE") {
chrome.storage.local.get(["isEnabled", "settings"], (result) => {
sendResponse(result);
});
return true; // Required for async response
}
if (message.type === "TOGGLE_ENABLED") {
chrome.storage.local.get("isEnabled", ({ isEnabled }) => {
chrome.storage.local.set({ isEnabled: !isEnabled }, () => {
sendResponse({ isEnabled: !isEnabled });
});
});
return true; // Required for async response
}
});
Content scripts run in the context of web pages, allowing you to read and modify page content:
// src/content/index.js
// Check if extension is enabled before running
chrome.runtime.sendMessage({ type: "GET_STATE" }, (response) => {
if (response && response.isEnabled) {
initializeExtension(response.settings);
}
});
function initializeExtension(settings) {
console.log("Extension initialized with settings:", settings);
// Example: Add a custom style to the page based on theme setting
if (settings.theme === "dark") {
const style = document.createElement("style");
style.textContent = `
body {
background-color: #222 !important;
color: #eee !important;
}
a {
color: #5af !important;
}
`;
document.head.appendChild(style);
}
// Example: Add a floating button to the page
const button = document.createElement("button");
button.textContent = "Extension Action";
button.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
z-index: 9999;
padding: 10px 15px;
background: #4285f4;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
`;
button.addEventListener("click", () => {
// Send message to background script
chrome.runtime.sendMessage({
type: "BUTTON_CLICKED",
data: { url: window.location.href }
});
});
document.body.appendChild(button);
}
The popup is the interface users see when clicking your extension icon:
<!-- src/popup/popup.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Extension</title>
<style>
body {
width: 300px;
padding: 15px;
font-family: system-ui, -apple-system, sans-serif;
}
h1 {
font-size: 18px;
margin-top: 0;
}
.toggle {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.toggle-switch {
position: relative;
display: inline-block;
width: 40px;
height: 24px;
margin-right: 10px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: 0.4s;
border-radius: 24px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: 4px;
background-color: white;
transition: 0.4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #4285f4;
}
input:checked + .slider:before {
transform: translateX(16px);
}
.footer {
margin-top: 15px;
display: flex;
justify-content: space-between;
}
</style>
</head>
<body>
<h1>My Browser Extension</h1>
<div class="toggle">
<label class="toggle-switch">
<input type="checkbox" id="enabled-toggle" />
<span class="slider"></span>
</label>
<span>Enable extension</span>
</div>
<div id="status-message"></div>
<div class="footer">
<button id="options-button">Options</button>
<span id="version">v1.0.0</span>
</div>
<script src="popup.js"></script>
</body>
</html>
// src/popup/popup.js
// Get UI elements
const enabledToggle = document.getElementById("enabled-toggle");
const statusMessage = document.getElementById("status-message");
const optionsButton = document.getElementById("options-button");
const versionElement = document.getElementById("version");
// Set version from manifest
chrome.runtime.getManifest().version;
versionElement.textContent = `v${chrome.runtime.getManifest().version}`;
// Get initial state
chrome.runtime.sendMessage({ type: "GET_STATE" }, (response) => {
if (response) {
enabledToggle.checked = response.isEnabled;
updateStatusMessage(response.isEnabled);
}
});
// Toggle enabled state
enabledToggle.addEventListener("change", () => {
chrome.runtime.sendMessage({ type: "TOGGLE_ENABLED" }, (response) => {
updateStatusMessage(response.isEnabled);
});
});
// Open options page
optionsButton.addEventListener("click", () => {
chrome.runtime.openOptionsPage();
});
// Update status message based on enabled state
function updateStatusMessage(isEnabled) {
statusMessage.textContent = isEnabled
? "Extension is currently active on this site."
: "Extension is disabled. Toggle to enable.";
statusMessage.style.color = isEnabled ? "#4285f4" : "#999";
}
The options page lets users customize the extension:
<!-- src/options/options.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Extension Options</title>
<style>
body {
font-family: system-ui, -apple-system, sans-serif;
margin: 0;
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
}
.option-group {
margin-bottom: 20px;
padding: 15px;
border: 1px solid #eee;
border-radius: 5px;
}
h2 {
font-size: 18px;
margin-top: 0;
}
label {
display: block;
margin-bottom: 10px;
}
select,
input[type="text"] {
padding: 8px;
border-radius: 4px;
border: 1px solid #ddd;
width: 300px;
max-width: 100%;
}
.buttons {
margin-top: 20px;
}
button {
padding: 8px 16px;
background: #4285f4;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-right: 10px;
}
button.secondary {
background: #f1f1f1;
color: #333;
}
.saved-message {
color: green;
margin-top: 15px;
opacity: 0;
transition: opacity 0.3s;
}
.saved-message.visible {
opacity: 1;
}
</style>
</head>
<body>
<h1>Extension Options</h1>
<div class="option-group">
<h2>Appearance</h2>
<label>
Theme:
<select id="theme-select">
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="system">Follow System</option>
</select>
</label>
</div>
<div class="option-group">
<h2>Notifications</h2>
<label>
<input type="checkbox" id="notifications-checkbox" />
Enable notifications
</label>
</div>
<div class="buttons">
<button id="save-button">Save Changes</button>
<button id="reset-button" class="secondary">Reset to Defaults</button>
</div>
<div id="saved-message" class="saved-message">
Settings saved successfully!
</div>
<script src="options.js"></script>
</body>
</html>
// src/options/options.js
// Get UI elements
const themeSelect = document.getElementById("theme-select");
const notificationsCheckbox = document.getElementById("notifications-checkbox");
const saveButton = document.getElementById("save-button");
const resetButton = document.getElementById("reset-button");
const savedMessage = document.getElementById("saved-message");
// Load current settings
loadSettings();
// Save settings
saveButton.addEventListener("click", () => {
const settings = {
theme: themeSelect.value,
notifications: notificationsCheckbox.checked
};
chrome.storage.local.set({ settings }, () => {
showSavedMessage();
});
});
// Reset to defaults
resetButton.addEventListener("click", () => {
const defaultSettings = {
theme: "light",
notifications: true
};
chrome.storage.local.set({ settings: defaultSettings }, () => {
loadSettings();
showSavedMessage();
});
});
// Load settings from storage
function loadSettings() {
chrome.storage.local.get("settings", (result) => {
const settings = result.settings || {
theme: "light",
notifications: true
};
themeSelect.value = settings.theme;
notificationsCheckbox.checked = settings.notifications;
});
}
// Show saved message
function showSavedMessage() {
savedMessage.classList.add("visible");
setTimeout(() => {
savedMessage.classList.remove("visible");
}, 3000);
}
To test your extension during development:
Chrome: Go to chrome://extensions/, enable “Developer mode”, and click “Load unpacked” to select your extension directory.
Brave: Go to brave://extensions/, enable “Developer mode”, and click “Load unpacked” to select your extension directory.
Firefox: Go to about:debugging#/runtime/this-firefox, click “Load Temporary Add-on”, and select your manifest.json file.
Edge: Go to edge://extensions/, enable “Developer mode”, and click “Load unpacked” to select your extension directory.
During testing, take advantage of the browser’s developer tools. You can access your background script’s console by clicking “inspect” on the extension’s card in the extensions management page.
When your extension is ready for distribution:
Create a production build: Use webpack to bundle your code and generate optimized assets.
Create a ZIP archive: Package your extension’s files for submission.
Publish to stores:
Remember to create store listings with compelling descriptions, screenshots, and privacy policies.
To create successful browser extensions:
Building browser extensions is a powerful way to enhance the web experience. With modern extension APIs and good development practices, you can create tools that integrate seamlessly with browsers and provide real value to users.
Have you built any browser extensions? What challenges did you face? Let me know in the comments below!

October 15, 2025
Learn about prompt poisoning attacks in AI systems and practical strategies to secure your applications from hidden malicious instructions.

May 12, 2025
Learn how to build a Model Context Protocol server using Node.js to integrate arithmetic functions with AI assistants.