Conversation
…026203-281) module/instance values used as filesystem path components were not validated, allowing traversal sequences (../default), absolute paths (/etc/passwd), and other escape forms to select credentials outside the intended binding directory. Adds _validate_path_component() (allowlist: single non-traversal segment) and _assert_within_base() (canonical-path confinement via Path.resolve) to resolver.py; wires both into _validate_inputs(), _load_from_mount(), and the flat-path branch. Extends the same guard to aicore/__init__.py (_get_secret, _get_aicore_base_url, _get_secret_dir_mtime). Protection is automatic for all SDK consumers — no code changes required in agent or application code. Parametrized regression tests cover all attack classes from the Jira ticket: relative traversal, absolute POSIX/Windows paths, UNC paths, embedded separators, dot components, NUL/control characters, overlong values, and symlink escape. Proof that a rejected value reads no files and attempts no env-var fallback is included. Documentation updated in secret_resolver, aicore, and agent_memory user-guides.
Contributor
Author
|
Closing to reopen without internal ticket reference in public repo. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes HASI2026203-281 (MEDIUM — path traversal / tenant-isolation bypass).
The secret resolver and AI Core config module built filesystem paths from
unvalidated
module/instance/instance_nameinputs. A crafted value(
../default,/etc/passwd) could select credentials outside the intendedservice binding — a potential tenant-isolation bypass in multitenant agents.
Changes (SDK only — no consumer code changes required)
core/secret_resolver/resolver.py_validate_path_component(): rejects separators, absolute/UNC paths,./..,NUL/control chars, >255-char values. Runs before any path assembly or
env-var fallback — rejected values read no files.
_assert_within_base(): canonical-path confinement (Path.resolve) asdefense-in-depth against symlink/TOCTOU escape.
_validate_inputs()and both mount attempts(
_load_from_mount+ flat-path branch).aicore/__init__.py_validate_path_component()+_assert_within_base()called at the start of_get_secret(),_get_aicore_base_url(), and_get_secret_dir_mtime()before the f-string path assembly.
Tests
NUL/control chars, dot components, overlong values, symlink escape.
Docs
secret_resolver,aicore, andagent_memoryuser-guides.No consumer changes required
This is a library-level control. Every agent or app using the SDK is protected
automatically on the next version upgrade. All observed production instance
values (
default,aicore-instance,hr-advisor-destination-instance,BTP tenant subdomains) are valid single-component identifiers and continue to
work unchanged.
The only behavioural change: a value that previously silently resolved to a
different binding now raises
ValueError(fail-closed — correct behaviour).Test plan
pytest tests/core/unit/secret_resolver/ -v— 62 passedpytest tests/aicore/unit/test_aicore.py -v— 103 passedpytest tests/agent_memory/unit/ -v— 231 passedpytest tests/ -q --ignore=tests/aicore/integration— 3579 passed (29 pre-existing integration/telemetry failures unrelated to this change)ruff check resolver.py aicore/__init__.py— all checks passed