fix(extension): generate the API token with a CSPRNG and restrict the discovery file

The bearer token came from Math.random(), which is predictable. Use crypto.randomBytes
and write piagentui-port.json (which stores the token) with mode 0600.
This commit is contained in:
evandro committed 2026-10-10 04:24:44 -03:00
1 parent e8927ebf78
commit ceca5bf301
1 file changed
+9 -3
+9 -3
View File
@@ -3,7 +3,7 @@ import { WebSocketServer, WebSocket } from 'ws'
import * as http from 'node:http'
import * as fs from 'node:fs'
import * as path from 'node:path'
import { randomUUID } from 'node:crypto'
import { randomBytes, randomUUID } from 'node:crypto'
import {
validateBearerToken,
preventPathTraversal,
@@ -721,7 +721,8 @@ export default function (pi: ExtensionAPI) {
}
function generateBearerToken(): string {
return Array.from({ length: 4 }, () => Math.random().toString(36).substring(2)).join('-')
// CSPRNG: the token is the only credential guarding the local API.
return randomBytes(24).toString('base64url')
}
const settingsStore = createPiAgentUiSettingsStore(resolvePiAgentUiSettingsPaths())
@@ -2065,7 +2066,12 @@ export default function (pi: ExtensionAPI) {
if (!fs.existsSync(piAgentDir)) {
fs.mkdirSync(piAgentDir, { recursive: true })
}
fs.writeFileSync(discoveryPath, JSON.stringify({ port: localPort, token: localToken }, null, 2), 'utf8')
// The file holds the API token: keep it readable by the owner only (mode is ignored on Windows).
fs.writeFileSync(discoveryPath, JSON.stringify({ port: localPort, token: localToken }, null, 2), {
encoding: 'utf8',
mode: 0o600,
})
fs.chmodSync(discoveryPath, 0o600)
console.log(`[PiAgentUi] Wrote connection info to: ${discoveryPath}`)
} catch (err) {
console.error('[PiAgentUi] Failed to write discovery file:', err)