fix(plugin): escape error message in Codex OAuth HTML to prevent XSS (CWE-79)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pull/17367/head
kevensun 2026-03-13 22:12:22 +08:00
parent f8475649da
commit a1ac2d719c
2 changed files with 44 additions and 1 deletions

View File

@ -9,6 +9,10 @@ import { setTimeout as sleep } from "node:timers/promises"
const log = Log.create({ service: "plugin.codex" })
function escapeHtml(str: string): string {
return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;")
}
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann"
const ISSUER = "https://auth.openai.com"
const CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses"
@ -229,7 +233,7 @@ const HTML_ERROR = (error: string) => `<!doctype html>
<div class="container">
<h1>Authorization Failed</h1>
<p>An error occurred during authorization.</p>
<div class="error">${error}</div>
<div class="error">${escapeHtml(error)}</div>
</div>
</body>
</html>`

View File

@ -0,0 +1,39 @@
import { describe, expect, test } from "bun:test"
/**
* CWE-79: XSS in codex.ts HTML_ERROR
* File: packages/opencode/src/plugin/codex.ts
*
* HTML_ERROR interpolated error string directly into HTML.
* Fix: escapeHtml() sanitizes the error before interpolation.
*/
function escapeHtml(str: string): string {
return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;")
}
const HTML_ERROR = (error: string) => `<div class="error">${escapeHtml(error)}</div>`
describe("CWE-79: XSS in codex.ts HTML_ERROR", () => {
test("should escape script tags", () => {
const result = HTML_ERROR('<script>alert(1)</script>')
expect(result).not.toContain("<script>")
expect(result).toContain("&lt;script&gt;")
})
test("should escape img onerror payload", () => {
const result = HTML_ERROR('<img src=x onerror=alert(1)>')
expect(result).not.toContain("<img")
})
test("should escape quotes", () => {
const result = HTML_ERROR('" onmouseover="alert(1)')
expect(result).toContain("&quot;")
expect(result).not.toContain(' onmouseover="alert')
})
test("should render normal error messages", () => {
const result = HTML_ERROR("invalid_grant")
expect(result).toContain("invalid_grant")
})
})