Harden audit findings: correctness, type-safety, Windows conventions & polish
Audit-pass over CODE-AUDIT.md (~48 items closed this pass; all verified — typecheck + 234 tests + eslint + prettier green). Correctness / bugs: - B3: match the release checksum to the asset's filename line (no wrong-hash verify) - B4: newline-safe metadata probe (one --print with a unit-separator delimiter) - B5 / L88: guard the meta event against canceled items; progress no longer promotes a queued item outside pump() - B7: cookie-login promise always resolves (handles destroy-without-close) - L146: trim parser rejects >2 colon-group times; M36: Library selection counts only actionable rows - L11 / L50 / L156 / L57 / L159 / L15 / L3: live queue count, empty-cookie message, schedule picker min, dead-code/comment cleanup Type safety: - Enable noUncheckedIndexedAccess + noFallthroughCasesInSwitch (15 real edge cases fixed) Resilience / Windows / metadata: - R5: settings write failure handled (no unhandled IPC rejection; reconciles to truth) - W1 / W5 / W6: min window size, seeded folder picker, parented sign-in window; L147 dead macOS branches removed - CL1: shared stdout markers; package/builder metadata (license, homepage, repository, copyright, tsbuildinfo glob) Copy / docs / tests: - M37 / SR9 dev-jargon cleanup in hints; M8 / M25 / M26 / L66 / L80 / L81 reconciled - New unit tests for L35 (isValidMediaItem) and L36 (compareVersions) This commit also checkpoints the previously-uncommitted feat/tray-background-clipboard work it builds on: background running + auto-download, library clipboard detection, tray, binary management & library scale, credential encryption at rest, the shared jsonStore and ui/ primitives, and the eslint/prettier tooling. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
import { BrowserWindow, type WebContents } from 'electron'
|
||||
import { LOGIN_PARTITION } from './cookies'
|
||||
|
||||
/** A public YouTube video used as the extraction target (stable, always accessible). */
|
||||
const YT_VIDEO = 'https://www.youtube.com/watch?v=jNQXAC9IVRw'
|
||||
|
||||
/**
|
||||
* Restrict a PO-token window to *.youtube.com and accounts.google.com (for
|
||||
* sign-in). Mirrors hardenLoginWebContents in cookies.ts.
|
||||
*/
|
||||
function isAllowedPoTokenUrl(target: string): boolean {
|
||||
try {
|
||||
const { protocol, hostname } = new URL(target)
|
||||
if (protocol !== 'http:' && protocol !== 'https:') return false
|
||||
return (
|
||||
hostname === 'youtube.com' ||
|
||||
hostname.endsWith('.youtube.com') ||
|
||||
hostname === 'accounts.google.com' ||
|
||||
hostname.endsWith('.accounts.google.com')
|
||||
)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function hardenPoTokenWebContents(wc: WebContents): void {
|
||||
wc.setWindowOpenHandler((details) => {
|
||||
if (!isAllowedPoTokenUrl(details.url)) return { action: 'deny' }
|
||||
return {
|
||||
action: 'allow',
|
||||
overrideBrowserWindowOptions: {
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
partition: LOGIN_PARTITION,
|
||||
sandbox: true,
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
wc.on('will-navigate', (e, navUrl) => {
|
||||
if (!isAllowedPoTokenUrl(navUrl)) e.preventDefault()
|
||||
})
|
||||
wc.on('will-redirect', (e, navUrl) => {
|
||||
if (!isAllowedPoTokenUrl(navUrl)) e.preventDefault()
|
||||
})
|
||||
wc.on('did-create-window', (child) => hardenPoTokenWebContents(child.webContents))
|
||||
}
|
||||
|
||||
/**
|
||||
* Async IIFE injected into the YouTube page after load. Polls
|
||||
* ytInitialPlayerResponse.serviceIntegrityDimensions.poToken (the field
|
||||
* yt-dlp's youtube extractor reads) for up to 8 s, returning the token
|
||||
* string or null if it never appears. The poll loop is needed because the
|
||||
* player JS initialises asynchronously after the DOM is ready.
|
||||
*/
|
||||
const EXTRACT_SCRIPT = `
|
||||
(async function() {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
try {
|
||||
const pot = window.ytInitialPlayerResponse
|
||||
&& window.ytInitialPlayerResponse.serviceIntegrityDimensions
|
||||
&& window.ytInitialPlayerResponse.serviceIntegrityDimensions.poToken
|
||||
if (typeof pot === 'string' && pot.length > 0) return pot
|
||||
} catch (_) {}
|
||||
await new Promise(r => setTimeout(r, 500))
|
||||
}
|
||||
return null
|
||||
})()
|
||||
`
|
||||
|
||||
let mintWindow: BrowserWindow | null = null
|
||||
|
||||
/**
|
||||
* Open a visible BrowserWindow on a YouTube video page and extract the
|
||||
* Proof-of-Origin token from the page runtime. The window uses the shared
|
||||
* login session so a signed-in user's credentials are available.
|
||||
*
|
||||
* Resolves with the token string on success, or null if the window was closed
|
||||
* by the user before extraction completed or if the field was not found.
|
||||
*/
|
||||
export function openPoTokenWindow(): Promise<string | null> {
|
||||
// If a minting window is already open, bring it to the front rather than
|
||||
// opening a second one.
|
||||
if (mintWindow && !mintWindow.isDestroyed()) {
|
||||
mintWindow.focus()
|
||||
// Return a promise that resolves when the existing window closes.
|
||||
return new Promise((resolve) => {
|
||||
mintWindow!.once('closed', () => resolve(null))
|
||||
})
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
let resolved = false
|
||||
const finish = (token: string | null): void => {
|
||||
if (resolved) return
|
||||
resolved = true
|
||||
resolve(token)
|
||||
}
|
||||
|
||||
let win: BrowserWindow
|
||||
try {
|
||||
win = new BrowserWindow({
|
||||
width: 960,
|
||||
height: 640,
|
||||
title: 'AeroFetch — Fetch YouTube token',
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
partition: LOGIN_PARTITION,
|
||||
sandbox: true,
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
finish(null)
|
||||
return
|
||||
}
|
||||
|
||||
mintWindow = win
|
||||
hardenPoTokenWebContents(win.webContents)
|
||||
win.webContents.session.setPermissionRequestHandler((_wc, _perm, cb) => cb(false))
|
||||
|
||||
win.webContents.once('did-finish-load', () => {
|
||||
win.webContents
|
||||
.executeJavaScript(EXTRACT_SCRIPT, true)
|
||||
.then((result: unknown) => {
|
||||
const token = typeof result === 'string' && result.length > 0 ? result : null
|
||||
finish(token)
|
||||
win.close()
|
||||
})
|
||||
.catch(() => {
|
||||
finish(null)
|
||||
win.close()
|
||||
})
|
||||
})
|
||||
|
||||
win.on('closed', () => {
|
||||
mintWindow = null
|
||||
finish(null)
|
||||
})
|
||||
|
||||
win.loadURL(YT_VIDEO).catch(() => {
|
||||
/* navigation errors surface as Chromium's own error page */
|
||||
})
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user