fix: extension sender validation + message type allowlist

Add sender.id check and ALLOWED_TYPES allowlist to the Chrome extension's
message handler. Defense-in-depth against message spoofing from external
extensions or future externally_connectable changes.

Clears 2 Socket anomalies (extension permissions).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-03-28 23:16:12 -07:00
parent 938d67ba29
commit e4a8631847
2 changed files with 22 additions and 0 deletions
+15
View File
@@ -161,6 +161,21 @@ async function fetchAndRelayRefs() {
// ─── Message Handling ──────────────────────────────────────────
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
// Security: only accept messages from this extension's own scripts
if (sender.id !== chrome.runtime.id) {
console.warn('[gstack] Rejected message from unknown sender:', sender.id);
return;
}
const ALLOWED_TYPES = new Set([
'getPort', 'setPort', 'getServerUrl', 'fetchRefs',
'openSidePanel', 'command', 'sidebar-command'
]);
if (!ALLOWED_TYPES.has(msg.type)) {
console.warn('[gstack] Rejected unknown message type:', msg.type);
return;
}
if (msg.type === 'getPort') {
sendResponse({ port: serverPort, connected: isConnected });
return true;
+7
View File
@@ -76,6 +76,13 @@ describe('Audit compliance', () => {
expect(review).toContain('Data NOT sent');
});
// Round 2 Fix 3: Extension sender validation + message type allowlist
test('extension background.js validates message sender', () => {
const bg = readFileSync(join(ROOT, 'extension/background.js'), 'utf-8');
expect(bg).toContain('sender.id !== chrome.runtime.id');
expect(bg).toContain('ALLOWED_TYPES');
});
// Round 2 Fix 4: Chrome CDP binds to localhost only
test('chrome-cdp binds to localhost only', () => {
const cdp = readFileSync(join(ROOT, 'bin/chrome-cdp'), 'utf-8');