Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/disk-hygiene/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "disk-hygiene",
"version": "0.17.2",
"version": "0.17.3",
"description": "Context-aware disk hygiene for arbitrary directory trees: inventories orphaned and temporary artifacts, classifies evidence into review tiers, and offers exact-path cleanup only after a fresh safety preview and explicit per-tier approval. The target is read-only by default; OS-managed paths, links and mount points, VCS-tracked content, changed entries, and live-handle uncertainty fail closed.",
"author": {
"name": "Melodic Software",
Expand Down
8 changes: 8 additions & 0 deletions plugins/disk-hygiene/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
All notable changes to the `disk-hygiene` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.17.3]

### Fixed

- **PowerShell guard now surfaces move/rename/overwrite spellings (#387).** `Move-Item`, `Rename-Item`,
`Set-Content`, `Out-File`, `New-Item -Force`, output redirection, and `Format-Volume`/`Clear-Disk`
join the existing deletion-spelling `ask` bar on the PowerShell lane.
Comment thread
kyle-sexton marked this conversation as resolved.

## [0.17.2]

### Fixed
Expand Down
20 changes: 19 additions & 1 deletion plugins/disk-hygiene/skills/clean/scripts/destructive_guard.py
Original file line number Diff line number Diff line change
Expand Up @@ -934,8 +934,15 @@ def is_exact_kill_switch_probe(command: str) -> bool:
r"(?i)(?<![\w./\\-])("
r"remove-item|rm|rmdir|del|erase|rd|ri|clear-content|clear-recyclebin|rimraf|unlink"
r"|sendtorecyclebin|deletefile|deletedirectory|removedirectory"
r"|move-item|rename-item|mv|move|ren|rename"
r"|set-content|out-file|add-content"
r"|format-volume|clear-disk|initialize-disk"
Comment thread
kyle-sexton marked this conversation as resolved.
r")(?![\w-])"
)
_POWERSHELL_NEW_ITEM_FORCE = re.compile(
r"(?i)(?<![\w./\\-])new-item(?![\w-]).*-force\b"
)
_POWERSHELL_OUTPUT_REDIRECT = re.compile(r"(?<![<>])>(?![=>])")
_POWERSHELL_DOTNET_DELETE = re.compile(r"(?i)(::\s*delete|\.\s*delete\s*\()")
# robocopy is an executable normally invocable by full path
# (C:\Windows\System32\robocopy.exe), so unlike the cmdlet word list its
Expand Down Expand Up @@ -983,7 +990,18 @@ def powershell_decision(command: str, enabled: bool) -> tuple[str, str] | None:
if match:
return _powershell_mutation_verdict(
enabled,
f'disk-hygiene flagged the deletion spelling "{match.group(0)}".',
f'disk-hygiene flagged the mutation spelling "{match.group(0)}".',
)
new_item_force = _POWERSHELL_NEW_ITEM_FORCE.search(command)
if new_item_force:
return _powershell_mutation_verdict(
enabled,
'disk-hygiene flagged New-Item -Force (truncates an existing file).',
)
if _POWERSHELL_OUTPUT_REDIRECT.search(command):
return _powershell_mutation_verdict(
enabled,
"disk-hygiene flagged shell output redirection (may overwrite a file).",
)
if _POWERSHELL_DOTNET_DELETE.search(command):
return _powershell_mutation_verdict(
Expand Down
6 changes: 6 additions & 0 deletions plugins/disk-hygiene/skills/clean/scripts/test_hygiene.py
Original file line number Diff line number Diff line change
Expand Up @@ -4396,6 +4396,12 @@ def test_powershell_deletion_spellings_force_final_prompt(self) -> None:
"C:\\Windows\\System32\\robocopy.exe C:\\src C:\\dst /MIR",
"& 'C:/Windows/System32/robocopy.exe' C:/src C:/dst /PURGE",
"[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile('x', 'OnlyErrorDialogs', 'SendToRecycleBin')",
"Move-Item C:/tmp/old C:/tmp/new",
"Rename-Item C:/tmp/old C:/tmp/new",
"Set-Content C:/tmp/file.txt 'overwrite'",
"Out-File C:/tmp/file.txt -Force",
"New-Item C:/tmp/file.txt -ItemType File -Force",
"'data' > C:/tmp/file.txt",
Comment on lines +4399 to +4404

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.

🔴 Important — several of the newly added regex alternatives have no test coverage at all

_POWERSHELL_MUTATION_WORDS (destructive_guard.py#L937-L939) adds mv|move|ren|rename (aliases for Move-Item/Rename-Item), add-content, and format-volume|clear-disk|initialize-disk — but this test only exercises Move-Item, Rename-Item, Set-Content, Out-File, New-Item -Force, and bare >. A grep of the whole test file turns up zero hits for Add-Content, Format-Volume, Clear-Disk, or Initialize-Disk in any PowerShell test, and no test exercises the mv/move/ren/rename aliases on the PowerShell lane (the one mv hit at line 2918 is an unrelated Bash-lane test). Since this is a blocklist-by-design lane, an untested alternative that's subtly wrong (typo, wrong precedence, word-boundary miss) would silently defer instead of prompting, and nothing in the suite would catch it.

Suggest adding one case per newly-added alternative (mv, move, ren, rename, Add-Content, Format-Volume, Clear-Disk, Initialize-Disk) to this loop.

Comment on lines +4399 to +4404

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.

🔴 Important — audit-only (kill-switch) deny path isn't re-verified for any of the new spellings

test_powershell_deletion_spellings_denied_in_audit_only_mode (test_hygiene.py#L4457-L4472) is the counterpart to this test — it asserts enabled=False turns the same spellings into deny instead of ask (kill-switch B2 behavior called out in this module's own docstring at destructive_guard.py:970-972). This PR extends the ask-path list here but leaves that deny-path list untouched, so none of Move-Item, Rename-Item, Set-Content, Out-File, New-Item -Force, or output redirection are verified to actually deny in audit-only mode — only that they prompt when the kill switch is on. _powershell_mutation_verdict routes both branches through shared code today, but that symmetry is exactly the kind of thing a future refactor could break unnoticed without a test on both sides.

):
result = self.run_guard_powershell(command)
assert result is not None, command
Expand Down