* fix(sandbox): avoid shell in FsBridge writes
Replace sh -c with interpolated path by shell-free 'tee -- <path>' argv form,
piping content via stdin. Prevents command injection through filenames
containing shell metacharacters inside the sandbox container.
Co-authored-by: evgyur <evgyur@gmail.com>
* fix(security): fail-closed on pairing DB errors across channels
On IsPaired lookup error, deny instead of granting access. Covers the shared
CheckDMPolicy/CheckGroupPolicy helpers (Slack/Discord/Feishu/WhatsApp/Zalo) and
the four inline Telegram pairing checks.
Co-authored-by: Srini <srinis.k@gmail.com>
* fix(security): harden provider URL validation against SSRF
Enforce scheme check for all provider types; restrict local types (ollama,
claude_cli, acp) to an explicit localhost allowlist instead of skipping checks;
resolve remote hostnames and reject any IP in a private/reserved range via the
shared security.IsBlocked CIDR list (covers loopback, link-local, metadata,
multicast, and unspecified 0.0.0.0/::). Closes the wildcard-DNS bypass and the
local-type escape hatch. Operator opt-in via GOCLAW_ALLOW_PRIVATE_PROVIDER_URLS.
Exports security.IsBlocked as the single source of truth for blocked ranges.
Co-authored-by: Linh Vo Van <linh.vo@e-cq.net>
* feat(pipeline): add fail-closed tool call authorization gate
Gate tool execution against the server-side AllowedTools allowlist built from the
RBAC/tenant-aware filtered tool set. Resolve the tool-call prefix before the
allowlist lookup so prefixed agents are not wrongly blocked, re-check deny on lazy
MCP activation, and expand IsDenied to cover aliased tool names.
Co-authored-by: Huy Doan <tui@pm.me>
* fix(security): expand file-serve deny-list defense-in-depth
Add absolute-path deny prefixes (/home, /Users, /srv, /var/lib, /var/www, /opt)
and an explicit fail-closed log when no file-serving boundary is configured.
Co-authored-by: Linh Vo Van <linh.vo@e-cq.net>
* fix(providers): allow claude cli executable paths
Refs: #1185
---------
Co-authored-by: evgyur <evgyur@gmail.com>
Co-authored-by: Srini <srinis.k@gmail.com>
Co-authored-by: Linh Vo Van <linh.vo@e-cq.net>
Co-authored-by: Huy Doan <tui@pm.me>
* feat(providers): add Google Cloud Vertex AI provider (#576)
Add `vertex` built-in provider type that routes Gemini calls through Google
Cloud Vertex AI's OpenAI-compatible endpoint. Enterprises on GCP can now use
regional endpoints for data residency, consolidate AI spend under existing GCP
billing, enforce IAM/VPC-SC controls, and use committed-use discounts instead
of standalone Google AI Studio API keys.
Implementation reuses OpenAIProvider via the OpenAI-compat path; the only
provider-specific logic is OAuth2 auth wiring:
- New factory NewVertexProvider in internal/providers/vertex.go builds an
*http.Client with oauth2.Transport, which auto-refreshes GCP access tokens
(1-hour lifetime) transparently. Credentials precedence:
inline SA JSON > credentials_file path > Application Default Credentials
(works on GKE/Cloud Run/Compute Engine via metadata server).
- OpenAIProvider gets WithHTTPClient() + WithoutAuthHeader() options so the
oauth2 transport injects Authorization rather than doRequest() setting a
static Bearer header.
- Endpoint URL computed at registration time from project_id + region:
https://{region}-aiplatform.googleapis.com/v1/projects/{p}/locations/{r}/endpoints/openapi
- Store: api_key column holds AES-256-GCM-encrypted SA JSON (same as other
providers); settings JSONB holds {project_id, region, model}.
- Env vars: GOCLAW_VERTEX_{API_KEY,CREDENTIALS_FILE,PROJECT_ID,REGION,MODEL}.
Registration wired through all three paths: config-driven startup, DB-driven
startup, and HTTP CRUD in-memory registration. Vertex handled before the
generic "api_key empty" guard so ADC deployments register correctly.
Code-review fixes applied:
- H1 (correctness): Gemini thought_signature detection in openai.go now
recognizes providerType="vertex" and apiBase suffix "aiplatform". Previously
only worked because the default model string coincidentally contained
"gemini"; custom model IDs or fine-tuned endpoint numeric IDs would drop the
signature on passback and trigger HTTP 400 mid-tool-loop. Regression test
added (TestVertexProviderForwardsThoughtSignatureOnToolCalls).
- M1 (hardening): region and project_id are regex-validated before URL
concatenation to prevent hostname injection (e.g. region="evil.com/a?").
- M2 (hardening): APIBaseOverride must be https + *.googleapis.com host to
prevent data exfiltration via crafted DB rows.
- M3 (documentation): CredentialsFile marked operator-only in the struct
comment — never expose via admin UI or DB settings without path allow-list.
Tests: 17 Vertex-related unit tests. go build ./... + go build -tags sqliteonly
./... + go vet ./... all clean. Pre-existing TestSignMediaPath failure on
Windows (file_token.go uses path/filepath) is unrelated to this change.
* chore: trigger CI on digitopvn/goclaw fork
* ci: ping
* ci: retrigger workflows