@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
package/dist/security/audit.js
CHANGED
|
@@ -1,49 +1,62 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { resolveChannelDefaultAccountId } from "../channels/plugins/helpers.js";
|
|
1
|
+
import { resolveSandboxConfigForAgent } from "../agents/sandbox.js";
|
|
3
2
|
import { resolveBrowserConfig, resolveProfile } from "../browser/config.js";
|
|
3
|
+
import { resolveBrowserControlAuth } from "../browser/control-auth.js";
|
|
4
|
+
import { listChannelPlugins } from "../channels/plugins/index.js";
|
|
5
|
+
import { formatCliCommand } from "../cli/command-format.js";
|
|
4
6
|
import { resolveConfigPath, resolveStateDir } from "../config/paths.js";
|
|
5
7
|
import { resolveGatewayAuth } from "../gateway/auth.js";
|
|
6
|
-
import { formatCliCommand } from "../cli/command-format.js";
|
|
7
8
|
import { buildGatewayConnectionDetails } from "../gateway/call.js";
|
|
9
|
+
import { resolveGatewayProbeAuth } from "../gateway/probe-auth.js";
|
|
8
10
|
import { probeGateway } from "../gateway/probe.js";
|
|
9
|
-
import {
|
|
10
|
-
import { collectAttackSurfaceSummaryFindings, collectExposureMatrixFindings, collectHooksHardeningFindings, collectIncludeFilePermFindings, collectModelHygieneFindings, collectSmallModelRiskFindings, collectPluginsTrustFindings, collectSecretsInConfigFindings, collectStateDeepFilesystemFindings, collectSyncedFolderFindings, readConfigSnapshotForAudit, } from "./audit-extra.js";
|
|
11
|
-
import { readChannelAllowFromStore } from "../pairing/pairing-store.js";
|
|
12
|
-
import { resolveNativeCommandsEnabled, resolveNativeSkillsEnabled } from "../config/commands.js";
|
|
11
|
+
import { collectChannelSecurityFindings } from "./audit-channel.js";
|
|
12
|
+
import { collectAttackSurfaceSummaryFindings, collectExposureMatrixFindings, collectGatewayHttpNoAuthFindings, collectGatewayHttpSessionKeyOverrideFindings, collectHooksHardeningFindings, collectIncludeFilePermFindings, collectInstalledSkillsCodeSafetyFindings, collectSandboxBrowserHashLabelFindings, collectMinimalProfileOverrideFindings, collectModelHygieneFindings, collectNodeDenyCommandPatternFindings, collectSmallModelRiskFindings, collectSandboxDangerousConfigFindings, collectSandboxDockerNoopFindings, collectPluginsTrustFindings, collectSecretsInConfigFindings, collectPluginsCodeSafetyFindings, collectStateDeepFilesystemFindings, collectSyncedFolderFindings, readConfigSnapshotForAudit, } from "./audit-extra.js";
|
|
13
13
|
import { formatPermissionDetail, formatPermissionRemediation, inspectPathPermissions, } from "./audit-fs.js";
|
|
14
|
+
import { DEFAULT_GATEWAY_HTTP_TOOL_DENY } from "./dangerous-tools.js";
|
|
14
15
|
function countBySeverity(findings) {
|
|
15
16
|
let critical = 0;
|
|
16
17
|
let warn = 0;
|
|
17
18
|
let info = 0;
|
|
18
19
|
for (const f of findings) {
|
|
19
|
-
if (f.severity === "critical")
|
|
20
|
+
if (f.severity === "critical") {
|
|
20
21
|
critical += 1;
|
|
21
|
-
|
|
22
|
+
}
|
|
23
|
+
else if (f.severity === "warn") {
|
|
22
24
|
warn += 1;
|
|
23
|
-
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
24
27
|
info += 1;
|
|
28
|
+
}
|
|
25
29
|
}
|
|
26
30
|
return { critical, warn, info };
|
|
27
31
|
}
|
|
28
32
|
function normalizeAllowFromList(list) {
|
|
29
|
-
if (!Array.isArray(list))
|
|
33
|
+
if (!Array.isArray(list)) {
|
|
30
34
|
return [];
|
|
35
|
+
}
|
|
31
36
|
return list.map((v) => String(v).trim()).filter(Boolean);
|
|
32
37
|
}
|
|
33
|
-
function
|
|
34
|
-
const
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
s.includes('dmpolicy="open"')) {
|
|
38
|
-
return "critical";
|
|
38
|
+
function collectEnabledInsecureOrDangerousFlags(cfg) {
|
|
39
|
+
const enabledFlags = [];
|
|
40
|
+
if (cfg.gateway?.controlUi?.allowInsecureAuth === true) {
|
|
41
|
+
enabledFlags.push("gateway.controlUi.allowInsecureAuth=true");
|
|
39
42
|
}
|
|
40
|
-
if (
|
|
41
|
-
|
|
43
|
+
if (cfg.gateway?.controlUi?.dangerouslyDisableDeviceAuth === true) {
|
|
44
|
+
enabledFlags.push("gateway.controlUi.dangerouslyDisableDeviceAuth=true");
|
|
42
45
|
}
|
|
43
|
-
if (
|
|
44
|
-
|
|
46
|
+
if (cfg.hooks?.gmail?.allowUnsafeExternalContent === true) {
|
|
47
|
+
enabledFlags.push("hooks.gmail.allowUnsafeExternalContent=true");
|
|
45
48
|
}
|
|
46
|
-
|
|
49
|
+
if (Array.isArray(cfg.hooks?.mappings)) {
|
|
50
|
+
for (const [index, mapping] of cfg.hooks.mappings.entries()) {
|
|
51
|
+
if (mapping?.allowUnsafeExternalContent === true) {
|
|
52
|
+
enabledFlags.push(`hooks.mappings[${index}].allowUnsafeExternalContent=true`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (cfg.tools?.exec?.applyPatch?.workspaceOnly === false) {
|
|
57
|
+
enabledFlags.push("tools.exec.applyPatch.workspaceOnly=false");
|
|
58
|
+
}
|
|
59
|
+
return enabledFlags;
|
|
47
60
|
}
|
|
48
61
|
async function collectFilesystemFindings(params) {
|
|
49
62
|
const findings = [];
|
|
@@ -66,7 +79,7 @@ async function collectFilesystemFindings(params) {
|
|
|
66
79
|
checkId: "fs.state_dir.perms_world_writable",
|
|
67
80
|
severity: "critical",
|
|
68
81
|
title: "State dir is world-writable",
|
|
69
|
-
detail: `${formatPermissionDetail(params.stateDir, stateDirPerms)}; other users can write into your
|
|
82
|
+
detail: `${formatPermissionDetail(params.stateDir, stateDirPerms)}; other users can write into your Pool Bot state.`,
|
|
70
83
|
remediation: formatPermissionRemediation({
|
|
71
84
|
targetPath: params.stateDir,
|
|
72
85
|
perms: stateDirPerms,
|
|
@@ -81,7 +94,7 @@ async function collectFilesystemFindings(params) {
|
|
|
81
94
|
checkId: "fs.state_dir.perms_group_writable",
|
|
82
95
|
severity: "warn",
|
|
83
96
|
title: "State dir is group-writable",
|
|
84
|
-
detail: `${formatPermissionDetail(params.stateDir, stateDirPerms)}; group users can write into your
|
|
97
|
+
detail: `${formatPermissionDetail(params.stateDir, stateDirPerms)}; group users can write into your Pool Bot state.`,
|
|
85
98
|
remediation: formatPermissionRemediation({
|
|
86
99
|
targetPath: params.stateDir,
|
|
87
100
|
perms: stateDirPerms,
|
|
@@ -113,6 +126,7 @@ async function collectFilesystemFindings(params) {
|
|
|
113
126
|
exec: params.execIcacls,
|
|
114
127
|
});
|
|
115
128
|
if (configPerms.ok) {
|
|
129
|
+
const skipReadablePermWarnings = configPerms.isSymlink;
|
|
116
130
|
if (configPerms.isSymlink) {
|
|
117
131
|
findings.push({
|
|
118
132
|
checkId: "fs.config.symlink",
|
|
@@ -136,7 +150,7 @@ async function collectFilesystemFindings(params) {
|
|
|
136
150
|
}),
|
|
137
151
|
});
|
|
138
152
|
}
|
|
139
|
-
else if (configPerms.worldReadable) {
|
|
153
|
+
else if (!skipReadablePermWarnings && configPerms.worldReadable) {
|
|
140
154
|
findings.push({
|
|
141
155
|
checkId: "fs.config.perms_world_readable",
|
|
142
156
|
severity: "critical",
|
|
@@ -151,7 +165,7 @@ async function collectFilesystemFindings(params) {
|
|
|
151
165
|
}),
|
|
152
166
|
});
|
|
153
167
|
}
|
|
154
|
-
else if (configPerms.groupReadable) {
|
|
168
|
+
else if (!skipReadablePermWarnings && configPerms.groupReadable) {
|
|
155
169
|
findings.push({
|
|
156
170
|
checkId: "fs.config.perms_group_readable",
|
|
157
171
|
severity: "warn",
|
|
@@ -181,12 +195,13 @@ function collectGatewayConfigFindings(cfg, env) {
|
|
|
181
195
|
const hasToken = typeof auth.token === "string" && auth.token.trim().length > 0;
|
|
182
196
|
const hasPassword = typeof auth.password === "string" && auth.password.trim().length > 0;
|
|
183
197
|
const hasSharedSecret = (auth.mode === "token" && hasToken) || (auth.mode === "password" && hasPassword);
|
|
184
|
-
const hasTailscaleAuth = auth.allowTailscale
|
|
198
|
+
const hasTailscaleAuth = auth.allowTailscale && tailscaleMode === "serve";
|
|
185
199
|
const hasGatewayAuth = hasSharedSecret || hasTailscaleAuth;
|
|
186
|
-
// HTTP /tools/invoke
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
|
|
200
|
+
// HTTP /tools/invoke is intended for narrow automation, not session orchestration/admin operations.
|
|
201
|
+
// If operators opt-in to re-enabling these tools over HTTP, warn loudly so the choice is explicit.
|
|
202
|
+
const gatewayToolsAllowRaw = Array.isArray(cfg.gateway?.tools?.allow)
|
|
203
|
+
? cfg.gateway?.tools?.allow
|
|
204
|
+
: [];
|
|
190
205
|
const gatewayToolsAllow = new Set(gatewayToolsAllowRaw
|
|
191
206
|
.map((v) => (typeof v === "string" ? v.trim().toLowerCase() : ""))
|
|
192
207
|
.filter(Boolean));
|
|
@@ -203,7 +218,7 @@ function collectGatewayConfigFindings(cfg, env) {
|
|
|
203
218
|
"If you keep them enabled, keep gateway.bind loopback-only (or tailnet-only), restrict network exposure, and treat the gateway token/password as full-admin.",
|
|
204
219
|
});
|
|
205
220
|
}
|
|
206
|
-
if (bind !== "loopback" && !hasSharedSecret) {
|
|
221
|
+
if (bind !== "loopback" && !hasSharedSecret && auth.mode !== "trusted-proxy") {
|
|
207
222
|
findings.push({
|
|
208
223
|
checkId: "gateway.bind_no_auth",
|
|
209
224
|
severity: "critical",
|
|
@@ -253,9 +268,9 @@ function collectGatewayConfigFindings(cfg, env) {
|
|
|
253
268
|
if (cfg.gateway?.controlUi?.allowInsecureAuth === true) {
|
|
254
269
|
findings.push({
|
|
255
270
|
checkId: "gateway.control_ui.insecure_auth",
|
|
256
|
-
severity: "
|
|
257
|
-
title: "Control UI
|
|
258
|
-
detail: "gateway.controlUi.allowInsecureAuth=true
|
|
271
|
+
severity: "warn",
|
|
272
|
+
title: "Control UI insecure auth toggle enabled",
|
|
273
|
+
detail: "gateway.controlUi.allowInsecureAuth=true does not bypass secure context or device identity checks; only dangerouslyDisableDeviceAuth disables Control UI device identity checks.",
|
|
259
274
|
remediation: "Disable it or switch to HTTPS (Tailscale Serve) or localhost.",
|
|
260
275
|
});
|
|
261
276
|
}
|
|
@@ -268,6 +283,16 @@ function collectGatewayConfigFindings(cfg, env) {
|
|
|
268
283
|
remediation: "Disable it unless you are in a short-lived break-glass scenario.",
|
|
269
284
|
});
|
|
270
285
|
}
|
|
286
|
+
const enabledDangerousFlags = collectEnabledInsecureOrDangerousFlags(cfg);
|
|
287
|
+
if (enabledDangerousFlags.length > 0) {
|
|
288
|
+
findings.push({
|
|
289
|
+
checkId: "config.insecure_or_dangerous_flags",
|
|
290
|
+
severity: "warn",
|
|
291
|
+
title: "Insecure or dangerous config flags enabled",
|
|
292
|
+
detail: `Detected ${enabledDangerousFlags.length} enabled flag(s): ${enabledDangerousFlags.join(", ")}.`,
|
|
293
|
+
remediation: "Disable these flags when not actively debugging, or keep deployment scoped to trusted/local-only networks.",
|
|
294
|
+
});
|
|
295
|
+
}
|
|
271
296
|
const token = typeof auth.token === "string" && auth.token.trim().length > 0 ? auth.token.trim() : null;
|
|
272
297
|
if (auth.mode === "token" && token && token.length < 24) {
|
|
273
298
|
findings.push({
|
|
@@ -277,8 +302,54 @@ function collectGatewayConfigFindings(cfg, env) {
|
|
|
277
302
|
detail: `gateway auth token is ${token.length} chars; prefer a long random token.`,
|
|
278
303
|
});
|
|
279
304
|
}
|
|
280
|
-
|
|
281
|
-
|
|
305
|
+
if (auth.mode === "trusted-proxy") {
|
|
306
|
+
const trustedProxies = cfg.gateway?.trustedProxies ?? [];
|
|
307
|
+
const trustedProxyConfig = cfg.gateway?.auth?.trustedProxy;
|
|
308
|
+
findings.push({
|
|
309
|
+
checkId: "gateway.trusted_proxy_auth",
|
|
310
|
+
severity: "critical",
|
|
311
|
+
title: "Trusted-proxy auth mode enabled",
|
|
312
|
+
detail: 'gateway.auth.mode="trusted-proxy" delegates authentication to a reverse proxy. ' +
|
|
313
|
+
"Ensure your proxy (Pomerium, Caddy, nginx) handles auth correctly and that gateway.trustedProxies " +
|
|
314
|
+
"only contains IPs of your actual proxy servers.",
|
|
315
|
+
remediation: "Verify: (1) Your proxy terminates TLS and authenticates users. " +
|
|
316
|
+
"(2) gateway.trustedProxies is restricted to proxy IPs only. " +
|
|
317
|
+
"(3) Direct access to the Gateway port is blocked by firewall. " +
|
|
318
|
+
"See /gateway/trusted-proxy-auth for setup guidance.",
|
|
319
|
+
});
|
|
320
|
+
if (trustedProxies.length === 0) {
|
|
321
|
+
findings.push({
|
|
322
|
+
checkId: "gateway.trusted_proxy_no_proxies",
|
|
323
|
+
severity: "critical",
|
|
324
|
+
title: "Trusted-proxy auth enabled but no trusted proxies configured",
|
|
325
|
+
detail: 'gateway.auth.mode="trusted-proxy" but gateway.trustedProxies is empty. ' +
|
|
326
|
+
"All requests will be rejected.",
|
|
327
|
+
remediation: "Set gateway.trustedProxies to the IP(s) of your reverse proxy.",
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
if (!trustedProxyConfig?.userHeader) {
|
|
331
|
+
findings.push({
|
|
332
|
+
checkId: "gateway.trusted_proxy_no_user_header",
|
|
333
|
+
severity: "critical",
|
|
334
|
+
title: "Trusted-proxy auth missing userHeader config",
|
|
335
|
+
detail: 'gateway.auth.mode="trusted-proxy" but gateway.auth.trustedProxy.userHeader is not configured.',
|
|
336
|
+
remediation: "Set gateway.auth.trustedProxy.userHeader to the header name your proxy uses " +
|
|
337
|
+
'(e.g., "x-forwarded-user", "x-pomerium-claim-email").',
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const allowUsers = trustedProxyConfig?.allowUsers ?? [];
|
|
341
|
+
if (allowUsers.length === 0) {
|
|
342
|
+
findings.push({
|
|
343
|
+
checkId: "gateway.trusted_proxy_no_allowlist",
|
|
344
|
+
severity: "warn",
|
|
345
|
+
title: "Trusted-proxy auth allows all authenticated users",
|
|
346
|
+
detail: "gateway.auth.trustedProxy.allowUsers is empty, so any user authenticated by your proxy can access the Gateway.",
|
|
347
|
+
remediation: "Consider setting gateway.auth.trustedProxy.allowUsers to restrict access to specific users " +
|
|
348
|
+
'(e.g., ["nick@example.com"]).',
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (bind !== "loopback" && auth.mode !== "trusted-proxy" && !cfg.gateway?.auth?.rateLimit) {
|
|
282
353
|
findings.push({
|
|
283
354
|
checkId: "gateway.auth_no_rate_limit",
|
|
284
355
|
severity: "warn",
|
|
@@ -290,7 +361,7 @@ function collectGatewayConfigFindings(cfg, env) {
|
|
|
290
361
|
}
|
|
291
362
|
return findings;
|
|
292
363
|
}
|
|
293
|
-
function collectBrowserControlFindings(cfg) {
|
|
364
|
+
function collectBrowserControlFindings(cfg, env) {
|
|
294
365
|
const findings = [];
|
|
295
366
|
let resolved;
|
|
296
367
|
try {
|
|
@@ -306,12 +377,25 @@ function collectBrowserControlFindings(cfg) {
|
|
|
306
377
|
});
|
|
307
378
|
return findings;
|
|
308
379
|
}
|
|
309
|
-
if (!resolved.enabled)
|
|
380
|
+
if (!resolved.enabled) {
|
|
310
381
|
return findings;
|
|
382
|
+
}
|
|
383
|
+
const browserAuth = resolveBrowserControlAuth(cfg, env);
|
|
384
|
+
if (!browserAuth.token && !browserAuth.password) {
|
|
385
|
+
findings.push({
|
|
386
|
+
checkId: "browser.control_no_auth",
|
|
387
|
+
severity: "critical",
|
|
388
|
+
title: "Browser control has no auth",
|
|
389
|
+
detail: "Browser control HTTP routes are enabled but no gateway.auth token/password is configured. " +
|
|
390
|
+
"Any local process (or SSRF to loopback) can call browser control endpoints.",
|
|
391
|
+
remediation: "Set gateway.auth.token (recommended) or gateway.auth.password so browser control HTTP routes require authentication. Restarting the gateway will auto-generate gateway.auth.token when browser control is enabled.",
|
|
392
|
+
});
|
|
393
|
+
}
|
|
311
394
|
for (const name of Object.keys(resolved.profiles)) {
|
|
312
395
|
const profile = resolveProfile(resolved, name);
|
|
313
|
-
if (!profile || profile.cdpIsLoopback)
|
|
396
|
+
if (!profile || profile.cdpIsLoopback) {
|
|
314
397
|
continue;
|
|
398
|
+
}
|
|
315
399
|
let url;
|
|
316
400
|
try {
|
|
317
401
|
url = new URL(profile.cdpUrl);
|
|
@@ -333,8 +417,9 @@ function collectBrowserControlFindings(cfg) {
|
|
|
333
417
|
}
|
|
334
418
|
function collectLoggingFindings(cfg) {
|
|
335
419
|
const redact = cfg.logging?.redactSensitive;
|
|
336
|
-
if (redact !== "off")
|
|
420
|
+
if (redact !== "off") {
|
|
337
421
|
return [];
|
|
422
|
+
}
|
|
338
423
|
return [
|
|
339
424
|
{
|
|
340
425
|
checkId: "logging.redact_off",
|
|
@@ -350,10 +435,12 @@ function collectElevatedFindings(cfg) {
|
|
|
350
435
|
const enabled = cfg.tools?.elevated?.enabled;
|
|
351
436
|
const allowFrom = cfg.tools?.elevated?.allowFrom ?? {};
|
|
352
437
|
const anyAllowFromKeys = Object.keys(allowFrom).length > 0;
|
|
353
|
-
if (enabled === false)
|
|
438
|
+
if (enabled === false) {
|
|
354
439
|
return findings;
|
|
355
|
-
|
|
440
|
+
}
|
|
441
|
+
if (!anyAllowFromKeys) {
|
|
356
442
|
return findings;
|
|
443
|
+
}
|
|
357
444
|
for (const [provider, list] of Object.entries(allowFrom)) {
|
|
358
445
|
const normalized = normalizeAllowFromList(list);
|
|
359
446
|
if (normalized.includes("*")) {
|
|
@@ -375,310 +462,39 @@ function collectElevatedFindings(cfg) {
|
|
|
375
462
|
}
|
|
376
463
|
return findings;
|
|
377
464
|
}
|
|
378
|
-
|
|
465
|
+
function collectExecRuntimeFindings(cfg) {
|
|
379
466
|
const findings = [];
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
const configAllowFrom = normalizeAllowFromList(input.allowFrom);
|
|
392
|
-
const hasWildcard = configAllowFrom.includes("*");
|
|
393
|
-
const dmScope = params.cfg.session?.dmScope ?? "main";
|
|
394
|
-
const storeAllowFrom = await readChannelAllowFromStore(input.provider).catch(() => []);
|
|
395
|
-
const normalizeEntry = input.normalizeEntry ?? ((value) => value);
|
|
396
|
-
const normalizedCfg = configAllowFrom
|
|
397
|
-
.filter((value) => value !== "*")
|
|
398
|
-
.map((value) => normalizeEntry(value))
|
|
399
|
-
.map((value) => value.trim())
|
|
400
|
-
.filter(Boolean);
|
|
401
|
-
const normalizedStore = storeAllowFrom
|
|
402
|
-
.map((value) => normalizeEntry(value))
|
|
403
|
-
.map((value) => value.trim())
|
|
404
|
-
.filter(Boolean);
|
|
405
|
-
const allowCount = Array.from(new Set([...normalizedCfg, ...normalizedStore])).length;
|
|
406
|
-
const isMultiUserDm = hasWildcard || allowCount > 1;
|
|
407
|
-
if (input.dmPolicy === "open") {
|
|
408
|
-
const allowFromKey = `${input.allowFromPath}allowFrom`;
|
|
409
|
-
findings.push({
|
|
410
|
-
checkId: `channels.${input.provider}.dm.open`,
|
|
411
|
-
severity: "critical",
|
|
412
|
-
title: `${input.label} DMs are open`,
|
|
413
|
-
detail: `${policyPath}="open" allows anyone to DM the bot.`,
|
|
414
|
-
remediation: `Use pairing/allowlist; if you really need open DMs, ensure ${allowFromKey} includes "*".`,
|
|
415
|
-
});
|
|
416
|
-
if (!hasWildcard) {
|
|
417
|
-
findings.push({
|
|
418
|
-
checkId: `channels.${input.provider}.dm.open_invalid`,
|
|
419
|
-
severity: "warn",
|
|
420
|
-
title: `${input.label} DM config looks inconsistent`,
|
|
421
|
-
detail: `"open" requires ${allowFromKey} to include "*".`,
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
if (input.dmPolicy === "disabled") {
|
|
426
|
-
findings.push({
|
|
427
|
-
checkId: `channels.${input.provider}.dm.disabled`,
|
|
428
|
-
severity: "info",
|
|
429
|
-
title: `${input.label} DMs are disabled`,
|
|
430
|
-
detail: `${policyPath}="disabled" ignores inbound DMs.`,
|
|
431
|
-
});
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
if (dmScope === "main" && isMultiUserDm) {
|
|
435
|
-
findings.push({
|
|
436
|
-
checkId: `channels.${input.provider}.dm.scope_main_multiuser`,
|
|
437
|
-
severity: "warn",
|
|
438
|
-
title: `${input.label} DMs share the main session`,
|
|
439
|
-
detail: "Multiple DM senders currently share the main session, which can leak context across users.",
|
|
440
|
-
remediation: 'Set session.dmScope="per-channel-peer" to isolate DM sessions per sender.',
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
};
|
|
444
|
-
for (const plugin of params.plugins) {
|
|
445
|
-
if (!plugin.security)
|
|
446
|
-
continue;
|
|
447
|
-
const accountIds = plugin.config.listAccountIds(params.cfg);
|
|
448
|
-
const defaultAccountId = resolveChannelDefaultAccountId({
|
|
449
|
-
plugin,
|
|
450
|
-
cfg: params.cfg,
|
|
451
|
-
accountIds,
|
|
467
|
+
const globalExecHost = cfg.tools?.exec?.host;
|
|
468
|
+
const defaultSandboxMode = resolveSandboxConfigForAgent(cfg).mode;
|
|
469
|
+
const defaultHostIsExplicitSandbox = globalExecHost === "sandbox";
|
|
470
|
+
if (defaultHostIsExplicitSandbox && defaultSandboxMode === "off") {
|
|
471
|
+
findings.push({
|
|
472
|
+
checkId: "tools.exec.host_sandbox_no_sandbox_defaults",
|
|
473
|
+
severity: "warn",
|
|
474
|
+
title: "Exec host is sandbox but sandbox mode is off",
|
|
475
|
+
detail: "tools.exec.host is explicitly set to sandbox while agents.defaults.sandbox.mode=off. " +
|
|
476
|
+
"In this mode, exec runs directly on the gateway host.",
|
|
477
|
+
remediation: 'Enable sandbox mode (`agents.defaults.sandbox.mode="non-main"` or `"all"`) or set tools.exec.host to "gateway" with approvals.',
|
|
452
478
|
});
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
providerId: "discord",
|
|
472
|
-
providerSetting: coerceNativeSetting(discordCfg.commands?.nativeSkills),
|
|
473
|
-
globalSetting: params.cfg.commands?.nativeSkills,
|
|
474
|
-
});
|
|
475
|
-
const slashEnabled = nativeEnabled || nativeSkillsEnabled;
|
|
476
|
-
if (slashEnabled) {
|
|
477
|
-
const defaultGroupPolicy = params.cfg.channels?.defaults?.groupPolicy;
|
|
478
|
-
const groupPolicy = discordCfg.groupPolicy ?? defaultGroupPolicy ?? "allowlist";
|
|
479
|
-
const guildEntries = discordCfg.guilds ?? {};
|
|
480
|
-
const guildsConfigured = Object.keys(guildEntries).length > 0;
|
|
481
|
-
const hasAnyUserAllowlist = Object.values(guildEntries).some((guild) => {
|
|
482
|
-
if (!guild || typeof guild !== "object")
|
|
483
|
-
return false;
|
|
484
|
-
const g = guild;
|
|
485
|
-
if (Array.isArray(g.users) && g.users.length > 0)
|
|
486
|
-
return true;
|
|
487
|
-
const channels = g.channels;
|
|
488
|
-
if (!channels || typeof channels !== "object")
|
|
489
|
-
return false;
|
|
490
|
-
return Object.values(channels).some((channel) => {
|
|
491
|
-
if (!channel || typeof channel !== "object")
|
|
492
|
-
return false;
|
|
493
|
-
const c = channel;
|
|
494
|
-
return Array.isArray(c.users) && c.users.length > 0;
|
|
495
|
-
});
|
|
496
|
-
});
|
|
497
|
-
const dmAllowFromRaw = discordCfg.dm?.allowFrom;
|
|
498
|
-
const dmAllowFrom = Array.isArray(dmAllowFromRaw) ? dmAllowFromRaw : [];
|
|
499
|
-
const storeAllowFrom = await readChannelAllowFromStore("discord").catch(() => []);
|
|
500
|
-
const ownerAllowFromConfigured = normalizeAllowFromList([...dmAllowFrom, ...storeAllowFrom]).length > 0;
|
|
501
|
-
const useAccessGroups = params.cfg.commands?.useAccessGroups !== false;
|
|
502
|
-
if (!useAccessGroups &&
|
|
503
|
-
groupPolicy !== "disabled" &&
|
|
504
|
-
guildsConfigured &&
|
|
505
|
-
!hasAnyUserAllowlist) {
|
|
506
|
-
findings.push({
|
|
507
|
-
checkId: "channels.discord.commands.native.unrestricted",
|
|
508
|
-
severity: "critical",
|
|
509
|
-
title: "Discord slash commands are unrestricted",
|
|
510
|
-
detail: "commands.useAccessGroups=false disables sender allowlists for Discord slash commands unless a per-guild/channel users allowlist is configured; with no users allowlist, any user in allowed guild channels can invoke /… commands.",
|
|
511
|
-
remediation: "Set commands.useAccessGroups=true (recommended), or configure channels.discord.guilds.<id>.users (or channels.discord.guilds.<id>.channels.<channel>.users).",
|
|
512
|
-
});
|
|
513
|
-
}
|
|
514
|
-
else if (useAccessGroups &&
|
|
515
|
-
groupPolicy !== "disabled" &&
|
|
516
|
-
guildsConfigured &&
|
|
517
|
-
!ownerAllowFromConfigured &&
|
|
518
|
-
!hasAnyUserAllowlist) {
|
|
519
|
-
findings.push({
|
|
520
|
-
checkId: "channels.discord.commands.native.no_allowlists",
|
|
521
|
-
severity: "warn",
|
|
522
|
-
title: "Discord slash commands have no allowlists",
|
|
523
|
-
detail: "Discord slash commands are enabled, but neither an owner allowFrom list nor any per-guild/channel users allowlist is configured; /… commands will be rejected for everyone.",
|
|
524
|
-
remediation: "Add your user id to channels.discord.dm.allowFrom (or approve yourself via pairing), or configure channels.discord.guilds.<id>.users.",
|
|
525
|
-
});
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
if (plugin.id === "slack") {
|
|
530
|
-
const slackCfg = account
|
|
531
|
-
?.config ?? {};
|
|
532
|
-
const nativeEnabled = resolveNativeCommandsEnabled({
|
|
533
|
-
providerId: "slack",
|
|
534
|
-
providerSetting: coerceNativeSetting(slackCfg.commands?.native),
|
|
535
|
-
globalSetting: params.cfg.commands?.native,
|
|
536
|
-
});
|
|
537
|
-
const nativeSkillsEnabled = resolveNativeSkillsEnabled({
|
|
538
|
-
providerId: "slack",
|
|
539
|
-
providerSetting: coerceNativeSetting(slackCfg.commands?.nativeSkills),
|
|
540
|
-
globalSetting: params.cfg.commands?.nativeSkills,
|
|
541
|
-
});
|
|
542
|
-
const slashCommandEnabled = nativeEnabled ||
|
|
543
|
-
nativeSkillsEnabled ||
|
|
544
|
-
slackCfg.slashCommand?.enabled === true;
|
|
545
|
-
if (slashCommandEnabled) {
|
|
546
|
-
const useAccessGroups = params.cfg.commands?.useAccessGroups !== false;
|
|
547
|
-
if (!useAccessGroups) {
|
|
548
|
-
findings.push({
|
|
549
|
-
checkId: "channels.slack.commands.slash.useAccessGroups_off",
|
|
550
|
-
severity: "critical",
|
|
551
|
-
title: "Slack slash commands bypass access groups",
|
|
552
|
-
detail: "Slack slash/native commands are enabled while commands.useAccessGroups=false; this can allow unrestricted /… command execution from channels/users you didn't explicitly authorize.",
|
|
553
|
-
remediation: "Set commands.useAccessGroups=true (recommended).",
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
else {
|
|
557
|
-
const dmAllowFromRaw = account?.dm
|
|
558
|
-
?.allowFrom;
|
|
559
|
-
const dmAllowFrom = Array.isArray(dmAllowFromRaw) ? dmAllowFromRaw : [];
|
|
560
|
-
const storeAllowFrom = await readChannelAllowFromStore("slack").catch(() => []);
|
|
561
|
-
const ownerAllowFromConfigured = normalizeAllowFromList([...dmAllowFrom, ...storeAllowFrom]).length > 0;
|
|
562
|
-
const channels = slackCfg.channels ?? {};
|
|
563
|
-
const hasAnyChannelUsersAllowlist = Object.values(channels).some((value) => {
|
|
564
|
-
if (!value || typeof value !== "object")
|
|
565
|
-
return false;
|
|
566
|
-
const channel = value;
|
|
567
|
-
return Array.isArray(channel.users) && channel.users.length > 0;
|
|
568
|
-
});
|
|
569
|
-
if (!ownerAllowFromConfigured && !hasAnyChannelUsersAllowlist) {
|
|
570
|
-
findings.push({
|
|
571
|
-
checkId: "channels.slack.commands.slash.no_allowlists",
|
|
572
|
-
severity: "warn",
|
|
573
|
-
title: "Slack slash commands have no allowlists",
|
|
574
|
-
detail: "Slack slash/native commands are enabled, but neither an owner allowFrom list nor any channels.<id>.users allowlist is configured; /… commands will be rejected for everyone.",
|
|
575
|
-
remediation: "Approve yourself via pairing (recommended), or set channels.slack.dm.allowFrom and/or channels.slack.channels.<id>.users.",
|
|
576
|
-
});
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
const dmPolicy = plugin.security.resolveDmPolicy?.({
|
|
582
|
-
cfg: params.cfg,
|
|
583
|
-
accountId: defaultAccountId,
|
|
584
|
-
account,
|
|
479
|
+
}
|
|
480
|
+
const agents = Array.isArray(cfg.agents?.list) ? cfg.agents.list : [];
|
|
481
|
+
const riskyAgents = agents
|
|
482
|
+
.filter((entry) => entry &&
|
|
483
|
+
typeof entry === "object" &&
|
|
484
|
+
typeof entry.id === "string" &&
|
|
485
|
+
entry.tools?.exec?.host === "sandbox" &&
|
|
486
|
+
resolveSandboxConfigForAgent(cfg, entry.id).mode === "off")
|
|
487
|
+
.map((entry) => entry.id)
|
|
488
|
+
.slice(0, 5);
|
|
489
|
+
if (riskyAgents.length > 0) {
|
|
490
|
+
findings.push({
|
|
491
|
+
checkId: "tools.exec.host_sandbox_no_sandbox_agents",
|
|
492
|
+
severity: "warn",
|
|
493
|
+
title: "Agent exec host uses sandbox while sandbox mode is off",
|
|
494
|
+
detail: `agents.list.*.tools.exec.host is set to sandbox for: ${riskyAgents.join(", ")}. ` +
|
|
495
|
+
"With sandbox mode off, exec runs directly on the gateway host.",
|
|
496
|
+
remediation: 'Enable sandbox mode for these agents (`agents.list[].sandbox.mode`) or set their tools.exec.host to "gateway".',
|
|
585
497
|
});
|
|
586
|
-
if (dmPolicy) {
|
|
587
|
-
await warnDmPolicy({
|
|
588
|
-
label: plugin.meta.label ?? plugin.id,
|
|
589
|
-
provider: plugin.id,
|
|
590
|
-
dmPolicy: dmPolicy.policy,
|
|
591
|
-
allowFrom: dmPolicy.allowFrom,
|
|
592
|
-
policyPath: dmPolicy.policyPath,
|
|
593
|
-
allowFromPath: dmPolicy.allowFromPath,
|
|
594
|
-
normalizeEntry: dmPolicy.normalizeEntry,
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
if (plugin.security.collectWarnings) {
|
|
598
|
-
const warnings = await plugin.security.collectWarnings({
|
|
599
|
-
cfg: params.cfg,
|
|
600
|
-
accountId: defaultAccountId,
|
|
601
|
-
account,
|
|
602
|
-
});
|
|
603
|
-
for (const message of warnings ?? []) {
|
|
604
|
-
const trimmed = String(message).trim();
|
|
605
|
-
if (!trimmed)
|
|
606
|
-
continue;
|
|
607
|
-
findings.push({
|
|
608
|
-
checkId: `channels.${plugin.id}.warning.${findings.length + 1}`,
|
|
609
|
-
severity: classifyChannelWarningSeverity(trimmed),
|
|
610
|
-
title: `${plugin.meta.label ?? plugin.id} security warning`,
|
|
611
|
-
detail: trimmed.replace(/^-\s*/, ""),
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
if (plugin.id === "telegram") {
|
|
616
|
-
const allowTextCommands = params.cfg.commands?.text !== false;
|
|
617
|
-
if (!allowTextCommands)
|
|
618
|
-
continue;
|
|
619
|
-
const telegramCfg = account?.config ??
|
|
620
|
-
{};
|
|
621
|
-
const defaultGroupPolicy = params.cfg.channels?.defaults?.groupPolicy;
|
|
622
|
-
const groupPolicy = telegramCfg.groupPolicy ?? defaultGroupPolicy ?? "allowlist";
|
|
623
|
-
const groups = telegramCfg.groups;
|
|
624
|
-
const groupsConfigured = Boolean(groups) && Object.keys(groups ?? {}).length > 0;
|
|
625
|
-
const groupAccessPossible = groupPolicy === "open" || (groupPolicy === "allowlist" && groupsConfigured);
|
|
626
|
-
if (!groupAccessPossible)
|
|
627
|
-
continue;
|
|
628
|
-
const storeAllowFrom = await readChannelAllowFromStore("telegram").catch(() => []);
|
|
629
|
-
const storeHasWildcard = storeAllowFrom.some((v) => String(v).trim() === "*");
|
|
630
|
-
const groupAllowFrom = Array.isArray(telegramCfg.groupAllowFrom)
|
|
631
|
-
? telegramCfg.groupAllowFrom
|
|
632
|
-
: [];
|
|
633
|
-
const groupAllowFromHasWildcard = groupAllowFrom.some((v) => String(v).trim() === "*");
|
|
634
|
-
const anyGroupOverride = Boolean(groups &&
|
|
635
|
-
Object.values(groups).some((value) => {
|
|
636
|
-
if (!value || typeof value !== "object")
|
|
637
|
-
return false;
|
|
638
|
-
const group = value;
|
|
639
|
-
const allowFrom = Array.isArray(group.allowFrom) ? group.allowFrom : [];
|
|
640
|
-
if (allowFrom.length > 0)
|
|
641
|
-
return true;
|
|
642
|
-
const topics = group.topics;
|
|
643
|
-
if (!topics || typeof topics !== "object")
|
|
644
|
-
return false;
|
|
645
|
-
return Object.values(topics).some((topicValue) => {
|
|
646
|
-
if (!topicValue || typeof topicValue !== "object")
|
|
647
|
-
return false;
|
|
648
|
-
const topic = topicValue;
|
|
649
|
-
const topicAllow = Array.isArray(topic.allowFrom) ? topic.allowFrom : [];
|
|
650
|
-
return topicAllow.length > 0;
|
|
651
|
-
});
|
|
652
|
-
}));
|
|
653
|
-
const hasAnySenderAllowlist = storeAllowFrom.length > 0 || groupAllowFrom.length > 0 || anyGroupOverride;
|
|
654
|
-
if (storeHasWildcard || groupAllowFromHasWildcard) {
|
|
655
|
-
findings.push({
|
|
656
|
-
checkId: "channels.telegram.groups.allowFrom.wildcard",
|
|
657
|
-
severity: "critical",
|
|
658
|
-
title: "Telegram group allowlist contains wildcard",
|
|
659
|
-
detail: 'Telegram group sender allowlist contains "*", which allows any group member to run /… commands and control directives.',
|
|
660
|
-
remediation: 'Remove "*" from channels.telegram.groupAllowFrom and pairing store; prefer explicit user ids/usernames.',
|
|
661
|
-
});
|
|
662
|
-
continue;
|
|
663
|
-
}
|
|
664
|
-
if (!hasAnySenderAllowlist) {
|
|
665
|
-
const providerSetting = telegramCfg.commands
|
|
666
|
-
?.nativeSkills;
|
|
667
|
-
const skillsEnabled = resolveNativeSkillsEnabled({
|
|
668
|
-
providerId: "telegram",
|
|
669
|
-
providerSetting,
|
|
670
|
-
globalSetting: params.cfg.commands?.nativeSkills,
|
|
671
|
-
});
|
|
672
|
-
findings.push({
|
|
673
|
-
checkId: "channels.telegram.groups.allowFrom.missing",
|
|
674
|
-
severity: "critical",
|
|
675
|
-
title: "Telegram group commands have no sender allowlist",
|
|
676
|
-
detail: `Telegram group access is enabled but no sender allowlist is configured; this allows any group member to invoke /… commands` +
|
|
677
|
-
(skillsEnabled ? " (including skill commands)." : "."),
|
|
678
|
-
remediation: "Approve yourself via pairing (recommended), or set channels.telegram.groupAllowFrom (or per-group groups.<id>.allowFrom).",
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
498
|
}
|
|
683
499
|
return findings;
|
|
684
500
|
}
|
|
@@ -688,29 +504,9 @@ async function maybeProbeGateway(params) {
|
|
|
688
504
|
const isRemoteMode = params.cfg.gateway?.mode === "remote";
|
|
689
505
|
const remoteUrlRaw = typeof params.cfg.gateway?.remote?.url === "string" ? params.cfg.gateway.remote.url.trim() : "";
|
|
690
506
|
const remoteUrlMissing = isRemoteMode && !remoteUrlRaw;
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
const remote = params.cfg.gateway?.remote;
|
|
695
|
-
const token = mode === "remote"
|
|
696
|
-
? typeof remote?.token === "string" && remote.token.trim()
|
|
697
|
-
? remote.token.trim()
|
|
698
|
-
: undefined
|
|
699
|
-
: process.env.POOLBOT_GATEWAY_TOKEN?.trim() ||
|
|
700
|
-
process.env.CLAWDBOT_GATEWAY_TOKEN?.trim() ||
|
|
701
|
-
(typeof authToken === "string" && authToken.trim() ? authToken.trim() : undefined);
|
|
702
|
-
const password = process.env.POOLBOT_GATEWAY_PASSWORD?.trim() ||
|
|
703
|
-
process.env.CLAWDBOT_GATEWAY_PASSWORD?.trim() ||
|
|
704
|
-
(mode === "remote"
|
|
705
|
-
? typeof remote?.password === "string" && remote.password.trim()
|
|
706
|
-
? remote.password.trim()
|
|
707
|
-
: undefined
|
|
708
|
-
: typeof authPassword === "string" && authPassword.trim()
|
|
709
|
-
? authPassword.trim()
|
|
710
|
-
: undefined);
|
|
711
|
-
return { token, password };
|
|
712
|
-
};
|
|
713
|
-
const auth = !isRemoteMode || remoteUrlMissing ? resolveAuth("local") : resolveAuth("remote");
|
|
507
|
+
const auth = !isRemoteMode || remoteUrlMissing
|
|
508
|
+
? resolveGatewayProbeAuth({ cfg: params.cfg, mode: "local" })
|
|
509
|
+
: resolveGatewayProbeAuth({ cfg: params.cfg, mode: "remote" });
|
|
714
510
|
const res = await params.probe({ url, auth, timeoutMs: params.timeoutMs }).catch((err) => ({
|
|
715
511
|
ok: false,
|
|
716
512
|
url,
|
|
@@ -743,10 +539,17 @@ export async function runSecurityAudit(opts) {
|
|
|
743
539
|
findings.push(...collectAttackSurfaceSummaryFindings(cfg));
|
|
744
540
|
findings.push(...collectSyncedFolderFindings({ stateDir, configPath }));
|
|
745
541
|
findings.push(...collectGatewayConfigFindings(cfg, env));
|
|
746
|
-
findings.push(...collectBrowserControlFindings(cfg));
|
|
542
|
+
findings.push(...collectBrowserControlFindings(cfg, env));
|
|
747
543
|
findings.push(...collectLoggingFindings(cfg));
|
|
748
544
|
findings.push(...collectElevatedFindings(cfg));
|
|
749
|
-
findings.push(...
|
|
545
|
+
findings.push(...collectExecRuntimeFindings(cfg));
|
|
546
|
+
findings.push(...collectHooksHardeningFindings(cfg, env));
|
|
547
|
+
findings.push(...collectGatewayHttpNoAuthFindings(cfg, env));
|
|
548
|
+
findings.push(...collectGatewayHttpSessionKeyOverrideFindings(cfg));
|
|
549
|
+
findings.push(...collectSandboxDockerNoopFindings(cfg));
|
|
550
|
+
findings.push(...collectSandboxDangerousConfigFindings(cfg));
|
|
551
|
+
findings.push(...collectNodeDenyCommandPatternFindings(cfg));
|
|
552
|
+
findings.push(...collectMinimalProfileOverrideFindings(cfg));
|
|
750
553
|
findings.push(...collectSecretsInConfigFindings(cfg));
|
|
751
554
|
findings.push(...collectModelHygieneFindings(cfg));
|
|
752
555
|
findings.push(...collectSmallModelRiskFindings({ cfg, env }));
|
|
@@ -766,7 +569,14 @@ export async function runSecurityAudit(opts) {
|
|
|
766
569
|
findings.push(...(await collectIncludeFilePermFindings({ configSnapshot, env, platform, execIcacls })));
|
|
767
570
|
}
|
|
768
571
|
findings.push(...(await collectStateDeepFilesystemFindings({ cfg, env, stateDir, platform, execIcacls })));
|
|
572
|
+
findings.push(...(await collectSandboxBrowserHashLabelFindings({
|
|
573
|
+
execDockerRawFn: opts.execDockerRawFn,
|
|
574
|
+
})));
|
|
769
575
|
findings.push(...(await collectPluginsTrustFindings({ cfg, stateDir })));
|
|
576
|
+
if (opts.deep === true) {
|
|
577
|
+
findings.push(...(await collectPluginsCodeSafetyFindings({ stateDir })));
|
|
578
|
+
findings.push(...(await collectInstalledSkillsCodeSafetyFindings({ cfg, stateDir })));
|
|
579
|
+
}
|
|
770
580
|
}
|
|
771
581
|
if (opts.includeChannelSecurity !== false) {
|
|
772
582
|
const plugins = opts.plugins ?? listChannelPlugins();
|
|
@@ -779,7 +589,7 @@ export async function runSecurityAudit(opts) {
|
|
|
779
589
|
probe: opts.probeGatewayFn ?? probeGateway,
|
|
780
590
|
})
|
|
781
591
|
: undefined;
|
|
782
|
-
if (deep?.gateway?.attempted && deep.gateway.ok
|
|
592
|
+
if (deep?.gateway?.attempted && !deep.gateway.ok) {
|
|
783
593
|
findings.push({
|
|
784
594
|
checkId: "gateway.probe_failed",
|
|
785
595
|
severity: "warn",
|