GitHub Security Advisory: GHSA-9wmw-9wph-2vwp
Package: dagu (NPM)
Severity: HIGH (CVSS 7.5)
CWE: CWE-306: Missing Authentication for Critical Function
Vulnerable versions: < 2.2.4
Patched in: 2.2.4

Summary: Dagu: SSE Authentication Bypass in Basic Auth Mode

Details:
# 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=basi