From c7137270d170d10a9e27f3e5a6f6979dd24b0223 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 15 Mar 2026 23:30:04 -0700 Subject: [PATCH] Security: split audit runtime surfaces --- src/security/audit-channel.collect.runtime.ts | 1 + src/security/audit-channel.runtime.ts | 8 +- src/security/audit.deep.runtime.ts | 4 + src/security/audit.nondeep.runtime.ts | 26 +++++ src/security/audit.ts | 94 +++++++++---------- 5 files changed, 81 insertions(+), 52 deletions(-) create mode 100644 src/security/audit-channel.collect.runtime.ts create mode 100644 src/security/audit.deep.runtime.ts create mode 100644 src/security/audit.nondeep.runtime.ts diff --git a/src/security/audit-channel.collect.runtime.ts b/src/security/audit-channel.collect.runtime.ts new file mode 100644 index 00000000000..6a33ff6a93a --- /dev/null +++ b/src/security/audit-channel.collect.runtime.ts @@ -0,0 +1 @@ +export { collectChannelSecurityFindings } from "./audit-channel.js"; diff --git a/src/security/audit-channel.runtime.ts b/src/security/audit-channel.runtime.ts index 147f686862a..71fa1cbea6c 100644 --- a/src/security/audit-channel.runtime.ts +++ b/src/security/audit-channel.runtime.ts @@ -1,9 +1,9 @@ -export { - isNumericTelegramUserId, - normalizeTelegramAllowFromEntry, -} from "../../extensions/telegram/src/allow-from.js"; export { readChannelAllowFromStore } from "../pairing/pairing-store.js"; export { isDiscordMutableAllowEntry, isZalouserMutableGroupEntry, } from "./mutable-allowlist-detectors.js"; +export { + isNumericTelegramUserId, + normalizeTelegramAllowFromEntry, +} from "../../extensions/telegram/src/allow-from.js"; diff --git a/src/security/audit.deep.runtime.ts b/src/security/audit.deep.runtime.ts new file mode 100644 index 00000000000..25662225d9c --- /dev/null +++ b/src/security/audit.deep.runtime.ts @@ -0,0 +1,4 @@ +export { + collectInstalledSkillsCodeSafetyFindings, + collectPluginsCodeSafetyFindings, +} from "./audit-extra.async.js"; diff --git a/src/security/audit.nondeep.runtime.ts b/src/security/audit.nondeep.runtime.ts new file mode 100644 index 00000000000..5a962bf8386 --- /dev/null +++ b/src/security/audit.nondeep.runtime.ts @@ -0,0 +1,26 @@ +export { + collectAttackSurfaceSummaryFindings, + collectExposureMatrixFindings, + collectGatewayHttpNoAuthFindings, + collectGatewayHttpSessionKeyOverrideFindings, + collectHooksHardeningFindings, + collectLikelyMultiUserSetupFindings, + collectMinimalProfileOverrideFindings, + collectModelHygieneFindings, + collectNodeDangerousAllowCommandFindings, + collectNodeDenyCommandPatternFindings, + collectSandboxDangerousConfigFindings, + collectSandboxDockerNoopFindings, + collectSecretsInConfigFindings, + collectSmallModelRiskFindings, + collectSyncedFolderFindings, +} from "./audit-extra.sync.js"; + +export { + collectSandboxBrowserHashLabelFindings, + collectIncludeFilePermFindings, + collectPluginsTrustFindings, + collectStateDeepFilesystemFindings, + collectWorkspaceSkillSymlinkEscapeFindings, + readConfigSnapshotForAudit, +} from "./audit-extra.async.js"; diff --git a/src/security/audit.ts b/src/security/audit.ts index 0b13ecc5531..4e3ef0a6920 100644 --- a/src/security/audit.ts +++ b/src/security/audit.ts @@ -21,32 +21,6 @@ import { } from "../infra/exec-safe-bin-runtime-policy.js"; import { normalizeTrustedSafeBinDirs } from "../infra/exec-safe-bin-trust.js"; import { isBlockedHostnameOrIp, isPrivateNetworkAllowedByPolicy } from "../infra/net/ssrf.js"; -import { collectChannelSecurityFindings } from "./audit-channel.js"; -import { - collectAttackSurfaceSummaryFindings, - collectExposureMatrixFindings, - collectGatewayHttpNoAuthFindings, - collectGatewayHttpSessionKeyOverrideFindings, - collectHooksHardeningFindings, - collectIncludeFilePermFindings, - collectInstalledSkillsCodeSafetyFindings, - collectLikelyMultiUserSetupFindings, - collectSandboxBrowserHashLabelFindings, - collectMinimalProfileOverrideFindings, - collectModelHygieneFindings, - collectNodeDangerousAllowCommandFindings, - collectNodeDenyCommandPatternFindings, - collectSmallModelRiskFindings, - collectSandboxDangerousConfigFindings, - collectSandboxDockerNoopFindings, - collectPluginsTrustFindings, - collectSecretsInConfigFindings, - collectPluginsCodeSafetyFindings, - collectStateDeepFilesystemFindings, - collectSyncedFolderFindings, - collectWorkspaceSkillSymlinkEscapeFindings, - readConfigSnapshotForAudit, -} from "./audit-extra.js"; import { formatPermissionDetail, formatPermissionRemediation, @@ -138,12 +112,32 @@ type AuditExecutionContext = { }; let channelPluginsModulePromise: Promise | undefined; +let auditNonDeepModulePromise: Promise | undefined; +let auditDeepModulePromise: Promise | undefined; +let auditChannelModulePromise: + | Promise + | undefined; async function loadChannelPlugins() { channelPluginsModulePromise ??= import("../channels/plugins/index.js"); return await channelPluginsModulePromise; } +async function loadAuditNonDeepModule() { + auditNonDeepModulePromise ??= import("./audit.nondeep.runtime.js"); + return await auditNonDeepModulePromise; +} + +async function loadAuditDeepModule() { + auditDeepModulePromise ??= import("./audit.deep.runtime.js"); + return await auditDeepModulePromise; +} + +async function loadAuditChannelModule() { + auditChannelModulePromise ??= import("./audit-channel.collect.runtime.js"); + return await auditChannelModulePromise; +} + function countBySeverity(findings: SecurityAuditFinding[]): SecurityAuditSummary { let critical = 0; let warn = 0; @@ -1144,6 +1138,7 @@ async function createAuditExecutionContext( const deepTimeoutMs = Math.max(250, opts.deepTimeoutMs ?? 5000); const stateDir = opts.stateDir ?? resolveStateDir(env); const configPath = opts.configPath ?? resolveConfigPath(env, stateDir); + const { readConfigSnapshotForAudit } = await loadAuditNonDeepModule(); const configSnapshot = includeFilesystem ? opts.configSnapshot !== undefined ? opts.configSnapshot @@ -1174,28 +1169,29 @@ export async function runSecurityAudit(opts: SecurityAuditOptions): Promise