mirror of https://github.com/garrytan/gstack.git
fix: require auth on cookie-picker data routes (CRITICAL-01)
- Add Bearer token auth gate on all /cookie-picker/* data/action routes - GET /cookie-picker HTML page stays unauthenticated (UI shell) - Token embedded in served HTML for picker's fetch calls - CORS preflight now allows Authorization header
This commit is contained in:
parent
e16bf23ca0
commit
480f4bb23d
|
|
@ -53,6 +53,7 @@ export async function handleCookiePickerRoute(
|
||||||
url: URL,
|
url: URL,
|
||||||
req: Request,
|
req: Request,
|
||||||
bm: BrowserManager,
|
bm: BrowserManager,
|
||||||
|
authToken?: string,
|
||||||
): Promise<Response> {
|
): Promise<Response> {
|
||||||
const pathname = url.pathname;
|
const pathname = url.pathname;
|
||||||
const port = parseInt(url.port, 10) || 9400;
|
const port = parseInt(url.port, 10) || 9400;
|
||||||
|
|
@ -64,7 +65,7 @@ export async function handleCookiePickerRoute(
|
||||||
headers: {
|
headers: {
|
||||||
'Access-Control-Allow-Origin': corsOrigin(port),
|
'Access-Control-Allow-Origin': corsOrigin(port),
|
||||||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
||||||
'Access-Control-Allow-Headers': 'Content-Type',
|
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -72,13 +73,24 @@ export async function handleCookiePickerRoute(
|
||||||
try {
|
try {
|
||||||
// GET /cookie-picker — serve the picker UI
|
// GET /cookie-picker — serve the picker UI
|
||||||
if (pathname === '/cookie-picker' && req.method === 'GET') {
|
if (pathname === '/cookie-picker' && req.method === 'GET') {
|
||||||
const html = getCookiePickerHTML(port);
|
const html = getCookiePickerHTML(port, authToken);
|
||||||
return new Response(html, {
|
return new Response(html, {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: { 'Content-Type': 'text/html; charset=utf-8' },
|
headers: { 'Content-Type': 'text/html; charset=utf-8' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Auth gate: all data/action routes below require Bearer token ───
|
||||||
|
if (authToken) {
|
||||||
|
const authHeader = req.headers.get('authorization');
|
||||||
|
if (!authHeader || authHeader !== `Bearer ${authToken}`) {
|
||||||
|
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
|
||||||
|
status: 401,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GET /cookie-picker/browsers — list installed browsers
|
// GET /cookie-picker/browsers — list installed browsers
|
||||||
if (pathname === '/cookie-picker/browsers' && req.method === 'GET') {
|
if (pathname === '/cookie-picker/browsers' && req.method === 'GET') {
|
||||||
const browsers = findInstalledBrowsers();
|
const browsers = findInstalledBrowsers();
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
* No cookie values exposed anywhere.
|
* No cookie values exposed anywhere.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function getCookiePickerHTML(serverPort: number): string {
|
export function getCookiePickerHTML(serverPort: number, authToken?: string): string {
|
||||||
const baseUrl = `http://127.0.0.1:${serverPort}`;
|
const baseUrl = `http://127.0.0.1:${serverPort}`;
|
||||||
|
|
||||||
return `<!DOCTYPE html>
|
return `<!DOCTYPE html>
|
||||||
|
|
@ -330,6 +330,7 @@ export function getCookiePickerHTML(serverPort: number): string {
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
const BASE = '${baseUrl}';
|
const BASE = '${baseUrl}';
|
||||||
|
const AUTH_TOKEN = '${authToken || ''}';
|
||||||
let activeBrowser = null;
|
let activeBrowser = null;
|
||||||
let activeProfile = 'Default';
|
let activeProfile = 'Default';
|
||||||
let allProfiles = [];
|
let allProfiles = [];
|
||||||
|
|
@ -372,7 +373,9 @@ export function getCookiePickerHTML(serverPort: number): string {
|
||||||
|
|
||||||
// ─── API ────────────────────────────────
|
// ─── API ────────────────────────────────
|
||||||
async function api(path, opts) {
|
async function api(path, opts) {
|
||||||
const res = await fetch(BASE + '/cookie-picker' + path, opts);
|
const headers = { ...(opts?.headers || {}) };
|
||||||
|
if (AUTH_TOKEN) headers['Authorization'] = 'Bearer ' + AUTH_TOKEN;
|
||||||
|
const res = await fetch(BASE + '/cookie-picker' + path, { ...opts, headers });
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const err = new Error(data.error || 'Request failed');
|
const err = new Error(data.error || 'Request failed');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue