Skip to content

Harden command registration path handling#8

Closed
mnriem wants to merge 1 commit into
mainfrom
mnriem-fix-command-path-traversal
Closed

Harden command registration path handling#8
mnriem wants to merge 1 commit into
mainfrom
mnriem-fix-command-path-traversal

Conversation

@mnriem

@mnriem mnriem commented Jun 22, 2026

Copy link
Copy Markdown
Owner

What this fixes

CommandRegistrar.register_commands() read each command body from source_dir / cmd_file using the manifest's file field without validating it. Unlike the parallel skill and preset readers — which already reject absolute paths and .. traversal and require the resolved path to stay within the extension directory — the shared command path applied no such check.

As a result, a manifest with file: ../../../etc/passwd (or a bare absolute path) could cause an arbitrary host file to be read verbatim into a generated agent command file at a predictable path under the project (e.g. .claude/commands/..., mirrored for each detected agent).

Changes

  • agents.pyregister_commands(): add the same containment guard used by the skill/preset readers before reading the source file — reject absolute paths and ensure the resolved path stays within source_dir; skip the command otherwise.
  • extensions.pyExtensionManifest._validate(): reject an absolute or ..-containing file value at manifest-load time with a clear ValidationError (defense-in-depth, surfaces a real error instead of a silent skip).
  • Tests:
    • test_registrar_path_traversal.py: new TestCommandFileTraversal covering the file read path (the existing tests only covered command name/alias output traversal). Asserts traversal/absolute file values register nothing and never leak file contents into generated commands.
    • test_extensions.py: parametrized manifest-load validation test for the file field.

Verification

New and targeted suites pass. Full suite: 3975 passed, with 2 pre-existing failures in test_init_dir.py (a SPECIFY_INIT_DIR core-scripts environment issue) confirmed unrelated to these changes by stashing.

CommandRegistrar.register_commands() read each command body from
source_dir / cmd_file without validating the manifest 'file' field,
unlike the parallel skill and preset readers which already reject
absolute paths and '..' traversal. A malicious extension/preset/bundle
manifest with file: ../../../etc/passwd (or an absolute path) could
read arbitrary host files verbatim into a generated agent command at a
predictable path (GHSA-w5fv-7w9x-7fc5, CWE-22).

Add the same containment guard at the command read site and reject a
traversal/absolute 'file' at manifest-load time in
ExtensionManifest._validate() for defense-in-depth, plus regression
tests for both the read path and the manifest validator.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mnriem

mnriem commented Jun 22, 2026

Copy link
Copy Markdown
Owner Author

Reopening against upstream github/spec-kit.

@mnriem mnriem closed this Jun 22, 2026
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.

1 participant