@@ -5,14 +5,11 @@ import {
55 normalizeStringifiedOptionalString ,
66} from "@openclaw/normalization-core/string-coerce" ;
77import { normalizeUniqueStringEntries } from "@openclaw/normalization-core/string-normalization" ;
8- import { pickSandboxToolPolicy } from "../agents/sandbox-tool- policy.js" ;
8+ import { resolveConfiguredToolPolicies } from "../agents/agent-tools. policy.js" ;
99import { resolveSandboxConfigForAgent } from "../agents/sandbox/config.js" ;
1010import { isDangerousNetworkMode , normalizeNetworkMode } from "../agents/sandbox/network-mode.js" ;
11- import { resolveSandboxToolPolicyForAgent } from "../agents/sandbox/tool-policy.js" ;
12- import type { SandboxToolPolicy } from "../agents/sandbox/types.js" ;
1311import { getBlockedBindReason } from "../agents/sandbox/validate-sandbox-security.js" ;
1412import { isToolAllowedByPolicies } from "../agents/tool-policy-match.js" ;
15- import { resolveToolProfilePolicy } from "../agents/tool-policy.js" ;
1613import { formatCliCommand } from "../cli/command-format.js" ;
1714import type { GatewayAuthConfig } from "../config/types.gateway.js" ;
1815import type { OpenClawConfig } from "../config/types.openclaw.js" ;
@@ -25,6 +22,7 @@ import {
2522 resolveNodeCommandAllowlist ,
2623} from "../gateway/node-command-policy.js" ;
2724import { collectAuditModelRefs } from "./audit-model-refs.js" ;
25+ import { GATEWAY_CONTROL_PLANE_TOOLS } from "./dangerous-tools.js" ;
2826
2927/**
3028 * Synchronous security audit collector functions.
@@ -279,36 +277,6 @@ function listKnownNodeCommands(cfg: OpenClawConfig): Set<string> {
279277 return out ;
280278}
281279
282- function resolveToolPolicies ( params : {
283- cfg : OpenClawConfig ;
284- agentTools ?: AgentToolsConfig ;
285- sandboxMode ?: "off" | "non-main" | "all" ;
286- agentId ?: string | null ;
287- } ) : SandboxToolPolicy [ ] {
288- const policies : SandboxToolPolicy [ ] = [ ] ;
289- const profile = params . agentTools ?. profile ?? params . cfg . tools ?. profile ;
290- const profilePolicy = resolveToolProfilePolicy ( profile ) ;
291- if ( profilePolicy ) {
292- policies . push ( profilePolicy ) ;
293- }
294-
295- const globalPolicy = pickSandboxToolPolicy ( params . cfg . tools ?? undefined ) ;
296- if ( globalPolicy ) {
297- policies . push ( globalPolicy ) ;
298- }
299-
300- const agentPolicy = pickSandboxToolPolicy ( params . agentTools ) ;
301- if ( agentPolicy ) {
302- policies . push ( agentPolicy ) ;
303- }
304-
305- if ( params . sandboxMode === "all" ) {
306- policies . push ( resolveSandboxToolPolicyForAgent ( params . cfg , params . agentId ?? undefined ) ) ;
307- }
308-
309- return policies ;
310- }
311-
312280function looksLikeNodeCommandPattern ( value : string ) : boolean {
313281 if ( ! value ) {
314282 return false ;
@@ -500,15 +468,14 @@ function listPotentialMultiUserSignals(cfg: OpenClawConfig): string[] {
500468 return Array . from ( out ) ;
501469}
502470
503- function collectRiskyToolExposureContexts ( cfg : OpenClawConfig ) : {
504- riskyContexts : string [ ] ;
505- hasRuntimeRisk : boolean ;
506- } {
507- const contexts : Array < {
508- label : string ;
509- agentId ?: string ;
510- tools ?: AgentToolsConfig ;
511- } > = [ { label : "agents.defaults" } ] ;
471+ type AuditAgentToolContext = {
472+ label : string ;
473+ agentId ?: string ;
474+ tools ?: AgentToolsConfig ;
475+ } ;
476+
477+ function listAuditAgentToolContexts ( cfg : OpenClawConfig ) : AuditAgentToolContext [ ] {
478+ const contexts : AuditAgentToolContext [ ] = [ { label : "agents.defaults" } ] ;
512479 for ( const agent of cfg . agents ?. list ?? [ ] ) {
513480 if ( ! agent || typeof agent !== "object" || typeof agent . id !== "string" ) {
514481 continue ;
@@ -519,12 +486,18 @@ function collectRiskyToolExposureContexts(cfg: OpenClawConfig): {
519486 tools : agent . tools ,
520487 } ) ;
521488 }
489+ return contexts ;
490+ }
522491
492+ function collectRiskyToolExposureContexts ( cfg : OpenClawConfig ) : {
493+ riskyContexts : string [ ] ;
494+ hasRuntimeRisk : boolean ;
495+ } {
523496 const riskyContexts : string [ ] = [ ] ;
524497 let hasRuntimeRisk = false ;
525- for ( const context of contexts ) {
498+ for ( const context of listAuditAgentToolContexts ( cfg ) ) {
526499 const sandboxMode = resolveSandboxConfigForAgent ( cfg , context . agentId ) . mode ;
527- const policies = resolveToolPolicies ( {
500+ const policies = resolveConfiguredToolPolicies ( {
528501 cfg,
529502 agentTools : context . tools ,
530503 sandboxMode,
@@ -555,6 +528,30 @@ function collectRiskyToolExposureContexts(cfg: OpenClawConfig): {
555528 return { riskyContexts, hasRuntimeRisk } ;
556529}
557530
531+ function collectControlPlaneToolExposureContexts ( cfg : OpenClawConfig ) : string [ ] {
532+ const exposedContexts : string [ ] = [ ] ;
533+ for ( const context of listAuditAgentToolContexts ( cfg ) ) {
534+ const sandboxMode = resolveSandboxConfigForAgent ( cfg , context . agentId ) . mode ;
535+ const policies = resolveConfiguredToolPolicies ( {
536+ cfg,
537+ agentTools : context . tools ,
538+ sandboxMode,
539+ agentId : context . agentId ?? null ,
540+ } ) ;
541+ const controlPlaneTools = GATEWAY_CONTROL_PLANE_TOOLS . filter ( ( tool ) =>
542+ isToolAllowedByPolicies ( tool , policies ) ,
543+ ) ;
544+ if ( controlPlaneTools . length === 0 ) {
545+ continue ;
546+ }
547+ const profile = context . tools ?. profile ?? cfg . tools ?. profile ?? "none" ;
548+ exposedContexts . push (
549+ `${ context . label } (profile=${ profile } ; controlPlane=[${ controlPlaneTools . join ( ", " ) } ])` ,
550+ ) ;
551+ }
552+ return exposedContexts ;
553+ }
554+
558555// --------------------------------------------------------------------------
559556// Exported collectors
560557// --------------------------------------------------------------------------
@@ -1208,6 +1205,21 @@ export function collectExposureMatrixFindings(cfg: OpenClawConfig): SecurityAudi
12081205 } ) ;
12091206 }
12101207
1208+ const controlPlaneContexts = collectControlPlaneToolExposureContexts ( cfg ) ;
1209+ if ( controlPlaneContexts . length > 0 ) {
1210+ findings . push ( {
1211+ checkId : "security.exposure.open_groups_with_control_plane_tools" ,
1212+ severity : "critical" ,
1213+ title : "Open group/DM policy with gateway/cron control-plane tools exposed" ,
1214+ detail :
1215+ `Found inbound policy="open" at:\n${ openInboundPolicies . map ( ( p ) => `- ${ p } ` ) . join ( "\n" ) } \n` +
1216+ `Control-plane tool exposure contexts:\n${ controlPlaneContexts . map ( ( line ) => `- ${ line } ` ) . join ( "\n" ) } \n` +
1217+ "Prompt injection in open conversations can trigger persistent gateway config changes or scheduled automation." ,
1218+ remediation :
1219+ 'For open groups or DMs, deny control-plane tools (`gateway`, `cron`) and prefer tools.profile="messaging". Tighten dmPolicy/groupPolicy to pairing or allowlist when possible.' ,
1220+ } ) ;
1221+ }
1222+
12111223 return findings ;
12121224}
12131225
0 commit comments