mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-02 03:35:09 +02:00
feat(security): expose security status on /health for shield icon
The /health endpoint now returns a `security` field with the classifier
status, suitable for driving the sidepanel shield icon:
{
status: 'protected' | 'degraded' | 'inactive',
layers: { testsavant, transcript, canary },
lastUpdated: ISO8601
}
Backend plumbing:
* server.ts imports getStatus from security.ts (pure-string, safe in
compiled binary) and includes it in the /health response.
* sidebar-agent.ts writes ~/.gstack/security/session-state.json when the
classifier warmup completes (success OR failure). This is the cross-
process handoff — server.ts reads the state file via getStatus() to
surface the result to the sidepanel.
The sidepanel rendering (SVG shield icon + color states + tooltip) is a
follow-up commit in the extension/ code.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,7 +25,7 @@ import {
|
||||
runContentFilters, type ContentFilterResult,
|
||||
markHiddenElements, getCleanTextWithStripping, cleanupHiddenMarkers,
|
||||
} from './content-security';
|
||||
import { generateCanary, injectCanary } from './security';
|
||||
import { generateCanary, injectCanary, getStatus as getSecurityStatus } from './security';
|
||||
import { handleSnapshot, SNAPSHOT_FLAGS } from './snapshot';
|
||||
import {
|
||||
initRegistry, validateToken as validateScopedToken, checkScope, checkDomain,
|
||||
@@ -1447,6 +1447,11 @@ async function start() {
|
||||
queueLength: messageQueue.length,
|
||||
},
|
||||
session: sidebarSession ? { id: sidebarSession.id, name: sidebarSession.name } : null,
|
||||
// Security module status — drives the shield icon in the sidepanel.
|
||||
// Returns {status: 'protected'|'degraded'|'inactive', layers: {...}}.
|
||||
// Source of truth is ~/.gstack/security/session-state.json, written
|
||||
// by sidebar-agent as the classifier warms up.
|
||||
security: getSecurityStatus(),
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
|
||||
@@ -15,7 +15,7 @@ import * as path from 'path';
|
||||
import { safeUnlink } from './error-handling';
|
||||
import {
|
||||
checkCanaryInStructure, logAttempt, hashPayload, extractDomain,
|
||||
combineVerdict, type LayerSignal,
|
||||
combineVerdict, writeSessionState, readSessionState, type LayerSignal,
|
||||
} from './security';
|
||||
import {
|
||||
loadTestsavant, scanPageContent, checkTranscript,
|
||||
@@ -696,10 +696,22 @@ async function main() {
|
||||
// Warm up the ML classifier in the background. First call triggers a 112MB
|
||||
// download (~30s on average broadband). Non-blocking — the sidebar stays
|
||||
// functional on cold start; classifier just reports 'off' until warmed.
|
||||
//
|
||||
// On warmup completion (success or failure), write the classifier status to
|
||||
// ~/.gstack/security/session-state.json so server.ts's /health endpoint can
|
||||
// report it to the sidepanel for shield icon rendering.
|
||||
loadTestsavant((msg) => console.log(`[security-classifier] ${msg}`))
|
||||
.then(() => {
|
||||
const s = getClassifierStatus();
|
||||
console.log(`[sidebar-agent] Classifier warmup complete: ${JSON.stringify(s)}`);
|
||||
const existing = readSessionState();
|
||||
writeSessionState({
|
||||
sessionId: existing?.sessionId ?? String(process.pid),
|
||||
canary: existing?.canary ?? '',
|
||||
warnedDomains: existing?.warnedDomains ?? [],
|
||||
classifierStatus: s,
|
||||
lastUpdated: new Date().toISOString(),
|
||||
});
|
||||
})
|
||||
.catch((err) => console.warn('[sidebar-agent] Classifier warmup failed (degraded mode):', err?.message));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user