Release v1.10.0-alpha — Bulk Delete Backup & Restore #42
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "release/1.10.0"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Release v1.10.0-alpha — Bulk Delete Backup & Restore
Changes
Summary
Adds a backup and restore system for bulk delete operations. Deleted work package data is saved to persistent JSON files, discoverable via
list_delete_backups, and recoverable viarestore_deleted_work_packages. Storage is configurable for both local and Docker sidecar deployments. Total tool count: 69.Checklist
References
Version: 1.10.0-alpha (Gravity PM)
Epic: OP#2389 — Bulk Delete Backup & Restore System
PR Review — Release v1.10.0: Bulk Delete Backup & Restore
Reviewer: Gravity Bot (automated)
Scope: 7 files changed, ~350 lines added, ~30 lines removed, 7 commits
Files:
index.js,test.js,package.json,README.md,CLAUDE.md,docker-compose.example.yml,wiki-drafts/06-tool-reference.mdVerdict: Acceptable (with one important security finding to address)
The PR is well-structured, follows existing patterns consistently, has good test coverage (10 new e2e tests across 3 test suites, all passing), and documentation is accurate. One path traversal vulnerability in
restore_deleted_work_packagesshould be fixed before or shortly after merge.Findings
🔴 Important — Path traversal in
backup_fileparameterFile:
index.jsline 2271Description: The
backup_fileinput is joined directly withbackupDirviapathMod.join(backupDir, args.backup_file)without sanitizing directory traversal sequences. An LLM or malicious caller could supply../../etc/passwdor../../../some/other/file.jsonto read arbitrary JSON files on the filesystem.Suggested fix: Validate that the resolved path stays within
backupDir:Alternatively, strip any path components:
const safeName = pathMod.basename(args.backup_file);🟡 Minor — Timestamp regex doesn't fully reconstruct the millisecond portion
File:
index.jsline 2232Description: The regex
/-(\d{2})-(\d{2})-(\d+)Z$/reconstructs the time portion, but the milliseconds group(\d+)captures the full millisecond value (e.g.,806) without a decimal separator. The displayed timestamp looks like2026-03-17 05:06:24Zdropping the milliseconds entirely, since the replacement:$1:$2Zignores group 3. This is cosmetic and not incorrect, but the milliseconds are silently discarded.Impact: Purely cosmetic. The timestamp display is still useful without milliseconds.
🟡 Minor — Setup wizard uses sync
appendFileSyncin async contextFile:
index.jsline ~4968Description: The setup wizard section uses
appendFileSync(sync import fromnode:fs) while the rest of the new code usesnode:fs/promises. This is fine functionally but is a minor inconsistency with the async-first pattern used in the backup/restore tools.Impact: No functional issue. The setup wizard already uses other sync filesystem operations, so this is consistent within that function's own conventions.
⚪ Nitpick —
list_delete_backupsreads and parses every backup fileFile:
index.jslines 2226Description: For each backup file, the tool reads and JSON-parses the full content to extract IDs and count. With large backups (many deleted WPs with full HAL data), this could be slow if there are many backup files. A future optimization could store metadata (count, IDs) in the filename or a separate index file.
Impact: Negligible at current scale (max 20 files by default). Only relevant if
BULK_DELETE_MAX_BACKUPSis set very high.⚪ Nitpick — Restore tool does not restore custom fields
File:
index.jslines 2299-2340Description: The restore tool maps
subject,description,type,status,priority,assignee,version,parent,storyPoints,estimatedTime,startDate, anddueDate. Custom fields from the original work package are not restored. This is documented behavior (the tool description lists what is restored), but worth noting for users with heavy custom field usage.Impact: Documented limitation. The tool description accurately states what fields are restored.
Quality Assessment
ok()/err()pattern, dynamic imports, HAL cleanup, same error handling structure as other bulk toolsbackup_fileaccepts string,project_idusesz.union([z.string(), z.number()])matching existing patterns. Path traversal is the gap.err(), correctnew Error()usagebackup_fileparameter (see finding above)mcp-backupsadded to both stdio and HTTP mode examples, env var set correctlyTest Results
HUMAN REVIEW
backup_filepath traversal before merge or as a fast-follow. The risk is moderate: the input comes from an LLM which could be prompt-injected, and the file content is parsed as JSON then used to make API calls. AddingpathMod.basename()sanitization is a one-line fix.Reviewed by Gravity Bot — advisory only, does not approve or reject
Release v1.10.0 — Bulk Delete Backup & Restoreto Release v1.10.0-alpha — Bulk Delete Backup & Restore@ai wrote in #42 (comment):
PR Review Followup
Responding to review posted on 2026-03-17 (review comment) and Mike's feedback.
Triage Summary
Fixes Applied
backup_fileparameter — fixed in6d12cb3pathMod.basename()to strip directory traversal sequences7bb2f0fcustom_optionslinksNot Actioned
Test Results
This PR should now be ready for merge.
This followup was generated by the gr-pr-followup skill.