ruborg 0.7.3 → 0.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 314fb4f24b1b5544d95257f5d3f315099f221ecc92b7920e935066319db72458
4
- data.tar.gz: 0ef7e5183f7596e1417b4b12915557d9c177b0b2504f6a143c7ff8aa59f726aa
3
+ metadata.gz: 288150d1a678f2df71346d94b797019f31a52008de43663aa19cb9fd3584d5a6
4
+ data.tar.gz: 460fe4bff0085989bc9a2d2ae4db7ae4591aff9a928ae9d320cd079d4d6a276d
5
5
  SHA512:
6
- metadata.gz: fb9bcdb3517dc9fd2c1c05d868e8015fd5fe3a55c00e5c277cf7b0257f238535821f8dbcda594dc9bc4b413a8b96d4739d327e1b420f6bad08ac19e724193199
7
- data.tar.gz: 6544e4d87969bd59ca7912653ecaebedeca75795f934edb93ad9435a0e48cff5b0c95734aa5bdc70572a38cbed014ea8ac93cff4abee97867a910acba47d5240
6
+ metadata.gz: 34f36001b129d8e0bd005ba1e908361df50df198626fe98b5015afa6cbce2860b055086e87c6f81e4aebec101c1297832b525d78e1ce995d5582d40ee9065eaf
7
+ data.tar.gz: b881f8bbd05e49893afb7213ab22d64d01117f3dedbdee6772b80720489d367b7a2bb6a1c33a9e63161f6673b3683198b33010a7c54dcf70e38e56461f7ec1ee
data/CHANGELOG.md CHANGED
@@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.7.5] - 2025-10-09
11
+
12
+ ### Added
13
+ - **Automatic Immutable Attribute Removal**: Ruborg now automatically detects and removes Linux immutable flags (`chattr +i`) when deleting files with `--remove-source`
14
+ - Checks files with `lsattr` before deletion
15
+ - Removes immutable flag with `chattr -i` if present
16
+ - Works for both single files (per-file mode) and directories (standard mode)
17
+ - Gracefully handles systems without lsattr/chattr (macOS, etc.)
18
+ - Logs all immutable attribute operations for audit trail
19
+
20
+ ### Technical Details
21
+ - Per-file mode: Checks each file individually before deletion (lib/ruborg/backup.rb:365)
22
+ - Standard mode: Recursively removes immutable from all files in directory (lib/ruborg/backup.rb:400-414)
23
+ - Linux-only feature - silently skips on other platforms
24
+ - Raises error if chattr command fails with proper error context
25
+ - Immutable flag detection uses precise parsing of lsattr flags field (lib/ruborg/backup.rb:386-387)
26
+ - Test coverage: 6 comprehensive specs covering all immutable file scenarios
27
+
28
+ ## [0.7.4] - 2025-10-09
29
+
30
+ ### Fixed
31
+ - **Passbolt Error Reporting**: Enhanced error handling for Passbolt CLI failures
32
+ - Now captures and logs stderr from Passbolt CLI commands
33
+ - Error messages include actual Passbolt CLI output for easier debugging
34
+ - Improved error logging with detailed failure context
35
+
36
+ ### Changed
37
+ - **Passbolt Environment Variables**: Passbolt env vars now explicitly passed to subprocess
38
+ - Preserves `PASSBOLT_SERVER_ADDRESS`, `PASSBOLT_USER_PRIVATE_KEY_FILE`, `PASSBOLT_USER_PASSWORD`
39
+ - Also preserves `PASSBOLT_GPG_HOME` and `PASSBOLT_CONFIG`
40
+ - Ensures Passbolt CLI has access to required configuration when run by Ruborg
41
+
10
42
  ## [0.7.3] - 2025-10-09
11
43
 
12
44
  ### Changed
data/README.md CHANGED
@@ -398,6 +398,25 @@ Error: Cannot use --remove-source: 'allow_remove_source' must be true (boolean).
398
398
  Current value: "true" (String). Set 'allow_remove_source: true' in configuration.
399
399
  ```
400
400
 
401
+ **📌 Immutable File Handling (Linux)**
402
+
403
+ Ruborg automatically detects and removes Linux immutable attributes (`chattr +i`) when deleting files with `--remove-source`:
404
+
405
+ - **Automatic Detection**: Checks files with `lsattr` before deletion
406
+ - **Automatic Removal**: Removes immutable flag with `chattr -i` if present
407
+ - **Platform-Aware**: Gracefully skips on non-Linux systems (macOS, BSD, etc.)
408
+ - **Comprehensive**: Works for both single files and directories (recursive)
409
+ - **Logged**: All immutable attribute operations are logged for audit trail
410
+
411
+ **Root Privileges**: If your files have immutable attributes, you'll need root privileges to remove them. Configure sudoers for ruborg:
412
+
413
+ ```bash
414
+ # /etc/sudoers.d/ruborg
415
+ michail ALL=(root) NOPASSWD: /usr/local/bin/ruborg
416
+ ```
417
+
418
+ This allows running ruborg with sudo for file deletion without password prompts.
419
+
401
420
  ### List Archives
402
421
 
403
422
  ```bash
data/SECURITY.md CHANGED
@@ -19,7 +19,15 @@ Ruborg implements several security measures to protect your backup operations:
19
19
  - Refuses to delete system directories even when targeted via symlinks
20
20
  - Uses `FileUtils.rm_rf` with `secure: true` option
21
21
 
22
- ### 4. Safe YAML Loading
22
+ ### 4. Immutable File Handling (Linux)
23
+ - **Automatic Detection**: Checks for Linux immutable attributes (`lsattr`) before file deletion
24
+ - **Safe Removal**: Removes immutable flag (`chattr -i`) only when necessary for deletion
25
+ - **Platform-Aware**: Feature only active on Linux systems with lsattr/chattr commands available
26
+ - **Error Handling**: Raises informative errors if immutable flag cannot be removed
27
+ - **Audit Trail**: All immutable attribute operations are logged for security auditing
28
+ - **Root Required**: Removing immutable attributes requires root privileges (use sudo with appropriate sudoers configuration)
29
+
30
+ ### 5. Safe YAML Loading
23
31
  - Uses `YAML.safe_load_file` to prevent arbitrary code execution
24
32
  - Rejects YAML files containing Ruby objects or other dangerous constructs
25
33
  - Only permits basic data types and Symbol class
@@ -229,6 +237,20 @@ We will respond within 48 hours and work with you to address the issue.
229
237
 
230
238
  ## Security Audit History
231
239
 
240
+ - **v0.7.5** (2025-10-09): Immutable file handling - security review passed
241
+ - **NEW FEATURE**: Automatic detection and removal of Linux immutable attributes (`chattr +i`) when deleting files with `--remove-source`
242
+ - **PLATFORM-AWARE**: Feature only active on Linux systems with lsattr/chattr commands available
243
+ - **SAFE OPERATION**: Checks files with `lsattr` before deletion, removes immutable flag with `chattr -i` only when necessary
244
+ - **ERROR HANDLING**: Raises informative errors if immutable flag cannot be removed (operation not permitted)
245
+ - **AUDIT TRAIL**: All immutable attribute operations logged for security monitoring
246
+ - **ROOT REQUIRED**: Removing immutable attributes requires root privileges (documented sudoers configuration)
247
+ - **SECURITY REVIEW**: No new security vulnerabilities introduced
248
+ - Uses Open3.capture3 for safe command execution (no shell injection)
249
+ - Precise flag parsing prevents false positives from filenames containing 'i'
250
+ - Works for both single files (per-file mode) and directories (recursive, standard mode)
251
+ - Gracefully handles systems without lsattr/chattr (macOS, BSD, etc.)
252
+ - Test coverage: 6 comprehensive specs covering all scenarios
253
+
232
254
  - **v0.7.1** (2025-10-08): Paranoid mode duplicate detection - security review passed
233
255
  - **NEW FEATURE**: SHA256 content hashing for detecting file changes even when mtime/size are identical
234
256
  - **NEW FEATURE**: Smart skip statistics showing backed-up and skipped file counts
data/lib/ruborg/backup.rb CHANGED
@@ -361,10 +361,58 @@ module Ruborg
361
361
  raise BorgError, "Refusing to delete system path: #{real_path}"
362
362
  end
363
363
 
364
+ # Check for immutable attribute and remove it if present
365
+ remove_immutable_attribute(real_path)
366
+
364
367
  @logger&.info("Removing file: #{real_path}")
365
368
  FileUtils.rm(real_path)
366
369
  end
367
370
 
371
+ def remove_immutable_attribute(file_path)
372
+ # Check if lsattr command is available (Linux only)
373
+ return unless system("which lsattr > /dev/null 2>&1")
374
+
375
+ # Get file attributes
376
+ require "open3"
377
+ stdout, stderr, status = Open3.capture3("lsattr", file_path)
378
+
379
+ unless status.success?
380
+ @logger&.warn("Could not check attributes for #{file_path}: #{stderr.strip}")
381
+ return
382
+ end
383
+
384
+ # Check if immutable flag is set (format: "----i--------e----- /path/to/file")
385
+ # Extract the flags portion (everything before the file path)
386
+ flags = stdout.split.first || ""
387
+ return unless flags.include?("i")
388
+
389
+ @logger&.info("Removing immutable attribute from: #{file_path}")
390
+ _chattr_stdout, chattr_stderr, chattr_status = Open3.capture3("chattr", "-i", file_path)
391
+
392
+ unless chattr_status.success?
393
+ @logger&.error("Failed to remove immutable attribute from #{file_path}: #{chattr_stderr.strip}")
394
+ raise BorgError, "Cannot remove immutable file: #{file_path}. Error: #{chattr_stderr.strip}"
395
+ end
396
+
397
+ @logger&.info("Successfully removed immutable attribute from: #{file_path}")
398
+ end
399
+
400
+ def remove_immutable_from_directory(dir_path)
401
+ # Check if lsattr command is available (Linux only)
402
+ return unless system("which lsattr > /dev/null 2>&1")
403
+
404
+ require "find"
405
+ require "open3"
406
+
407
+ # Remove immutable from directory itself
408
+ remove_immutable_attribute(dir_path)
409
+
410
+ # Recursively remove immutable from all files in directory
411
+ Find.find(dir_path) do |path|
412
+ remove_immutable_attribute(path) if File.file?(path)
413
+ end
414
+ end
415
+
368
416
  def remove_source_files
369
417
  require "fileutils"
370
418
 
@@ -398,8 +446,12 @@ module Ruborg
398
446
  @logger&.info("Removing #{file_type}: #{real_path}")
399
447
 
400
448
  if File.directory?(real_path)
449
+ # Remove immutable attributes from all files in directory
450
+ remove_immutable_from_directory(real_path)
401
451
  FileUtils.rm_rf(real_path, secure: true)
402
452
  elsif File.file?(real_path)
453
+ # Remove immutable attribute from single file
454
+ remove_immutable_attribute(real_path)
403
455
  FileUtils.rm(real_path)
404
456
  end
405
457
 
@@ -17,11 +17,12 @@ module Ruborg
17
17
  @logger&.info("Retrieving password from Passbolt (resource_id: #{@resource_id})")
18
18
 
19
19
  cmd = ["passbolt", "get", "resource", "--id", @resource_id, "--json"]
20
- output, status = execute_command(cmd)
20
+ output, status, error_msg = execute_command(cmd)
21
21
 
22
22
  unless status
23
23
  @logger&.error("Failed to retrieve password from Passbolt for resource #{@resource_id}")
24
- raise PassboltError, "Failed to retrieve password from Passbolt"
24
+ error_detail = error_msg ? ": #{error_msg}" : ""
25
+ raise PassboltError, "Failed to retrieve password from Passbolt#{error_detail}"
25
26
  end
26
27
 
27
28
  @logger&.info("Successfully retrieved password from Passbolt")
@@ -38,8 +39,29 @@ module Ruborg
38
39
 
39
40
  def execute_command(cmd)
40
41
  require "open3"
41
- stdout, _, status = Open3.capture3(*cmd)
42
- [stdout, status.success?]
42
+
43
+ # Preserve Passbolt environment variables
44
+ env = {}
45
+ %w[
46
+ PASSBOLT_SERVER_ADDRESS
47
+ PASSBOLT_USER_PRIVATE_KEY_FILE
48
+ PASSBOLT_USER_PASSWORD
49
+ PASSBOLT_GPG_HOME
50
+ PASSBOLT_CONFIG
51
+ ].each do |var|
52
+ env[var] = ENV[var] if ENV[var]
53
+ end
54
+
55
+ stdout, stderr, status = Open3.capture3(env, *cmd)
56
+
57
+ # Log stderr if command failed
58
+ error_msg = nil
59
+ if !status.success? && !stderr.strip.empty?
60
+ error_msg = stderr.strip
61
+ @logger&.error("Passbolt CLI error: #{error_msg}")
62
+ end
63
+
64
+ [stdout, status.success?, error_msg]
43
65
  end
44
66
 
45
67
  def parse_password(json_output)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ruborg
4
- VERSION = "0.7.3"
4
+ VERSION = "0.7.5"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruborg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.3
4
+ version: 0.7.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michail Pantelelis