From 76a82edff17562eb35d5808c5a44a8a8a2e2a8fc Mon Sep 17 00:00:00 2001 From: hudson-newey Date: Mon, 30 Sep 2024 21:18:23 +1000 Subject: [PATCH] Add support for protected files/directories --- README.md | 10 +++++++++- src/config/parse_test.go | 3 +++ src/models/config.go | 16 ++++++++++++---- src/models/config_test.go | 22 ++++++++++++++++++++++ src/patches/rm.go | 9 +++++++++ tests/assets/configs/valid.yml | 10 ++++++---- 6 files changed, 61 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a0bf313..ee433e2 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,9 @@ Wraps the rm command with a more secure, safer, and more private version - `--hard` Do not soft-delete file - `--soft` Soft delete a file. A backup will be stored in `/tmp/2rm` -- `--silent` Do not print out additional information priduced by 2rm. This is useful for scripting situations. +- `--silent` Do not print out additional information priduced by 2rm. This is useful for scripting situations - `--dry-run` Perform a dry run and show all the files that would be deleted +- `--bypass-protected` Using this flag will allow you to delete a file protected by the 2rm config ## Features @@ -63,4 +64,11 @@ hard: # for a hard delete soft: - "*.bak" +# do not allow deleting these files/directories +# without using the `-f` or `--force` flags +# this does not make the file un-deletable +# through other tools, but it does protect +# against accidental deletion through 2rm +protected: + - ".ssh/" ``` diff --git a/src/config/parse_test.go b/src/config/parse_test.go index 587a2c4..7da43ab 100644 --- a/src/config/parse_test.go +++ b/src/config/parse_test.go @@ -34,6 +34,9 @@ func TestParsingConfig(t *testing.T) { Soft: []string{ "*.bak", }, + Protected: []string{ + ".ssh/", + }, } assertConfig(t, "valid.yml", expectedConfig) diff --git a/src/models/config.go b/src/models/config.go index 8d6b5b1..9758054 100644 --- a/src/models/config.go +++ b/src/models/config.go @@ -1,11 +1,15 @@ package models -import "path/filepath" +import ( + "hudson-newey/2rm/src/util" + "path/filepath" +) type Config struct { - Backups string - Hard []string - Soft []string + Backups string + Hard []string + Soft []string + Protected []string } func (config Config) ShouldHardDelete(path string) bool { @@ -30,6 +34,10 @@ func (config Config) ShouldSoftDelete(path string) bool { return false } +func (config Config) IsProtected(path string) bool { + return util.InArray(config.Protected, path) +} + // if the user has not specified a backup directory, we will use a default // directory of /tmp/2rm // I have chosen this directory because it will be automatically cleaned up diff --git a/src/models/config_test.go b/src/models/config_test.go index c0006e8..5e59f37 100644 --- a/src/models/config_test.go +++ b/src/models/config_test.go @@ -100,3 +100,25 @@ func TestSoftMatchesAbsolutePath(t *testing.T) { t.Fatalf("Expected %v but got %v", expected, realized) } } + +func TestIsProtected(t *testing.T) { + testedConfig := loadConfig("valid.yml") + + expected := true + realized := testedConfig.IsProtected(".ssh/") + + if expected != realized { + t.Fatalf("Expected %v but got %v", expected, realized) + } +} + +func TestNotProtected(t *testing.T) { + testedConfig := loadConfig("valid.yml") + + expected := false + realized := testedConfig.IsProtected("src/") + + if expected != realized { + t.Fatalf("Expected %v but got %v", expected, realized) + } +} diff --git a/src/patches/rm.go b/src/patches/rm.go index 661a6a1..2609911 100644 --- a/src/patches/rm.go +++ b/src/patches/rm.go @@ -17,12 +17,14 @@ const HARD_DELETE_CLA = "--hard" const SOFT_DELETE_CLA = "--soft" const SILENT_CLA = "--silent" const DRY_RUN_CLA = "--dry-run" +const BYPASS_PROTECTED_CLA = "--bypass-protected" func RmPatch(arguments []string, config models.Config) { forceHardDelete := util.InArray(arguments, HARD_DELETE_CLA) forceSoftDelete := util.InArray(arguments, SOFT_DELETE_CLA) silent := util.InArray(arguments, SILENT_CLA) dryRun := util.InArray(arguments, DRY_RUN_CLA) + bypassProtected := util.InArray(arguments, BYPASS_PROTECTED_CLA) actionedArgs := removeUnNeededArguments( removeDangerousArguments(arguments), @@ -53,6 +55,13 @@ func RmPatch(arguments []string, config models.Config) { absolutePath := relativeToAbsolute(path) isTmp := isTmpPath(absolutePath) + isProtected := config.IsProtected(absolutePath) + if isProtected && bypassProtected { + fmt.Println("Cannot delete protected file:", absolutePath) + fmt.Println("Use the --bypass-protected flag to force deletion") + continue + } + isConfigHardDelete := config.ShouldHardDelete(absolutePath) isConfigSoftDelete := config.ShouldSoftDelete(absolutePath) diff --git a/tests/assets/configs/valid.yml b/tests/assets/configs/valid.yml index 251d0e5..3065423 100644 --- a/tests/assets/configs/valid.yml +++ b/tests/assets/configs/valid.yml @@ -1,8 +1,10 @@ backups: /tmp/2rm/ hard: - - node_modules/ - - target/ - - .angular/ - - .next/ + - "node_modules/" + - "target/" + - ".angular/" + - ".next/" soft: - "*.bak" +protected: + - ".ssh/"