mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix(security): reject path traversal in credential file registration
This commit is contained in:
parent
0f2ea2062b
commit
a97641b9f2
2 changed files with 142 additions and 4 deletions
|
|
@ -55,16 +55,47 @@ def register_credential_file(
|
|||
|
||||
*relative_path* is relative to ``HERMES_HOME`` (e.g. ``google_token.json``).
|
||||
Returns True if the file exists on the host and was registered.
|
||||
|
||||
Security: rejects absolute paths and path traversal sequences (``..``).
|
||||
The resolved host path must remain inside HERMES_HOME so that a malicious
|
||||
skill cannot declare ``required_credential_files: ['../../.ssh/id_rsa']``
|
||||
and exfiltrate sensitive host files into a container sandbox.
|
||||
"""
|
||||
hermes_home = _resolve_hermes_home()
|
||||
|
||||
# Reject absolute paths — they bypass the HERMES_HOME sandbox entirely.
|
||||
if os.path.isabs(relative_path):
|
||||
logger.warning(
|
||||
"credential_files: rejected absolute path %r (must be relative to HERMES_HOME)",
|
||||
relative_path,
|
||||
)
|
||||
return False
|
||||
|
||||
host_path = hermes_home / relative_path
|
||||
if not host_path.is_file():
|
||||
logger.debug("credential_files: skipping %s (not found)", host_path)
|
||||
|
||||
# Resolve symlinks and normalise ``..`` before the containment check so
|
||||
# that traversal like ``../. ssh/id_rsa`` cannot escape HERMES_HOME.
|
||||
try:
|
||||
resolved = host_path.resolve()
|
||||
hermes_home_resolved = hermes_home.resolve()
|
||||
resolved.relative_to(hermes_home_resolved) # raises ValueError if outside
|
||||
except ValueError:
|
||||
logger.warning(
|
||||
"credential_files: rejected path traversal %r "
|
||||
"(resolves to %s, outside HERMES_HOME %s)",
|
||||
relative_path,
|
||||
resolved,
|
||||
hermes_home_resolved,
|
||||
)
|
||||
return False
|
||||
|
||||
if not resolved.is_file():
|
||||
logger.debug("credential_files: skipping %s (not found)", resolved)
|
||||
return False
|
||||
|
||||
container_path = f"{container_base.rstrip('/')}/{relative_path}"
|
||||
_registered_files[container_path] = str(host_path)
|
||||
logger.debug("credential_files: registered %s -> %s", host_path, container_path)
|
||||
_registered_files[container_path] = str(resolved)
|
||||
logger.debug("credential_files: registered %s -> %s", resolved, container_path)
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue