Skip to content

fix: reject symlinked integration state - #5176

Open
mnriem wants to merge 5 commits into
github:mainfrom
mnriem:mnriem-integration-state-symlink-fix
Open

mnriem wants to merge 5 commits into
github:mainfrom
mnriem:mnriem-integration-state-symlink-fix

Conversation

@mnriem

@mnriem mnriem commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Use deny-mode FileHelper I/O in specify_cli.integration_state for .specify/integration.json reads and writes, independently of lifecycle transactions or operational symlink opt-in. Reject linked state files and .specify directories before lifecycle observers access them, while preserving existing write notifications and structured read errors.

Add regression coverage for existing, dangling, contained, and cyclic links, built-in integration installation, normal state updates, and trusted project-root aliases. Clarify in the existing filesystem-policy design that the specify_cli.integration_state helpers are an explicit deny-only managed-metadata exception, with coverage proving that these helpers reject contained state links when allow_symlinks is true while the general configured helper still permits contained links. This guarantee does not cover unmigrated consumers such as bundles.project.active_integration, which retains containment-checked reads and can follow contained state-file links.

Testing

  • Tested locally with uv run specify --help
  • Ran existing tests with uv sync && uv run pytest
  • Tested with a sample project (if applicable)

uv run specify --help passed. The full suite at 8355df49 used the documented worktree-local interpreter rather than uv run pytest: LC_ALL=en_US.UTF-8 .venv/bin/python -m pytest tests -q passed with 13,690 passed, 130 skipped on macOS with Python 3.13.17. Test dependencies were installed with uv sync --extra test (passed).

For the policy clarification at c88e0980, .venv/bin/python -m pytest tests/integrations/test_integration_state.py tests/specify_cli/integrations/test_command_install.py tests/specify_cli/test_file_policy.py -q --tb=short passed with 254 passed. The full suite was not rerun after the review follow-ups. The follow-up at 13c22352 only narrows documentation wording; git diff --check passed and no additional tests were run for that documentation-only change.

Sample-project checks are automated tests using temporary initialized projects, including normal installation and rejection of linked state before setup. No manual coding-agent slash-command testing was run; command templates and scripts are unchanged.

AI Disclosure

  • I did not use AI assistance for this contribution
  • I did use AI assistance (fill in the disclosure below)

AI disclosure: Prepared on behalf of @mnriem with GitHub Copilot using GPT-6.1 Sol (gpt-6.1-sol), with runtime-configured reasoning settings not exposed to the agent, in autonomous execution under user direction. A security-review subagent was also used; its model was not exposed. AI assistance covered assessment, implementation, the approved design clarification, regression tests, automated validation, commit text, and this PR description.

Use deny-mode FileHelper I/O for integration state independently of lifecycle observers. Preserve structured read errors and transaction notifications, and cover linked state files and directories.

Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI auto review requested due to automatic review settings October 29, 2026 13:36

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.

🟡 Changes recommended

The integration-state error recommends enabling symlinks even though state handling always denies them, giving affected projects guidance that cannot resolve the failure.

1 open issue
What changed in this PR

Hardens Specify CLI’s integration-state I/O against symlinks below the trusted project root, independently of lifecycle transactions and the operational symlink opt-in.

Changes:

  • Route integration-state reads and writes through deny-mode FileHelper.
  • Reject linked destinations before lifecycle observers run, while retaining notifications and structured errors.
  • Add regression coverage for linked state, installation rejection, normal updates, and trusted root aliases.

Validation was limited to isolated standard-library checks; the full suite and before/after regression comparison were not rerun.

File Description
tests/​specify_cli/​integrations/​test_command_status.py Expect a state-read error when project-root resolution fails.
tests/​specify_cli/​integrations/​test_command_install.py Verify installation rejects linked state before setup under either symlink policy.
tests/​integrations/​test_integration_state.py Cover link rejection, observer behavior, normal state updates, and trusted root aliases.
src/​specify_cli/​integration_state.py Enforce deny-mode state I/O and validate destinations before observer access.

Review effort: Max (auto)

Comment thread src/specify_cli/integration_state.py
Replace FileHelper's generic opt-in hint with state-specific guidance to replace linked entries and clarify that allow_symlinks does not apply. Preserve deny-only behavior and cover read, write, and CLI diagnostics.

Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI auto review requested due to automatic review settings October 29, 2026 13:53
@mnriem

mnriem commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator Author

Updated in a999552: integration-state read and write diagnostics now tell users to replace linked entries and clarify that allow_symlinks does not apply to integration state, without changing deny-only behavior. The regression assertions also cover CLI output under both symlink-policy settings. Posted on behalf of @mnriem by GitHub Copilot using GPT-6.1 Sol (gpt-6.1-sol), autonomous execution with runtime-configured reasoning settings not exposed; AI assistance covered the fix, regression tests, and this summary.

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.

🟡 Changes recommended

The integration test’s symlink assertion can fail spuriously on Windows because Path.readlink() may return a prefixed absolute path that differs from the expected target even when the link is unchanged.

1 open issue
1 resolved since last review

Review effort: Max (auto)

Comment thread tests/integrations/test_integration_state.py Outdated
Compare the recorded readlink value before and after denied integration-state writes instead of comparing it with the requested target spelling. This avoids Windows extended-path prefix mismatches.

Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI auto review requested due to automatic review settings October 29, 2026 14:21
@mnriem

mnriem commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator Author

Updated in 10aaf36: the integration-state preservation test now compares the link target returned by readlink before and after the denied write, avoiding assumptions about Windows path-prefix spelling. Posted on behalf of @mnriem by GitHub Copilot using GPT-6.1 Sol (gpt-6.1-sol), autonomous execution with runtime-configured reasoning settings not exposed; AI assistance covered the test change and this summary.

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.

🔵 Needs a closer look

An unpublished finding about a mismatch between integration-state symlink handling and the documented filesystem policy requires human assessment.

No open issues 🎉

1 resolved since last review

Review effort: Max (auto)

@mnriem mnriem added the triage-must-have Verdict: high-value, important work for Spec Kit — do first label Oct 29, 2026
Document integration state as managed metadata outside the operational symlink opt-in. Cover contained state links with allow_symlinks enabled in the shared readers, writer, and installation CLI while confirming the general configured helper still permits contained links.

Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI auto review requested due to automatic review settings October 29, 2026 16:24
@mnriem

mnriem commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator Author

Updated in c88e098: following the maintainer’s decision, the filesystem-policy design now explicitly identifies integration state as deny-only managed metadata. Added coverage showing that contained state links remain rejected with allow_symlinks enabled while the general configured helper still permits them, and extended installation checks to contained targets. Runtime behavior is unchanged. Posted on behalf of @mnriem by GitHub Copilot using GPT-6.1 Sol (gpt-6.1-sol), autonomous execution with runtime-configured reasoning settings not exposed; AI assistance covered the approved design clarification, tests, and this summary.

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.

🟡 Changes recommended

The documentation incorrectly claims that state reads always deny links, while bundle install/update still follow contained integration.json links.

1 open issue

Review effort: Max (auto)

Comment thread design/file-helper.md Outdated
Clarify that deny-only state I/O applies to specify_cli.integration_state, not unmigrated bundle readers that retain containment-checked reads. No runtime behavior changes.

Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI auto review requested due to automatic review settings October 29, 2026 16:45
@mnriem

mnriem commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator Author

Updated in 13c2235: the filesystem-policy guarantee now explicitly applies to specify_cli.integration_state rather than every reader of integration.json. The design and PR description distinguish the legacy bundle reader, which retains containment-checked reads and can follow contained links. Bundle runtime behavior is unchanged. Posted on behalf of @mnriem by GitHub Copilot using GPT-6.1 Sol (gpt-6.1-sol), autonomous execution with runtime-configured reasoning settings not exposed; AI assistance covered the documentation correction and this summary.

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.

🔵 Needs a closer look

Human assessment is needed to weigh an unpublished finding about missing documentation for integration-state symlink restrictions.

No open issues 🎉

1 resolved since last review

Review effort: Max (auto)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage-must-have Verdict: high-value, important work for Spec Kit — do first

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants