Skip to content

Commit

Permalink
Add support for --overwrite flag
Browse files Browse the repository at this point in the history
  • Loading branch information
hudson-newey committed Oct 2, 2024
1 parent 76a82ed commit 53503f9
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 6 deletions.
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

"rm with guard rails"

Wraps the rm command with a more secure, safer, and more private version
A wrapper for the "rm" command with soft-deletes, config-based deletion, debug information, and saner defaults

## Command line arguments
## Additional command line arguments

- `--overwrite` Overwrite the disk location location with zeros
- `--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
Expand Down Expand Up @@ -42,6 +43,23 @@ By using the `/tmp` directory, the operating system will **automatically hard de
Sometimes you want to hard delete a file/directory every time that you run the `rm` command e.g. you probably want your `node_modules` hard deleted every time and never want to soft delete them.
In this case, you can modify your `~/.local/share/2rm/config.yml` file to always hard delete `node_modules`.

### Overwriting disk location with zeros

When deleting a file with the linux inbuilt `rm` command, the file is still avaliable on disk.

Meaning that the file can still be recovered by any sufficiantly technical user.

This can be problematic when dealing with sensitive files such as private keys that if leaked could lead to catastrophic consequences.

You can overwrite a files disk location (rendering it unrecoverable) by using the `--overwrite` flag.

2rm will still soft-delete the file by default, but the soft-deleted file will be completely filled with zeros.

I made the decision that overwritten files will still be soft deleted because it might be useful for timestamp logging/auditing purposes.
E.g. "when did I overwrite xyz"

If you want to fully delete a file from disk and the file system use both the `--overwrite` and `--hard` flags.

### Config-based deletion

You can specify what directories are soft-deleted anb hard-deleted by using the `~/.local/share/2rm/config.yml` file.
Expand All @@ -54,6 +72,14 @@ You can specify what directories are soft-deleted anb hard-deleted by using the
# any files that are soft deleted will be
# backed up in the `backups` directory
backups: /tmp/2rm/
# whenever files matching these paths are deleted
# the disk location will be overwritten with zeros
overwrite:
# when deleting ssh keys, we always want to
# overwrite them with zeros to protect
# against attackers recovering the production
# ssh keys
- ".ssh/*"
hard:
- "node_modules/"
- "target/"
Expand Down
3 changes: 3 additions & 0 deletions src/config/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ func assertConfig(t *testing.T, configPath string, expectedConfig models.Config)
func TestParsingConfig(t *testing.T) {
expectedConfig := models.Config{
Backups: "/tmp/2rm/",
Overwrite: []string{
".ssh/*",
},
Hard: []string{
"node_modules/",
"target/",
Expand Down
32 changes: 29 additions & 3 deletions src/models/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,24 @@ import (
)

type Config struct {
Backups string
Hard []string
Soft []string
// the backup location for soft-deletes
Backups string

// any file paths that match these patterns will be overwritten with
// zeros when deleted
Overwrite []string

// any file paths that match these patterns will be hard-deleted
Hard []string

// any file paths that match these patterns will be soft-deleted
// soft-deletes take precedence over hard-deletes
// meaning that if a file matches both a hard and soft delete pattern
// the file will be soft-deleted
Soft []string

// any file paths that match these patterns will be protected from deletion
// protected files cannot be deleted without the --bypass-protected flag
Protected []string
}

Expand All @@ -34,6 +49,17 @@ func (config Config) ShouldSoftDelete(path string) bool {
return false
}

func (config Config) ShouldOverwrite(path string) bool {
for _, overwritePath := range config.Overwrite {
matched := matchesPattern(overwritePath, path)
if matched {
return true
}
}

return false
}

func (config Config) IsProtected(path string) bool {
return util.InArray(config.Protected, path)
}
Expand Down
22 changes: 22 additions & 0 deletions src/models/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,25 @@ func TestNotProtected(t *testing.T) {
t.Fatalf("Expected %v but got %v", expected, realized)
}
}

func TestShouldOverwrite(t *testing.T) {
testedConfig := loadConfig("valid.yml")

expected := true
realized := testedConfig.ShouldOverwrite(".ssh/test.pem")

if expected != realized {
t.Fatalf("Expected %v but got %v", expected, realized)
}
}

func TestNotShouldOverwrite(t *testing.T) {
testedConfig := loadConfig("valid.yml")

expected := false
realized := testedConfig.ShouldOverwrite("non-existent.txt")

if expected != realized {
t.Fatalf("Expected %v but got %v", expected, realized)
}
}
48 changes: 47 additions & 1 deletion src/patches/rm.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ const SOFT_DELETE_CLA = "--soft"
const SILENT_CLA = "--silent"
const DRY_RUN_CLA = "--dry-run"
const BYPASS_PROTECTED_CLA = "--bypass-protected"
const OVERWRITE_CLA = "--overwrite"

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)
overwrite := util.InArray(arguments, OVERWRITE_CLA)

actionedArgs := removeUnNeededArguments(
removeDangerousArguments(arguments),
Expand Down Expand Up @@ -65,6 +67,18 @@ func RmPatch(arguments []string, config models.Config) {
isConfigHardDelete := config.ShouldHardDelete(absolutePath)
isConfigSoftDelete := config.ShouldSoftDelete(absolutePath)

// overwriting a file is not exclusive to hard/soft deletes
// meaning that you can overwrite the contents of a file with zeros and
// also soft delete it
// I have made this decision because I think soft-deleting an
// overwritten file has auditing/logging use cases
// e.g. Who deleted this file? When was it deleted?
// if we hard deleted the file, we would lose this information
isConfigOverwrite := config.ShouldOverwrite(absolutePath)
if overwrite || isConfigOverwrite {
overwriteFile(absolutePath)
}

if isTmp || forceHardDelete || isConfigHardDelete && !isConfigSoftDelete && !forceSoftDelete {
hardDelete([]string{path}, extractedArguments)
} else {
Expand All @@ -91,8 +105,16 @@ func shouldPassthrough(arguments []string) bool {
}

func removeUnNeededArguments(arguments []string) []string {
unNeededArguments := []string{"-r", HARD_DELETE_CLA, SOFT_DELETE_CLA, SILENT_CLA}
returnedArguments := []string{}
unNeededArguments := []string{
"-r",
HARD_DELETE_CLA,
SOFT_DELETE_CLA,
SILENT_CLA,
DRY_RUN_CLA,
BYPASS_PROTECTED_CLA,
OVERWRITE_CLA,
}

for _, arg := range arguments {
if !util.InArray(unNeededArguments, arg) {
Expand Down Expand Up @@ -205,3 +227,27 @@ func hardDelete(filePaths []string, arguments []string) {
command := "rm -r " + strings.Join(arguments, " ") + " " + strings.Join(filePaths, " ")
commands.Execute(command)
}

func overwriteFile(filePath string) {
file, err := os.OpenFile(filePath, os.O_RDWR, 0644)
if err != nil {
fmt.Println("Error opening file:", err)
os.Exit(2)
}
defer file.Close()

fileInfo, err := file.Stat()
if err != nil {
fmt.Println("Error getting file info:", err)
os.Exit(2)
}

fileSize := fileInfo.Size()
zeroBytes := make([]byte, fileSize)

_, err = file.WriteAt(zeroBytes, 0)
if err != nil {
fmt.Println("Error writing to file:", err)
os.Exit(2)
}
}
2 changes: 2 additions & 0 deletions tests/assets/configs/valid.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
backups: /tmp/2rm/
overwrite:
- ".ssh/*"
hard:
- "node_modules/"
- "target/"
Expand Down

0 comments on commit 53503f9

Please sign in to comment.