Ctrl K
ComunidadGuías…

src/main/url-guard.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import type { IpcMainInvokeEvent } from 'electron'
import { app } from 'electron'
import { join, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'

// The page a packaged build loads. Shared with window.ts so the IPC sender
// check below compares against exactly what the window loaded.
export const rendererIndexPath = join(import.meta.dirname, '../renderer/index.html')

// Chromium and Node can spell the same file URL differently (percent-escapes,
// Windows drive-letter case), so frames are matched by path, not by string.
// Windows paths are case-insensitive.
function pathKey(path: string): string {
  const absolute = resolve(path)
  return process.platform === 'win32' ? absolute.toLowerCase() : absolute
}
const rendererIndexKey = pathKey(rendererIndexPath)

// The Vite dev server, in a dev build only. The bundle still reads the env var
// at runtime, so an installed app launched with it set must not load (or
// trust) whatever page it points at.
export function devRendererUrl(): string | undefined {
  return app.isPackaged ? undefined : process.env['ELECTRON_RENDERER_URL']
}

// The one scheme check for every route that hands a URL to the OS
// (shell:open-external and setWindowOpenHandler), so they can't drift.
// IPC arguments are untyped at runtime, hence the string check.
export function isSafeExternalUrl(url: unknown): url is string {
  return typeof url === 'string' && /^https?:\/\//i.test(url)
}

// Whether an IPC call comes from the app's own page. Any page the window loads
// gets the preload's API, and a navigation keeps the same WebContents, so the
// calling frame's URL is the only thing that tells them apart. A null
// frame (destroyed or navigated away) is never trusted.
export function isTrustedFrame(event: IpcMainInvokeEvent): boolean {
  try {
    const url = new URL(event.senderFrame?.url ?? '')
    const devUrl = devRendererUrl()
    if (devUrl) return url.origin === new URL(devUrl).origin
    // fileURLToPath throws on any non-file: URL and ignores #hash and ?query; a
    // remote file host becomes a UNC path, which never matches.
    return pathKey(fileURLToPath(url)) === rendererIndexKey
  } catch {
    return false
  }
}