Loading files
be03022d0This file is pinned to the selected release. Download the original to open it in your design tool.
import { randomBytes, timingSafeEqual } from 'node:crypto'
import { closeSync, constants, fchmodSync, fstatSync, fsyncSync, openSync, readFileSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import { secureStateDirectory } from './secureState.js'
const FILE_NAME = 'hook-credential'
const TOKEN_RE = /^[A-Za-z0-9_-]{43}$/
export function hookCredentialPath(dataDir: string): string {
return join(dataDir, FILE_NAME)
}
function secureDataDirectory(dataDir: string): boolean {
try {
secureStateDirectory(dataDir, false)
return true
} catch {
return false
}
}
export function readHookCredential(dataDir: string): string | null {
let fd: number | null = null
try {
if (!secureDataDirectory(dataDir)) return null
const file = hookCredentialPath(dataDir)
fd = openSync(file, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK)
const stat = fstatSync(fd)
const uid = typeof process.getuid === 'function' ? process.getuid() : null
if (!stat.isFile() || stat.size > 128) return null
if (uid !== null && stat.uid !== uid) return null
if ((stat.mode & 0o777) !== 0o600) return null
const value = readFileSync(fd, 'utf8').trim()
return TOKEN_RE.test(value) ? value : null
} catch {
return null
} finally {
if (fd !== null) closeSync(fd)
}
}
export function loadOrCreateHookCredential(dataDir: string): string {
const existing = readHookCredential(dataDir)
if (existing) return existing
try {
secureStateDirectory(dataDir)
} catch {
throw new Error('hook credential directory has unsafe owner, mode, or type')
}
const value = randomBytes(32).toString('base64url')
const file = hookCredentialPath(dataDir)
let fd: number
try {
fd = openSync(file, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600)
} catch (error) {
const raced = readHookCredential(dataDir)
if (raced) return raced
throw error
}
try {
writeFileSync(fd, `${value}\n`)
fchmodSync(fd, 0o600)
fsyncSync(fd)
} finally {
closeSync(fd)
}
const directoryFd = openSync(dataDir, 'r')
try { fsyncSync(directoryFd) } finally { closeSync(directoryFd) }
return value
}
export function hookCredentialMatches(expected: string, supplied: unknown): boolean {
if (typeof supplied !== 'string' || supplied.length !== expected.length) return false
return timingSafeEqual(Buffer.from(expected), Buffer.from(supplied))
}