aegis-bridge 2.16.0 → 2.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Aegis Dashboard</title>
|
|
7
7
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🛡️</text></svg>" />
|
|
8
|
-
<script type="module" crossorigin src="/dashboard/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/dashboard/assets/index-iq1EAlmt.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/dashboard/assets/index-DnIfWYzW.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body class="bg-[#0a0a0f] text-gray-200 antialiased">
|
package/dist/server.js
CHANGED
|
@@ -1467,11 +1467,16 @@ app.post('/v1/templates', async (req, reply) => {
|
|
|
1467
1467
|
if (!finalData.workDir) {
|
|
1468
1468
|
return reply.status(400).send({ error: 'workDir is required (provide sessionId or explicit workDir)' });
|
|
1469
1469
|
}
|
|
1470
|
+
// Issue #1125: Validate workDir for path traversal at template creation time
|
|
1471
|
+
const safeWorkDir = await validateWorkDirWithConfig(finalData.workDir);
|
|
1472
|
+
if (typeof safeWorkDir === 'object') {
|
|
1473
|
+
return reply.status(400).send({ error: `Invalid workDir: ${safeWorkDir.error}`, code: safeWorkDir.code });
|
|
1474
|
+
}
|
|
1470
1475
|
try {
|
|
1471
1476
|
const template = await templateStore.createTemplate({
|
|
1472
1477
|
name,
|
|
1473
1478
|
description,
|
|
1474
|
-
workDir:
|
|
1479
|
+
workDir: safeWorkDir,
|
|
1475
1480
|
prompt: finalData.prompt,
|
|
1476
1481
|
claudeCommand: finalData.claudeCommand,
|
|
1477
1482
|
env: finalData.env,
|
package/dist/ws-terminal.js
CHANGED
|
@@ -77,14 +77,21 @@ export function registerWsTerminalRoute(app, sessions, tmux, auth) {
|
|
|
77
77
|
socket.close();
|
|
78
78
|
return;
|
|
79
79
|
}
|
|
80
|
-
const session = sessions.getSession(sessionId);
|
|
81
|
-
if (!session) {
|
|
82
|
-
sendError(socket, 'Session not found');
|
|
83
|
-
socket.close();
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
80
|
// Check if already authenticated via Bearer header in preHandler
|
|
87
81
|
const preAuthed = auth.authEnabled && req.headers?.authorization?.startsWith('Bearer ');
|
|
82
|
+
// #1130: When auth is required but not yet provided, do NOT check session
|
|
83
|
+
// existence — that would leak whether a session ID is valid to unauthenticated clients.
|
|
84
|
+
// For pre-authenticated clients (Bearer header) or when auth is disabled, check immediately.
|
|
85
|
+
let session = null;
|
|
86
|
+
const deferSessionCheck = auth.authEnabled && !preAuthed;
|
|
87
|
+
if (!deferSessionCheck) {
|
|
88
|
+
session = sessions.getSession(sessionId);
|
|
89
|
+
if (!session) {
|
|
90
|
+
sendError(socket, 'Session not found');
|
|
91
|
+
socket.close();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
88
95
|
// Create subscriber
|
|
89
96
|
const subscriber = {
|
|
90
97
|
lastContent: '',
|
|
@@ -104,25 +111,27 @@ export function registerWsTerminalRoute(app, sessions, tmux, auth) {
|
|
|
104
111
|
}
|
|
105
112
|
}, AUTH_TIMEOUT_MS);
|
|
106
113
|
}
|
|
107
|
-
// Get or create shared session poll
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
poll
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
poll.
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
// Get or create shared session poll (only after session is confirmed to exist)
|
|
115
|
+
if (session) {
|
|
116
|
+
let poll = sessionPolls.get(sessionId);
|
|
117
|
+
if (!poll) {
|
|
118
|
+
poll = {
|
|
119
|
+
timer: null,
|
|
120
|
+
tickCount: 0,
|
|
121
|
+
subscribers: new Map(),
|
|
122
|
+
};
|
|
123
|
+
sessionPolls.set(sessionId, poll);
|
|
124
|
+
// Start the shared poll timer
|
|
125
|
+
poll.timer = setInterval(async () => {
|
|
126
|
+
poll.tickCount++;
|
|
127
|
+
await tickPoll(sessionId, sessions, tmux, poll);
|
|
128
|
+
}, POLL_INTERVAL_MS);
|
|
129
|
+
}
|
|
130
|
+
poll.subscribers.set(socket, subscriber);
|
|
121
131
|
}
|
|
122
|
-
poll.subscribers.set(socket, subscriber);
|
|
123
132
|
// Handle pong responses for keep-alive
|
|
124
133
|
socket.on('pong', () => {
|
|
125
|
-
const sub =
|
|
134
|
+
const sub = sessionPolls.get(sessionId)?.subscribers.get(socket);
|
|
126
135
|
if (sub)
|
|
127
136
|
sub.lastPongAt = Date.now();
|
|
128
137
|
});
|
|
@@ -176,6 +185,29 @@ export function registerWsTerminalRoute(app, sessions, tmux, auth) {
|
|
|
176
185
|
clearTimeout(subscriber.authTimer);
|
|
177
186
|
subscriber.authTimer = null;
|
|
178
187
|
}
|
|
188
|
+
// #1130: Now that the client is authenticated, check session existence.
|
|
189
|
+
// This was deferred to avoid leaking valid session IDs to unauthenticated clients.
|
|
190
|
+
const authedSession = sessions.getSession(sessionId);
|
|
191
|
+
if (!authedSession) {
|
|
192
|
+
sendError(socket, 'Session not found');
|
|
193
|
+
evictSubscriber(sessionId, socket, subscriber);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
// Register subscriber to the session poll now that session is confirmed
|
|
197
|
+
let authedPoll = sessionPolls.get(sessionId);
|
|
198
|
+
if (!authedPoll) {
|
|
199
|
+
authedPoll = {
|
|
200
|
+
timer: null,
|
|
201
|
+
tickCount: 0,
|
|
202
|
+
subscribers: new Map(),
|
|
203
|
+
};
|
|
204
|
+
sessionPolls.set(sessionId, authedPoll);
|
|
205
|
+
authedPoll.timer = setInterval(async () => {
|
|
206
|
+
authedPoll.tickCount++;
|
|
207
|
+
await tickPoll(sessionId, sessions, tmux, authedPoll);
|
|
208
|
+
}, POLL_INTERVAL_MS);
|
|
209
|
+
}
|
|
210
|
+
authedPoll.subscribers.set(socket, subscriber);
|
|
179
211
|
send(socket, { type: 'status', status: 'authenticated' });
|
|
180
212
|
return;
|
|
181
213
|
}
|
|
@@ -189,9 +221,15 @@ export function registerWsTerminalRoute(app, sessions, tmux, auth) {
|
|
|
189
221
|
await sessions.sendMessage(sessionId, msg.text);
|
|
190
222
|
}
|
|
191
223
|
else if (msg.type === 'resize') {
|
|
224
|
+
const resizeSession = sessions.getSession(sessionId);
|
|
225
|
+
if (!resizeSession) {
|
|
226
|
+
sendError(socket, 'Session no longer exists');
|
|
227
|
+
evictSubscriber(sessionId, socket, subscriber);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
192
230
|
const cols = clamp(msg.cols ?? 80, 10, 500, 80);
|
|
193
231
|
const rows = clamp(msg.rows ?? 24, 5, 200, 24);
|
|
194
|
-
await tmux.resizePane(
|
|
232
|
+
await tmux.resizePane(resizeSession.windowId, cols, rows);
|
|
195
233
|
}
|
|
196
234
|
}
|
|
197
235
|
catch (e) {
|