mirror of
https://github.com/stablyai/orca.git
synced 2026-10-01 08:01:56 +00:00
* Split speech session lifecycle * Split terminal output scheduler pipeline * Split mobile browser pane modules * Prune resolved max-lines suppressions * Split pane tree equalization logic * Extract mobile troubleshoot screen styles * Split external automation manager * Split main window service attachments * Split hosted review creation checks * Split automation dispatch event handling * Split settings navigation metadata * Split daemon initialization lifecycle * Split GitLab item dialog * Split relay dispatcher layers * Split mobile host screen * Retarget mobile view settings source test * Split runtime file client layers * Split ports panel layers * Split runtime environments pane layers * Split local PTY provider responsibilities * Split CDP bridge responsibilities * Split relay Git handler responsibilities * Track moved relay Git fetch audit * Split Linear item drawer responsibilities * Split telemetry event schema responsibilities * Split resource usage status responsibilities * Split remote terminal multiplexer responsibilities * Split Git worktree responsibilities * Split Codex hook service responsibilities * Keep mirrored hook trust type private * Fix F3-speech for #17123 * Fix F1-cycle for #17131 * Fix F4-navtest for #17157 * Fix F2-allowlist for #17161
120 lines
4.4 KiB
TypeScript
120 lines
4.4 KiB
TypeScript
import type { HookDefinition } from '../agent-hooks/installer-utils'
|
|
import {
|
|
codexHookSourcePathsEqual,
|
|
computeTrustKey,
|
|
computeTrustedHash,
|
|
normalizeCodexHookSourcePath,
|
|
parseTrustKey,
|
|
readHookTrustEntries,
|
|
type CodexEventLabel,
|
|
type CodexHookTrustState,
|
|
type CodexTrustEntry
|
|
} from './config-toml-trust'
|
|
import { createCodexHookTrustEntry, getCodexHookTrustSignature } from './codex-hook-identity'
|
|
import { getSystemCodexConfigTomlPath } from './codex-hook-definition'
|
|
|
|
export type TrustedSystemHookSignatureState = {
|
|
enabled: boolean
|
|
trustedHash: string
|
|
}
|
|
|
|
export function getTrustedSystemUserHookSignatures(
|
|
systemConfigPath: string,
|
|
systemHooks: Record<string, HookDefinition[]>,
|
|
isManagedCommand: (command: string | undefined) => boolean
|
|
): Map<string, TrustedSystemHookSignatureState> {
|
|
const signatures = new Map<string, TrustedSystemHookSignatureState>()
|
|
let trustEntries: Map<string, CodexHookTrustState>
|
|
try {
|
|
trustEntries = readHookTrustEntries(getSystemCodexConfigTomlPath())
|
|
} catch (error) {
|
|
// Why: a hand-broken system config.toml should only disable user-hook trust mirroring, not block Orca's managed runtime hooks.
|
|
console.warn('[codex-hook-service] failed to read system hook trust entries', error)
|
|
return signatures
|
|
}
|
|
const trustedHashesByEvent = getTrustedSystemHookHashesByEvent(systemConfigPath, trustEntries)
|
|
for (const [eventName, definitions] of Object.entries(systemHooks)) {
|
|
if (!Array.isArray(definitions)) {
|
|
continue
|
|
}
|
|
definitions.forEach((definition, groupIndex) => {
|
|
const hooks = Array.isArray(definition.hooks) ? definition.hooks : []
|
|
hooks.forEach((hook, handlerIndex) => {
|
|
if (isManagedCommand(hook.command)) {
|
|
return
|
|
}
|
|
const entry = createCodexHookTrustEntry(
|
|
systemConfigPath,
|
|
eventName,
|
|
groupIndex,
|
|
handlerIndex,
|
|
definition,
|
|
hook
|
|
)
|
|
if (!entry) {
|
|
return
|
|
}
|
|
const state = resolveTrustedSystemHookState(entry, trustEntries, trustedHashesByEvent)
|
|
if (!state) {
|
|
return
|
|
}
|
|
const signature = getCodexHookTrustSignature(entry)
|
|
// Why: runtime deduping collapses identical definitions; if any duplicate stays enabled, keep the mirrored hook enabled.
|
|
if (state.enabled || !signatures.has(signature)) {
|
|
signatures.set(signature, state)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
return signatures
|
|
}
|
|
|
|
function resolveTrustedSystemHookState(
|
|
entry: CodexTrustEntry,
|
|
trustEntries: ReadonlyMap<string, CodexHookTrustState>,
|
|
trustedHashesByEvent: ReadonlyMap<CodexEventLabel, Map<string, boolean>>
|
|
): TrustedSystemHookSignatureState | null {
|
|
const expectedHash = computeTrustedHash(entry)
|
|
const state = trustEntries.get(computeTrustKey(entry))
|
|
if (state?.trustedHash === expectedHash) {
|
|
return { enabled: state.enabled !== false, trustedHash: expectedHash }
|
|
}
|
|
const reorderedEnabled = trustedHashesByEvent.get(entry.eventLabel)?.get(expectedHash)
|
|
if (reorderedEnabled !== undefined) {
|
|
return { enabled: reorderedEnabled, trustedHash: expectedHash }
|
|
}
|
|
if (state?.trustedHash) {
|
|
// Why: carry a key-matched system hash verbatim — recomputing caused #7110 re-approval loops since Codex owns its hash algorithm.
|
|
return { enabled: state.enabled !== false, trustedHash: state.trustedHash }
|
|
}
|
|
return null
|
|
}
|
|
|
|
function getTrustedSystemHookHashesByEvent(
|
|
systemConfigPath: string,
|
|
trustEntries: ReadonlyMap<string, CodexHookTrustState>
|
|
): Map<CodexEventLabel, Map<string, boolean>> {
|
|
const trustedHashesByEvent = new Map<CodexEventLabel, Map<string, boolean>>()
|
|
const canonicalSystemConfigPath = normalizeCodexHookSourcePath(systemConfigPath)
|
|
for (const [key, state] of trustEntries) {
|
|
const parsed = parseTrustKey(key)
|
|
if (!parsed || !state.trustedHash) {
|
|
continue
|
|
}
|
|
if (!codexHookSourcePathsEqual(parsed.sourcePath, canonicalSystemConfigPath)) {
|
|
continue
|
|
}
|
|
let hashes = trustedHashesByEvent.get(parsed.eventLabel)
|
|
if (!hashes) {
|
|
hashes = new Map()
|
|
trustedHashesByEvent.set(parsed.eventLabel, hashes)
|
|
}
|
|
const enabled = state.enabled !== false
|
|
// Why: Codex trust keys include hook indices, but the hash still proves the same event+command identity was approved after a reorder.
|
|
if (enabled || !hashes.has(state.trustedHash)) {
|
|
hashes.set(state.trustedHash, enabled)
|
|
}
|
|
}
|
|
return trustedHashesByEvent
|
|
}
|