Skip to content

fix: confine event hook script paths to the project tree - #4133

Merged
mnriem merged 2 commits into
github:mainfrom
SebTardif:fix/4131-event-script-path-confine
Aug 17, 2026
Merged

fix: confine event hook script paths to the project tree#4133
mnriem merged 2 commits into
github:mainfrom
SebTardif:fix/4131-event-script-path-confine

Conversation

@SebTardif

Copy link
Copy Markdown
Contributor

Description

Event hook script resolution joined the first scripts: token onto the .specify or extension base with Path. An absolute token discarded that base and ran a host binary.

This rejects anchored tokens (absolute, drive, UNC) and requires the resolved path to stay inside the project root. Relative .. walks that remain in the project still work, including documented extension ../../scripts/... references to core scripts. The same check is in the CLI resolver and the generated stdlib dispatcher.

Introduced in #3704 (f8e474d, 2026-07-29).

Same-repo path confinement: #3088, #2296, #3869.

Closes #4131

Testing

  • Tested locally with uv run specify --help
  • Ran existing tests with uv sync && uv run pytest (tests/integrations/test_events.py, 121 passed)
  • Tested with a sample project (if applicable) (not applicable; no slash-command change)

Added resolver tests for absolute tokens, a walk out of the project, Windows-anchored tokens, extension ../../scripts/..., symlink escape, and an inline-dispatcher run that refuses an absolute host script.

AI Disclosure

  • I did not use AI assistance for this contribution
  • I did use AI assistance (describe below)

Code and tests written with Grok (model: grok-4.6) under direct supervision. Reviewed and tested locally before opening this PR.

Posted on behalf of @SebTardif by Grok (model: grok-4.6).

Event dispatch joined the first scripts: token onto the .specify or
extension base with Path. An absolute token discarded the base and ran
a host binary. Reject anchored tokens and require the resolved path to
stay inside the project root.

Assisted-by: Grok (model: grok-4.6, supervised)
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
@SebTardif
SebTardif requested a review from mnriem as a code owner August 15, 2026 09:56
Copilot AI balanced review requested due to automatic review settings August 15, 2026 09:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread src/specify_cli/events.py

@mnriem mnriem left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address Copilot feedback

Generated dispatchers only delegate when EVENT_SCRIPT_PATH_CONFINEMENT
is True, so an older global install cannot bypass the project-tree
guard.

Assisted-by: Grok (xAI, under direct human supervision)
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
Copilot AI review requested due to automatic review settings August 17, 2026 18:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@SebTardif

Copy link
Copy Markdown
Contributor Author

@mnriem

Please address Copilot feedback

Addressed in 4c3361b. The dispatcher no longer delegates to an older installed specify_cli.events that lacks path confinement.

Posted on behalf of @SebTardif by Grok (xAI).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

src/specify_cli/events.py:105

  • On supported Python 3.11/3.12, Path.resolve() raises RuntimeError for a symlink loop. Because _resolve_argv() runs before the fallback's execution try, a loop in a script path escapes as a dispatcher traceback instead of being rejected like the other path-resolution failures. Catch RuntimeError here so the generated dispatcher continues to fail closed.

This issue also appears on line 586 of the same file.

    try:
        root = project_root.resolve()
        candidate = (base / token).resolve()
        candidate.relative_to(root)
    except (OSError, ValueError):
        return None

src/specify_cli/events.py:591

  • Path.resolve() can raise RuntimeError for symlink loops on supported Python 3.11/3.12. This resolver is called before resolve_and_run_event_command() enters its subprocess exception boundary, so such a token leaks a traceback rather than degrading to “no runnable script.” Include RuntimeError in the confinement failure handling, matching the generated helper.
    try:
        root = project_root.resolve()
        candidate = (base / token).resolve()
        candidate.relative_to(root)
    except (OSError, ValueError):
        return None
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@mnriem
mnriem self-requested a review August 17, 2026 20:51
@mnriem
mnriem merged commit ae60333 into github:main Aug 17, 2026
14 of 18 checks passed
@mnriem

mnriem commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Thank you!

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.

[Bug]: Event hook scripts: paths are not confined to the extension or project base

3 participants