mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-15 00:17:27 +00:00
fix(security): harden upstream critical surfaces (#32)
* fix(security): harden upstream critical surfaces Refs #30 * fix(security): close pre-landing review gaps Refs #30 * fix(security): close official release blockers
This commit is contained in:
1 parent
2a48e62c6b
commit
532ff91d8e
59 files changed
+2194
-397
No files matched your search
+3
-3
@@ -978,7 +978,7 @@ Team activity and audit trail.
|
||||
|
||||
## 20. Secure CLI Credentials
|
||||
|
||||
CLI authentication credentials for secure command execution. Requires **admin role** (full gateway token or empty gateway token in dev/single-user mode).
|
||||
CLI authentication credentials for secure command execution. Requires **admin role** (gateway token or empty-token local/dev fallback).
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
@@ -1014,7 +1014,7 @@ CLI authentication credentials for secure command execution. Requires **admin ro
|
||||
|
||||
## 21. Runtime & Packages Management
|
||||
|
||||
Manage system (apk), Python (pip), and Node (npm) package installation in the GoClaw runtime container. These endpoints do not inspect host-level runtimes. Requires authentication. When `GOCLAW_GATEWAY_TOKEN` is empty (dev/single-user mode), all users get admin role and can manage packages.
|
||||
Manage system (apk), Python (pip), and Node (npm) package installation in the GoClaw runtime container. These endpoints do not inspect host-level runtimes. Requires authentication. Empty-token admin access is limited to loopback local development or explicit `GOCLAW_ALLOW_INSECURE_NO_AUTH=1`; external binds require `GOCLAW_GATEWAY_TOKEN`.
|
||||
|
||||
### List Installed Packages
|
||||
|
||||
@@ -1178,7 +1178,7 @@ Workspace file management.
|
||||
| `GET` | `/v1/storage/files` | List files with depth limiting |
|
||||
| `GET` | `/v1/storage/files/{path...}` | Read file (JSON or raw) |
|
||||
| `POST` | `/v1/storage/files` | Upload file (admin) |
|
||||
| `DELETE` | `/v1/storage/files/{path...}` | Delete file/directory |
|
||||
| `DELETE` | `/v1/storage/files/{path...}` | Delete file/directory (admin) |
|
||||
| `PUT` | `/v1/storage/move` | Move/rename file (admin) |
|
||||
| `GET` | `/v1/storage/size` | Stream storage size (Server-Sent Events, cached 60 min) |
|
||||
|
||||
|
||||
@@ -32,6 +32,15 @@ Or in WebSocket `connect`:
|
||||
|
||||
The gateway token is compared using **constant-time comparison** (`crypto/subtle.ConstantTimeCompare`) in both HTTP and WebSocket auth paths to prevent timing attacks. The comparison reveals no information about where the provided token first differs from the expected token.
|
||||
|
||||
Externally reachable deployments must configure a gateway token. If `gateway.token` / `GOCLAW_GATEWAY_TOKEN` is empty while the gateway binds to `0.0.0.0`, `::`, or a non-loopback address, startup fails before the health endpoint reports ready.
|
||||
|
||||
Empty-token compatibility is only for local development:
|
||||
|
||||
- bind `GOCLAW_HOST` to loopback (`127.0.0.1`, `localhost`, or `::1`), or
|
||||
- set `GOCLAW_ALLOW_INSECURE_NO_AUTH=1` explicitly.
|
||||
|
||||
The explicit opt-in applies to both HTTP and WebSocket. Do not use it on shared hosts, Docker ports exposed outside the machine, or production deployments.
|
||||
|
||||
---
|
||||
|
||||
## 2. API Keys
|
||||
@@ -100,7 +109,7 @@ GoClaw tries authentication methods in this priority order:
|
||||
1. **Gateway token** (exact match via constant-time comparison) → `RoleAdmin` or `RoleOwner` for configured owner IDs
|
||||
2. **API key** (SHA-256 hash lookup in `api_keys` table) → role from scopes
|
||||
3. **Browser pairing** (sender ID must be paired with "browser" device type) → `RoleOperator` (HTTP only; requires `X-GoClaw-Sender-Id` header)
|
||||
4. **No auth configured** (backward compatibility: if no gateway token is set) → full-access dev mode
|
||||
4. **No auth configured and local/dev mode explicitly allowed** → full-access dev mode
|
||||
5. **No valid auth found** → `401 Unauthorized`
|
||||
|
||||
### HTTP Request Flow
|
||||
@@ -116,13 +125,15 @@ flowchart TD
|
||||
G -->|Yes| H[Derive role from scopes]
|
||||
G -->|No| I{Gateway token configured?}
|
||||
I -->|Yes| J[401 Unauthorized]
|
||||
I -->|No| K[Full-access backward compat]
|
||||
I -->|No| K{Local/dev fallback allowed?}
|
||||
K -->|No| J
|
||||
K -->|Yes| O[Full-access backward compat]
|
||||
C -->|Check paired device| L{Device paired?}
|
||||
L -->|Yes| M[RoleOperator]
|
||||
L -->|No| J
|
||||
E --> N[Authenticate request]
|
||||
H --> N
|
||||
K --> N
|
||||
O --> N
|
||||
M --> N
|
||||
```
|
||||
|
||||
@@ -164,7 +175,7 @@ On successful API key authentication, `last_used_at` is updated asynchronously (
|
||||
|
||||
### Backward Compatibility
|
||||
|
||||
If no gateway token is configured (`gateway.token` is empty in `config.json`), unauthenticated requests run in backward-compatibility full-access mode. This enables self-hosted deployments without strict authentication. Once a gateway token is configured, all requests must authenticate or use browser pairing.
|
||||
If no gateway token is configured (`gateway.token` is empty in `config.json`), unauthenticated requests run in backward-compatibility full-access mode only for loopback local development or when `GOCLAW_ALLOW_INSECURE_NO_AUTH=1` is set. Once a gateway token is configured, all requests must authenticate or use browser pairing.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ GoClaw determines the tenant from the credentials used to connect:
|
||||
| **API key** (tenant-bound) | Auto from key's `tenant_id` | Normal SaaS integration |
|
||||
| **API key** (system-level) + `X-GoClaw-Tenant-Id` | Header value (UUID or slug), while keeping the key's original role | Cross-tenant tools |
|
||||
| **Browser pairing** | Master tenant by default, or a membership-validated tenant hint | Dashboard operators |
|
||||
| **No credentials** | Master tenant | Dev/single-user mode |
|
||||
| **No credentials** | Master tenant | Loopback local development or explicit `GOCLAW_ALLOW_INSECURE_NO_AUTH=1` only |
|
||||
|
||||
**Owner IDs:** Configured via `GOCLAW_OWNER_IDS` env var (comma-separated). Only owners get cross-tenant access with the gateway token. Default: `system`.
|
||||
|
||||
|
||||
@@ -119,7 +119,6 @@ Parity enforced by `ui/web/src/__tests__/i18n-tts-key-parity.test.ts` (vitest).
|
||||
|
||||
---
|
||||
|
||||
<<<<<<< HEAD
|
||||
## Image Generation
|
||||
|
||||
Native `image_generation` support in the Codex provider (`POST /codex/responses`) + passthrough in the OpenAI-compat path.
|
||||
@@ -138,7 +137,7 @@ Native `image_generation` support in the Codex provider (`POST /codex/responses`
|
||||
**Persistence:** `internal/agent/media.go persistAssistantImages()` writes final images to `{workspace}/media/{sha256}.{ext}`, returns `MediaRef` entries, clears inline `Images[]`. Idempotent on hash. Invoked from `pipeline.FinalizeStage` via `Deps.PersistAssistantImages` callback.
|
||||
|
||||
**Web UI:** Download filename resolver (`imageGenDownloadName`) in `ui/web/src/components/chat/media-gallery.tsx`. Image generation works automatically when the agent has the `create_image` tool — no user-facing toggle.
|
||||
=======
|
||||
|
||||
## Webhook Subsystem
|
||||
|
||||
External systems invoke agents or send channel messages via webhooks without gateway tokens.
|
||||
@@ -188,7 +187,6 @@ Raw webhook secret encrypted at rest via AES-256-GCM using `GOCLAW_ENCRYPTION_KE
|
||||
|
||||
All webhook calls logged with canonical `{"body_hash":"<sha256-hex>","meta":{...}}` shape in `webhook_calls.request_payload` (JSON).
|
||||
Used by idempotency checker to detect body mismatches on replay.
|
||||
>>>>>>> a83f4090 (fix(webhooks): address post-review findings (K1-K10))
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user