Skip to content

fix(storage): reject traversal segments before VikingFS access checks#557

Merged
MaojiaSheng merged 1 commit intovolcengine:mainfrom
lyfmt:fix/vikingfs-path-traversal-auth-bypass
Mar 12, 2026
Merged

fix(storage): reject traversal segments before VikingFS access checks#557
MaojiaSheng merged 1 commit intovolcengine:mainfrom
lyfmt:fix/vikingfs-path-traversal-auth-bypass

Conversation

@lyfmt
Copy link
Copy Markdown
Contributor

@lyfmt lyfmt commented Mar 12, 2026

Description

Fix a path traversal / authorization bypass in VikingFS.

VikingFS currently performs access control on the raw URI string before converting it to an AGFS storage path. However, the AGFS layer canonicalizes filesystem paths and cleans . / .. segments. This creates a mismatch where a crafted URI can pass OpenViking's scope-based access check but be resolved by AGFS to a different underlying path.

Example attack patterns:

  • viking://resources/../_system/users.json
  • viking://resources/../../_system/accounts.json
  • viking://resources/../../other_account/_system/users.json

This PR hardens VikingFS by rejecting ambiguous traversal-style path components before authorization and before URI-to-path mapping, so the object being authorized is the same object ultimately accessed by storage.

Related Issue

No existing public issue. Vulnerability discovered via code audit.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement
  • Test update

Changes Made

  • Add shared URI normalization and validation in VikingFS before access checks and URI-to-path conversion
  • Reject traversal-style components such as . and .., plus platform-specific ambiguous forms such as backslash-separated and drive-prefixed path components
  • Apply the protection at the shared VikingFS boundary so read/write/rm/mv flows all reject unsafe URIs consistently
  • Add regression tests covering traversal-style URIs on read_file, write, rm, and mv
  • Keep valid resource-path reads working as before

Testing

  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have tested this on the following platforms:
    • Linux
    • macOS
    • Windows

Commands run locally:

.venv/bin/ruff check openviking/storage/viking_fs.py tests/misc/test_vikingfs_uri_guard.py
.venv/bin/pytest tests/misc/test_vikingfs_uri_guard.py tests/misc/test_mkdir.py -q
.venv/bin/pytest tests/server/test_api_content.py -q

Checklist

  • My code follows the project's coding style
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

Screenshots (if applicable)

N/A

Additional Notes

Security impact:

  • Prevents scope confusion caused by authorizing a raw URI while AGFS reads a canonicalized path
  • Protects internal storage targets such as account _system data from traversal-style access attempts

Behavioral impact:

  • No change for legitimate URIs
  • The only new behavior is failing fast on ambiguous URIs containing traversal-style components

Residual note:

  • The test output still shows one unrelated repository warning from openviking/storage/vectordb/utils/validation.py about PydanticDeprecatedSince212; this warning predates this PR and is not introduced by the current change

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 12, 2026

CLA assistant check
All committers have signed the CLA.

@MaojiaSheng
Copy link
Copy Markdown
Collaborator

/review

@MaojiaSheng
Copy link
Copy Markdown
Collaborator

great job, we'll merge soon

@github-actions
Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Redundant Normalization

_ensure_access normalizes the URI and passes the normalized version to _is_accessible, which then normalizes it again. This redundant work can be optimized by either avoiding the double normalization or passing the already computed parts.

def _ensure_access(self, uri: str, ctx: Optional[RequestContext]) -> None:
    real_ctx = self._ctx_or_default(ctx)
    normalized_uri, _ = self._normalized_uri_parts(uri)
    if not self._is_accessible(normalized_uri, real_ctx):
        raise PermissionError(f"Access denied for {uri}")
Behavior Change Check

_extract_space_from_uri no longer returns None early for non-viking:// URIs; instead, it normalizes them first. Verify this does not break existing callers that rely on the old None return behavior for non-viking:// URIs.

def _extract_space_from_uri(self, uri: str) -> Optional[str]:
    """Extract space segment from URI if present.

    URIs are WYSIWYG: viking://{scope}/{space}/...
    For user/agent, the second segment is space unless it's a known structure dir.
    For session, the second segment is always space (when 3+ parts).
    """
    _, parts = self._normalized_uri_parts(uri)

@MaojiaSheng MaojiaSheng merged commit 471c81a into volcengine:main Mar 12, 2026
1 check passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in OpenViking project Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants