fix(cli): suppress non-CLI listeners under --cli-only (#1840)#1869
Conversation
- Gate webhook, WASM, Signal, HTTP, gateway, tunnel, and orchestrator startup behind --cli-only - Restore the expected --cli-only contract so no non-CLI listeners bind or expose services - Prevent unintended network exposure from fallback listeners and managed tunnels - Stop registering job_prompt when no orchestrator is running to consume prompts - Fix sandbox readiness reporting under --cli-only so it reports disabled, not unavailable - Centralize the guard through non_cli_channels_enabled() for consistent startup behavior - Add regression coverage for unguarded network startup paths in async_main - Document --cli-only listener suppression in NETWORK_SECURITY.md
There was a problem hiding this comment.
Code Review
This pull request introduces a --cli-only mode to suppress all non-CLI network services, such as webhooks, WASM channels, and managed tunnels, ensuring no network listeners start when the flag is active. The changes include documentation updates and a regression test suite that performs a structural check on main.rs to verify all ingress paths are guarded. A potential panic was identified in the test code where string slicing using byte offsets could split multi-byte UTF-8 characters; a byte-based search approach was suggested to resolve this.
| let window_start = abs.saturating_sub(500); | ||
| let window = &async_main_body[window_start..abs]; |
There was a problem hiding this comment.
Slicing a string using byte offsets derived from saturating_sub can lead to a panic if the offset lands in the middle of a multi-byte UTF-8 character. Using byte-based searching (as suggested) avoids this risk. Additionally, when detecting keywords like 'enable_non_cli', ensure you use word-boundary checks instead of simple substring containment to avoid false positives where the keyword is part of a larger string.
| let window_start = abs.saturating_sub(500); | |
| let window = &async_main_body[window_start..abs]; | |
| let window_start = abs.saturating_sub(500); | |
| let window = &async_main_body.as_bytes()[window_start..abs]; | |
| assert!( | |
| window.windows("enable_non_cli".len()).any(|w| w == "enable_non_cli".as_bytes()), |
References
- When truncating or slicing a UTF-8 string, use character-aware methods or byte-based operations to avoid panics caused by slicing in the middle of a multi-byte character.
- When detecting commands or keywords in a string, use token-based or word-boundary checks instead of simple substring containment to avoid false positives.
zmanian
left a comment
There was a problem hiding this comment.
Review -- APPROVE
Good security fix closing a real gap where --cli-only wasn't suppressing network listeners (webhooks, WASM channels, HTTP, Signal, gateway, tunnels, orchestrator API). Single enable_non_cli guard applied consistently. NETWORK_SECURITY.md updated.
Minor nits (non-blocking)
non_cli_channels_enabled(cli_only: bool) -> boolis just!cli_only-- the indirection adds no value- Source grep regression test is clever but fragile (500-char window is arbitrary)
|
can it merge? |
…hook-server-1840 # Conflicts: # src/NETWORK_SECURITY.md
Review — paranoid architectThanks for tightening High — relay channels bypass the guard
if let Some(ref ext_mgr) = components.extension_manager {
ext_mgr.set_relay_channel_manager(Arc::clone(&channels)).await;
ext_mgr.restore_relay_channels(&ext_user_id).await;
}Under
The comment at The Medium — tool-registration view of
|
Guard persisted relay channel restoration behind the non-CLI startup flag so CLI-only mode does not make relay calls or hot-add relay channels. Update the CLI-only regression test to check guarded startup calls with byte-based, brace-delimited source scanning, and document that CLI- only suppresses network-facing services while preserving scheduler-backed job tools.
…hook-server-1840 # Conflicts: # src/main.rs
|
updated |
Summary
tunnels, and the sandbox orchestrator API could still start network listeners
without a consumer, and sandbox readiness could be misreported as unavailable instead of disabled
Change Type
Linked Issue
Fixes #1840
Validation
cargo fmt --all -- --checkcargo clippy --all --benches --tests --examples --all-features -- -D warningscargo buildcargo test --features integrationif database-backed or integration behavior changedreview-prorpr-shepherd --fixwas run before requesting reviewSecurity Impact
This fixes unintended listener startup under --cli-only, including webhook ingress, gateway HTTP serving, managed
tunnels, and the sandbox orchestrator API. The value of the change is that --cli-only now consistently suppresses non-
CLI network-facing services, which restores operator trust in the mode’s security boundary.
Database Impact
None
Blast Radius
Rollback Plan
Revert the commit. The change is limited to startup wiring and documentation, with no schema, migration, or config-
format changes.
Review Follow-Through
Review track:
B — bug fix affecting startup behavior and security-sensitive listener initialization