3.1 KiB
3.1 KiB
pi-webui Security Hardening Notes
Scope
This document summarizes hardening changes applied in server/index.ts and client/main.ts for safer markdown rendering, stronger session security defaults, and bounded parsing of untrusted local files.
Implemented protections
1) Markdown link allowlist in assistant output
- Added
shared/markdown-security.tswith:ALLOWED_MARKDOWN_LINK_SCHEMES(explicit allowlist)sanitizeMarkdownLinks()used by assistant message rendering inclient/main.ts
- Allowed URL schemes are currently:
http,https,mailto,tel- Relative links (
./,../,/) and fragment links (#) are preserved.
- Unsafe inline destinations are rewritten before rendering, and a global
markdown-blockpost-render patch removes unsafehref/srcattributes from reference links, autolinks, thinking blocks, and artifact previews as a sink-level defense.
2) Security response headers
server/index.ts now applies middleware and websocket headers for:
Content-Security-PolicyX-Content-Type-Options: nosniffReferrer-Policy: strict-origin-when-cross-originX-Frame-Options: DENYStrict-Transport-Security
This applies to HTTP responses and websocket handshake responses.
3) Token policy
- Anonymous tokenless mode is no longer implicit.
- Server startup requires
PI_WEBUI_TOKENfor every non-loopback binding. - Tokenless local development requires both
PI_WEBUI_ALLOW_ANONYMOUS=trueand loopbackHOST(127.0.0.1,::1, orlocalhost). PI_WEBUI_TOKENis still not accepted from query parameters.- WebSocket auth accepts:
Sec-WebSocket-Protocol(pi-webui-token.<base64url-token>)x-pi-webui-token/Authorization: Bearer ...
- REST auth accepts:
x-pi-webui-token/Authorization: Bearer ...
- Query token support was removed from server-side token parsing while client bootstrap remains via
/?token=-> localStorage + immediate URL cleanup.
4) Bounded parsing for skills and sessions
Added budgeted parsing limits with explicit defaults:
- Skills:
PI_WEBUI_SKILL_MAX_FILESPI_WEBUI_SKILL_PARSE_BYTE_BUDGETPI_WEBUI_SKILL_FILE_MAX_BYTESPI_WEBUI_SKILL_MAX_DEPTHPI_WEBUI_SKILL_DIRECTORY_ENTRY_LIMIT
- Sessions:
PI_WEBUI_SESSION_DIRECTORY_SCAN_LIMITPI_WEBUI_SESSION_MAX_FILESPI_WEBUI_SESSION_PARSE_BYTE_BUDGETPI_WEBUI_SESSION_FILE_MAX_BYTES
Both listing flows stop parsing new files when request limits are reached and logically set hasMore where relevant. Session parsing reads only a bounded prefix from large files, so large sessions remain visible while deep full-text matches beyond the prefix are intentionally unavailable.
Remaining risks
- CSP is intentionally compatible with current app assumptions and still allows inline styles (
style-src 'unsafe-inline') because markdown + KaTeX rendering relies on inline style attributes. - Bounded session full-text search inspects only each file prefix; operators can raise limits deliberately when deeper historical search is required.
- Very large skill/session trees may hide older results after configured file/count budgets are reached.