Reduced permissions in the manifest
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
# Chat Log
|
||||
|
||||
### User
|
||||
Implement SA-006 permission changes: remove `<all_urls>` and unnecessary `tabs`, request exact access to the configured self-hosted backend, and keep page capture behind `activeTab`.
|
||||
|
||||
### Assistant outcome
|
||||
Updated the Firefox extension manifest to use `activeTab` and `storage` only, with Firefox-compatible optional HTTP/HTTPS host permissions. The options login page now normalizes the backend to an origin and requests only that origin during the user-initiated login. The popup checks that exact grant before session validation or link submission, so it does not perform backend calls without permission.
|
||||
|
||||
## 2026-08-24
|
||||
|
||||
### User
|
||||
|
||||
@@ -181,6 +181,7 @@
|
||||
161. The popup still shows the sign-in block even though the authenticated session text is displayed; show the block only when signed out.
|
||||
162. The authenticated session text and sign-in block are still shown together.
|
||||
163. Use the VIBE directory to log interactions.
|
||||
164. Implement SA-006: remove broad Firefox extension host access and unnecessary tabs permission, request exact configured backend origin access, and keep page capture behind activeTab.
|
||||
|
||||
## Future entries
|
||||
|
||||
|
||||
Binary file not shown.
@@ -6,11 +6,11 @@
|
||||
"default_locale": "en-US",
|
||||
"permissions": [
|
||||
"activeTab",
|
||||
"storage",
|
||||
"tabs"
|
||||
"storage"
|
||||
],
|
||||
"host_permissions": [
|
||||
"<all_urls>"
|
||||
"optional_permissions": [
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
],
|
||||
"action": {
|
||||
"default_title": "__MSG_extensionName__",
|
||||
|
||||
+38
-4
@@ -13,6 +13,34 @@ const signOutButton = document.getElementById('sign-out');
|
||||
|
||||
const t = window.linklogI18n;
|
||||
|
||||
function normalizeBackendOrigin(backendUrl) {
|
||||
try {
|
||||
const url = new URL(backendUrl);
|
||||
if (!['http:', 'https:'].includes(url.protocol)) {
|
||||
return null;
|
||||
}
|
||||
return url.origin;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function hasBackendPermission(backendUrl) {
|
||||
const origin = normalizeBackendOrigin(backendUrl);
|
||||
if (!origin) return false;
|
||||
return browser.permissions.contains({origins: [`${origin}/*`]});
|
||||
}
|
||||
|
||||
async function requestBackendPermission(backendUrl) {
|
||||
const origin = normalizeBackendOrigin(backendUrl);
|
||||
if (!origin) return false;
|
||||
try {
|
||||
return await browser.permissions.request({origins: [`${origin}/*`]});
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function setStatus(message, isError = false) {
|
||||
statusEl.textContent = message;
|
||||
statusEl.classList.remove('hidden');
|
||||
@@ -25,7 +53,7 @@ async function loadSettings() {
|
||||
backendUrlInput.value = settings.backendUrl || '';
|
||||
emailInput.value = settings.email || '';
|
||||
|
||||
if (settings.accessToken && settings.backendUrl) {
|
||||
if (settings.accessToken && settings.backendUrl && await hasBackendPermission(settings.backendUrl)) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${settings.backendUrl}/api/auth/me`,
|
||||
@@ -61,7 +89,7 @@ async function clearSession() {
|
||||
|
||||
async function saveSettingsAndLogin(event) {
|
||||
event.preventDefault();
|
||||
const backendUrl = backendUrlInput.value.trim();
|
||||
const backendUrl = normalizeBackendOrigin(backendUrlInput.value.trim());
|
||||
const email = emailInput.value.trim();
|
||||
const password = passwordInput.value;
|
||||
const otp = otpInput.value.trim();
|
||||
@@ -71,6 +99,11 @@ async function saveSettingsAndLogin(event) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await requestBackendPermission(backendUrl))) {
|
||||
setStatus(t('unableToLogIn'), true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${backendUrl}/api/auth/login`, {
|
||||
method: 'POST',
|
||||
@@ -104,8 +137,9 @@ async function saveSettingsAndLogin(event) {
|
||||
|
||||
async function signOut() {
|
||||
const settings = await browser.storage.local.get(['accessToken']);
|
||||
if (settings.accessToken) {
|
||||
await fetch(`${backendUrlInput.value.trim()}/api/auth/logout`, {
|
||||
const backendUrl = normalizeBackendOrigin(backendUrlInput.value.trim());
|
||||
if (settings.accessToken && await hasBackendPermission(backendUrl)) {
|
||||
await fetch(`${backendUrl}/api/auth/logout`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ token: settings.accessToken }),
|
||||
|
||||
+21
-3
@@ -16,6 +16,24 @@ const authSession = document.getElementById('auth-session');
|
||||
|
||||
const t = window.linklogI18n;
|
||||
|
||||
function normalizeBackendOrigin(backendUrl) {
|
||||
try {
|
||||
const url = new URL(backendUrl);
|
||||
if (!['http:', 'https:'].includes(url.protocol)) {
|
||||
return null;
|
||||
}
|
||||
return url.origin;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function hasBackendPermission(backendUrl) {
|
||||
const origin = normalizeBackendOrigin(backendUrl);
|
||||
if (!origin) return false;
|
||||
return browser.permissions.contains({origins: [`${origin}/*`]});
|
||||
}
|
||||
|
||||
function setStatus(message, isError = false) {
|
||||
statusEl.textContent = message;
|
||||
statusEl.classList.remove('hidden');
|
||||
@@ -33,7 +51,7 @@ async function getSettings() {
|
||||
}
|
||||
|
||||
async function validateSession(settings) {
|
||||
if (!settings.backendUrl || !settings.accessToken) return null;
|
||||
if (!settings.backendUrl || !settings.accessToken || !(await hasBackendPermission(settings.backendUrl))) return null;
|
||||
try {
|
||||
const response = await fetch(`${settings.backendUrl}/api/auth/me`, {
|
||||
headers: {'Authorization': `Bearer ${settings.accessToken}`},
|
||||
@@ -157,7 +175,7 @@ async function populateCurrentTab() {
|
||||
|
||||
async function checkExistingLink() {
|
||||
const settings = await getSettings();
|
||||
if (!settings.backendUrl || !settings.accessToken || !titleInput.value || !urlInput.value) return;
|
||||
if (!settings.backendUrl || !settings.accessToken || !(await hasBackendPermission(settings.backendUrl)) || !titleInput.value || !urlInput.value) return;
|
||||
try {
|
||||
const response = await fetch(`${settings.backendUrl}/api/links/check?${new URLSearchParams({
|
||||
title: titleInput.value,
|
||||
@@ -181,7 +199,7 @@ async function handleSubmit(event) {
|
||||
const token = settings.accessToken;
|
||||
const backendUrl = settings.backendUrl;
|
||||
|
||||
if (!token || !backendUrl) {
|
||||
if (!token || !backendUrl || !(await hasBackendPermission(backendUrl))) {
|
||||
setStatus(t('configureAndLogIn'), true);
|
||||
browser.runtime.openOptionsPage();
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user