Skip to content

fix(sidecar): prevent path traversal in asset materialization#3316

Merged
danielaskdd merged 2 commits into
HKUDS:mainfrom
VectorPeak:fix/sidecar-asset-name-boundary
Jun 24, 2026
Merged

fix(sidecar): prevent path traversal in asset materialization#3316
danielaskdd merged 2 commits into
HKUDS:mainfrom
VectorPeak:fix/sidecar-asset-name-boundary

Conversation

@VectorPeak

@VectorPeak VectorPeak commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Description

This fixes a path traversal risk in sidecar asset materialization, where parser-provided asset names were used to build filesystem output paths before copying or writing asset bytes.

The vulnerable path is built from an asset directory plus a parser/adaptor-controlled suggested name:

target_name = _allocate_unique_name(spec.suggested_name, used_names)
target_path = assets_dir / target_name

AssetSpec.suggested_name is not itself a trusted filesystem boundary. If a current or future parser/adaptor supplies a path-like name such as ../evil.png, ..\evil.png, or an absolute path, the filesystem path resolver can interpret that value as path structure rather than a plain filename. Since the same target_path is then passed to shutil.copyfile() or write_bytes(), crafted asset metadata could cause LightRAG to copy or write asset bytes outside the intended <base>.blocks.assets/ directory.

The concrete impact is file creation or overwrite within the LightRAG process permissions when the target parent directories already exist. That can corrupt generated sidecar output, replace adjacent files, or place attacker-controlled asset bytes in another reachable location. The issue is therefore a path traversal / output path boundary bug at the final sidecar asset write sink.

The sidecar writer is the correct place to enforce this boundary because it is the component that materializes parser-produced assets on disk. Relying on every parser/adaptor to always provide a basename leaves the final write path too permissive.

Changes Made

  • Collapse parser-suggested asset names to safe basenames before output filename allocation.
  • Preserve existing duplicate-name handling after filename normalization.
  • Add a resolved-path containment check before any sidecar asset copy or byte write.

Checklist

  • Changes tested locally
  • Code reviewed
  • Documentation updated (if necessary)
  • Unit tests added (if applicable)

Additional Notes

Evidence:

The issue can be reproduced at the path-construction layer with crafted asset names. This local console reproduction only resolves paths; it does not write files.

LightRAG sidecar asset path traversal evidence demo
Scope: local console path-resolution reproduction; no files are written.
intended assets dir: C:\Users\MiaoXing\AppData\Local\Temp\lightrag-sidecar-demo\demo.pdf.parsed\demo.blocks.assets

payload        : ../evil.png
before target  : C:\Users\MiaoXing\AppData\Local\Temp\lightrag-sidecar-demo\demo.pdf.parsed\evil.png
before inside? : False
safe basename  : evil.png
after target   : C:\Users\MiaoXing\AppData\Local\Temp\lightrag-sidecar-demo\demo.pdf.parsed\demo.blocks.assets\evil.png
after inside?  : True
---
payload        : ..\evil.png
before target  : C:\Users\MiaoXing\AppData\Local\Temp\lightrag-sidecar-demo\demo.pdf.parsed\evil.png
before inside? : False
safe basename  : evil.png
after target   : C:\Users\MiaoXing\AppData\Local\Temp\lightrag-sidecar-demo\demo.pdf.parsed\demo.blocks.assets\evil.png
after inside?  : True
path traversal evidence before fix path traversal evidence after fix

Possible call chain / impact:

lightrag/parser/external/_base.py
  -> BaseExternalParser.parse(...)
  -> self.build_ir(raw_dir, rs.document_name)
  -> write_sidecar(ir, parsed_dir=rs.parsed_dir, ...)
  -> _materialize_assets(ir.assets, assets_dir)
  -> shutil.copyfile(src_path, target_path) / target_path.write_bytes(...)

lightrag/parser/native_base.py
  -> NativeParserBase.parse(...)
  -> self.build_ir(...)
  -> write_sidecar(ir, parsed_dir=parsed_dir, clean_parsed_dir=False, ...)
  -> _materialize_assets(ir.assets, assets_dir)

This PR only changes the final asset output-name boundary in the sidecar writer. It does not change document parsing, chunking, embedding, retrieval, or normal sidecar asset paths for basename-only asset names.

Validation:

  • .\.venv\Scripts\python.exe -m ruff check lightrag\sidecar\writer.py - passed
  • pre-commit run --files lightrag\sidecar\writer.py - passed
  • git diff --check upstream/main...HEAD - passed

@VectorPeak
VectorPeak force-pushed the fix/sidecar-asset-name-boundary branch from 32f2d1c to 02c9cbf Compare June 24, 2026 01:42
@VectorPeak
VectorPeak force-pushed the fix/sidecar-asset-name-boundary branch from 02c9cbf to 9052859 Compare June 24, 2026 01:54
@danielaskdd

Copy link
Copy Markdown
Collaborator

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown
Contributor

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 9052859cf7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@VectorPeak
VectorPeak marked this pull request as ready for review June 24, 2026 02:35
@VectorPeak VectorPeak changed the title fix(sidecar): keep materialized asset names inside the assets directory fix(sidecar): prevent path traversal in asset materialization Jun 24, 2026
Regression tests for PR #3316: assert parser-suggested asset names are
collapsed to a contained basename and that the relative_to containment
guard skips any target resolving outside the assets dir.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@danielaskdd
danielaskdd merged commit 580c239 into HKUDS:main Jun 24, 2026
3 checks passed
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.

2 participants