@poolzin/pool-bot 2026.2.24 → 2026.2.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/acp/client.js +207 -18
- package/dist/acp/event-mapper.js +87 -22
- package/dist/acp/meta.js +12 -6
- package/dist/acp/secret-file.js +22 -0
- package/dist/agents/agent-paths.js +8 -9
- package/dist/agents/agent-scope.js +17 -5
- package/dist/agents/auth-profiles/oauth.js +148 -64
- package/dist/agents/auth-profiles/session-override.js +13 -7
- package/dist/agents/bash-process-registry.test-helpers.js +29 -0
- package/dist/agents/bash-tools.exec-approval-request.js +20 -0
- package/dist/agents/bash-tools.exec-host-gateway.js +240 -0
- package/dist/agents/bash-tools.exec-host-node.js +235 -0
- package/dist/agents/bash-tools.exec-runtime.js +2 -25
- package/dist/agents/bash-tools.exec-types.js +1 -0
- package/dist/agents/bash-tools.process.js +224 -218
- package/dist/agents/bedrock-discovery.js +3 -1
- package/dist/agents/byteplus-models.js +97 -0
- package/dist/agents/chutes-oauth.js +1 -0
- package/dist/agents/cli-runner/helpers.js +4 -0
- package/dist/agents/compaction.js +41 -14
- package/dist/agents/content-blocks.js +16 -0
- package/dist/agents/doubao-models.js +121 -0
- package/dist/agents/failover-error.js +2 -0
- package/dist/agents/huggingface-models.js +5 -3
- package/dist/agents/live-model-filter.js +5 -0
- package/dist/agents/minimax-vlm.js +10 -8
- package/dist/agents/model-auth.js +6 -0
- package/dist/agents/model-catalog.js +3 -1
- package/dist/agents/model-fallback.js +96 -101
- package/dist/agents/model-selection.js +7 -1
- package/dist/agents/models-config.providers.js +364 -165
- package/dist/agents/ollama-stream.js +117 -4
- package/dist/agents/opencode-zen-models.js +22 -11
- package/dist/agents/pi-embedded-helpers/errors.js +55 -33
- package/dist/agents/pi-embedded-helpers/messaging-dedupe.js +10 -5
- package/dist/agents/pi-embedded-helpers/thinking.js +10 -5
- package/dist/agents/pi-embedded-helpers.js +1 -1
- package/dist/agents/pi-embedded-payloads.js +1 -0
- package/dist/agents/pi-embedded-runner/compact.js +29 -7
- package/dist/agents/pi-embedded-runner/extensions.js +28 -26
- package/dist/agents/pi-embedded-runner/google.js +20 -8
- package/dist/agents/pi-embedded-runner/run/attempt.js +95 -36
- package/dist/agents/pi-embedded-runner/run.js +71 -12
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +11 -2
- package/dist/agents/pi-embedded-runner/session-manager-cache.js +11 -7
- package/dist/agents/pi-embedded-runner/system-prompt.js +2 -0
- package/dist/agents/pi-embedded-runner/thinking.js +42 -0
- package/dist/agents/pi-embedded-runner/tool-name-allowlist.js +19 -0
- package/dist/agents/pi-embedded-runner/utils.js +7 -10
- package/dist/agents/pi-embedded-subscribe.handlers.lifecycle.js +45 -56
- package/dist/agents/pi-embedded-subscribe.handlers.tools.js +2 -2
- package/dist/agents/pi-embedded-subscribe.js +9 -4
- package/dist/agents/pi-embedded-subscribe.tools.js +68 -14
- package/dist/agents/pi-embedded-utils.js +3 -0
- package/dist/agents/pi-extensions/compaction-safeguard-runtime.js +4 -20
- package/dist/agents/pi-extensions/compaction-safeguard.js +75 -33
- package/dist/agents/pi-settings.js +40 -0
- package/dist/agents/pi-tools.policy.js +2 -1
- package/dist/agents/provider/config-loader.js +1 -1
- package/dist/agents/sandbox/browser.js +170 -33
- package/dist/agents/sandbox/config-hash.js +14 -27
- package/dist/agents/sandbox/config.js +21 -2
- package/dist/agents/sandbox/constants.js +2 -0
- package/dist/agents/sandbox/docker.js +16 -2
- package/dist/agents/sandbox/novnc-auth.js +62 -0
- package/dist/agents/sandbox/sanitize-env-vars.js +1 -1
- package/dist/agents/sandbox/shared.js +10 -6
- package/dist/agents/sandbox-paths.js +24 -11
- package/dist/agents/schema/clean-for-gemini.js +132 -85
- package/dist/agents/session-slug.js +10 -5
- package/dist/agents/session-tool-result-guard-wrapper.js +1 -0
- package/dist/agents/session-tool-result-guard.js +3 -1
- package/dist/agents/session-transcript-repair.js +40 -6
- package/dist/agents/skills/bundled-dir.js +19 -5
- package/dist/agents/skills/env-overrides.js +124 -43
- package/dist/agents/skills/frontmatter.js +6 -6
- package/dist/agents/skills/plugin-skills.js +14 -7
- package/dist/agents/skills/workspace.js +1 -0
- package/dist/agents/skills.test-helpers.js +13 -0
- package/dist/agents/stable-stringify.js +12 -0
- package/dist/agents/subagent-announce.js +251 -49
- package/dist/agents/subagent-lifecycle-events.js +19 -0
- package/dist/agents/subagent-registry-cleanup.js +31 -0
- package/dist/agents/subagent-registry-completion.js +68 -0
- package/dist/agents/subagent-registry-queries.js +117 -0
- package/dist/agents/subagent-registry-state.js +46 -0
- package/dist/agents/subagent-registry.js +252 -221
- package/dist/agents/subagent-registry.mocks.shared.js +12 -0
- package/dist/agents/subagent-registry.store.js +1 -0
- package/dist/agents/subagent-registry.types.js +1 -0
- package/dist/agents/subagent-spawn.js +195 -7
- package/dist/agents/system-prompt.js +22 -6
- package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
- package/dist/agents/test-helpers/fast-coding-tools.js +1 -18
- package/dist/agents/test-helpers/fast-core-tools.js +1 -17
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
- package/dist/agents/timeout.js +18 -6
- package/dist/agents/tool-call-id.js +1 -1
- package/dist/agents/tool-display-common.js +162 -29
- package/dist/agents/tool-images.js +82 -9
- package/dist/agents/tool-policy-shared.js +108 -0
- package/dist/agents/tool-policy.js +51 -26
- package/dist/agents/tools/browser-tool.js +160 -54
- package/dist/agents/tools/canvas-tool.js +27 -1
- package/dist/agents/tools/common.js +45 -0
- package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
- package/dist/agents/tools/discord-actions-guild.js +4 -1
- package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
- package/dist/agents/tools/gateway-tool.js +3 -1
- package/dist/agents/tools/image-tool.js +214 -99
- package/dist/agents/tools/nodes-utils.js +1 -10
- package/dist/agents/tools/sessions-history-tool.js +140 -108
- package/dist/agents/tools/sessions-send-helpers.js +12 -6
- package/dist/agents/tools/sessions-spawn-tool.js +8 -2
- package/dist/agents/tools/subagents-tool.js +2 -1
- package/dist/agents/tools/whatsapp-actions.js +10 -2
- package/dist/agents/tools/whatsapp-target-auth.js +18 -0
- package/dist/agents/transcript-policy.js +22 -8
- package/dist/agents/venice-models.js +11 -3
- package/dist/agents/workspace.js +222 -46
- package/dist/auto-reply/commands-registry.data.js +51 -0
- package/dist/auto-reply/commands-registry.js +19 -21
- package/dist/auto-reply/fallback-state.js +114 -0
- package/dist/auto-reply/group-activation.js +10 -5
- package/dist/auto-reply/inbound-debounce.js +10 -5
- package/dist/auto-reply/model-runtime.js +68 -0
- package/dist/auto-reply/reply/abort.js +1 -1
- package/dist/auto-reply/reply/agent-runner-execution.js +40 -5
- package/dist/auto-reply/reply/agent-runner.js +165 -39
- package/dist/auto-reply/reply/bash-command.js +41 -39
- package/dist/auto-reply/reply/command-gates.js +25 -0
- package/dist/auto-reply/reply/commands-allowlist.js +111 -72
- package/dist/auto-reply/reply/commands-bash.js +6 -5
- package/dist/auto-reply/reply/commands-config.js +30 -28
- package/dist/auto-reply/reply/commands-core.js +2 -1
- package/dist/auto-reply/reply/commands-info.js +1 -0
- package/dist/auto-reply/reply/commands-models.js +65 -14
- package/dist/auto-reply/reply/commands-session.js +237 -82
- package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/auto-reply/reply/commands-setunset.js +45 -0
- package/dist/auto-reply/reply/commands-subagents/action-agents.js +44 -0
- package/dist/auto-reply/reply/commands-subagents/action-focus.js +64 -0
- package/dist/auto-reply/reply/commands-subagents/action-help.js +4 -0
- package/dist/auto-reply/reply/commands-subagents/action-info.js +45 -0
- package/dist/auto-reply/reply/commands-subagents/action-kill.js +60 -0
- package/dist/auto-reply/reply/commands-subagents/action-list.js +44 -0
- package/dist/auto-reply/reply/commands-subagents/action-log.js +29 -0
- package/dist/auto-reply/reply/commands-subagents/action-send.js +119 -0
- package/dist/auto-reply/reply/commands-subagents/action-spawn.js +52 -0
- package/dist/auto-reply/reply/commands-subagents/action-unfocus.js +30 -0
- package/dist/auto-reply/reply/commands-subagents/shared.js +303 -0
- package/dist/auto-reply/reply/commands-subagents.js +51 -587
- package/dist/auto-reply/reply/commands-tts.js +10 -5
- package/dist/auto-reply/reply/config-value.js +10 -5
- package/dist/auto-reply/reply/directive-handling.model-picker.js +12 -6
- package/dist/auto-reply/reply/directive-handling.persist.js +9 -21
- package/dist/auto-reply/reply/directive-handling.shared.js +24 -4
- package/dist/auto-reply/reply/followup-runner.js +1 -0
- package/dist/auto-reply/reply/get-reply-directives-utils.js +23 -14
- package/dist/auto-reply/reply/get-reply-directives.js +17 -28
- package/dist/auto-reply/reply/get-reply-inline-actions.js +1 -0
- package/dist/auto-reply/reply/get-reply.js +71 -12
- package/dist/auto-reply/reply/model-selection.js +80 -39
- package/dist/auto-reply/reply/queue/enqueue.js +10 -5
- package/dist/auto-reply/reply/queue/state.js +13 -12
- package/dist/auto-reply/reply/reply-payloads.js +67 -36
- package/dist/auto-reply/reply/reply-reference.js +9 -8
- package/dist/auto-reply/reply/route-reply.js +15 -8
- package/dist/auto-reply/reply/session-reset-prompt.js +1 -1
- package/dist/auto-reply/reply/session.js +22 -6
- package/dist/auto-reply/reply/strip-inbound-meta.js +147 -0
- package/dist/auto-reply/reply/subagents-utils.js +56 -30
- package/dist/auto-reply/reply/typing.js +46 -21
- package/dist/auto-reply/send-policy.js +14 -7
- package/dist/auto-reply/status.js +140 -16
- package/dist/auto-reply/templating.js +10 -5
- package/dist/auto-reply/thinking.js +7 -16
- package/dist/auto-reply/tokens.js +21 -5
- package/dist/browser/bridge-server.js +36 -20
- package/dist/browser/cdp.helpers.js +7 -14
- package/dist/browser/cdp.js +35 -15
- package/dist/browser/chrome.profile-decoration.js +7 -4
- package/dist/browser/config.js +30 -0
- package/dist/browser/extension-relay-auth.js +55 -0
- package/dist/browser/extension-relay.js +74 -29
- package/dist/browser/navigation-guard.js +39 -0
- package/dist/browser/paths.js +77 -0
- package/dist/browser/profiles.js +13 -8
- package/dist/browser/pw-ai-module.js +10 -5
- package/dist/browser/pw-session.js +76 -39
- package/dist/browser/pw-tools-core.interactions.js +14 -7
- package/dist/browser/pw-tools-core.state.js +12 -6
- package/dist/browser/routes/agent.act.js +431 -424
- package/dist/browser/routes/agent.shared.js +47 -3
- package/dist/browser/routes/agent.snapshot.js +122 -116
- package/dist/browser/routes/agent.storage.js +303 -297
- package/dist/browser/routes/tabs.js +154 -100
- package/dist/browser/server-context.js +7 -0
- package/dist/browser/server-lifecycle.js +37 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/allow-from.js +26 -0
- package/dist/channels/allowlists/resolve-utils.js +43 -19
- package/dist/channels/channel-config.js +14 -7
- package/dist/channels/draft-stream-loop.js +7 -0
- package/dist/channels/model-overrides.js +82 -0
- package/dist/channels/plugins/account-action-gate.js +13 -0
- package/dist/channels/plugins/message-actions.js +10 -0
- package/dist/channels/plugins/normalize/imessage.js +14 -7
- package/dist/channels/plugins/normalize/slack.js +10 -5
- package/dist/channels/plugins/normalize/telegram.js +14 -7
- package/dist/channels/plugins/outbound/discord.js +80 -8
- package/dist/channels/plugins/outbound/signal.js +11 -11
- package/dist/channels/plugins/setup-helpers.js +10 -5
- package/dist/channels/sender-label.js +14 -7
- package/dist/channels/session.js +4 -2
- package/dist/channels/status-reactions.js +297 -0
- package/dist/channels/telegram/api.js +18 -0
- package/dist/cli/argv.js +84 -21
- package/dist/cli/banner.js +3 -2
- package/dist/cli/browser-cli-actions-input/register.files-downloads.js +65 -56
- package/dist/cli/cli-name.js +11 -11
- package/dist/cli/cli-utils.js +13 -3
- package/dist/cli/command-format.js +1 -1
- package/dist/cli/config-cli.js +1 -1
- package/dist/cli/daemon-cli/lifecycle-core.js +31 -19
- package/dist/cli/daemon-cli/lifecycle.js +64 -2
- package/dist/cli/daemon-cli/restart-health.js +126 -0
- package/dist/cli/daemon-cli/status.gather.js +9 -13
- package/dist/cli/daemon-cli/status.print.js +2 -10
- package/dist/cli/deps.js +27 -22
- package/dist/cli/exec-approvals-cli.js +92 -124
- package/dist/cli/gateway-cli/run-loop.js +23 -5
- package/dist/cli/memory-cli.js +158 -61
- package/dist/cli/node-cli/register.js +14 -5
- package/dist/cli/nodes-cli/register.push.js +63 -0
- package/dist/cli/nodes-media-utils.js +26 -0
- package/dist/cli/outbound-send-deps.js +2 -9
- package/dist/cli/outbound-send-mapping.js +11 -0
- package/dist/cli/pairing-cli.js +40 -14
- package/dist/cli/plugins-cli.js +250 -73
- package/dist/cli/ports.js +11 -10
- package/dist/cli/program/build-program.js +3 -1
- package/dist/cli/program/command-registry.js +214 -136
- package/dist/cli/program/command-tree.js +16 -0
- package/dist/cli/program/help.js +43 -12
- package/dist/cli/program/preaction.js +13 -9
- package/dist/cli/program/register.configure.js +3 -18
- package/dist/cli/program/register.maintenance.js +2 -2
- package/dist/cli/program/register.onboard.js +2 -0
- package/dist/cli/program/register.status-health-sessions.js +16 -17
- package/dist/cli/program/register.subclis.js +93 -52
- package/dist/cli/route.js +12 -8
- package/dist/cli/system-cli.js +36 -46
- package/dist/cli/test-runtime-capture.js +24 -0
- package/dist/cli/update-cli/shared.js +22 -9
- package/dist/cli/update-cli/update-command.js +89 -14
- package/dist/cli/update-cli/wizard.js +6 -12
- package/dist/commands/agent/run-context.js +18 -5
- package/dist/commands/agent/session-store.js +17 -4
- package/dist/commands/agent.js +185 -89
- package/dist/commands/agents.bindings.js +14 -7
- package/dist/commands/agents.commands.add.js +13 -9
- package/dist/commands/agents.commands.identity.js +12 -6
- package/dist/commands/agents.commands.list.js +11 -6
- package/dist/commands/agents.config.js +8 -10
- package/dist/commands/agents.providers.js +12 -6
- package/dist/commands/auth-choice-options.js +103 -75
- package/dist/commands/auth-choice.apply.byteplus.js +55 -0
- package/dist/commands/auth-choice.apply.js +4 -0
- package/dist/commands/auth-choice.apply.minimax.js +61 -13
- package/dist/commands/auth-choice.apply.openai.js +3 -1
- package/dist/commands/auth-choice.apply.volcengine.js +55 -0
- package/dist/commands/auth-choice.preferred-provider.js +2 -0
- package/dist/commands/channels/remove.js +13 -6
- package/dist/commands/channels/shared.js +4 -14
- package/dist/commands/channels.mock-harness.js +23 -0
- package/dist/commands/configure.commands.js +14 -0
- package/dist/commands/configure.gateway.js +2 -4
- package/dist/commands/configure.js +1 -1
- package/dist/commands/configure.shared.js +11 -0
- package/dist/commands/daemon-install-helpers.js +2 -2
- package/dist/commands/daemon-install-runtime-warning.js +11 -0
- package/dist/commands/dashboard.js +12 -10
- package/dist/commands/docs.js +14 -8
- package/dist/commands/doctor-config-flow.js +11 -9
- package/dist/commands/doctor-legacy-config.js +281 -0
- package/dist/commands/doctor-state-integrity.js +99 -23
- package/dist/commands/doctor-update.js +12 -9
- package/dist/commands/models/list.list-command.js +7 -5
- package/dist/commands/models/set-image.js +2 -21
- package/dist/commands/node-daemon-install-helpers.js +10 -8
- package/dist/commands/onboard-auth.config-minimax.js +54 -80
- package/dist/commands/onboard-auth.config-opencode.js +2 -18
- package/dist/commands/onboard-auth.credentials.js +90 -13
- package/dist/commands/onboard-auth.js +1 -1
- package/dist/commands/onboard-auth.models.js +6 -5
- package/dist/commands/onboard-hooks.js +1 -1
- package/dist/commands/onboard-non-interactive/api-keys.js +14 -7
- package/dist/commands/onboard-non-interactive/local/auth-choice.js +64 -49
- package/dist/commands/onboard-provider-auth-flags.js +14 -0
- package/dist/commands/onboard-remote.js +14 -7
- package/dist/commands/onboard.js +11 -13
- package/dist/commands/sandbox-display.js +6 -5
- package/dist/commands/sessions.test-helpers.js +61 -0
- package/dist/commands/status-all/diagnosis.js +14 -10
- package/dist/commands/status-all/format.js +1 -0
- package/dist/commands/status.gateway-probe.js +1 -16
- package/dist/commands/systemd-linger.js +12 -6
- package/dist/config/agent-limits.js +2 -0
- package/dist/config/commands.js +32 -15
- package/dist/config/config-paths.js +9 -11
- package/dist/config/config.js +1 -1
- package/dist/config/defaults.js +22 -2
- package/dist/config/discord-preview-streaming.js +104 -0
- package/dist/config/env-substitution.js +62 -34
- package/dist/config/env-vars.js +45 -7
- package/dist/config/includes.js +4 -0
- package/dist/config/io.js +656 -171
- package/dist/config/legacy.migrations.part-1.js +189 -78
- package/dist/config/legacy.shared.js +3 -1
- package/dist/config/merge-patch.js +54 -4
- package/dist/config/prototype-keys.js +4 -0
- package/dist/config/redact-snapshot.js +404 -76
- package/dist/config/schema.help.js +44 -7
- package/dist/config/schema.js +58 -570
- package/dist/config/schema.labels.js +38 -6
- package/dist/config/sessions/delivery-info.js +10 -3
- package/dist/config/sessions/main-session.js +10 -5
- package/dist/config/sessions/session-file.js +33 -0
- package/dist/config/sessions/session-key.js +10 -5
- package/dist/config/sessions/store.js +1 -1
- package/dist/config/sessions.js +1 -0
- package/dist/config/validation.js +140 -85
- package/dist/config/zod-schema.agent-runtime.js +11 -0
- package/dist/config/zod-schema.hooks.js +40 -11
- package/dist/config/zod-schema.installs.js +20 -0
- package/dist/config/zod-schema.js +156 -20
- package/dist/config/zod-schema.providers-core.js +78 -4
- package/dist/config/zod-schema.providers.js +6 -1
- package/dist/config/zod-schema.session.js +41 -2
- package/dist/cron/run-log.js +3 -0
- package/dist/cron/schedule.js +21 -10
- package/dist/cron/service/ops.js +35 -21
- package/dist/cron/service/timer.js +116 -16
- package/dist/cron/stagger.js +3 -1
- package/dist/daemon/cmd-argv.js +21 -0
- package/dist/daemon/cmd-set.js +58 -0
- package/dist/daemon/service-types.js +1 -0
- package/dist/discord/api.js +12 -6
- package/dist/discord/draft-chunking.js +22 -0
- package/dist/discord/draft-stream.js +124 -0
- package/dist/discord/monitor/agent-components.js +1 -1
- package/dist/discord/monitor/commands.js +5 -0
- package/dist/discord/monitor/exec-approvals.js +357 -162
- package/dist/discord/monitor/gateway-plugin.js +2 -1
- package/dist/discord/monitor/listeners.js +37 -27
- package/dist/discord/monitor/message-handler.js +4 -1
- package/dist/discord/monitor/message-handler.preflight.js +65 -8
- package/dist/discord/monitor/message-handler.process.js +246 -217
- package/dist/discord/monitor/message-utils.js +143 -6
- package/dist/discord/monitor/model-picker-preferences.js +143 -0
- package/dist/discord/monitor/model-picker.js +651 -0
- package/dist/discord/monitor/native-command.js +573 -16
- package/dist/discord/monitor/provider.allowlist.js +223 -0
- package/dist/discord/monitor/provider.js +275 -347
- package/dist/discord/monitor/provider.lifecycle.js +100 -0
- package/dist/discord/monitor/reply-delivery.js +123 -16
- package/dist/discord/monitor/thread-bindings.discord-api.js +215 -0
- package/dist/discord/monitor/thread-bindings.js +4 -0
- package/dist/discord/monitor/thread-bindings.lifecycle.js +177 -0
- package/dist/discord/monitor/thread-bindings.manager.js +423 -0
- package/dist/discord/monitor/thread-bindings.messages.js +55 -0
- package/dist/discord/monitor/thread-bindings.state.js +358 -0
- package/dist/discord/monitor/thread-bindings.types.js +6 -0
- package/dist/discord/resolve-users.js +33 -21
- package/dist/discord/send.channels.js +15 -0
- package/dist/discord/send.js +3 -2
- package/dist/discord/send.outbound.js +82 -26
- package/dist/discord/send.permissions.js +83 -30
- package/dist/discord/send.reactions.js +8 -4
- package/dist/discord/token.js +10 -5
- package/dist/discord/voice/command.js +263 -0
- package/dist/discord/voice/manager.js +531 -0
- package/dist/gateway/auth.js +72 -13
- package/dist/gateway/call.js +152 -83
- package/dist/gateway/canvas-capability.js +75 -0
- package/dist/gateway/client.js +28 -4
- package/dist/gateway/config-reload.js +3 -4
- package/dist/gateway/control-plane-audit.js +28 -0
- package/dist/gateway/control-plane-rate-limit.js +53 -0
- package/dist/gateway/control-ui.js +219 -96
- package/dist/gateway/events.js +1 -0
- package/dist/gateway/hooks-mapping.js +88 -38
- package/dist/gateway/hooks.js +109 -54
- package/dist/gateway/http-auth-helpers.js +3 -2
- package/dist/gateway/http-common.js +22 -0
- package/dist/gateway/http-endpoint-helpers.js +1 -0
- package/dist/gateway/method-scopes.js +169 -0
- package/dist/gateway/net.js +74 -9
- package/dist/gateway/node-invoke-system-run-approval.js +14 -35
- package/dist/gateway/node-registry.js +10 -5
- package/dist/gateway/openai-http.js +1 -0
- package/dist/gateway/openresponses-http.js +121 -110
- package/dist/gateway/origin-check.js +1 -18
- package/dist/gateway/probe-auth.js +2 -0
- package/dist/gateway/protocol/index.js +4 -2
- package/dist/gateway/protocol/schema/cron.js +1 -0
- package/dist/gateway/protocol/schema/devices.js +1 -0
- package/dist/gateway/protocol/schema/protocol-schemas.js +4 -1
- package/dist/gateway/protocol/schema/push.js +18 -0
- package/dist/gateway/protocol/schema/sessions.js +6 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/role-policy.js +17 -0
- package/dist/gateway/server/ws-connection/connect-policy.js +37 -0
- package/dist/gateway/server/ws-connection/message-handler.js +175 -148
- package/dist/gateway/server-chat.js +83 -25
- package/dist/gateway/server-constants.js +10 -9
- package/dist/gateway/server-cron.js +1 -0
- package/dist/gateway/server-http.js +247 -54
- package/dist/gateway/server-maintenance.js +20 -5
- package/dist/gateway/server-methods/agent.js +162 -24
- package/dist/gateway/server-methods/chat.js +465 -130
- package/dist/gateway/server-methods/config.js +193 -152
- package/dist/gateway/server-methods/devices.js +17 -3
- package/dist/gateway/server-methods/models.js +11 -1
- package/dist/gateway/server-methods/nodes.helpers.js +12 -0
- package/dist/gateway/server-methods/nodes.js +251 -69
- package/dist/gateway/server-methods/push.js +53 -0
- package/dist/gateway/server-methods/sessions.js +64 -8
- package/dist/gateway/server-methods/usage.js +162 -75
- package/dist/gateway/server-node-events.js +29 -0
- package/dist/gateway/server-reload-handlers.js +2 -3
- package/dist/gateway/server-runtime-config.js +39 -13
- package/dist/gateway/server-runtime-state.js +2 -0
- package/dist/gateway/server-startup-memory.js +17 -11
- package/dist/gateway/server-ws-runtime.js +1 -0
- package/dist/gateway/server.impl.js +296 -139
- package/dist/gateway/session-preview.test-helpers.js +11 -0
- package/dist/gateway/session-utils.fs.js +32 -34
- package/dist/gateway/sessions-resolve.js +17 -5
- package/dist/gateway/startup-auth.js +126 -0
- package/dist/gateway/test-helpers.agent-results.js +15 -0
- package/dist/gateway/test-helpers.mocks.js +37 -14
- package/dist/gateway/test-helpers.openai-mock.js +14 -7
- package/dist/gateway/test-helpers.server.js +161 -77
- package/dist/gateway/tools-invoke-http.js +21 -10
- package/dist/hooks/bundled/bootstrap-extra-files/handler.js +3 -1
- package/dist/hooks/bundled/command-logger/handler.js +7 -2
- package/dist/hooks/bundled/session-memory/handler.js +170 -38
- package/dist/hooks/frontmatter.js +6 -6
- package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
- package/dist/hooks/gmail-watcher.js +11 -6
- package/dist/hooks/internal-hooks.js +11 -1
- package/dist/hooks/llm-slug-generator.js +4 -1
- package/dist/hooks/workspace.js +47 -17
- package/dist/imessage/accounts.js +9 -20
- package/dist/imessage/monitor/inbound-processing.js +2 -1
- package/dist/infra/archive-path.js +49 -0
- package/dist/infra/archive.js +174 -73
- package/dist/infra/control-ui-assets.js +14 -6
- package/dist/infra/device-pairing.js +204 -144
- package/dist/infra/env.js +10 -5
- package/dist/infra/exec-approvals-allowlist.js +141 -70
- package/dist/infra/exec-approvals-analysis.js +78 -20
- package/dist/infra/exec-approvals.js +5 -17
- package/dist/infra/exec-safe-bin-policy.js +277 -0
- package/dist/infra/fixed-window-rate-limit.js +33 -0
- package/dist/infra/fs-safe.js +71 -39
- package/dist/infra/gateway-lock.js +6 -2
- package/dist/infra/git-root.js +61 -0
- package/dist/infra/heartbeat-active-hours.js +2 -2
- package/dist/infra/heartbeat-reason.js +40 -0
- package/dist/infra/heartbeat-runner.js +72 -32
- package/dist/infra/heartbeat-wake.js +6 -12
- package/dist/infra/host-env-security-policy.json +19 -0
- package/dist/infra/host-env-security.js +66 -0
- package/dist/infra/install-source-utils.js +91 -7
- package/dist/infra/net/ssrf.js +131 -38
- package/dist/infra/node-pairing.js +50 -105
- package/dist/infra/npm-integrity.js +45 -0
- package/dist/infra/npm-pack-install.js +40 -0
- package/dist/infra/outbound/bound-delivery-router.js +88 -0
- package/dist/infra/outbound/channel-adapters.js +20 -7
- package/dist/infra/outbound/channel-selection.js +12 -6
- package/dist/infra/outbound/envelope.js +1 -1
- package/dist/infra/outbound/format.js +12 -6
- package/dist/infra/outbound/message-action-runner.js +107 -327
- package/dist/infra/outbound/message.js +59 -36
- package/dist/infra/outbound/outbound-policy.js +52 -25
- package/dist/infra/outbound/outbound-send-service.js +58 -71
- package/dist/infra/outbound/payloads.js +14 -7
- package/dist/infra/outbound/session-binding-service.js +123 -0
- package/dist/infra/pairing-files.js +10 -0
- package/dist/infra/path-guards.js +25 -0
- package/dist/infra/plain-object.js +9 -0
- package/dist/infra/provider-usage.fetch.codex.js +7 -15
- package/dist/infra/provider-usage.fetch.gemini.js +14 -11
- package/dist/infra/provider-usage.fetch.shared.js +30 -1
- package/dist/infra/provider-usage.fetch.zai.js +10 -9
- package/dist/infra/push-apns.js +365 -0
- package/dist/infra/restart-sentinel.js +16 -1
- package/dist/infra/restart.js +229 -26
- package/dist/infra/retry-policy.js +4 -2
- package/dist/infra/retry.js +9 -5
- package/dist/infra/scp-host.js +54 -0
- package/dist/infra/session-cost-usage.js +107 -59
- package/dist/infra/session-maintenance-warning.js +3 -1
- package/dist/infra/shell-env.js +98 -34
- package/dist/infra/ssh-config.js +12 -6
- package/dist/infra/system-run-command.js +49 -4
- package/dist/infra/update-channels.js +10 -5
- package/dist/infra/update-startup.js +86 -9
- package/dist/line/accounts.js +5 -7
- package/dist/line/bot-access.js +8 -20
- package/dist/line/bot-handlers.js +3 -1
- package/dist/link-understanding/detect.js +15 -7
- package/dist/media/constants.js +15 -6
- package/dist/media/image-ops.js +7 -0
- package/dist/media/inbound-path-policy.js +114 -0
- package/dist/media/input-files.js +16 -0
- package/dist/media/local-roots.js +3 -2
- package/dist/media-understanding/apply.js +4 -1
- package/dist/media-understanding/concurrency.js +8 -20
- package/dist/memory/backend-config.js +45 -6
- package/dist/memory/embeddings.js +10 -4
- package/dist/memory/fs-utils.js +23 -0
- package/dist/memory/manager-search.js +12 -6
- package/dist/memory/manager-sync-ops.js +12 -2
- package/dist/memory/qmd-manager.js +466 -53
- package/dist/memory/query-expansion.js +167 -3
- package/dist/memory/status-format.js +10 -5
- package/dist/memory/sync-memory-files.js +1 -1
- package/dist/memory/test-manager.js +8 -0
- package/dist/node-host/invoke-system-run.js +281 -0
- package/dist/node-host/invoke.js +55 -337
- package/dist/pairing/pairing-store.js +22 -0
- package/dist/plugin-sdk/allow-from.js +1 -1
- package/dist/plugin-sdk/command-auth.js +3 -1
- package/dist/plugin-sdk/index.js +6 -3
- package/dist/plugin-sdk/temp-path.js +47 -0
- package/dist/plugin-sdk/webhook-targets.js +32 -0
- package/dist/plugins/bundled-dir.js +9 -6
- package/dist/plugins/discovery.js +217 -23
- package/dist/plugins/hook-runner-global.js +16 -0
- package/dist/plugins/hooks.js +50 -0
- package/dist/plugins/install.js +28 -16
- package/dist/plugins/loader.js +192 -26
- package/dist/plugins/logger.js +8 -0
- package/dist/plugins/manifest-registry.js +3 -0
- package/dist/plugins/path-safety.js +34 -0
- package/dist/plugins/registry.js +5 -2
- package/dist/plugins/runtime/index.js +271 -206
- package/dist/plugins/runtime.js +3 -17
- package/dist/plugins/update.js +78 -12
- package/dist/process/spawn-utils.js +14 -7
- package/dist/providers/github-copilot-models.js +4 -1
- package/dist/providers/github-copilot-token.js +11 -6
- package/dist/providers/qwen-portal-oauth.js +14 -6
- package/dist/routing/account-id.js +30 -0
- package/dist/routing/resolve-route.js +3 -7
- package/dist/routing/session-key.js +2 -16
- package/dist/security/audit-channel.js +100 -20
- package/dist/security/audit-extra.async.js +505 -179
- package/dist/security/audit-extra.js +12 -2
- package/dist/security/audit-extra.sync.js +421 -35
- package/dist/security/audit-fs.js +31 -13
- package/dist/security/audit.js +180 -370
- package/dist/security/dm-policy-shared.js +68 -0
- package/dist/security/external-content.js +46 -14
- package/dist/security/fix.js +49 -85
- package/dist/security/scan-paths.js +20 -0
- package/dist/security/secret-equal.js +3 -7
- package/dist/security/windows-acl.js +30 -15
- package/dist/shared/entry-status.js +6 -0
- package/dist/shared/frontmatter.js +5 -5
- package/dist/shared/node-list-parse.js +13 -0
- package/dist/shared/node-match.js +11 -4
- package/dist/shared/operator-scope-compat.js +42 -0
- package/dist/shared/text-chunking.js +29 -0
- package/dist/signal/accounts.js +7 -20
- package/dist/signal/monitor/event-handler.js +3 -1
- package/dist/slack/accounts.js +6 -19
- package/dist/slack/actions.js +11 -3
- package/dist/slack/blocks.test-helpers.js +31 -0
- package/dist/slack/monitor/auth.js +1 -1
- package/dist/slack/monitor/message-handler/dispatch.js +50 -29
- package/dist/slack/monitor/mrkdwn.js +8 -0
- package/dist/slack/monitor/replies.js +15 -7
- package/dist/slack/monitor/slash.js +22 -13
- package/dist/slack/resolve-channels.js +10 -5
- package/dist/slack/send.js +102 -12
- package/dist/slack/stream-mode.js +10 -0
- package/dist/slack/streaming.js +4 -2
- package/dist/telegram/accounts.js +19 -14
- package/dist/telegram/bot/helpers.js +3 -5
- package/dist/telegram/bot-access.js +35 -36
- package/dist/telegram/bot-handlers.js +120 -148
- package/dist/telegram/bot-message-context.js +68 -9
- package/dist/telegram/bot-message-dispatch.js +477 -210
- package/dist/telegram/bot-native-commands.js +16 -0
- package/dist/telegram/draft-stream.js +44 -8
- package/dist/telegram/inline-buttons.js +5 -15
- package/dist/telegram/monitor.js +11 -7
- package/dist/telegram/network-config.js +19 -7
- package/dist/telegram/reasoning-lane-coordinator.js +128 -0
- package/dist/telegram/send.js +3 -2
- package/dist/telegram/sent-message-cache.js +5 -6
- package/dist/telegram/status-reaction-variants.js +208 -0
- package/dist/telegram/sticker-cache.js +11 -9
- package/dist/terminal/prompt-select-styled.js +9 -0
- package/dist/terminal/theme.js +12 -12
- package/dist/test-utils/command-runner.js +6 -0
- package/dist/test-utils/internal-hook-event-payload.js +10 -0
- package/dist/test-utils/model-auth-mock.js +12 -0
- package/dist/test-utils/provider-usage-fetch.js +14 -0
- package/dist/test-utils/temp-home.js +33 -0
- package/dist/tts/tts.js +80 -567
- package/dist/tui/components/chat-log.js +50 -8
- package/dist/tui/theme/theme.js +10 -12
- package/dist/tui/tui-command-handlers.js +36 -27
- package/dist/tui/tui-event-handlers.js +122 -32
- package/dist/tui/tui-local-shell.js +16 -6
- package/dist/tui/tui.js +236 -48
- package/dist/utils/account-id.js +2 -4
- package/dist/utils/boolean.js +10 -5
- package/dist/utils/directive-tags.js +11 -0
- package/dist/utils/mask-api-key.js +10 -0
- package/dist/utils/queue-helpers.js +67 -12
- package/dist/utils/run-with-concurrency.js +39 -0
- package/dist/web/auto-reply/deliver-reply.js +8 -4
- package/dist/web/auto-reply/mentions.js +10 -5
- package/dist/web/auto-reply/monitor/group-members.js +14 -7
- package/dist/web/auto-reply/monitor/process-message.js +45 -24
- package/dist/web/inbound/access-control.js +5 -2
- package/dist/web/login-qr.js +12 -6
- package/dist/web/media.js +126 -15
- package/docs/tools/slash-commands.md +5 -1
- package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
- package/extensions/bluebubbles/src/monitor.ts +208 -1950
- package/extensions/feishu/src/external-keys.ts +19 -0
- package/extensions/lobster/src/windows-spawn.ts +193 -0
- package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
- package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
- package/package.json +1 -1
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Re-export barrel for security audit collector functions.
|
|
3
|
+
*
|
|
4
|
+
* Maintains backward compatibility with existing imports from audit-extra.
|
|
5
|
+
* Implementation split into:
|
|
6
|
+
* - audit-extra.sync.ts: Config-based checks (no I/O)
|
|
7
|
+
* - audit-extra.async.ts: Filesystem/plugin checks (async I/O)
|
|
8
|
+
*/
|
|
9
|
+
// Sync collectors
|
|
10
|
+
export { collectAttackSurfaceSummaryFindings, collectExposureMatrixFindings, collectGatewayHttpNoAuthFindings, collectGatewayHttpSessionKeyOverrideFindings, collectHooksHardeningFindings, collectMinimalProfileOverrideFindings, collectModelHygieneFindings, collectNodeDenyCommandPatternFindings, collectSandboxDangerousConfigFindings, collectSandboxDockerNoopFindings, collectSecretsInConfigFindings, collectSmallModelRiskFindings, collectSyncedFolderFindings, } from "./audit-extra.sync.js";
|
|
11
|
+
// Async collectors
|
|
12
|
+
export { collectSandboxBrowserHashLabelFindings, collectIncludeFilePermFindings, collectInstalledSkillsCodeSafetyFindings, collectPluginsCodeSafetyFindings, collectPluginsTrustFindings, collectStateDeepFilesystemFindings, readConfigSnapshotForAudit, } from "./audit-extra.async.js";
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { isToolAllowedByPolicies } from "../agents/pi-tools.policy.js";
|
|
2
2
|
import { resolveSandboxConfigForAgent, resolveSandboxToolPolicyForAgent, } from "../agents/sandbox.js";
|
|
3
|
+
import { getBlockedBindReason } from "../agents/sandbox/validate-sandbox-security.js";
|
|
3
4
|
import { resolveToolProfilePolicy } from "../agents/tool-policy.js";
|
|
4
5
|
import { resolveBrowserConfig } from "../browser/config.js";
|
|
5
6
|
import { formatCliCommand } from "../cli/command-format.js";
|
|
6
7
|
import { resolveGatewayAuth } from "../gateway/auth.js";
|
|
8
|
+
import { resolveNodeCommandAllowlist } from "../gateway/node-command-policy.js";
|
|
9
|
+
import { inferParamBFromIdOrName } from "../shared/model-param-b.js";
|
|
10
|
+
import { pickSandboxToolPolicy } from "./audit-tool-policy.js";
|
|
7
11
|
const SMALL_MODEL_PARAM_B_MAX = 300;
|
|
8
12
|
// --------------------------------------------------------------------------
|
|
9
13
|
// Helpers
|
|
@@ -46,6 +50,14 @@ function looksLikeEnvRef(value) {
|
|
|
46
50
|
const v = value.trim();
|
|
47
51
|
return v.startsWith("${") && v.endsWith("}");
|
|
48
52
|
}
|
|
53
|
+
function isGatewayRemotelyExposed(cfg) {
|
|
54
|
+
const bind = typeof cfg.gateway?.bind === "string" ? cfg.gateway.bind : "loopback";
|
|
55
|
+
if (bind !== "loopback") {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
const tailscaleMode = cfg.gateway?.tailscale?.mode ?? "off";
|
|
59
|
+
return tailscaleMode === "serve" || tailscaleMode === "funnel";
|
|
60
|
+
}
|
|
49
61
|
function addModel(models, raw, source) {
|
|
50
62
|
if (typeof raw !== "string") {
|
|
51
63
|
return;
|
|
@@ -96,25 +108,6 @@ const LEGACY_MODEL_PATTERNS = [
|
|
|
96
108
|
const WEAK_TIER_MODEL_PATTERNS = [
|
|
97
109
|
{ id: "anthropic.haiku", re: /\bhaiku\b/i, label: "Haiku tier (smaller model)" },
|
|
98
110
|
];
|
|
99
|
-
function inferParamBFromIdOrName(text) {
|
|
100
|
-
const raw = text.toLowerCase();
|
|
101
|
-
const matches = raw.matchAll(/(?:^|[^a-z0-9])[a-z]?(\d+(?:\.\d+)?)b(?:[^a-z0-9]|$)/g);
|
|
102
|
-
let best = null;
|
|
103
|
-
for (const match of matches) {
|
|
104
|
-
const numRaw = match[1];
|
|
105
|
-
if (!numRaw) {
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
const value = Number(numRaw);
|
|
109
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
if (best === null || value > best) {
|
|
113
|
-
best = value;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return best;
|
|
117
|
-
}
|
|
118
111
|
function isGptModel(id) {
|
|
119
112
|
return /\bgpt-/i.test(id);
|
|
120
113
|
}
|
|
@@ -132,16 +125,52 @@ function extractAgentIdFromSource(source) {
|
|
|
132
125
|
const match = source.match(/^agents\.list\.([^.]*)\./);
|
|
133
126
|
return match?.[1] ?? null;
|
|
134
127
|
}
|
|
135
|
-
function
|
|
136
|
-
if (!
|
|
137
|
-
return
|
|
128
|
+
function hasConfiguredDockerConfig(docker) {
|
|
129
|
+
if (!docker || typeof docker !== "object") {
|
|
130
|
+
return false;
|
|
138
131
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
132
|
+
return Object.values(docker).some((value) => value !== undefined);
|
|
133
|
+
}
|
|
134
|
+
function normalizeNodeCommand(value) {
|
|
135
|
+
return typeof value === "string" ? value.trim() : "";
|
|
136
|
+
}
|
|
137
|
+
function listKnownNodeCommands(cfg) {
|
|
138
|
+
const baseCfg = {
|
|
139
|
+
...cfg,
|
|
140
|
+
gateway: {
|
|
141
|
+
...cfg.gateway,
|
|
142
|
+
nodes: {
|
|
143
|
+
...cfg.gateway?.nodes,
|
|
144
|
+
denyCommands: [],
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
const out = new Set();
|
|
149
|
+
for (const platform of ["ios", "android", "macos", "linux", "windows", "unknown"]) {
|
|
150
|
+
const allow = resolveNodeCommandAllowlist(baseCfg, { platform });
|
|
151
|
+
for (const cmd of allow) {
|
|
152
|
+
const normalized = normalizeNodeCommand(cmd);
|
|
153
|
+
if (normalized) {
|
|
154
|
+
out.add(normalized);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
143
157
|
}
|
|
144
|
-
return
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
function looksLikeNodeCommandPattern(value) {
|
|
161
|
+
if (!value) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
if (/[?*[\]{}(),|]/.test(value)) {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
if (value.startsWith("/") ||
|
|
168
|
+
value.endsWith("/") ||
|
|
169
|
+
value.startsWith("^") ||
|
|
170
|
+
value.endsWith("$")) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
return /\s/.test(value) || value.includes("group:");
|
|
145
174
|
}
|
|
146
175
|
function resolveToolPolicies(params) {
|
|
147
176
|
const policies = [];
|
|
@@ -150,11 +179,11 @@ function resolveToolPolicies(params) {
|
|
|
150
179
|
if (profilePolicy) {
|
|
151
180
|
policies.push(profilePolicy);
|
|
152
181
|
}
|
|
153
|
-
const globalPolicy =
|
|
182
|
+
const globalPolicy = pickSandboxToolPolicy(params.cfg.tools ?? undefined);
|
|
154
183
|
if (globalPolicy) {
|
|
155
184
|
policies.push(globalPolicy);
|
|
156
185
|
}
|
|
157
|
-
const agentPolicy =
|
|
186
|
+
const agentPolicy = pickSandboxToolPolicy(params.agentTools);
|
|
158
187
|
if (agentPolicy) {
|
|
159
188
|
policies.push(agentPolicy);
|
|
160
189
|
}
|
|
@@ -232,13 +261,16 @@ function listGroupPolicyOpen(cfg) {
|
|
|
232
261
|
export function collectAttackSurfaceSummaryFindings(cfg) {
|
|
233
262
|
const group = summarizeGroupPolicy(cfg);
|
|
234
263
|
const elevated = cfg.tools?.elevated?.enabled !== false;
|
|
235
|
-
const
|
|
264
|
+
const webhooksEnabled = cfg.hooks?.enabled === true;
|
|
265
|
+
const internalHooksEnabled = cfg.hooks?.internal?.enabled === true;
|
|
236
266
|
const browserEnabled = cfg.browser?.enabled ?? true;
|
|
237
267
|
const detail = `groups: open=${group.open}, allowlist=${group.allowlist}` +
|
|
238
268
|
`\n` +
|
|
239
269
|
`tools.elevated: ${elevated ? "enabled" : "disabled"}` +
|
|
240
270
|
`\n` +
|
|
241
|
-
`hooks: ${
|
|
271
|
+
`hooks.webhooks: ${webhooksEnabled ? "enabled" : "disabled"}` +
|
|
272
|
+
`\n` +
|
|
273
|
+
`hooks.internal: ${internalHooksEnabled ? "enabled" : "disabled"}` +
|
|
242
274
|
`\n` +
|
|
243
275
|
`browser control: ${browserEnabled ? "enabled" : "disabled"}`;
|
|
244
276
|
return [
|
|
@@ -258,7 +290,7 @@ export function collectSyncedFolderFindings(params) {
|
|
|
258
290
|
severity: "warn",
|
|
259
291
|
title: "State/config path looks like a synced folder",
|
|
260
292
|
detail: `stateDir=${params.stateDir}, configPath=${params.configPath}. Synced folders (iCloud/Dropbox/OneDrive/Google Drive) can leak tokens and transcripts onto other devices.`,
|
|
261
|
-
remediation: `Keep
|
|
293
|
+
remediation: `Keep POOLBOT_STATE_DIR on a local-only volume and re-run "${formatCliCommand("poolbot security audit --fix")}".`,
|
|
262
294
|
});
|
|
263
295
|
}
|
|
264
296
|
return findings;
|
|
@@ -286,7 +318,7 @@ export function collectSecretsInConfigFindings(cfg) {
|
|
|
286
318
|
}
|
|
287
319
|
return findings;
|
|
288
320
|
}
|
|
289
|
-
export function collectHooksHardeningFindings(cfg) {
|
|
321
|
+
export function collectHooksHardeningFindings(cfg, env = process.env) {
|
|
290
322
|
const findings = [];
|
|
291
323
|
if (cfg.hooks?.enabled !== true) {
|
|
292
324
|
return findings;
|
|
@@ -303,16 +335,22 @@ export function collectHooksHardeningFindings(cfg) {
|
|
|
303
335
|
const gatewayAuth = resolveGatewayAuth({
|
|
304
336
|
authConfig: cfg.gateway?.auth,
|
|
305
337
|
tailscaleMode: cfg.gateway?.tailscale?.mode ?? "off",
|
|
338
|
+
env,
|
|
306
339
|
});
|
|
340
|
+
const poolbotGatewayToken = typeof env.POOLBOT_GATEWAY_TOKEN === "string" && env.POOLBOT_GATEWAY_TOKEN.trim()
|
|
341
|
+
? env.POOLBOT_GATEWAY_TOKEN.trim()
|
|
342
|
+
: null;
|
|
307
343
|
const gatewayToken = gatewayAuth.mode === "token" &&
|
|
308
344
|
typeof gatewayAuth.token === "string" &&
|
|
309
345
|
gatewayAuth.token.trim()
|
|
310
346
|
? gatewayAuth.token.trim()
|
|
311
|
-
:
|
|
347
|
+
: poolbotGatewayToken
|
|
348
|
+
? poolbotGatewayToken
|
|
349
|
+
: null;
|
|
312
350
|
if (token && gatewayToken && token === gatewayToken) {
|
|
313
351
|
findings.push({
|
|
314
352
|
checkId: "hooks.token_reuse_gateway_token",
|
|
315
|
-
severity: "
|
|
353
|
+
severity: "critical",
|
|
316
354
|
title: "Hooks token reuses the Gateway token",
|
|
317
355
|
detail: "hooks.token matches gateway.auth token; compromise of hooks expands blast radius to the Gateway API.",
|
|
318
356
|
remediation: "Use a separate hooks.token dedicated to hook ingress.",
|
|
@@ -328,6 +366,309 @@ export function collectHooksHardeningFindings(cfg) {
|
|
|
328
366
|
remediation: "Use a dedicated path like '/hooks'.",
|
|
329
367
|
});
|
|
330
368
|
}
|
|
369
|
+
const allowRequestSessionKey = cfg.hooks?.allowRequestSessionKey === true;
|
|
370
|
+
const defaultSessionKey = typeof cfg.hooks?.defaultSessionKey === "string" ? cfg.hooks.defaultSessionKey.trim() : "";
|
|
371
|
+
const allowedPrefixes = Array.isArray(cfg.hooks?.allowedSessionKeyPrefixes)
|
|
372
|
+
? cfg.hooks.allowedSessionKeyPrefixes
|
|
373
|
+
.map((prefix) => prefix.trim())
|
|
374
|
+
.filter((prefix) => prefix.length > 0)
|
|
375
|
+
: [];
|
|
376
|
+
const remoteExposure = isGatewayRemotelyExposed(cfg);
|
|
377
|
+
if (!defaultSessionKey) {
|
|
378
|
+
findings.push({
|
|
379
|
+
checkId: "hooks.default_session_key_unset",
|
|
380
|
+
severity: "warn",
|
|
381
|
+
title: "hooks.defaultSessionKey is not configured",
|
|
382
|
+
detail: "Hook agent runs without explicit sessionKey use generated per-request keys. Set hooks.defaultSessionKey to keep hook ingress scoped to a known session.",
|
|
383
|
+
remediation: 'Set hooks.defaultSessionKey (for example, "hook:ingress").',
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
if (allowRequestSessionKey) {
|
|
387
|
+
findings.push({
|
|
388
|
+
checkId: "hooks.request_session_key_enabled",
|
|
389
|
+
severity: remoteExposure ? "critical" : "warn",
|
|
390
|
+
title: "External hook payloads may override sessionKey",
|
|
391
|
+
detail: "hooks.allowRequestSessionKey=true allows `/hooks/agent` callers to choose the session key. Treat hook token holders as full-trust unless you also restrict prefixes.",
|
|
392
|
+
remediation: "Set hooks.allowRequestSessionKey=false (recommended) or constrain hooks.allowedSessionKeyPrefixes.",
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
if (allowRequestSessionKey && allowedPrefixes.length === 0) {
|
|
396
|
+
findings.push({
|
|
397
|
+
checkId: "hooks.request_session_key_prefixes_missing",
|
|
398
|
+
severity: remoteExposure ? "critical" : "warn",
|
|
399
|
+
title: "Request sessionKey override is enabled without prefix restrictions",
|
|
400
|
+
detail: "hooks.allowRequestSessionKey=true and hooks.allowedSessionKeyPrefixes is unset/empty, so request payloads can target arbitrary session key shapes.",
|
|
401
|
+
remediation: 'Set hooks.allowedSessionKeyPrefixes (for example, ["hook:"]) or disable request overrides.',
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
return findings;
|
|
405
|
+
}
|
|
406
|
+
export function collectGatewayHttpSessionKeyOverrideFindings(cfg) {
|
|
407
|
+
const findings = [];
|
|
408
|
+
const chatCompletionsEnabled = cfg.gateway?.http?.endpoints?.chatCompletions?.enabled === true;
|
|
409
|
+
const responsesEnabled = cfg.gateway?.http?.endpoints?.responses?.enabled === true;
|
|
410
|
+
if (!chatCompletionsEnabled && !responsesEnabled) {
|
|
411
|
+
return findings;
|
|
412
|
+
}
|
|
413
|
+
const enabledEndpoints = [
|
|
414
|
+
chatCompletionsEnabled ? "/v1/chat/completions" : null,
|
|
415
|
+
responsesEnabled ? "/v1/responses" : null,
|
|
416
|
+
].filter((entry) => Boolean(entry));
|
|
417
|
+
findings.push({
|
|
418
|
+
checkId: "gateway.http.session_key_override_enabled",
|
|
419
|
+
severity: "info",
|
|
420
|
+
title: "HTTP API session-key override is enabled",
|
|
421
|
+
detail: `${enabledEndpoints.join(", ")} accept x-poolbot-session-key for per-request session routing. ` +
|
|
422
|
+
"Treat API credential holders as trusted principals.",
|
|
423
|
+
});
|
|
424
|
+
return findings;
|
|
425
|
+
}
|
|
426
|
+
export function collectGatewayHttpNoAuthFindings(cfg, env) {
|
|
427
|
+
const findings = [];
|
|
428
|
+
const tailscaleMode = cfg.gateway?.tailscale?.mode ?? "off";
|
|
429
|
+
const auth = resolveGatewayAuth({ authConfig: cfg.gateway?.auth, tailscaleMode, env });
|
|
430
|
+
if (auth.mode !== "none") {
|
|
431
|
+
return findings;
|
|
432
|
+
}
|
|
433
|
+
const chatCompletionsEnabled = cfg.gateway?.http?.endpoints?.chatCompletions?.enabled === true;
|
|
434
|
+
const responsesEnabled = cfg.gateway?.http?.endpoints?.responses?.enabled === true;
|
|
435
|
+
const enabledEndpoints = [
|
|
436
|
+
"/tools/invoke",
|
|
437
|
+
chatCompletionsEnabled ? "/v1/chat/completions" : null,
|
|
438
|
+
responsesEnabled ? "/v1/responses" : null,
|
|
439
|
+
].filter((entry) => Boolean(entry));
|
|
440
|
+
const remoteExposure = isGatewayRemotelyExposed(cfg);
|
|
441
|
+
findings.push({
|
|
442
|
+
checkId: "gateway.http.no_auth",
|
|
443
|
+
severity: remoteExposure ? "critical" : "warn",
|
|
444
|
+
title: "Gateway HTTP APIs are reachable without auth",
|
|
445
|
+
detail: `gateway.auth.mode="none" leaves ${enabledEndpoints.join(", ")} callable without a shared secret. ` +
|
|
446
|
+
"Treat this as trusted-local only and avoid exposing the gateway beyond loopback.",
|
|
447
|
+
remediation: "Set gateway.auth.mode to token/password (recommended). If you intentionally keep mode=none, keep gateway.bind=loopback and disable optional HTTP endpoints.",
|
|
448
|
+
});
|
|
449
|
+
return findings;
|
|
450
|
+
}
|
|
451
|
+
export function collectSandboxDockerNoopFindings(cfg) {
|
|
452
|
+
const findings = [];
|
|
453
|
+
const configuredPaths = [];
|
|
454
|
+
const agents = Array.isArray(cfg.agents?.list) ? cfg.agents.list : [];
|
|
455
|
+
const defaultsSandbox = cfg.agents?.defaults?.sandbox;
|
|
456
|
+
const hasDefaultDocker = hasConfiguredDockerConfig(defaultsSandbox?.docker);
|
|
457
|
+
const defaultMode = defaultsSandbox?.mode ?? "off";
|
|
458
|
+
const hasAnySandboxEnabledAgent = agents.some((entry) => {
|
|
459
|
+
if (!entry || typeof entry !== "object" || typeof entry.id !== "string") {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
return resolveSandboxConfigForAgent(cfg, entry.id).mode !== "off";
|
|
463
|
+
});
|
|
464
|
+
if (hasDefaultDocker && defaultMode === "off" && !hasAnySandboxEnabledAgent) {
|
|
465
|
+
configuredPaths.push("agents.defaults.sandbox.docker");
|
|
466
|
+
}
|
|
467
|
+
for (const entry of agents) {
|
|
468
|
+
if (!entry || typeof entry !== "object" || typeof entry.id !== "string") {
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
if (!hasConfiguredDockerConfig(entry.sandbox?.docker)) {
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
if (resolveSandboxConfigForAgent(cfg, entry.id).mode === "off") {
|
|
475
|
+
configuredPaths.push(`agents.list.${entry.id}.sandbox.docker`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (configuredPaths.length === 0) {
|
|
479
|
+
return findings;
|
|
480
|
+
}
|
|
481
|
+
findings.push({
|
|
482
|
+
checkId: "sandbox.docker_config_mode_off",
|
|
483
|
+
severity: "warn",
|
|
484
|
+
title: "Sandbox docker settings configured while sandbox mode is off",
|
|
485
|
+
detail: "These docker settings will not take effect until sandbox mode is enabled:\n" +
|
|
486
|
+
configuredPaths.map((entry) => `- ${entry}`).join("\n"),
|
|
487
|
+
remediation: 'Enable sandbox mode (`agents.defaults.sandbox.mode="non-main"` or `"all"`) where needed, or remove unused docker settings.',
|
|
488
|
+
});
|
|
489
|
+
return findings;
|
|
490
|
+
}
|
|
491
|
+
export function collectSandboxDangerousConfigFindings(cfg) {
|
|
492
|
+
const findings = [];
|
|
493
|
+
const agents = Array.isArray(cfg.agents?.list) ? cfg.agents.list : [];
|
|
494
|
+
const configs = [];
|
|
495
|
+
const defaultDocker = cfg.agents?.defaults?.sandbox?.docker;
|
|
496
|
+
if (defaultDocker && typeof defaultDocker === "object") {
|
|
497
|
+
configs.push({
|
|
498
|
+
source: "agents.defaults.sandbox.docker",
|
|
499
|
+
docker: defaultDocker,
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
for (const entry of agents) {
|
|
503
|
+
if (!entry || typeof entry !== "object" || typeof entry.id !== "string") {
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
const agentDocker = entry.sandbox?.docker;
|
|
507
|
+
if (agentDocker && typeof agentDocker === "object") {
|
|
508
|
+
configs.push({
|
|
509
|
+
source: `agents.list.${entry.id}.sandbox.docker`,
|
|
510
|
+
docker: agentDocker,
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
for (const { source, docker } of configs) {
|
|
515
|
+
const binds = Array.isArray(docker.binds) ? docker.binds : [];
|
|
516
|
+
for (const bind of binds) {
|
|
517
|
+
if (typeof bind !== "string") {
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
const blocked = getBlockedBindReason(bind);
|
|
521
|
+
if (!blocked) {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
if (blocked.kind === "non_absolute") {
|
|
525
|
+
findings.push({
|
|
526
|
+
checkId: "sandbox.bind_mount_non_absolute",
|
|
527
|
+
severity: "warn",
|
|
528
|
+
title: "Sandbox bind mount uses a non-absolute source path",
|
|
529
|
+
detail: `${source}.binds contains "${bind}" which uses source path "${blocked.sourcePath}". ` +
|
|
530
|
+
"Non-absolute bind sources are hard to validate safely and may resolve unexpectedly.",
|
|
531
|
+
remediation: `Rewrite "${bind}" to use an absolute host path (for example: /home/user/project:/project:ro).`,
|
|
532
|
+
});
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
const verb = blocked.kind === "covers" ? "covers" : "targets";
|
|
536
|
+
findings.push({
|
|
537
|
+
checkId: "sandbox.dangerous_bind_mount",
|
|
538
|
+
severity: "critical",
|
|
539
|
+
title: "Dangerous bind mount in sandbox config",
|
|
540
|
+
detail: `${source}.binds contains "${bind}" which ${verb} blocked path "${blocked.blockedPath}". ` +
|
|
541
|
+
"This can expose host system directories or the Docker socket to sandbox containers.",
|
|
542
|
+
remediation: `Remove "${bind}" from ${source}.binds. Use project-specific paths instead.`,
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
const network = typeof docker.network === "string" ? docker.network : undefined;
|
|
546
|
+
if (network && network.trim().toLowerCase() === "host") {
|
|
547
|
+
findings.push({
|
|
548
|
+
checkId: "sandbox.dangerous_network_mode",
|
|
549
|
+
severity: "critical",
|
|
550
|
+
title: "Network host mode in sandbox config",
|
|
551
|
+
detail: `${source}.network is "host" which bypasses container network isolation entirely.`,
|
|
552
|
+
remediation: `Set ${source}.network to "bridge" or "none".`,
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
const seccompProfile = typeof docker.seccompProfile === "string" ? docker.seccompProfile : undefined;
|
|
556
|
+
if (seccompProfile && seccompProfile.trim().toLowerCase() === "unconfined") {
|
|
557
|
+
findings.push({
|
|
558
|
+
checkId: "sandbox.dangerous_seccomp_profile",
|
|
559
|
+
severity: "critical",
|
|
560
|
+
title: "Seccomp unconfined in sandbox config",
|
|
561
|
+
detail: `${source}.seccompProfile is "unconfined" which disables syscall filtering.`,
|
|
562
|
+
remediation: `Remove ${source}.seccompProfile or use a custom seccomp profile file.`,
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
const apparmorProfile = typeof docker.apparmorProfile === "string" ? docker.apparmorProfile : undefined;
|
|
566
|
+
if (apparmorProfile && apparmorProfile.trim().toLowerCase() === "unconfined") {
|
|
567
|
+
findings.push({
|
|
568
|
+
checkId: "sandbox.dangerous_apparmor_profile",
|
|
569
|
+
severity: "critical",
|
|
570
|
+
title: "AppArmor unconfined in sandbox config",
|
|
571
|
+
detail: `${source}.apparmorProfile is "unconfined" which disables AppArmor enforcement.`,
|
|
572
|
+
remediation: `Remove ${source}.apparmorProfile or use a named AppArmor profile.`,
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
const browserExposurePaths = [];
|
|
577
|
+
const defaultBrowser = resolveSandboxConfigForAgent(cfg).browser;
|
|
578
|
+
if (defaultBrowser.enabled &&
|
|
579
|
+
defaultBrowser.network.trim().toLowerCase() === "bridge" &&
|
|
580
|
+
!defaultBrowser.cdpSourceRange?.trim()) {
|
|
581
|
+
browserExposurePaths.push("agents.defaults.sandbox.browser");
|
|
582
|
+
}
|
|
583
|
+
for (const entry of agents) {
|
|
584
|
+
if (!entry || typeof entry !== "object" || typeof entry.id !== "string") {
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
const browser = resolveSandboxConfigForAgent(cfg, entry.id).browser;
|
|
588
|
+
if (!browser.enabled) {
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
if (browser.network.trim().toLowerCase() !== "bridge") {
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
if (browser.cdpSourceRange?.trim()) {
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
browserExposurePaths.push(`agents.list.${entry.id}.sandbox.browser`);
|
|
598
|
+
}
|
|
599
|
+
if (browserExposurePaths.length > 0) {
|
|
600
|
+
findings.push({
|
|
601
|
+
checkId: "sandbox.browser_cdp_bridge_unrestricted",
|
|
602
|
+
severity: "warn",
|
|
603
|
+
title: "Sandbox browser CDP may be reachable by peer containers",
|
|
604
|
+
detail: "These sandbox browser configs use Docker bridge networking with no CDP source restriction:\n" +
|
|
605
|
+
browserExposurePaths.map((entry) => `- ${entry}`).join("\n"),
|
|
606
|
+
remediation: "Set sandbox.browser.network to a dedicated bridge network (recommended default: poolbot-sandbox-browser), " +
|
|
607
|
+
"or set sandbox.browser.cdpSourceRange (for example 172.21.0.1/32) to restrict container-edge CDP ingress.",
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
return findings;
|
|
611
|
+
}
|
|
612
|
+
export function collectNodeDenyCommandPatternFindings(cfg) {
|
|
613
|
+
const findings = [];
|
|
614
|
+
const denyListRaw = cfg.gateway?.nodes?.denyCommands;
|
|
615
|
+
if (!Array.isArray(denyListRaw) || denyListRaw.length === 0) {
|
|
616
|
+
return findings;
|
|
617
|
+
}
|
|
618
|
+
const denyList = denyListRaw.map(normalizeNodeCommand).filter(Boolean);
|
|
619
|
+
if (denyList.length === 0) {
|
|
620
|
+
return findings;
|
|
621
|
+
}
|
|
622
|
+
const knownCommands = listKnownNodeCommands(cfg);
|
|
623
|
+
const patternLike = denyList.filter((entry) => looksLikeNodeCommandPattern(entry));
|
|
624
|
+
const unknownExact = denyList.filter((entry) => !looksLikeNodeCommandPattern(entry) && !knownCommands.has(entry));
|
|
625
|
+
if (patternLike.length === 0 && unknownExact.length === 0) {
|
|
626
|
+
return findings;
|
|
627
|
+
}
|
|
628
|
+
const detailParts = [];
|
|
629
|
+
if (patternLike.length > 0) {
|
|
630
|
+
detailParts.push(`Pattern-like entries (not supported by exact matching): ${patternLike.join(", ")}`);
|
|
631
|
+
}
|
|
632
|
+
if (unknownExact.length > 0) {
|
|
633
|
+
detailParts.push(`Unknown command names (not in defaults/allowCommands): ${unknownExact.join(", ")}`);
|
|
634
|
+
}
|
|
635
|
+
const examples = Array.from(knownCommands).slice(0, 8);
|
|
636
|
+
findings.push({
|
|
637
|
+
checkId: "gateway.nodes.deny_commands_ineffective",
|
|
638
|
+
severity: "warn",
|
|
639
|
+
title: "Some gateway.nodes.denyCommands entries are ineffective",
|
|
640
|
+
detail: "gateway.nodes.denyCommands uses exact command-name matching only.\n" +
|
|
641
|
+
detailParts.map((entry) => `- ${entry}`).join("\n"),
|
|
642
|
+
remediation: `Use exact command names (for example: ${examples.join(", ")}). ` +
|
|
643
|
+
"If you need broader restrictions, remove risky commands from allowCommands/default workflows.",
|
|
644
|
+
});
|
|
645
|
+
return findings;
|
|
646
|
+
}
|
|
647
|
+
export function collectMinimalProfileOverrideFindings(cfg) {
|
|
648
|
+
const findings = [];
|
|
649
|
+
if (cfg.tools?.profile !== "minimal") {
|
|
650
|
+
return findings;
|
|
651
|
+
}
|
|
652
|
+
const overrides = (cfg.agents?.list ?? [])
|
|
653
|
+
.filter((entry) => {
|
|
654
|
+
return Boolean(entry &&
|
|
655
|
+
typeof entry === "object" &&
|
|
656
|
+
typeof entry.id === "string" &&
|
|
657
|
+
entry.tools?.profile &&
|
|
658
|
+
entry.tools.profile !== "minimal");
|
|
659
|
+
})
|
|
660
|
+
.map((entry) => `${entry.id}=${entry.tools?.profile}`);
|
|
661
|
+
if (overrides.length === 0) {
|
|
662
|
+
return findings;
|
|
663
|
+
}
|
|
664
|
+
findings.push({
|
|
665
|
+
checkId: "tools.profile_minimal_overridden",
|
|
666
|
+
severity: "warn",
|
|
667
|
+
title: "Global tools.profile=minimal is overridden by agent profiles",
|
|
668
|
+
detail: "Global minimal profile is set, but these agent profiles take precedence:\n" +
|
|
669
|
+
overrides.map((entry) => `- agents.list.${entry}`).join("\n"),
|
|
670
|
+
remediation: 'Set those agents to `tools.profile="minimal"` (or remove the agent override) if you want minimal tools enforced globally.',
|
|
671
|
+
});
|
|
331
672
|
return findings;
|
|
332
673
|
}
|
|
333
674
|
export function collectModelHygieneFindings(cfg) {
|
|
@@ -501,5 +842,50 @@ export function collectExposureMatrixFindings(cfg) {
|
|
|
501
842
|
remediation: `Set groupPolicy="allowlist" and keep elevated allowlists extremely tight.`,
|
|
502
843
|
});
|
|
503
844
|
}
|
|
845
|
+
const contexts = [{ label: "agents.defaults" }];
|
|
846
|
+
for (const agent of cfg.agents?.list ?? []) {
|
|
847
|
+
if (!agent || typeof agent !== "object" || typeof agent.id !== "string") {
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
contexts.push({
|
|
851
|
+
label: `agents.list.${agent.id}`,
|
|
852
|
+
agentId: agent.id,
|
|
853
|
+
tools: agent.tools,
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
const riskyContexts = [];
|
|
857
|
+
let hasRuntimeRisk = false;
|
|
858
|
+
for (const context of contexts) {
|
|
859
|
+
const sandboxMode = resolveSandboxConfigForAgent(cfg, context.agentId).mode;
|
|
860
|
+
const policies = resolveToolPolicies({
|
|
861
|
+
cfg,
|
|
862
|
+
agentTools: context.tools,
|
|
863
|
+
sandboxMode,
|
|
864
|
+
agentId: context.agentId ?? null,
|
|
865
|
+
});
|
|
866
|
+
const runtimeTools = ["exec", "process"].filter((tool) => isToolAllowedByPolicies(tool, policies));
|
|
867
|
+
const fsTools = ["read", "write", "edit", "apply_patch"].filter((tool) => isToolAllowedByPolicies(tool, policies));
|
|
868
|
+
const fsWorkspaceOnly = context.tools?.fs?.workspaceOnly ?? cfg.tools?.fs?.workspaceOnly;
|
|
869
|
+
const runtimeUnguarded = runtimeTools.length > 0 && sandboxMode !== "all";
|
|
870
|
+
const fsUnguarded = fsTools.length > 0 && sandboxMode !== "all" && fsWorkspaceOnly !== true;
|
|
871
|
+
if (!runtimeUnguarded && !fsUnguarded) {
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
if (runtimeUnguarded) {
|
|
875
|
+
hasRuntimeRisk = true;
|
|
876
|
+
}
|
|
877
|
+
riskyContexts.push(`${context.label} (sandbox=${sandboxMode}; runtime=[${runtimeTools.join(", ") || "off"}]; fs=[${fsTools.join(", ") || "off"}]; fs.workspaceOnly=${fsWorkspaceOnly === true ? "true" : "false"})`);
|
|
878
|
+
}
|
|
879
|
+
if (riskyContexts.length > 0) {
|
|
880
|
+
findings.push({
|
|
881
|
+
checkId: "security.exposure.open_groups_with_runtime_or_fs",
|
|
882
|
+
severity: hasRuntimeRisk ? "critical" : "warn",
|
|
883
|
+
title: "Open groupPolicy with runtime/filesystem tools exposed",
|
|
884
|
+
detail: `Found groupPolicy="open" at:\n${openGroups.map((p) => `- ${p}`).join("\n")}\n` +
|
|
885
|
+
`Risky tool exposure contexts:\n${riskyContexts.map((line) => `- ${line}`).join("\n")}\n` +
|
|
886
|
+
"Prompt injection in open groups can trigger command/file actions in these contexts.",
|
|
887
|
+
remediation: 'For open groups, prefer tools.profile="messaging" (or deny group:runtime/group:fs), set tools.fs.workspaceOnly=true, and use agents.defaults.sandbox.mode="all" for exposed agents.',
|
|
888
|
+
});
|
|
889
|
+
}
|
|
504
890
|
return findings;
|
|
505
891
|
}
|
|
@@ -41,7 +41,19 @@ export async function inspectPathPermissions(targetPath, opts) {
|
|
|
41
41
|
error: st.error,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
let effectiveMode = st.mode;
|
|
45
|
+
let effectiveIsDir = st.isDir;
|
|
46
|
+
if (st.isSymlink) {
|
|
47
|
+
try {
|
|
48
|
+
const target = await fs.stat(targetPath);
|
|
49
|
+
effectiveMode = typeof target.mode === "number" ? target.mode : st.mode;
|
|
50
|
+
effectiveIsDir = target.isDirectory();
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Keep lstat-derived metadata when target lookup fails.
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const bits = modeBits(effectiveMode);
|
|
45
57
|
const platform = opts?.platform ?? process.platform;
|
|
46
58
|
if (platform === "win32") {
|
|
47
59
|
const acl = await inspectWindowsAcl(targetPath, { env: opts?.env, exec: opts?.exec });
|
|
@@ -49,8 +61,8 @@ export async function inspectPathPermissions(targetPath, opts) {
|
|
|
49
61
|
return {
|
|
50
62
|
ok: true,
|
|
51
63
|
isSymlink: st.isSymlink,
|
|
52
|
-
isDir:
|
|
53
|
-
mode:
|
|
64
|
+
isDir: effectiveIsDir,
|
|
65
|
+
mode: effectiveMode,
|
|
54
66
|
bits,
|
|
55
67
|
source: "unknown",
|
|
56
68
|
worldWritable: false,
|
|
@@ -63,8 +75,8 @@ export async function inspectPathPermissions(targetPath, opts) {
|
|
|
63
75
|
return {
|
|
64
76
|
ok: true,
|
|
65
77
|
isSymlink: st.isSymlink,
|
|
66
|
-
isDir:
|
|
67
|
-
mode:
|
|
78
|
+
isDir: effectiveIsDir,
|
|
79
|
+
mode: effectiveMode,
|
|
68
80
|
bits,
|
|
69
81
|
source: "windows-acl",
|
|
70
82
|
worldWritable: acl.untrustedWorld.some((entry) => entry.canWrite),
|
|
@@ -77,8 +89,8 @@ export async function inspectPathPermissions(targetPath, opts) {
|
|
|
77
89
|
return {
|
|
78
90
|
ok: true,
|
|
79
91
|
isSymlink: st.isSymlink,
|
|
80
|
-
isDir:
|
|
81
|
-
mode:
|
|
92
|
+
isDir: effectiveIsDir,
|
|
93
|
+
mode: effectiveMode,
|
|
82
94
|
bits,
|
|
83
95
|
source: "posix",
|
|
84
96
|
worldWritable: isWorldWritable(bits),
|
|
@@ -102,32 +114,38 @@ export function formatPermissionRemediation(params) {
|
|
|
102
114
|
return `chmod ${mode} ${params.targetPath}`;
|
|
103
115
|
}
|
|
104
116
|
export function modeBits(mode) {
|
|
105
|
-
if (mode == null)
|
|
117
|
+
if (mode == null) {
|
|
106
118
|
return null;
|
|
119
|
+
}
|
|
107
120
|
return mode & 0o777;
|
|
108
121
|
}
|
|
109
122
|
export function formatOctal(bits) {
|
|
110
|
-
if (bits == null)
|
|
123
|
+
if (bits == null) {
|
|
111
124
|
return "unknown";
|
|
125
|
+
}
|
|
112
126
|
return bits.toString(8).padStart(3, "0");
|
|
113
127
|
}
|
|
114
128
|
export function isWorldWritable(bits) {
|
|
115
|
-
if (bits == null)
|
|
129
|
+
if (bits == null) {
|
|
116
130
|
return false;
|
|
131
|
+
}
|
|
117
132
|
return (bits & 0o002) !== 0;
|
|
118
133
|
}
|
|
119
134
|
export function isGroupWritable(bits) {
|
|
120
|
-
if (bits == null)
|
|
135
|
+
if (bits == null) {
|
|
121
136
|
return false;
|
|
137
|
+
}
|
|
122
138
|
return (bits & 0o020) !== 0;
|
|
123
139
|
}
|
|
124
140
|
export function isWorldReadable(bits) {
|
|
125
|
-
if (bits == null)
|
|
141
|
+
if (bits == null) {
|
|
126
142
|
return false;
|
|
143
|
+
}
|
|
127
144
|
return (bits & 0o004) !== 0;
|
|
128
145
|
}
|
|
129
146
|
export function isGroupReadable(bits) {
|
|
130
|
-
if (bits == null)
|
|
147
|
+
if (bits == null) {
|
|
131
148
|
return false;
|
|
149
|
+
}
|
|
132
150
|
return (bits & 0o040) !== 0;
|
|
133
151
|
}
|