Directory traversal issue when organizing a part
Metadata
Current evaluation
Fixed via PR #1562. Added validation to reject organize source paths escaping the part install directory, blocking directory traversal and absolute path misuse. Merged and closed.
Suggested action: —
No scores available.
Issue body
### Bug Description
When organizing a part, it's possible to organize from any directory, not just subdirectories of the install directory.
### To Reproduce
Run `python -m craft_parts` on the parts.yaml file below. Note how the contents of `/etc/apt` get placed into the prime directory.
It's also possible to use directory traversal strings to get a relative directory.
### part yaml
```shell
parts:
a:
plugin: nil
organize:
/etc/apt: ''
```
### Relevant log output
```shell
DEBUG:craft_parts.packages:get repository attribute: attr=configure, repository:<class 'craft_parts.packages.deb.Ubuntu'>
DEBUG:craft_parts.utils.os_utils:is_snap: False, SNAP_NAME set to None
DEBUG:craft_parts.sequencer:process a:1
DEBUG:craft_parts.sequencer:add action a:1(0)
DEBUG:craft_parts.sequencer:process a:2
DEBUG:craft_parts.sequencer:add action a:2(0)
DEBUG:craft_parts.sequencer:process a:3
DEBUG:craft_parts.sequencer:add action a:3(0)
DEBUG:craft_parts.sequencer:process a:4
DEBUG:craft_parts.sequencer:add action a:4(0)
DEBUG:craft_parts.sequencer:process a:5
DEBUG:craft_parts.sequencer:add action a:5(0)
INFO:craft_parts.executor.executor:Installing build-packages
DEBUG:craft_parts.packages:get repository attribute: attr=install_packages, repository:<class 'craft_parts.packages.deb.Ubuntu'>
DEBUG:craft_parts.executor.executor:verify plugin environment for part 'a'
DEBUG:craft_parts.packages:set repository attribute: attr=stage_packages_filters, value=None, repo:<class 'craft_parts.packages.deb.Ubuntu'>
Execute: Pull a
DEBUG:craft_parts.executor.executor:execute action a:Action(part_name='a', step=Step.PULL, action_type=ActionType.RUN, reason=None, project_vars=ProjectVarInfo(root={}), properties=ActionProperties(changed_files=None, changed_dirs=None))
Execute: Overlay a
DEBUG:craft_parts.executor.executor:execute action a:Action(part_name='a', step=Step.OVERLAY, action_type=ActionType.RUN, reason=None, project_vars=ProjectVarInfo(root={}), properties=ActionProperties(changed_files=None, changed_dirs=None))
Execute: Build a
DEBUG:craft_parts.executor.executor:execute action a:Action(part_name='a', step=Step.BUILD, action_type=ActionType.RUN, reason=None, project_vars=ProjectVarInfo(root={}), properties=ActionProperties(changed_files=None, changed_dirs=None))
DEBUG:craft_parts.state_manager.states:load state file: /home/lengau/Work/Code/craft-parts/parts/a/state/pull
DEBUG:craft_parts.packages:get repository attribute: attr=unpack_stage_packages, repository:<class 'craft_parts.packages.deb.Ubuntu'>
DEBUG:craft_parts.executor.part_handler:remove directory /home/lengau/Work/Code/craft-parts/parts/a/build
DEBUG:craft_parts.executor.step_handler:Executing PosixPath('/home/lengau/Work/Code/craft-parts/parts/a/run/build.sh')
DEBUG:craft_parts.executor.part_handler:Run pre-organize callbacks
DEBUG:craft_parts.packages:get repository attribute: attr=get_installed_packages, repository:<class 'craft_parts.packages.deb.Ubuntu'>
DEBUG:urllib3.connectionpool:http://localhost:None "GET /v2/snaps HTTP/1.1" 200 None
Execute: Stage a
DEBUG:craft_parts.executor.executor:execute action a:Action(part_name='a', step=Step.STAGE, action_type=ActionType.RUN, reason=None, project_vars=ProjectVarInfo(root={}), properties=ActionProperties(changed_files=None, changed_dirs=None))
Execute: Prime a
DEBUG:craft_parts.executor.executor:execute action a:Action(part_name='a', step=Step.PRIME, action_type=ActionType.RUN, reason=None, project_vars=ProjectVarInfo(root={}), properties=ActionProperties(changed_files=None, changed_dirs=None))
```
Evaluation history
| Date | Model | Scores | Action | Summary |
|---|---|---|---|---|
| qwen/qwen3.6-35b-a3b | — | — | Fixed via PR #1562. Added validation to reject organize source paths escaping the part install directory, blocking directory traversal and absolute path misuse. Merged and closed. | |
| qwen/qwen3.6-35b-a3b | — | — | Fixed and merged via PR #1562. The update rejects organize source paths that escape the part install directory, including absolute paths and directory traversal strings. This resolves the bug by validating paths before processing. | |
| qwen/qwen3.6-35b-a3b | — | — | Fixed via PR #1562. The organize step now rejects source paths escaping the part install directory, preventing directory traversal and absolute path vulnerabilities. Merged as a bugfix. | |
| qwen3.6-35b-a3b-mtp-q6 |
Staleness:
10
Complexity:
30
Confidence:
95
Support Request:
0
|
keep open | Directory traversal bug in craft-parts allows organize paths to escape the install directory. Triaged. A pull request has been opened to validate and reject out-of-bounds paths, awaiting review. |
Update history
No update history recorded yet.
Related issues
| Issue | Project | State | Summary | Similarity |
|---|---|---|---|---|
| #689 fix(organize): use the part's install directories | craft-parts | merged | Merged fix for organize_files() which incorrectly used relative paths, organizing files in cwd instead of base_dir. The function now accepts the part's install directories directly. Approved by two reviewers and passed CI. | |
| #1562 fix(organize): reject sources outside install dir | craft-parts | merged | Merged fix preventing the organize step from processing sources outside the part install directory. Blocks absolute paths and directory traversal while preserving symlink behavior. Unrelated CI failures did not block the merge. | |
| #690 fix(organize): use the part's install directories (#689) | craft-parts | merged | Merged fix for organize_files() using the current working directory instead of the project base directory. The function now accepts the part's install directories directly, eliminating relative path redefinition. |