summaryrefslogtreecommitdiff
path: root/internal/store/store_shred.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-18 08:43:10 +0300
committerPaul Buetow <paul@buetow.org>2026-04-18 08:43:10 +0300
commit526e3bd1bea7e2ef67c6984b91cdb9b3ac3be4e5 (patch)
tree5774a8d492f2442403d2b02ed55fd814e646f585 /internal/store/store_shred.go
parent7bca02bddcc88002e8edf72d2f20f526cf9d2668 (diff)
refactor: split store.go into focused files by responsibility (task o6)
Split 562-line store.go into four files: - store.go (264 lines): Store type, WalkIndexes, Search, action dispatch - store_crud.go (160 lines): Add, Import, Remove, buildPair - store_picker.go (160 lines): Fzf, PickerResult, key parsing - store_shred.go (54 lines): ShredFile, ShredAllExported No API or behaviour changes. All functions under 30 lines, all files under 300 lines. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/store/store_shred.go')
-rw-r--r--internal/store/store_shred.go54
1 files changed, 54 insertions, 0 deletions
diff --git a/internal/store/store_shred.go b/internal/store/store_shred.go
new file mode 100644
index 0000000..7d28f70
--- /dev/null
+++ b/internal/store/store_shred.go
@@ -0,0 +1,54 @@
+// store_shred.go provides secure file deletion for the secret store.
+// ShredFile uses GNU shred(1) when available and falls back to "rm -Pfv".
+// ShredAllExported iterates cfg.ExportDir and shreds every regular file,
+// mirroring Ruby's Geheim#shred_all_exported best-effort deletion strategy.
+package store
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "path/filepath"
+)
+
+// ShredAllExported removes (shreds) every regular file in cfg.ExportDir.
+// Uses GNU shred when available; falls back to "rm -Pfv" otherwise.
+// Mirrors Ruby's shred_all_exported: iterates all files and returns the last
+// non-nil error so that as many files as possible are shredded even on failure.
+func (s *Store) ShredAllExported(ctx context.Context) error {
+ entries, err := filepath.Glob(filepath.Join(s.cfg.ExportDir, "*"))
+ if err != nil {
+ return fmt.Errorf("listing export dir: %w", err)
+ }
+
+ var lastErr error
+ for _, entry := range entries {
+ info, err := os.Stat(entry)
+ if err != nil || !info.Mode().IsRegular() {
+ continue
+ }
+ if err := ShredFile(ctx, entry); err != nil {
+ // Record the error but keep shredding — security demands best-effort
+ // destruction of all exported secrets even if one fails.
+ lastErr = err
+ }
+ }
+ return lastErr
+}
+
+// ShredFile destroys a single file using shred(1) if available, or rm -Pfv.
+// This mirrors Ruby's Geheim#shred_file method.
+func ShredFile(ctx context.Context, filePath string) error {
+ if _, err := exec.LookPath("shred"); err == nil {
+ cmd := exec.CommandContext(ctx, "shred", "-vu", filePath)
+ cmd.Stdout = io.Discard
+ cmd.Stderr = io.Discard
+ return cmd.Run()
+ }
+ cmd := exec.CommandContext(ctx, "rm", "-Pfv", filePath)
+ cmd.Stdout = io.Discard
+ cmd.Stderr = io.Discard
+ return cmd.Run()
+}