fix(browser-bridge): auth bypass when secret empty, popup status fix, force ping on open
This commit is contained in:
parent
58751945eb
commit
fa5001341e
|
|
@ -0,0 +1 @@
|
||||||
|
{"uploadUuid":"8634d0ba5e1641b2a504640b5992b540","channel":"unlisted","xpiCrcHash":"124e35e2b615071abcbccd2e75a8f4247f42ddb700077632affa3a7c3c82c2ef"}
|
||||||
|
|
@ -60,6 +60,10 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
||||||
flushQueue();
|
flushQueue();
|
||||||
sendResponse({ ok: true });
|
sendResponse({ ok: true });
|
||||||
}
|
}
|
||||||
|
if (msg.type === 'FORCE_PING') {
|
||||||
|
pingBridge();
|
||||||
|
sendResponse({ ok: true });
|
||||||
|
}
|
||||||
if (msg.type === 'SET_TRACKING') {
|
if (msg.type === 'SET_TRACKING') {
|
||||||
chrome.storage.local.set({ 'verstak_tracking_enabled': msg.enabled });
|
chrome.storage.local.set({ 'verstak_tracking_enabled': msg.enabled });
|
||||||
sendResponse({ ok: true });
|
sendResponse({ ok: true });
|
||||||
|
|
@ -184,6 +188,7 @@ function flushQueue() {
|
||||||
'X-Verstak-Secret': secret,
|
'X-Verstak-Secret': secret,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
|
signal: AbortSignal.timeout(5000),
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
|
@ -191,13 +196,27 @@ function flushQueue() {
|
||||||
updateRecent(ev);
|
updateRecent(ev);
|
||||||
}
|
}
|
||||||
chrome.storage.local.set({ [STORAGE_KEY]: [] });
|
chrome.storage.local.set({ [STORAGE_KEY]: [] });
|
||||||
|
chrome.storage.local.set({
|
||||||
|
[BRIDGE_KEY]: { ...config, bridgeReachable: true, lastPing: Date.now() }
|
||||||
|
});
|
||||||
chrome.runtime.sendMessage({ type: 'UI_UPDATE' }).catch(() => {});
|
chrome.runtime.sendMessage({ type: 'UI_UPDATE' }).catch(() => {});
|
||||||
} else if (res.status === 401) {
|
} else if (res.status === 401) {
|
||||||
console.warn('[verstak] bridge auth failed, check secret');
|
console.warn('[verstak] bridge auth failed, check secret');
|
||||||
|
chrome.storage.local.set({
|
||||||
|
[BRIDGE_KEY]: { ...config, bridgeReachable: false, lastPing: Date.now() }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn('[verstak] bridge error', res.status);
|
||||||
|
chrome.storage.local.set({
|
||||||
|
[BRIDGE_KEY]: { ...config, bridgeReachable: false, lastPing: Date.now() }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err) => {
|
||||||
// Bridge not available — keep events in queue
|
console.warn('[verstak] bridge unreachable:', err.message);
|
||||||
|
chrome.storage.local.set({
|
||||||
|
[BRIDGE_KEY]: { ...config, bridgeReachable: false, lastPing: Date.now() }
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -207,17 +226,21 @@ function pingBridge() {
|
||||||
const config = data[BRIDGE_KEY] || DEFAULT_CONFIG;
|
const config = data[BRIDGE_KEY] || DEFAULT_CONFIG;
|
||||||
const port = config.port || 9786;
|
const port = config.port || 9786;
|
||||||
|
|
||||||
fetch(`http://127.0.0.1:${port}/api/ping`)
|
fetch(`http://127.0.0.1:${port}/api/ping`, {
|
||||||
|
signal: AbortSignal.timeout(3000),
|
||||||
|
})
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((info) => {
|
.then((info) => {
|
||||||
chrome.storage.local.set({
|
chrome.storage.local.set({
|
||||||
[BRIDGE_KEY]: { ...config, ...info, bridgeReachable: true, lastPing: Date.now() }
|
[BRIDGE_KEY]: { ...config, ...info, bridgeReachable: true, lastPing: Date.now() }
|
||||||
});
|
});
|
||||||
|
chrome.runtime.sendMessage({ type: 'UI_UPDATE' }).catch(() => {});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
chrome.storage.local.set({
|
chrome.storage.local.set({
|
||||||
[BRIDGE_KEY]: { ...config, bridgeReachable: false, lastPing: Date.now() }
|
[BRIDGE_KEY]: { ...config, bridgeReachable: false, lastPing: Date.now() }
|
||||||
});
|
});
|
||||||
|
chrome.runtime.sendMessage({ type: 'UI_UPDATE' }).catch(() => {});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Verstak Bridge",
|
"name": "Verstak Bridge",
|
||||||
"version": "1.0.2",
|
"version": "1.0.3",
|
||||||
"description": "Отслеживает активные вкладки и отправляет события в Verstak",
|
"description": "Отслеживает активные вкладки и отправляет события в Verstak",
|
||||||
"author": "Verstak",
|
"author": "Verstak",
|
||||||
"homepage_url": "https://git.mirv.top/mirivlad/verstak",
|
"homepage_url": "https://git.mirv.top/mirivlad/verstak",
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,15 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "verstak-bridge-firefox",
|
"dependencies": {
|
||||||
"version": "1.0.0",
|
"jsonwebtoken": "^9.0.3"
|
||||||
"private": true,
|
|
||||||
"description": "Verstak Bridge Firefox extension",
|
|
||||||
"scripts": {
|
|
||||||
"lint:firefox": "web-ext lint --source-dir .",
|
|
||||||
"build:firefox": "web-ext build --source-dir . --artifacts-dir ../web-ext-artifacts --overwrite-dest",
|
|
||||||
"sign:firefox": "../scripts/sign-firefox-xpi.sh",
|
|
||||||
"release:firefox": "../scripts/release-firefox-xpi.sh"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"web-ext": "^8.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,12 @@ const RECENT_KEY = 'verstak_recent';
|
||||||
const DEFAULT_PORT = 9786;
|
const DEFAULT_PORT = 9786;
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
updateUI();
|
// Force a fresh ping check on popup open, don't rely on stale cache
|
||||||
|
chrome.runtime.sendMessage({ type: 'FORCE_PING' });
|
||||||
|
// Read cached state for immediate render
|
||||||
restoreToggleState();
|
restoreToggleState();
|
||||||
restoreSettingsPanel();
|
restoreSettingsPanel();
|
||||||
|
updateUI();
|
||||||
|
|
||||||
document.getElementById('sync-btn').addEventListener('click', forceSync);
|
document.getElementById('sync-btn').addEventListener('click', forceSync);
|
||||||
document.getElementById('track-toggle').addEventListener('change', onToggle);
|
document.getElementById('track-toggle').addEventListener('change', onToggle);
|
||||||
|
|
@ -32,16 +35,22 @@ function updateUI() {
|
||||||
const portEl = document.getElementById('bridge-port');
|
const portEl = document.getElementById('bridge-port');
|
||||||
const dotEl = document.getElementById('status-dot');
|
const dotEl = document.getElementById('status-dot');
|
||||||
|
|
||||||
if (config.bridgeReachable) {
|
if (config.bridgeReachable === true) {
|
||||||
statusEl.textContent = 'Доступен';
|
statusEl.textContent = 'Доступен';
|
||||||
statusEl.className = 'value online';
|
statusEl.className = 'value online';
|
||||||
dotEl.className = 'dot online';
|
dotEl.className = 'dot online';
|
||||||
portEl.textContent = '127.0.0.1:' + (config.port || DEFAULT_PORT);
|
portEl.textContent = '127.0.0.1:' + (config.port || DEFAULT_PORT);
|
||||||
} else {
|
} else if (config.bridgeReachable === false) {
|
||||||
statusEl.textContent = 'Недоступен';
|
statusEl.textContent = 'Недоступен';
|
||||||
statusEl.className = 'value offline';
|
statusEl.className = 'value offline';
|
||||||
dotEl.className = 'dot offline';
|
dotEl.className = 'dot offline';
|
||||||
portEl.textContent = config.lastPing ? '—' : 'проверка...';
|
portEl.textContent = '127.0.0.1:' + (config.port || DEFAULT_PORT);
|
||||||
|
} else {
|
||||||
|
// Unknown — not yet checked
|
||||||
|
statusEl.textContent = 'Проверка...';
|
||||||
|
statusEl.className = 'value';
|
||||||
|
dotEl.className = 'dot';
|
||||||
|
portEl.textContent = '127.0.0.1:' + (config.port || DEFAULT_PORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update settings panel port field
|
// Update settings panel port field
|
||||||
|
|
@ -146,11 +155,12 @@ function savePort() {
|
||||||
chrome.storage.local.get(BRIDGE_KEY, (data) => {
|
chrome.storage.local.get(BRIDGE_KEY, (data) => {
|
||||||
const config = data[BRIDGE_KEY] || {};
|
const config = data[BRIDGE_KEY] || {};
|
||||||
config.port = port;
|
config.port = port;
|
||||||
|
config.bridgeReachable = null; // Force re-check on next popup open
|
||||||
chrome.storage.local.set({ [BRIDGE_KEY]: config }, () => {
|
chrome.storage.local.set({ [BRIDGE_KEY]: config }, () => {
|
||||||
statusEl.textContent = 'Сохранено';
|
statusEl.textContent = 'Сохранено';
|
||||||
statusEl.className = 'port-status ok';
|
statusEl.className = 'port-status ok';
|
||||||
// Trigger immediate ping with new port
|
// Trigger immediate ping with new port
|
||||||
chrome.runtime.sendMessage({ type: 'FORCE_FLUSH' });
|
chrome.runtime.sendMessage({ type: 'FORCE_PING' });
|
||||||
setTimeout(() => { updateUI(); statusEl.textContent = ''; }, 1500);
|
setTimeout(() => { updateUI(); statusEl.textContent = ''; }, 1500);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,11 @@ func (s *Server) handleEvents(w http.ResponseWriter, r *http.Request) {
|
||||||
// withAuth wraps a handler with shared-secret authentication.
|
// withAuth wraps a handler with shared-secret authentication.
|
||||||
func (s *Server) withAuth(next http.HandlerFunc) http.HandlerFunc {
|
func (s *Server) withAuth(next http.HandlerFunc) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// If no secret is configured, skip authentication
|
||||||
|
if s.secret == "" {
|
||||||
|
next(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
auth := r.Header.Get("X-Verstak-Secret")
|
auth := r.Header.Get("X-Verstak-Secret")
|
||||||
if auth != s.secret {
|
if auth != s.secret {
|
||||||
http.Error(w, "unauthorized", 401)
|
http.Error(w, "unauthorized", 401)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue