Skip to content

Security harden webhooks, HTTP secrets, and cron shell jobs - #907

Merged
DonPrus merged 28 commits into
nullclaw:mainfrom
racribeiro:main
May 28, 2026
Merged

DonPrus merged 28 commits into
nullclaw:mainfrom
racribeiro:main

Conversation

@racribeiro

Copy link
Copy Markdown
Contributor

Summary

  • Remove credentialed curl subprocess usage from provider and channel HTTP paths, and reject credential-bearing headers/token query params in remaining curl
    helpers.
  • Require explicit inbound trust for Telegram, Discord, and LINE by denying empty allow_from lists and requiring Telegram webhook secret-token validation.
  • Enforce shell security policy for cron shell jobs at create, update, manual run, and scheduled execution time.
  • Add local Docker/Compose build workflow and document the security changes in English and Chinese docs.

Security Notes

  • Telegram webhook requests now require X-Telegram-Bot-Api-Secret-Token to match channels.telegram.accounts..webhook_secret.
  • Empty allowlists now deny inbound messages on updated allowlist-based channels. Use ["*"] only for intentional allow-all behavior.
  • Provider/channel secrets are no longer passed through child process argv for the updated HTTP paths.
  • Cron shell jobs now use the shared process runner with policy validation, scrubbed environment, timeout, output limits, and improved descendant cleanup.

Validation

  • docker run --rm -v /home/ubuntu/claws/nullclaw:/app -w /app nullclaw:test-builder sh -lc 'ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache zig build test
    --summary all'
    • Passed: 6992/7006, 14 skipped
  • docker run --rm -v /home/ubuntu/claws/nullclaw:/app -w /app nullclaw:test-builder sh -lc 'ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache zig build
    -Doptimize=ReleaseSmall'
    • Passed
  • docker run --rm -v /home/ubuntu/claws/nullclaw:/app -w /app nullclaw:test-builder zig fmt --check src/
    • Passed

racribeiro added 21 commits May 10, 2026 18:02
Move credentialed provider and channel HTTP calls to std.http helpers and make remaining curl subprocess helpers reject credential headers or sensitive token query parameters.

Validation: docker run --rm -v /home/ubuntu/claws/nullclaw:/app -w /app nullclaw:test-builder sh -lc 'ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache zig build test --summary all'

Validation: docker run --rm -v /home/ubuntu/claws/nullclaw:/app -w /app nullclaw:test-builder sh -lc 'ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache zig build -Doptimize=ReleaseSmall'
Add Telegram webhook secret configuration and enforce X-Telegram-Bot-Api-Secret-Token before webhook dispatch. Change Telegram gateway, Discord, and LINE allowlist handling so an empty allow_from denies inbound messages and only explicit '*' allows all.

Validation: docker run --rm -v /home/ubuntu/claws/nullclaw:/app -w /app nullclaw:test-builder sh -lc 'ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache zig build test --summary all'

Validation: docker run --rm -v /home/ubuntu/claws/nullclaw:/app -w /app nullclaw:test-builder sh -lc 'ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache zig build -Doptimize=ReleaseSmall'
Validate cron shell commands at create, update, CLI run, and scheduled execution time. Run shell jobs through the shared process runner with scrubbed environment, timeout, output limits, and improved descendant cleanup on Linux.

Validation: docker run --rm -v /home/ubuntu/claws/nullclaw:/app -w /app nullclaw:test-builder sh -lc 'ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache zig build test --summary all'

Validation: docker run --rm -v /home/ubuntu/claws/nullclaw:/app -w /app nullclaw:test-builder sh -lc 'ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache zig build -Doptimize=ReleaseSmall'
Add Makefile shortcuts and docker compose build configuration for local gateway/agent images. Vendor websocket and wasm3 during Docker builds and verify the pinned Zig 0.16.0 toolchain.

Validation: docker run --rm -v /home/ubuntu/claws/nullclaw:/app -w /app nullclaw:test-builder sh -lc 'ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache zig build test --summary all'

Validation: docker run --rm -v /home/ubuntu/claws/nullclaw:/app -w /app nullclaw:test-builder sh -lc 'ZIG_GLOBAL_CACHE_DIR=/tmp/zig-global-cache zig build -Doptimize=ReleaseSmall'
Document deny-empty allowlist behavior, Telegram webhook secret requirements, and gateway API authentication changes in English and Chinese docs. Add the 2026-05-10 security patch plan and validation checklist.

Validation: docs-only commit; code validation already passed in prior grouped commits.
Add NULLCLAW_PORT to the Makefile and use it for the compose gateway command, published port, and healthcheck. The local workflow now defaults to 127.0.0.1:3210.

Validation: docker compose --profile gateway config

Validation: git diff --check
Bind the compose gateway port on 0.0.0.0 so the NULLCLAW_PORT workflow is reachable from host interfaces instead of loopback only.

Validation: docker compose --profile gateway config

Validation: git diff --check
Add a Makefile config target that runs nullclaw onboard in the agent container, and make up depend on config before starting the gateway. Also bind the gateway process to 0.0.0.0 inside the container to match the published host interface.

Validation: docker compose --profile gateway config

Validation: make -n up

Validation: git diff --check
Point the compose healthcheck at 127.0.0.1 so it checks the IPv4 listener used by the gateway's 0.0.0.0 bind.

Validation: docker compose --profile gateway config

Validation: git diff --check
Document the Makefile-backed Docker Compose setup, including make config, make up, NULLCLAW_PORT=3210, host-interface publishing, and health checks.

Validation: make -n config

Validation: make -n up

Validation: git diff --check
Mount ./workspace at /nullclaw-data/workspace for init, agent, and gateway services so the runtime workspace lives under the current checkout. Ignore the local workspace directory because it contains runtime state.

Validation: docker compose --profile gateway config

Validation: git diff --check
Add make agent for interactive nullclaw agent chat and make status for nullclaw status via the compose agent service.

Validation: make -n agent

Validation: make -n status

Validation: git diff --check
Make configuration an explicit make config step instead of a dependency of make up, so make down preserves the existing config volume and restart does not rerun onboarding.

Validation: make -n up

Validation: make -n config

Validation: git diff --check
Install git in the release image and set SHELL=/bin/sh so the compose workflow satisfies doctor checks and supports documented git/tool usage.

Validation: docker compose run --rm agent doctor

Validation: docker compose run --rm agent status

Validation: docker compose --profile gateway config

Validation: git diff --check
Drop EXPOSE 3000 from the runtime image because compose publishes NULLCLAW_PORT explicitly. This avoids docker ps showing an unused 3000/tcp port while the gateway runs on 3210.

Validation: docker compose --profile gateway config

Validation: docker compose --profile gateway up -d --build gateway

Validation: docker compose ps

Validation: git diff --check
Mount ./config.json into /nullclaw-data/config.json for init, agent, and gateway services. Run compose services as the host UID/GID from the Makefile so bind-mounted config and workspace files remain host-editable.

Validation: docker compose --profile gateway config

Validation: make down

Validation: make up

Validation: docker compose ps

Validation: git diff --check
Run docker compose down with both agent and gateway profiles so profile-created containers are stopped and removed by make down.

Validation: make down

Validation: make up

Validation: curl -sS -i http://127.0.0.1:3210/health

@addadi addadi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE - Excellent security patch.

Strengths:

  • Well-scoped, no feature creep
  • Constant-time webhook secret comparison
  • Comprehensive test coverage for all security boundaries
  • Provider paths migrated to std.http.Client (no argv exposure)
  • Cron shell jobs now routed through SecurityPolicy
  • Empty allow_from now denies (breaking change well-documented)

Minor notes:

  • Docker gateway binds 0.0.0.0 by default (documented in README, consider 127.0.0.1 for local dev)
  • Verify constantTimeEq implementation or use std.crypto.timing_safe.eql

CI: 6992/7006 tests passing (14 pre-existing failures unrelated to this PR).

Recommended merge sequence:

  1. Merge this PR
  2. Release security advisory with credential rotation guidance
  3. Update deployment docs for 0.0.0.0 binding implications

@addadi addadi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE

Excellent security hardening across 4 areas:

✅ Telegram webhook secret validation with constant-time comparison
✅ Credentialed curl rejection prevents argv secret leakage
✅ Cron shell jobs now enforce SecurityPolicy
✅ Empty allowlists default-deny (require explicit "*" for allow-all)

Test coverage: 6992/7006 passed (14 skipped)

Minor suggestions:

  • Consider if all new Docker runtime deps are needed
  • Document webhook secret rotation procedure
  • Add migration note for allowlist breaking change

Well-scoped, well-tested security patch.

@DonPrus
DonPrus merged commit b8f2f97 into nullclaw:main May 28, 2026
3 checks passed
beezzoo pushed a commit to beezzoo/nullclaw that referenced this pull request Aug 4, 2026
std.http.Client crashes in crypto.tls.Client.init when called from
the typing indicator thread (spawned per-message). PR nullclaw#907 migrated
telegram_api.post() from curlPostWithProxy to httpPostJsonWithProxy,
but std.http.Client is not safe for concurrent TLS init from threads
that don't own the Io context. Revert to curl subprocess for Telegram
API calls until upstream resolves the threading issue.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants