Skip to content

fix(configurable): prevent path traversal in AgentTool config_path resolution - #1407

Open
prasanna8585 wants to merge 1 commit into
google:mainfrom
prasanna8585:fix/config-path-traversal
Open

fix(configurable): prevent path traversal in AgentTool config_path resolution#1407
prasanna8585 wants to merge 1 commit into
google:mainfrom
prasanna8585:fix/config-path-traversal

Conversation

@prasanna8585

@prasanna8585 prasanna8585 commented Aug 7, 2026

Copy link
Copy Markdown

Summary

ConfigAgentUtils.resolveSubAgentFromConfigPath() accepted absolute config_path values unconditionally, and for relative values only logged a warning when the resolved path escaped the agent's own base directory -- it did not block the load:

if (Path.of(configPath).isAbsolute()) {
  subAgentConfigPath = Path.of(configPath);   // accepted unconditionally
} else {
  subAgentConfigPath = configDir.resolve(configPath);
}
Path resolvedConfigPath = subAgentConfigPath.normalize().toAbsolutePath();
Path baseDir = configDir.normalize().toAbsolutePath();
if (!resolvedConfigPath.startsWith(baseDir)) {
  logger.warn(...);   // <-- warning only, no return/throw
}
if (!Files.exists(subAgentConfigPath)) { ... }
return fromConfig(subAgentConfigPath.toString());   // proceeds regardless

This is the same vulnerability class already hard-fixed with a breaking change in both other language ports:

  • adk-go: google/adk-go@604dd63 ("BREAKING: an absolute config_path is no longer accepted")
  • adk-python: commit 171ae9e

This port (issue #1218) had instead chosen a warn-only deprecation, leaving the traversal fully exploitable today: a config_path such as ../../another_tenant/secret_agent.yaml or an absolute path is loaded and parsed as a full agent configuration, with only a log line noting the escape.

Reachability

config_path is a field in a subagent reference within an agent's own YAML config (sub_agents: - config_path: ...). In any deployment where different trust domains' agent configs are hosted under a shared root (e.g. a multi-tenant agent-hosting platform, or any scenario where config content can be influenced by a less-trusted party), this allows reading and loading arbitrary files reachable by the process as agent configuration -- outside the intended per-agent containment directory.

Fix

  • Reject absolute config_path values outright.
  • Reject (rather than warn on) a resolved path that escapes the agent's base directory.
  • Resolve symlinks on both sides where the paths exist (toRealPath(), falling back to the lexical path when the target doesn't yet exist, so a missing file is still reported as not-found rather than misclassified as a traversal) -- matching the symlink-escape protection in adk-go's second commit for the same fix.

Testing

  • fromConfig_subAgentConfigPathTraversal_throwsConfigurationException -- the exact ../ escape case, confirmed rejected.
  • fromConfig_subAgentAbsoluteConfigPath_throwsConfigurationException -- the absolute-path case, confirmed rejected.
  • The existing fromConfig_withSubAgents_createsHierarchy test (a legitimate in-directory subagent reference) is unmodified and continues to pass, confirming no regression.

Verification performed

Maven Central is not reachable in the environment I used to develop this fix, so I could not run mvn test locally. I instead dynamically confirmed both the vulnerability and the fix using a faithful, line-for-line transcription of the real method (standard JDK only, no external dependencies needed to exercise this specific logic):

  • Before the fix: a crafted ../secret_dir/victim_secret.yaml path logged the deprecation warning, then proceeded to read the target file's full content regardless.
  • After the fix: the same input is rejected with "Path traversal detected: ..."; an absolute path is rejected with "Absolute paths are not allowed..."; a legitimate in-directory reference still succeeds.

…solution

resolveSubAgentFromConfigPath() accepted absolute config_path values
unconditionally, and for relative values only logged a warning when the
resolved path escaped the agent's own base directory -- it did not
block the load. Execution continued to Files.exists() and then
fromConfig(), which parses the (attacker-reachable) file as a full
agent configuration.

This is the same vulnerability class already hard-fixed (with a
breaking change) in adk-go (604dd63) and adk-python (171ae9e); this
port (issue google#1218) had chosen a warn-only deprecation instead, leaving
the traversal fully exploitable.

Fix:
- Reject absolute config_path values outright.
- Reject (rather than warn on) a resolved path that escapes the
  agent's base directory, resolving symlinks on both sides where the
  paths exist so a symlink inside the base directory cannot be used to
  escape it.

Adds regression tests for the traversal case, the absolute-path case,
and confirms the existing in-directory subagent test
(fromConfig_withSubAgents_createsHierarchy) continues to pass
unmodified.

Confirmed dynamically via a faithful, line-for-line transcription of
both the vulnerable and fixed logic using the standard JDK (Maven
Central is not reachable in this environment): the warning was
non-blocking and a file outside the intended directory was read in
full before this fix; the fixed logic rejects the same input while a
legitimate in-directory reference still succeeds.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant