mirror of
https://github.com/stablyai/orca.git
synced 2026-10-07 16:02:29 +00:00
148 lines
4.0 KiB
TypeScript
148 lines
4.0 KiB
TypeScript
const CHROMIUM_EPOCH_OFFSET = 11644473600n
|
|
|
|
export function chromiumTimestampToUnix(chromiumTs: bigint | number | string): number {
|
|
if (!chromiumTs || chromiumTs === 0n || chromiumTs === 0 || chromiumTs === '0') {
|
|
return 0
|
|
}
|
|
try {
|
|
const ts =
|
|
typeof chromiumTs === 'bigint'
|
|
? chromiumTs
|
|
: BigInt(typeof chromiumTs === 'number' ? Math.round(chromiumTs) : chromiumTs)
|
|
if (ts === 0n) {
|
|
return 0
|
|
}
|
|
return Math.max(Number(ts / 1000000n - CHROMIUM_EPOCH_OFFSET), 0)
|
|
} catch {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// Why: each platform protects the Chromium key differently: macOS/Linux PBKDF2→AES-128-CBC, Windows DPAPI→AES-256-GCM.
|
|
|
|
export type EncryptionKeyResult =
|
|
| {
|
|
mode: 'aes-128-cbc'
|
|
keysByVersion: Partial<Record<'v10' | 'v11', Buffer>>
|
|
keyringUnavailable?: boolean
|
|
}
|
|
| { mode: 'aes-256-gcm'; key: Buffer }
|
|
|
|
export type ChromiumCookieColumnInfo = {
|
|
name: string
|
|
type?: string
|
|
notnull?: number | bigint
|
|
dflt_value?: unknown
|
|
}
|
|
|
|
export function parseSqliteDefaultValue(
|
|
raw: unknown,
|
|
type: string
|
|
): string | number | Buffer | null {
|
|
if (raw === null || raw === undefined) {
|
|
return null
|
|
}
|
|
if (typeof raw !== 'string') {
|
|
return typeof raw === 'number' || typeof raw === 'bigint' ? Number(raw) : String(raw)
|
|
}
|
|
|
|
const trimmed = raw.trim()
|
|
if (!trimmed || trimmed.toUpperCase() === 'NULL') {
|
|
return null
|
|
}
|
|
if (/^X''$/i.test(trimmed) || type.includes('BLOB')) {
|
|
return Buffer.alloc(0)
|
|
}
|
|
if (
|
|
(trimmed.startsWith("'") && trimmed.endsWith("'")) ||
|
|
(trimmed.startsWith('"') && trimmed.endsWith('"'))
|
|
) {
|
|
return trimmed.slice(1, -1).replaceAll("''", "'")
|
|
}
|
|
if (type.includes('INT')) {
|
|
const numeric = Number(trimmed)
|
|
return Number.isFinite(numeric) ? numeric : 0
|
|
}
|
|
return trimmed
|
|
}
|
|
|
|
export function normalizeSqliteCookieValue(
|
|
value: unknown
|
|
): string | number | bigint | Buffer | null {
|
|
if (value instanceof Uint8Array) {
|
|
return Buffer.from(value)
|
|
}
|
|
if (value === undefined || value === null) {
|
|
return null
|
|
}
|
|
if (typeof value === 'number' || typeof value === 'bigint' || typeof value === 'string') {
|
|
return value
|
|
}
|
|
return String(value)
|
|
}
|
|
|
|
export function isSqliteNotNull(column: ChromiumCookieColumnInfo): boolean {
|
|
return Number(column.notnull ?? 0) !== 0
|
|
}
|
|
|
|
export function fallbackChromiumCookieColumnValue(
|
|
column: ChromiumCookieColumnInfo,
|
|
sourceRow: Record<string, unknown>
|
|
): string | number | bigint | Buffer | null {
|
|
const type = (column.type ?? '').toUpperCase()
|
|
const defaultValue = parseSqliteDefaultValue(column.dflt_value, type)
|
|
if (defaultValue !== null) {
|
|
return defaultValue
|
|
}
|
|
if (!isSqliteNotNull(column)) {
|
|
return null
|
|
}
|
|
|
|
switch (column.name) {
|
|
case 'value':
|
|
case 'encrypted_value':
|
|
return Buffer.alloc(0)
|
|
case 'top_frame_site_key':
|
|
return ''
|
|
case 'source_port':
|
|
return -1
|
|
case 'last_update_utc':
|
|
return normalizeSqliteCookieValue(sourceRow.creation_utc) ?? 0
|
|
default:
|
|
if (type.includes('BLOB')) {
|
|
return Buffer.alloc(0)
|
|
}
|
|
if (type.includes('INT')) {
|
|
return 0
|
|
}
|
|
return ''
|
|
}
|
|
}
|
|
|
|
export function buildChromiumCookieInsertParams(
|
|
targetColumns: ChromiumCookieColumnInfo[],
|
|
sourceRow: Record<string, unknown>,
|
|
decryptedValue: Buffer
|
|
): (string | number | bigint | Buffer | null)[] {
|
|
return targetColumns.map((column) => {
|
|
if (column.name === 'encrypted_value') {
|
|
return Buffer.alloc(0)
|
|
}
|
|
if (column.name === 'value') {
|
|
return decryptedValue
|
|
}
|
|
|
|
const sourceHasColumn = Object.hasOwn(sourceRow, column.name)
|
|
const sourceValue = sourceHasColumn ? normalizeSqliteCookieValue(sourceRow[column.name]) : null
|
|
if (sourceValue !== null) {
|
|
return sourceValue
|
|
}
|
|
if (sourceHasColumn && !isSqliteNotNull(column)) {
|
|
return null
|
|
}
|
|
|
|
// Why: cookie columns drift across Chrome/Electron versions; missing NOT NULL columns need Chromium defaults, not NULL.
|
|
return fallbackChromiumCookieColumnValue(column, sourceRow)
|
|
})
|
|
}
|