OSVauco/opax-web/backend/socketHandlers.js
Chris Christiansen dc9f7b6626 feat(opax-web): add read-only Git BFF and hardened UI
Replace the generic mcp-call socket path with two authenticated, read-only Git actions. Add response normalization and error masking, a purpose-built frontend, container build configuration, and socket handler unit tests.
2026-09-09 17:26:07 +00:00

101 lines
3.0 KiB
JavaScript

// FORSLAG TIL opax-web/backend/socketHandlers.js (ENDELIG KORRIGERT)
/**
* Normaliserer resultatet fra MCP-toolen 'get_git_status'.
* Kaster feil hvis responsen ikke har forventet format.
* @param {any} data - Rådata fra agentClient.
* @returns {{status: string, details: string}} Normalisert status-objekt.
*/
function normalizeGitStatus(data) {
if (
!data ||
typeof data !== 'object' ||
Array.isArray(data) ||
typeof data.status !== 'string' ||
typeof data.details !== 'string'
) {
throw new Error('Unexpected Git status response shape.');
}
return {
status: data.status,
details: data.details,
};
}
/**
* Normaliserer resultatet fra MCP-toolen 'get_git_branch'.
* Kaster feil hvis responsen ikke har forventet format.
* @param {any} data - Rådata fra agentClient.
* @returns {{current_branch: string}} Normalisert branch-objekt.
*/
function normalizeGitBranch(data) {
if (
!data ||
typeof data !== 'object' ||
Array.isArray(data) ||
typeof data.current_branch !== 'string'
) {
// I motsetning til forrige versjon, tillater vi ikke lenger null her.
// Hvis branchen er borte, bør MCP-toolen returnere en tom string.
// Ugyldig format skal alltid feile.
throw new Error('Unexpected Git branch response shape.');
}
return {
current_branch: data.current_branch,
};
}
const SAFE_ACTIONS = {
'git:getStatus': {
tool: 'get_git_status',
responseEvent: 'git:status',
errorMessage: 'Kunne ikke hente Git-status.',
normalize: normalizeGitStatus,
},
'git:getBranch': {
tool: 'get_git_branch',
responseEvent: 'git:branch',
errorMessage: 'Kunne ikke hente aktiv branch.',
normalize: normalizeGitBranch,
},
};
/**
* Registrerer sikre, nettleser-eksponerte Socket.IO-handlere.
* @param {import('socket.io').Socket} socket - Den tilkoblede socketen.
* @param {{callAgentFn: function}} dependencies - Injisert avhengighet for å kalle MCP.
*/
export function registerBrowserSocketHandlers(socket, { callAgentFn }) {
// Avvis den gamle, usikre mcp-call-handleren
socket.on('mcp-call', (payload) => {
const safeId = (payload && typeof payload.id === 'string' && payload.id.length <= 128) ? payload.id : null;
socket.emit('mcp-result', {
id: safeId,
error: 'Denne funksjonen er ikke tilgjengelig.',
});
});
// Registrer de nye, sikre handlerne
for (const eventName in SAFE_ACTIONS) {
const config = SAFE_ACTIONS[eventName];
socket.on(eventName, async () => {
if (!socket.request.session?.user) {
return socket.emit(`${config.responseEvent}:error`, { message: 'Du må være logget inn.' });
}
try {
const result = await callAgentFn(config.tool, {});
const normalizedData = config.normalize(result);
socket.emit(`${config.responseEvent}:result`, { data: normalizedData });
} catch (e) {
// e.message logges ikke eller sendes til klienten.
socket.emit(`${config.responseEvent}:error`, { message: config.errorMessage });
}
});
}
}