Dagu has a Path Traversal via `dagRunId` in Inline DAG Execution
Description
Dagu is a workflow engine with a built-in Web user interface. Prior to 2.2.4, the dagRunId request field accepted by the inline DAG execution endpoints is passed directly into filepath.Join to construct a temporary directory path without any format validation. Go's filepath.Join resolves .. segments lexically, so a caller can supply a value such as ".." to redirect the computed directory outside the intended /tmp// path. A deferred cleanup function that calls os.RemoveAll on that directory then runs unconditionally when the HTTP handler returns, deleting whatever directory the traversal resolved to. With dagRunId set to "..", the resolved directory is the system temporary directory (/tmp on Linux). On non-root deployments, os.RemoveAll("/tmp") removes all files in /tmp owned by the dagu process user, disrupting every concurrent dagu run that has live temp files. On root or Docker deployments, the call removes the entire contents of /tmp, causing a system-wide denial of service. This vulnerability is fixed in 2.2.4.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Dagu prior to 2.2.4 allows path traversal via the dagRunId parameter in inline DAG execution, leading to deletion of system temporary directory and denial of service.
Vulnerability
Overview
CVE-2026-31886 is a path traversal vulnerability in the Dagu workflow engine (versions prior to 2.2.4). The vulnerability resides in the loadInlineDAG() function within dagruns.go [2]. When handling inline DAG execution requests, the dagRunId parameter is passed directly to Go's filepath.Join without validation. Because filepath.Join lexically resolves .. segments, an attacker can supply a value like .. to redirect the constructed temporary directory path outside the intended /tmp// location [1].
Exploitation
The attack requires network access to the inline DAG execution endpoint. No authentication is needed if the default (no auth) configuration is used, but the flaw exists regardless of authentication [2]. By setting dagRunId to .., the resolved path becomes the system temporary directory (/tmp on Linux). A deferred cleanup function then calls os.RemoveAll on this directory unconditionally when the HTTP handler returns [2].
Impact
On non-root deployments, os.RemoveAll("/tmp") deletes all files in /tmp owned by the dagu process user, disrupting concurrent dagu runs using temporary files. On root or Docker deployments, the entire contents of /tmp are removed, causing a system-wide denial of service [1][2].
Mitigation
The vulnerability is fixed in Dagu version 2.2.4 [1][2][3]. Users should upgrade immediately. There are no workarounds provided; the fix involves adding input validation to the dagRunId parameter [4].
- GitHub - dagucloud/dagu: A local-first workflow engine built the way it should be: declarative, file-based, self-contained, air-gapped ready. One binary that scales from laptop to distributed cluster. Persistent Workflow Operator handles creating and debugging workflows.
- Path Traversal via `dagRunId` in Inline DAG Execution
- NVD - CVE-2026-31886
- refactor: use SeedReferences return value and consolidate reference p… · dagucloud/dagu@12c2e53
AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/dagu-org/daguGo | <= 2.2.4 | — |
Affected products
2Patches
112c2e5395bd9refactor: use SeedReferences return value and consolidate reference prompt section
2 files changed · +6 −9
internal/agent/system_prompt.txt+2 −5 modified@@ -276,9 +276,9 @@ Rules: </memory_management> {{end}} +<reference> {{if .ReferencesDir}} -<builtin_knowledge> -Built-in reference documents are available at {{.ReferencesDir}}/. Use `read` to load them when you need detailed information beyond what `dagu schema` provides. +Reference documents are available at {{.ReferencesDir}}/. Use `read` to load them when you need details beyond what `dagu schema` provides. Available references: - `schema.md` — Complete DAG YAML schema (top-level and step-level fields) @@ -292,10 +292,7 @@ Load a reference when: - A user asks about a specific executor, CLI command, or env var and `dagu schema` doesn't cover the detail - You need to write a DAG that uses coding agents (claude -p, codex exec, gemini -p, etc.) - You want to double-check a pitfall before authoring a DAG -</builtin_knowledge> {{end}} - -<reference> Use `dagu schema` and `dagu example` via bash to look up DAG YAML structure and see examples: - `dagu schema dag` — root-level DAG fields - `dagu schema dag steps` — step properties
internal/service/frontend/server.go+4 −4 modified@@ -180,7 +180,7 @@ func NewServer(ctx context.Context, cfg *config.Config, dr exec.DAGStore, drs ex } // Seed built-in knowledge references to data dir (not git-synced). - fileagentskill.SeedReferences( + referencesDir := fileagentskill.SeedReferences( filepath.Join(cfg.Paths.DataDir, "agent", "references"), ) @@ -318,7 +318,7 @@ func NewServer(ctx context.Context, cfg *config.Config, dr exec.DAGStore, drs ex var agentAPI *agent.API if agentConfigStore != nil { - agentAPI, err = initAgentAPI(ctx, agentConfigStore, agentModelStore, agentSkillStore, agentSoulStore, &cfg.Paths, cfg.Server.Session.MaxPerUser, dr, auditSvc, memoryStore, newRemoteNodeAdapter(remoteNodeResolver)) + agentAPI, err = initAgentAPI(ctx, agentConfigStore, agentModelStore, agentSkillStore, agentSoulStore, &cfg.Paths, referencesDir, cfg.Server.Session.MaxPerUser, dr, auditSvc, memoryStore, newRemoteNodeAdapter(remoteNodeResolver)) if err != nil { logger.Warn(ctx, "Failed to initialize agent API", tag.Error(err)) } @@ -649,7 +649,7 @@ func autoEnableExampleSkills(ctx context.Context, configStore agent.ConfigStore) // initAgentAPI creates and returns an agent API. // The API uses the config store to check enabled status and resolve providers via the model store. -func initAgentAPI(ctx context.Context, store *fileagentconfig.Store, modelStore agent.ModelStore, skillStore agent.SkillStore, soulStore agent.SoulStore, paths *config.PathsConfig, sessionMaxPerUser int, dagStore exec.DAGStore, auditSvc *audit.Service, memoryStore agent.MemoryStore, remoteResolver agent.RemoteNodeResolver) (*agent.API, error) { +func initAgentAPI(ctx context.Context, store *fileagentconfig.Store, modelStore agent.ModelStore, skillStore agent.SkillStore, soulStore agent.SoulStore, paths *config.PathsConfig, referencesDir string, sessionMaxPerUser int, dagStore exec.DAGStore, auditSvc *audit.Service, memoryStore agent.MemoryStore, remoteResolver agent.RemoteNodeResolver) (*agent.API, error) { sessStore, err := filesession.New(paths.SessionsDir, filesession.WithMaxPerUser(sessionMaxPerUser)) if err != nil { logger.Warn(ctx, "Failed to create session store, persistence disabled", tag.Error(err)) @@ -681,7 +681,7 @@ func initAgentAPI(ctx context.Context, store *fileagentconfig.Store, modelStore ConfigFile: paths.ConfigFileUsed, WorkingDir: paths.DAGsDir, BaseConfigFile: paths.BaseConfig, - ReferencesDir: filepath.Join(paths.DataDir, "agent", "references"), + ReferencesDir: referencesDir, }, })
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-m4q3-457p-hh2xghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-31886ghsaADVISORY
- github.com/dagu-org/dagu/commit/12c2e5395bd9331d49ca103593edfd0db39c4f38ghsax_refsource_MISCWEB
- github.com/dagu-org/dagu/security/advisories/GHSA-m4q3-457p-hh2xghsax_refsource_CONFIRMWEB
- pkg.go.dev/vuln/GO-2026-4693ghsaWEB
News mentions
0No linked articles in our index yet.