ruborg 0.5.0 → 0.6.1

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: 1233e04a2f95e8e8aadb9ad97d043b2d9b52c446821ff89495073a6a8762b6cc
4
- data.tar.gz: ee3fd1ae2256299120d7f78aac3243c2de44f8f3c072c4c29dd44cb761caf769
3
+ metadata.gz: 53e7193db767d8d7c217f89a4af6e7b4b4e18cdcc38cf0a8dff138767b561c10
4
+ data.tar.gz: 25c0232d3e86628d9ffb350f1d85e08f71ca032e72183e8d592108533ef2bc51
5
5
  SHA512:
6
- metadata.gz: 59d6ad7e5797cbbd964390d0425bf1392695c24f938a40978ddea371552da84604bf0a8eb189b207318ed68dba966311d45659f2b6abd3a59711f766465a5b36
7
- data.tar.gz: 6c77a312c9b820dd22d7f0396dbb24fac70da4654023c89eebc94c962d367b963a448a923ce23574862276452c441e3ebd723601fd58a2634e3337d402a9c066
6
+ metadata.gz: 6149c0535369f3f3b8f9829985fe6fa47777d0e2a6bcaf87630044bb7d7d2426be02ab104c87b50bc309352f9bdb80cad123e1549fc7c941dfe60b052840c95e
7
+ data.tar.gz: c0b036527fe8a70f526eebf07173f740d45fb88cd1fec3cd67a70b7331d7f806346d42b51217eefe4db2e8a8a7257f378a902c7711de657d9ef1603a460aaf46
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.2.9
data/CHANGELOG.md CHANGED
@@ -7,6 +7,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.1] - 2025-10-08
11
+
12
+ ### Added
13
+ - **Enhanced Configuration Validation**: Comprehensive validation system to catch configuration errors early
14
+ - **Unknown Key Detection**: Detects typos and invalid configuration keys at all levels (global, repository, sources, retention, passbolt, borg_options)
15
+ - **Retention Policy Validation**: Validates retention policy structure and values
16
+ - Integer fields (keep_hourly, keep_daily, etc.) must be non-negative integers
17
+ - Time-based fields (keep_within, keep_files_modified_within) must use correct format (e.g., "7d", "30d")
18
+ - Validates time format with h/d/w/m/y suffixes
19
+ - Rejects empty retention policies
20
+ - Detects unknown retention keys
21
+ - **Passbolt Configuration Validation**: Validates passbolt config structure
22
+ - Requires non-empty `resource_id` string
23
+ - Type validation for resource_id field
24
+ - Detects unknown passbolt keys
25
+ - **Retention Mode Validation**: Validates `retention_mode` values (must be "standard" or "per_file")
26
+ - **Source Validation**: Validates source structure (name, paths, exclude fields)
27
+ - **Comprehensive Logging**: Added logging throughout backup, restore, and deletion operations
28
+ - Repository operations (initialization, pruning, archive management)
29
+ - Backup operations (file counts, progress in per-file mode)
30
+ - Restore operations (extraction start/completion)
31
+ - Source file deletion tracking (with `--remove-source`)
32
+ - Passbolt integration events (resource ID logged, never passwords)
33
+ - All sensitive data (passwords, encryption keys) protected from logs
34
+ - **Enhanced Test Suite**: Expanded test coverage to 220 examples (67 new tests added)
35
+ - 23 new configuration validation tests
36
+ - 28 new logging integration tests
37
+ - 10 new CLI validation tests
38
+ - 6 new type checking tests for boolean configurations
39
+ - All tests passing with 0 failures
40
+
41
+ ### Changed
42
+ - `global_settings` now includes `borg_path` (previously was in whitelist but not propagated)
43
+ - Validation errors are collected and reported together for better user experience
44
+ - All validation runs automatically on configuration load
45
+
46
+ ### Fixed
47
+ - **Configuration Consistency**: Fixed inconsistency where `borg_path` was allowed in VALID_GLOBAL_KEYS but not returned by `global_settings` method
48
+
49
+ ## [0.6.0] - 2025-10-08
50
+
51
+ ### Added
52
+ - **Configuration Validation Command**: New `ruborg validate` command to check configuration files for type errors
53
+ - **Automatic Schema Validation**: All commands now validate configuration on startup to catch errors early
54
+ - **Strict Boolean Type Checking**: All boolean config values (auto_init, auto_prune, allow_remove_source, etc.) now require actual boolean types
55
+ - Prevents type confusion attacks where strings like `'true'` or `"false"` bypass security checks
56
+ - Clear error messages show actual type vs expected type
57
+ - Validation runs automatically on config load
58
+ - Comprehensive validation test suite (10 new test cases)
59
+ - Documentation for configuration validation in README
60
+
61
+ ### Changed
62
+ - Boolean configuration values now use strict type checking throughout the codebase
63
+ - `auto_init`: only boolean `true` enables, everything else disables
64
+ - `auto_prune`: only boolean `true` enables, everything else disables
65
+ - `allow_remove_source`: strict checking - only `TrueClass` enables (security-critical)
66
+ - `allow_relocated_repo`: permissive normalization - only `false` disables (backward compatible)
67
+ - `allow_unencrypted_repo`: permissive normalization - only `false` disables (backward compatible)
68
+ - Config class now validates schema by default (can be disabled with `validate_types: false`)
69
+
70
+ ### Security
71
+ - **Type Confusion Protection (CWE-843)**: Strict boolean type checking prevents configuration bypass attacks
72
+ - Before: `allow_remove_source: 'false'` (string) would be truthy and enable deletion
73
+ - After: Only `allow_remove_source: true` (boolean) enables the dangerous operation
74
+ - Enhanced error messages guide users to fix type errors correctly
75
+ - SECURITY.md updated with type confusion findings and mitigations
76
+
10
77
  ## [0.5.0] - 2025-10-08
11
78
 
12
79
  ### Added
data/README.md CHANGED
@@ -17,7 +17,7 @@ A friendly Ruby frontend for [Borg Backup](https://www.borgbackup.org/). Ruborg
17
17
  - 📊 **Logging** - Comprehensive logging with daily rotation
18
18
  - 🗄️ **Multi-Repository** - Manage multiple backup repositories with different sources
19
19
  - 🔄 **Auto-initialization** - Automatically initialize repositories on first use
20
- e- ⏰ **Retention Policies** - Configure backup retention (hourly, daily, weekly, monthly, yearly)
20
+ - ⏰ **Retention Policies** - Configure backup retention (hourly, daily, weekly, monthly, yearly)
21
21
  - 🗑️ **Automatic Pruning** - Automatically remove old backups based on retention policies
22
22
  - 📁 **Per-File Backup Mode** - NEW! Backup each file as a separate archive with metadata-based retention
23
23
  - 🕒 **File Metadata Retention** - NEW! Prune based on file modification time, works even after files are deleted
@@ -25,7 +25,7 @@ e- ⏰ **Retention Policies** - Configure backup retention (hourly, daily, weekl
25
25
  - 📈 **Summary View** - Quick overview of all repositories and their configurations
26
26
  - 🔧 **Custom Borg Path** - Support for custom Borg executable paths per repository
27
27
  - 🏠 **Hostname Validation** - NEW! Restrict backups to specific hosts (global or per-repository)
28
- - ✅ **Well-tested** - Comprehensive test suite with RSpec (153 tests)
28
+ - ✅ **Well-tested** - Comprehensive test suite with RSpec (220 examples, 0 failures)
29
29
  - 🔒 **Security-focused** - Path validation, safe YAML loading, command injection protection
30
30
 
31
31
  ## Prerequisites
@@ -162,15 +162,173 @@ repositories:
162
162
  ```
163
163
 
164
164
  **Configuration Features:**
165
+ - **Automatic Type Validation**: Configuration is validated on startup to catch type errors early
166
+ - **Validation Command**: Run `ruborg validate` to check configuration files for errors
165
167
  - **Descriptions**: Add `description` field to document each repository's purpose
166
168
  - **Hostname Validation**: Optional `hostname` field to restrict backups to specific hosts (global or per-repository)
167
- - **Global Settings**: Hostname, compression, encryption, auto_init, log_file, borg_path, borg_options, and retention apply to all repositories
168
- - **Per-Repository Overrides**: Any global setting can be overridden at the repository level (including hostname and custom borg_path)
169
+ - **Source Deletion Safety**: `allow_remove_source` flag to explicitly enable `--remove-source` option (default: disabled)
170
+ - **Type-Safe Booleans**: Strict boolean validation prevents configuration errors (must use `true`/`false`, not strings)
171
+ - **Global Settings**: Hostname, compression, encryption, auto_init, allow_remove_source, log_file, borg_path, borg_options, and retention apply to all repositories
172
+ - **Per-Repository Overrides**: Any global setting can be overridden at the repository level (including hostname, allow_remove_source, and custom borg_path)
169
173
  - **Custom Borg Path**: Specify a custom Borg executable path if borg is not in PATH or to use a specific version
170
174
  - **Retention Policies**: Define how many backups to keep (hourly, daily, weekly, monthly, yearly)
171
175
  - **Multiple Sources**: Each repository can have multiple backup sources with their own exclude patterns
172
176
  - **Flexible Organization**: Organize backups by type (documents, databases, media) with different policies
173
177
 
178
+ ## Configuration Validation
179
+
180
+ Ruborg automatically validates your configuration on startup. All commands check for type errors and structural issues before executing.
181
+
182
+ ### Validate Configuration
183
+
184
+ Check your configuration file for errors:
185
+
186
+ ```bash
187
+ ruborg validate --config ruborg.yml
188
+ ```
189
+
190
+ **Validation checks:**
191
+ - **Unknown configuration keys**: Detects typos and invalid keys at all levels (catches `auto_prun` vs `auto_prune`)
192
+ - **Boolean types**: Must be `true` or `false`, not strings like `'true'`
193
+ - **Retention policies**: Validates structure and values
194
+ - Integer fields (keep_hourly, keep_daily, etc.) must be non-negative integers
195
+ - Time-based fields (keep_within, keep_files_modified_within) must use format like "7d", "30d"
196
+ - Rejects empty retention policies
197
+ - Detects unknown retention keys
198
+ - **Passbolt configuration**: Validates resource_id is non-empty string
199
+ - **Retention mode**: Must be "standard" or "per_file"
200
+ - **Compression values**: Must be one of: lz4, zstd, zlib, lzma, none
201
+ - **Encryption modes**: Must be valid Borg encryption mode
202
+ - **Repository structure**: Required fields (name, path, sources)
203
+ - **Source structure**: Required fields (name, paths), validates exclude arrays
204
+ - **Borg options**: Validates allow_relocated_repo and allow_unencrypted_repo
205
+
206
+ **Example validation output:**
207
+
208
+ ```
209
+ ✓ Configuration is valid
210
+ No type errors or warnings found
211
+ ```
212
+
213
+ Or with errors:
214
+
215
+ ```
216
+ ❌ ERRORS FOUND (2):
217
+ - global/auto_init: must be boolean (true or false), got String: "true"
218
+ - test-repo/allow_remove_source: must be boolean (true or false), got Integer: 1
219
+
220
+ Configuration has errors that must be fixed.
221
+ ```
222
+
223
+ ## Logging
224
+
225
+ Ruborg v0.6.1 includes comprehensive logging to help you track backup operations, troubleshoot issues, and maintain audit trails. Logs are written to `~/.ruborg/logs/ruborg.log` by default, or to a custom location specified in your configuration.
226
+
227
+ ### What Gets Logged
228
+
229
+ Ruborg logs operational information at various levels to help you monitor and debug backup operations:
230
+
231
+ #### Repository Operations
232
+ - Repository creation and initialization
233
+ - Repository path and encryption mode
234
+ - Per-file pruning operations (archive counts, file modification times)
235
+ - Archive deletion during pruning
236
+
237
+ #### Backup Operations
238
+ - Number of files found for backup (per-file mode)
239
+ - Individual file backup progress (per-file mode)
240
+ - Backup completion status
241
+ - Archive names (user-provided or auto-generated)
242
+
243
+ #### Restore Operations
244
+ - Archive extraction start (archive name, destination path)
245
+ - Specific paths being restored (if using `--path` option)
246
+ - Extraction completion status
247
+
248
+ #### Source File Deletion (when using `--remove-source`)
249
+ - Start of source file removal process
250
+ - Each file/directory being removed (with full resolved path)
251
+ - Warnings for non-existent or missing paths
252
+ - Errors when attempting to delete system directories (with path)
253
+ - Count of items successfully removed
254
+
255
+ #### Passbolt Integration
256
+ - Password retrieval start (includes Passbolt resource UUID)
257
+ - Password retrieval failures (includes resource UUID)
258
+
259
+ ### What Is NOT Logged
260
+
261
+ To protect sensitive information, the following are **never logged**:
262
+
263
+ - ✅ **Passwords and passphrases** - Neither from command line nor from Passbolt
264
+ - ✅ **File contents** - Only file paths and metadata
265
+ - ✅ **Encryption keys** - Repository encryption passphrases are never written to logs
266
+ - ✅ **Passbolt passwords** - Only resource IDs (UUIDs) are logged, never the actual passwords retrieved
267
+
268
+ ### Log Levels
269
+
270
+ - **INFO**: Normal operation events (backups, restores, deletions)
271
+ - **WARN**: Non-critical issues (missing paths, skipped operations)
272
+ - **ERROR**: Critical errors (system path deletion attempts, command failures)
273
+ - **DEBUG**: Detailed information for troubleshooting (requires DEBUG level configuration)
274
+
275
+ ### Configuring Logging
276
+
277
+ ```yaml
278
+ # Log to default location: ~/.ruborg/logs/ruborg.log
279
+ log_file: default
280
+
281
+ # OR custom log file path
282
+ log_file: /var/log/ruborg/backup.log
283
+
284
+ # OR disable file logging (stdout only)
285
+ log_file: stdout
286
+ ```
287
+
288
+ You can also override the log file location using the `--log` command-line option:
289
+
290
+ ```bash
291
+ ruborg backup --repository documents --log /tmp/debug.log
292
+ ```
293
+
294
+ ### Log Security Considerations
295
+
296
+ - **File Paths**: Logs contain file and directory paths being backed up. Secure your log files with appropriate permissions (recommended: `chmod 600` or `640`)
297
+ - **Passbolt Resource IDs**: UUID identifiers for Passbolt resources are logged. These are safe to log as they are unguessable and don't expose credentials, but logs should still be protected
298
+ - **Archive Names**: User-provided or auto-generated archive names are logged for audit purposes
299
+ - **System Paths**: When `--remove-source` attempts to delete system directories, the full path is logged in error messages for security auditing
300
+
301
+ ### Best Practices
302
+
303
+ 1. **Secure Log Files**: Set restrictive permissions on log files
304
+ ```bash
305
+ chmod 600 ~/.ruborg/logs/ruborg.log
306
+ ```
307
+
308
+ 2. **Log Rotation**: Configure log rotation to prevent logs from consuming excessive disk space
309
+ ```bash
310
+ # Example logrotate configuration
311
+ /home/user/.ruborg/logs/ruborg.log {
312
+ weekly
313
+ rotate 4
314
+ compress
315
+ missingok
316
+ notifempty
317
+ }
318
+ ```
319
+
320
+ 3. **Monitoring**: Review logs regularly to detect:
321
+ - Failed backup operations
322
+ - Unauthorized deletion attempts
323
+ - Passbolt password retrieval failures
324
+ - Unexpected file paths
325
+
326
+ 4. **Audit Trail**: Logs provide an audit trail for compliance purposes:
327
+ - What was backed up and when
328
+ - What was restored and where
329
+ - What was deleted (with `--remove-source`)
330
+ - Any errors or security-related events
331
+
174
332
  ## Usage
175
333
 
176
334
  ### Initialize a Repository
@@ -198,10 +356,48 @@ ruborg backup --repository databases --name "db-backup-2025-10-05"
198
356
  # Using custom configuration file
199
357
  ruborg backup --config /path/to/config.yml --repository documents
200
358
 
201
- # Remove source files after successful backup
359
+ # Remove source files after successful backup (requires allow_remove_source: true)
202
360
  ruborg backup --repository documents --remove-source
203
361
  ```
204
362
 
363
+ **IMPORTANT: Source File Deletion Safety**
364
+
365
+ The `--remove-source` option is disabled by default for safety. To use it, you must explicitly enable it in your configuration:
366
+
367
+ ```yaml
368
+ # Global setting - applies to all repositories
369
+ allow_remove_source: true
370
+
371
+ # OR per-repository setting
372
+ repositories:
373
+ - name: temp-backups
374
+ allow_remove_source: true # Only for this repository
375
+ ...
376
+ ```
377
+
378
+ **⚠️ TYPE SAFETY WARNING:** The value MUST be a boolean `true`, not a string:
379
+
380
+ ```yaml
381
+ # ✅ CORRECT - Boolean true
382
+ allow_remove_source: true
383
+
384
+ # ❌ WRONG - String 'true' (will be rejected)
385
+ allow_remove_source: 'true'
386
+ allow_remove_source: "true"
387
+
388
+ # ❌ WRONG - Other truthy values (will be rejected)
389
+ allow_remove_source: 1
390
+ allow_remove_source: yes
391
+ ```
392
+
393
+ Ruborg uses strict type checking to prevent configuration errors. Only the boolean value `true` (unquoted) will enable source deletion. Any other value, including string `'true'` or `"true"`, will be rejected with a detailed error message showing the actual type received.
394
+
395
+ Without `allow_remove_source: true` configured, using `--remove-source` will result in an error:
396
+ ```
397
+ Error: Cannot use --remove-source: 'allow_remove_source' must be true (boolean).
398
+ Current value: "true" (String). Set 'allow_remove_source: true' in configuration.
399
+ ```
400
+
205
401
  ### List Archives
206
402
 
207
403
  ```bash
@@ -272,31 +468,6 @@ Borg version: 1.2.8
272
468
  Please upgrade Borg or migrate the repository
273
469
  ```
274
470
 
275
- ## Logging
276
-
277
- Ruborg automatically logs all operations with daily rotation. Log file location priority:
278
-
279
- 1. **CLI option** (highest priority): `--log /path/to/custom.log`
280
- 2. **Config file**: `log_file: /path/to/log.log`
281
- 3. **Default**: `~/.ruborg/logs/ruborg.log`
282
-
283
- **Examples:**
284
-
285
- ```bash
286
- # Use CLI option (overrides config)
287
- ruborg backup --log /var/log/ruborg.log
288
-
289
- # Or set in config file
290
- log_file: /var/log/ruborg.log
291
- ```
292
-
293
- **Logs include:**
294
- - Operation start/completion timestamps
295
- - Paths being backed up
296
- - Archive names created
297
- - Success and error messages
298
- - Source file removal actions
299
-
300
471
  ## Passbolt Integration
301
472
 
302
473
  Ruborg can retrieve encryption passphrases from Passbolt using the Passbolt CLI:
@@ -437,6 +608,7 @@ See [SECURITY.md](SECURITY.md) for detailed security information and best practi
437
608
  | Command | Description | Options |
438
609
  |---------|-------------|---------|
439
610
  | `init REPOSITORY` | Initialize a new Borg repository | `--passphrase`, `--passbolt-id`, `--log` |
611
+ | `validate` | Validate configuration file for type errors | `--config`, `--log` |
440
612
  | `backup` | Create a backup using config file | `--config`, `--repository`, `--all`, `--name`, `--remove-source`, `--log` |
441
613
  | `list` | List all archives in repository | `--config`, `--repository`, `--log` |
442
614
  | `restore ARCHIVE` | Restore files from archive | `--config`, `--repository`, `--destination`, `--path`, `--log` |
data/SECURITY.md CHANGED
@@ -66,6 +66,81 @@ Ruborg implements several security measures to protect your backup operations:
66
66
  - Integrated into development workflow
67
67
  - Run: `bundle exec bundle-audit check`
68
68
 
69
+ ### 13. Boolean Type Safety (Type Confusion Protection)
70
+ - **Critical safety flag validation** for `allow_remove_source` configuration
71
+ - Uses strict type checking (`is_a?(TrueClass)`) to prevent type confusion attacks
72
+ - Rejects truthy values like strings `'true'`, `"false"`, integers `1`, or `"yes"`
73
+ - Only boolean `true` enables dangerous operations like `--remove-source`
74
+ - Provides detailed error messages showing actual type received vs expected
75
+ - Prevents configuration errors that could lead to unintended data loss
76
+ - **CWE-843 Mitigation**: Protects against type confusion vulnerabilities
77
+
78
+ ### 14. Logging Security (v0.6.1+)
79
+ - **Comprehensive logging** of backup operations for audit trails and troubleshooting
80
+ - **Sensitive data protection**: Passwords and passphrases are NEVER logged
81
+ - **Safe operational logging**: File paths, archive names, and operation status are logged
82
+ - **Passbolt resource IDs** (UUIDs) are logged but actual passwords are not
83
+ - **System path protection**: Failed deletion attempts are logged with full paths for security auditing
84
+ - **Log level support**: INFO, WARN, ERROR, DEBUG levels for appropriate detail
85
+
86
+ #### What Is Logged (Safe)
87
+ - Repository creation and initialization events
88
+ - Backup operation start/completion with file counts
89
+ - Individual file paths being backed up (per-file mode)
90
+ - Archive names (user-provided or auto-generated)
91
+ - Restore operations with destination paths
92
+ - Source file deletion events (when using `--remove-source`)
93
+ - Passbolt resource IDs (UUIDs) for password retrieval attempts
94
+ - System path deletion refusals with full path (security audit)
95
+ - Pruning operations with archive counts
96
+
97
+ #### What Is NEVER Logged (Protected)
98
+ - ✅ **Passwords and passphrases** - Neither from CLI nor Passbolt
99
+ - ✅ **Encryption keys** - Repository encryption keys never written to logs
100
+ - ✅ **Passbolt passwords** - Only resource UUIDs logged, not actual retrieved passwords
101
+ - ✅ **File contents** - Only paths and metadata, never file contents
102
+ - ✅ **Environment variables** with sensitive data
103
+
104
+ #### Log Security Recommendations
105
+ 1. **Protect log files** with restrictive permissions:
106
+ ```bash
107
+ chmod 600 ~/.ruborg/logs/ruborg.log
108
+ # Or for shared access with backup group:
109
+ chmod 640 ~/.ruborg/logs/ruborg.log
110
+ chown user:backup ~/.ruborg/logs/ruborg.log
111
+ ```
112
+
113
+ 2. **Configure log rotation** to prevent log files from growing indefinitely:
114
+ ```bash
115
+ # /etc/logrotate.d/ruborg
116
+ /home/user/.ruborg/logs/ruborg.log {
117
+ weekly
118
+ rotate 4
119
+ compress
120
+ missingok
121
+ notifempty
122
+ create 0600 user user
123
+ }
124
+ ```
125
+
126
+ 3. **Review logs regularly** for:
127
+ - Failed backup or restore operations
128
+ - Unauthorized `--remove-source` attempts
129
+ - Passbolt password retrieval failures
130
+ - System path deletion attempts (potential security issues)
131
+ - Unexpected file paths being backed up
132
+
133
+ 4. **Secure log storage locations**:
134
+ - Use absolute paths in `log_file` configuration
135
+ - Avoid logging to world-readable directories
136
+ - Consider logging to `/var/log/ruborg/` with proper permissions
137
+
138
+ 5. **Passbolt Resource IDs in Logs**:
139
+ - Resource IDs (UUIDs) are logged for operational debugging
140
+ - These are identifiers, not credentials - safe to log
141
+ - They cannot be used to access Passbolt without proper authentication
142
+ - Still, protect logs as they reveal which Passbolt resources are used
143
+
69
144
  ## Security Best Practices
70
145
 
71
146
  ### When Using `--remove-source`
@@ -76,6 +151,16 @@ Ruborg implements several security measures to protect your backup operations:
76
151
  2. **Never use on symlinks** to critical system directories
77
152
  3. **Verify backups** before using this flag in production
78
153
  4. **Use absolute paths** in configuration to avoid ambiguity
154
+ 5. **Use boolean values** for `allow_remove_source` - NEVER use quoted strings:
155
+ ```yaml
156
+ # ✅ CORRECT
157
+ allow_remove_source: true
158
+
159
+ # ❌ WRONG - Will be rejected
160
+ allow_remove_source: 'true'
161
+ allow_remove_source: "true"
162
+ allow_remove_source: 1
163
+ ```
79
164
 
80
165
  ### Configuration File Security
81
166
 
@@ -144,6 +229,29 @@ We will respond within 48 hours and work with you to address the issue.
144
229
 
145
230
  ## Security Audit History
146
231
 
232
+ - **v0.6.1** (2025-10-08): Enhanced logging with sensitive data protection
233
+ - **NEW FEATURE**: Comprehensive logging for backup operations, restoration, and deletion
234
+ - Passwords and passphrases are NEVER logged (neither CLI nor Passbolt passwords)
235
+ - Passbolt resource IDs (UUIDs) logged for debugging - identifiers only, not credentials
236
+ - File paths and archive names logged for audit trails
237
+ - System path deletion attempts logged with full paths for security monitoring
238
+ - Log levels: INFO, WARN, ERROR, DEBUG for appropriate detail
239
+ - Documentation added for logging security best practices
240
+ - Enhanced configuration validation with unknown key detection across all levels
241
+
242
+ - **v0.6.0** (2025-10-08): Configuration validation and type confusion protection
243
+ - **SECURITY FIX**: Implemented strict boolean type checking for `allow_remove_source`
244
+ - Prevents type confusion attacks (CWE-843) where string values bypass safety checks
245
+ - Added configuration validation command (`ruborg validate`) for proactive error detection
246
+ - Automatic schema validation on config load catches type errors early
247
+ - Added 10 comprehensive test cases for validation and type confusion scenarios
248
+ - Enhanced error messages to show actual type vs expected type
249
+ - Updated documentation with type safety warnings and examples
250
+
251
+ - **v0.5.0** (2025-10-08): Hostname validation
252
+ - Added hostname validation feature (optional global or per-repository)
253
+ - Prevents accidental execution of backups on wrong machines
254
+
147
255
  - **v0.4.0** (2025-10-06): Complete command injection elimination
148
256
  - **CRITICAL**: Fixed all remaining command injection vulnerabilities in repository.rb
149
257
  - Replaced all backtick execution with Open3.capture3/capture2e methods
@@ -210,3 +318,5 @@ Before deploying ruborg in production:
210
318
  - [ ] Configure borg_options for your security requirements
211
319
  - [ ] Use default archive names or sanitized custom names only
212
320
  - [ ] Ensure backup paths don't contain empty or nil values
321
+ - [ ] Use boolean `true` (not strings) for `allow_remove_source` configuration
322
+ - [ ] Configure hostname validation for multi-server environments
data/exe/ruborg CHANGED
@@ -3,4 +3,12 @@
3
3
 
4
4
  require_relative "../lib/ruborg"
5
5
 
6
- Ruborg::CLI.start(ARGV)
6
+ begin
7
+ Ruborg::CLI.start(ARGV)
8
+ rescue Ruborg::Error => e
9
+ warn "Error: #{e.message}"
10
+ exit 1
11
+ rescue Interrupt
12
+ warn "\nInterrupted"
13
+ exit 130
14
+ end
data/lib/ruborg/backup.rb CHANGED
@@ -3,11 +3,12 @@
3
3
  module Ruborg
4
4
  # Backup operations using Borg
5
5
  class Backup
6
- def initialize(repository, config:, retention_mode: "standard", repo_name: nil)
6
+ def initialize(repository, config:, retention_mode: "standard", repo_name: nil, logger: nil)
7
7
  @repository = repository
8
8
  @config = config
9
9
  @retention_mode = retention_mode
10
10
  @repo_name = repo_name
11
+ @logger = logger
11
12
  end
12
13
 
13
14
  def create(name: nil, remove_source: false)
@@ -37,19 +38,25 @@ module Ruborg
37
38
 
38
39
  raise BorgError, "No files found to backup" if files_to_backup.empty?
39
40
 
41
+ @logger&.info("Per-file mode: Found #{files_to_backup.size} file(s) to backup")
42
+
40
43
  timestamp = Time.now.strftime("%Y-%m-%d_%H-%M-%S")
41
44
 
42
- files_to_backup.each do |file_path|
45
+ files_to_backup.each_with_index do |file_path, index|
43
46
  # Generate hash-based archive name
44
47
  path_hash = generate_path_hash(file_path)
45
48
  archive_name = name_prefix || "#{@repo_name}-#{path_hash}-#{timestamp}"
46
49
 
50
+ @logger&.info("Backing up file #{index + 1}/#{files_to_backup.size}: #{file_path}")
51
+
47
52
  # Create archive for single file with original path as comment
48
53
  cmd = build_per_file_create_command(archive_name, file_path)
49
54
 
50
55
  execute_borg_command(cmd)
51
56
  end
52
57
 
58
+ @logger&.info("Per-file backup completed: #{files_to_backup.size} file(s) backed up")
59
+
53
60
  # NOTE: remove_source handled per file after successful backup
54
61
  remove_source_files if remove_source
55
62
  end
@@ -108,6 +115,9 @@ module Ruborg
108
115
  def extract(archive_name, destination: ".", path: nil)
109
116
  raise BorgError, "Repository does not exist" unless @repository.exists?
110
117
 
118
+ extract_target = path ? "#{path} from #{archive_name}" : archive_name
119
+ @logger&.info("Extracting #{extract_target} to #{destination}")
120
+
111
121
  cmd = [@repository.borg_path, "extract", "#{@repository.path}::#{archive_name}"]
112
122
  cmd << path if path
113
123
 
@@ -125,6 +135,8 @@ module Ruborg
125
135
  execute_borg_command(cmd)
126
136
  end
127
137
  end
138
+
139
+ @logger&.info("Extraction completed successfully")
128
140
  end
129
141
 
130
142
  def list_archives
@@ -132,8 +144,10 @@ module Ruborg
132
144
  end
133
145
 
134
146
  def delete(archive_name)
147
+ @logger&.info("Deleting archive: #{archive_name}")
135
148
  cmd = [@repository.borg_path, "delete", "#{@repository.path}::#{archive_name}"]
136
149
  execute_borg_command(cmd)
150
+ @logger&.info("Archive deleted successfully: #{archive_name}")
137
151
  end
138
152
 
139
153
  private
@@ -171,29 +185,45 @@ module Ruborg
171
185
  def remove_source_files
172
186
  require "fileutils"
173
187
 
188
+ @logger&.info("Removing source files after successful backup")
189
+
190
+ removed_count = 0
191
+
174
192
  @config.backup_paths.each do |path|
175
193
  # Resolve symlinks and validate path
176
194
  begin
177
195
  real_path = File.realpath(path)
178
196
  rescue Errno::ENOENT
179
197
  # Path doesn't exist, skip
198
+ @logger&.warn("Source path does not exist, skipping: #{path}")
180
199
  next
181
200
  end
182
201
 
183
202
  # Security check: ensure path hasn't been tampered with
184
- next unless File.exist?(real_path)
203
+ unless File.exist?(real_path)
204
+ @logger&.warn("Source path no longer exists, skipping: #{real_path}")
205
+ next
206
+ end
185
207
 
186
208
  # Additional safety: don't delete root or system directories
187
209
  if real_path == "/" || real_path.start_with?("/bin", "/sbin", "/usr", "/etc", "/sys", "/proc")
210
+ @logger&.error("Refusing to delete system path: #{real_path}")
188
211
  raise BorgError, "Refusing to delete system path: #{real_path}"
189
212
  end
190
213
 
214
+ file_type = File.directory?(real_path) ? "directory" : "file"
215
+ @logger&.info("Removing #{file_type}: #{real_path}")
216
+
191
217
  if File.directory?(real_path)
192
218
  FileUtils.rm_rf(real_path, secure: true)
193
219
  elsif File.file?(real_path)
194
220
  FileUtils.rm(real_path)
195
221
  end
222
+
223
+ removed_count += 1
196
224
  end
225
+
226
+ @logger&.info("Source file removal completed: #{removed_count} item(s) removed")
197
227
  end
198
228
 
199
229
  def validate_destination_path(destination)