mirror of
https://github.com/garrytan/gstack.git
synced 2026-08-05 13:58:37 +02:00
feat(security): POST /security-decision + relay reviewable banner fields
Two small server changes, one feature:
1. New POST /security-decision endpoint takes {tabId, decision} JSON
and writes the per-tab decision file. Auth-gated like every other
sidebar-agent control endpoint.
2. processAgentEvent relays the new reviewable/suspected_text/tabId
fields on security_event through to the chat entry so the sidepanel
banner can render [Allow] / [Block] buttons and the excerpt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
a487205605
commit
26fd1b2825
+28
-1
@@ -25,7 +25,7 @@ import {
|
|||||||
runContentFilters, type ContentFilterResult,
|
runContentFilters, type ContentFilterResult,
|
||||||
markHiddenElements, getCleanTextWithStripping, cleanupHiddenMarkers,
|
markHiddenElements, getCleanTextWithStripping, cleanupHiddenMarkers,
|
||||||
} from './content-security';
|
} from './content-security';
|
||||||
import { generateCanary, injectCanary, getStatus as getSecurityStatus } from './security';
|
import { generateCanary, injectCanary, getStatus as getSecurityStatus, writeDecision } from './security';
|
||||||
import { handleSnapshot, SNAPSHOT_FLAGS } from './snapshot';
|
import { handleSnapshot, SNAPSHOT_FLAGS } from './snapshot';
|
||||||
import {
|
import {
|
||||||
initRegistry, validateToken as validateScopedToken, checkScope, checkDomain,
|
initRegistry, validateToken as validateScopedToken, checkScope, checkDomain,
|
||||||
@@ -543,6 +543,11 @@ function processAgentEvent(event: any): void {
|
|||||||
channel: event.channel,
|
channel: event.channel,
|
||||||
tool: event.tool,
|
tool: event.tool,
|
||||||
signals: event.signals,
|
signals: event.signals,
|
||||||
|
// Reviewable flow fields — sidepanel renders [Allow] / [Block] buttons
|
||||||
|
// and the suspected text excerpt when reviewable=true.
|
||||||
|
reviewable: event.reviewable,
|
||||||
|
suspected_text: event.suspected_text,
|
||||||
|
tabId: event.tabId,
|
||||||
} as any);
|
} as any);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1966,6 +1971,28 @@ async function start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Kill hung agent
|
// Kill hung agent
|
||||||
|
// User's decision on a reviewable BLOCK (from the security banner).
|
||||||
|
// Writes ~/.gstack/security/decisions/tab-<id>.json that sidebar-agent
|
||||||
|
// polls. Accepts {tabId: number, decision: 'allow'|'block'} JSON body.
|
||||||
|
if (url.pathname === '/security-decision' && req.method === 'POST') {
|
||||||
|
if (!validateAuth(req)) {
|
||||||
|
return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, headers: { 'Content-Type': 'application/json' } });
|
||||||
|
}
|
||||||
|
const body = await req.json().catch(() => ({}));
|
||||||
|
const tabId = Number(body.tabId);
|
||||||
|
const decision = body.decision;
|
||||||
|
if (!Number.isFinite(tabId) || (decision !== 'allow' && decision !== 'block')) {
|
||||||
|
return new Response(JSON.stringify({ error: 'Invalid request' }), { status: 400, headers: { 'Content-Type': 'application/json' } });
|
||||||
|
}
|
||||||
|
writeDecision({
|
||||||
|
tabId,
|
||||||
|
decision,
|
||||||
|
ts: new Date().toISOString(),
|
||||||
|
reason: typeof body.reason === 'string' ? body.reason.slice(0, 200) : undefined,
|
||||||
|
});
|
||||||
|
return new Response(JSON.stringify({ ok: true }), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
||||||
|
}
|
||||||
|
|
||||||
if (url.pathname === '/sidebar-agent/kill' && req.method === 'POST') {
|
if (url.pathname === '/sidebar-agent/kill' && req.method === 'POST') {
|
||||||
if (!validateAuth(req)) {
|
if (!validateAuth(req)) {
|
||||||
return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, headers: { 'Content-Type': 'application/json' } });
|
return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, headers: { 'Content-Type': 'application/json' } });
|
||||||
|
|||||||
Reference in New Issue
Block a user