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 +4 -4
- data/CHANGELOG.md +32 -0
- data/README.md +19 -0
- data/SECURITY.md +23 -1
- data/lib/ruborg/backup.rb +52 -0
- data/lib/ruborg/passbolt.rb +26 -4
- data/lib/ruborg/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 288150d1a678f2df71346d94b797019f31a52008de43663aa19cb9fd3584d5a6
|
|
4
|
+
data.tar.gz: 460fe4bff0085989bc9a2d2ae4db7ae4591aff9a928ae9d320cd079d4d6a276d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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
|
|
data/lib/ruborg/passbolt.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
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)
|
data/lib/ruborg/version.rb
CHANGED