Skip to content

fix(integrations): guard _sha256 against unreadable managed files#3376

Open
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/manifest-sha256-unreadable
Open

fix(integrations): guard _sha256 against unreadable managed files#3376
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/manifest-sha256-unreadable

Conversation

@jawwad-ali

Copy link
Copy Markdown
Contributor

Description

manifest.py::_sha256 does an unguarded open(path, "rb"). Two callers invoke it on a regular file that exists but can't be opened (e.g. permission denied) without catching OSError:

  • check_modified() (line ~312) — only symlinks/non-regular files are guarded above; a readable-looking-but-unopenable regular file lets the OSError escape.
  • uninstall() (line ~361) — the sibling path.unlink() right below is wrapped in except OSError, but the hash read is not (asymmetric).

So specify integration upgrade / uninstall / switch dump a raw PermissionError traceback instead of handling it. Reproduced on main @ 92b7cf7.

Fix (single file — manifest.py)

  • check_modified(): catch OSError and treat an unreadable file as modified, consistent with how symlinks / non-regular files are already appended just above.
  • uninstall(): catch OSError around the hash read and treat it as skipped/preserved (can't verify ownership → don't delete), mirroring the existing unlink() OSError guard. The force short-circuit is unchanged.

Testing

New TestManifestUnreadableFile — deterministic and cross-platform via monkeypatch of _sha256 to raise PermissionError (no real chmod/symlink needed):

  • check_modified() returns the file as modified (was: raised);
  • uninstall(force=False) returns it in skipped, removes nothing, leaves it on disk (was: raised).

Both fail-before / pass-after (verified by source-stash). Note: 6 pre-existing symlink tests in this file fail locally on Windows (symlink privilege) — identical on clean main, unrelated to this change, and green on Linux CI.

AI Disclosure

  • I did use AI assistance (describe below)

Found and fixed with Claude Code (Claude Fable 5) under my direction. AI flagged the unguarded _sha256 against the guarded unlink() sibling; I reproduced the traceback path, wrote cross-platform monkeypatch tests, verified fail-before/pass-after, and confirmed the 6 remaining failures are pre-existing Windows-symlink environment issues.

manifest.py::_sha256 does an unguarded open(). check_modified() and
uninstall() both call it on a readable-but-unopenable regular file
(e.g. permission denied) without catching OSError, so
'specify integration upgrade/uninstall/switch' surface a raw
PermissionError traceback. Guard both call sites: in check_modified()
treat an unreadable file as modified (consistent with the adjacent
symlink / non-regular-file handling); in uninstall() treat it as skipped
and preserve it (mirroring the existing path.unlink() OSError guard just
below). The force short-circuit is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

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.

Pull request overview

This PR hardens IntegrationManifest’s hash-based change detection and uninstall logic so unreadable-but-existing managed files (e.g., permission denied on open()) don’t crash integration workflows with raw tracebacks.

Changes:

  • check_modified(): catches OSError from _sha256() and treats unreadable regular files as modified.
  • uninstall(force=False): catches OSError from _sha256() and preserves unreadable files by returning them in skipped.
  • Adds deterministic, cross-platform tests that simulate unreadable files by monkeypatching _sha256 to raise PermissionError.
Show a summary per file
File Description
src/specify_cli/integrations/manifest.py Adds OSError guards around _sha256() in check_modified() and uninstall() to avoid tracebacks and preserve safety semantics.
tests/integrations/test_manifest.py Adds tests verifying unreadable managed files are treated as modified and are preserved (skipped) on uninstall when not forced.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Low

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.

3 participants