chore(opencode): consolidate escape logic (#32360)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { createConnection } from "net"
|
||||
import { createServer } from "http"
|
||||
import { escapeHtml } from "@/util/html"
|
||||
import { OAUTH_CALLBACK_PORT, OAUTH_CALLBACK_PATH, parseRedirectUri } from "./oauth-provider"
|
||||
|
||||
// Current callback server configuration (may differ from defaults if custom redirectUri is used)
|
||||
@@ -26,15 +27,6 @@ const HTML_SUCCESS = `<!DOCTYPE html>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
function escapeHtml(value: string) {
|
||||
return value
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'")
|
||||
}
|
||||
|
||||
const HTML_ERROR = (error: string) => `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@@ -5,6 +5,7 @@ import os from "os"
|
||||
import { setTimeout as sleep } from "node:timers/promises"
|
||||
import { createServer } from "http"
|
||||
import { OpenAIWebSocketPool } from "./ws-pool"
|
||||
import { escapeHtml } from "@/util/html"
|
||||
|
||||
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann"
|
||||
const ISSUER = "https://auth.openai.com"
|
||||
@@ -178,7 +179,7 @@ const HTML_SUCCESS = `<!doctype html>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
const HTML_ERROR = (error: string) => `<!doctype html>
|
||||
export const renderOAuthError = (error: string) => `<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>OpenCode - Codex Authorization Failed</title>
|
||||
@@ -221,7 +222,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>`
|
||||
@@ -254,8 +255,8 @@ async function startOAuthServer(): Promise<{ port: number; redirectUri: string }
|
||||
const errorMsg = errorDescription || error
|
||||
pendingOAuth?.reject(new Error(errorMsg))
|
||||
pendingOAuth = undefined
|
||||
res.writeHead(200, { "Content-Type": "text/html" })
|
||||
res.end(HTML_ERROR(errorMsg))
|
||||
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" })
|
||||
res.end(renderOAuthError(errorMsg))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -263,8 +264,8 @@ async function startOAuthServer(): Promise<{ port: number; redirectUri: string }
|
||||
const errorMsg = "Missing authorization code"
|
||||
pendingOAuth?.reject(new Error(errorMsg))
|
||||
pendingOAuth = undefined
|
||||
res.writeHead(400, { "Content-Type": "text/html" })
|
||||
res.end(HTML_ERROR(errorMsg))
|
||||
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" })
|
||||
res.end(renderOAuthError(errorMsg))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -272,8 +273,8 @@ async function startOAuthServer(): Promise<{ port: number; redirectUri: string }
|
||||
const errorMsg = "Invalid state - potential CSRF attack"
|
||||
pendingOAuth?.reject(new Error(errorMsg))
|
||||
pendingOAuth = undefined
|
||||
res.writeHead(400, { "Content-Type": "text/html" })
|
||||
res.end(HTML_ERROR(errorMsg))
|
||||
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" })
|
||||
res.end(renderOAuthError(errorMsg))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -284,7 +285,7 @@ async function startOAuthServer(): Promise<{ port: number; redirectUri: string }
|
||||
.then((tokens) => current.resolve(tokens))
|
||||
.catch((err) => current.reject(err))
|
||||
|
||||
res.writeHead(200, { "Content-Type": "text/html" })
|
||||
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" })
|
||||
res.end(HTML_SUCCESS)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
||||
import { OAUTH_DUMMY_KEY } from "../auth"
|
||||
import { createServer } from "http"
|
||||
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||
import { escapeHtml } from "@/util/html"
|
||||
|
||||
// Public Grok-CLI OAuth client. xAI's auth server rejects loopback OAuth from
|
||||
// non-allowlisted clients, so we reuse the Grok-CLI client_id that xAI ships
|
||||
@@ -74,25 +75,6 @@ function generateState(): string {
|
||||
return base64UrlEncode(crypto.getRandomValues(new Uint8Array(32)).buffer)
|
||||
}
|
||||
|
||||
export function escapeHtml(value: string): string {
|
||||
return value.replace(/[&<>"']/g, (char) => {
|
||||
switch (char) {
|
||||
case "&":
|
||||
return "&"
|
||||
case "<":
|
||||
return "<"
|
||||
case ">":
|
||||
return ">"
|
||||
case '"':
|
||||
return """
|
||||
case "'":
|
||||
return "'"
|
||||
default:
|
||||
return char
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
interface TokenResponse {
|
||||
access_token: string
|
||||
refresh_token: string
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export function escapeHtml(value: string) {
|
||||
return value
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'")
|
||||
}
|
||||
Reference in New Issue
Block a user