ruborg 0.4.0 → 0.6.0
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 +43 -0
 - data/README.md +150 -4
 - data/SECURITY.md +34 -0
 - data/lib/ruborg/cli.rb +143 -4
 - data/lib/ruborg/config.rb +77 -2
 - data/lib/ruborg/repository.rb +8 -0
 - data/lib/ruborg/version.rb +1 -1
 - data/ruborg.gemspec +46 -0
 - data/ruborg.yml.example +3 -0
 - metadata +2 -1
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 821f1707180870ec6b2ffccda4ef4d901b3ee274d7402c26170ca294e80efb8b
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 62e2a5ee53cd75024b78e08e32295c34cf1829cd3bd34be9d6403c7ec1dfb810
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: acd1bcd0b6da0914888c11d50299ea2f365e2bf5a81423edbfc7e935a17d020b841de9043900ca86ed1df2ab8e50e1359806ad2857de3bd849b8f040c2909549
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 799d52c64a3bf858b2324f5101255a77dd428778b1eced9bfa11e54d9c2ef23642645a886902f83c05336c89a61d1a80b02ad83185fecf3cf0cac866d18e7c09
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -7,6 +7,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 
     | 
|
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            ## [Unreleased]
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
      
 10 
     | 
    
         
            +
            ## [0.6.0] - 2025-10-08
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            ### Added
         
     | 
| 
      
 13 
     | 
    
         
            +
            - **Configuration Validation Command**: New `ruborg validate` command to check configuration files for type errors
         
     | 
| 
      
 14 
     | 
    
         
            +
            - **Automatic Schema Validation**: All commands now validate configuration on startup to catch errors early
         
     | 
| 
      
 15 
     | 
    
         
            +
            - **Strict Boolean Type Checking**: All boolean config values (auto_init, auto_prune, allow_remove_source, etc.) now require actual boolean types
         
     | 
| 
      
 16 
     | 
    
         
            +
              - Prevents type confusion attacks where strings like `'true'` or `"false"` bypass security checks
         
     | 
| 
      
 17 
     | 
    
         
            +
              - Clear error messages show actual type vs expected type
         
     | 
| 
      
 18 
     | 
    
         
            +
              - Validation runs automatically on config load
         
     | 
| 
      
 19 
     | 
    
         
            +
            - Comprehensive validation test suite (10 new test cases)
         
     | 
| 
      
 20 
     | 
    
         
            +
            - Documentation for configuration validation in README
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            ### Changed
         
     | 
| 
      
 23 
     | 
    
         
            +
            - Boolean configuration values now use strict type checking throughout the codebase
         
     | 
| 
      
 24 
     | 
    
         
            +
              - `auto_init`: only boolean `true` enables, everything else disables
         
     | 
| 
      
 25 
     | 
    
         
            +
              - `auto_prune`: only boolean `true` enables, everything else disables
         
     | 
| 
      
 26 
     | 
    
         
            +
              - `allow_remove_source`: strict checking - only `TrueClass` enables (security-critical)
         
     | 
| 
      
 27 
     | 
    
         
            +
              - `allow_relocated_repo`: permissive normalization - only `false` disables (backward compatible)
         
     | 
| 
      
 28 
     | 
    
         
            +
              - `allow_unencrypted_repo`: permissive normalization - only `false` disables (backward compatible)
         
     | 
| 
      
 29 
     | 
    
         
            +
            - Config class now validates schema by default (can be disabled with `validate_types: false`)
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            ### Security
         
     | 
| 
      
 32 
     | 
    
         
            +
            - **Type Confusion Protection (CWE-843)**: Strict boolean type checking prevents configuration bypass attacks
         
     | 
| 
      
 33 
     | 
    
         
            +
              - Before: `allow_remove_source: 'false'` (string) would be truthy and enable deletion
         
     | 
| 
      
 34 
     | 
    
         
            +
              - After: Only `allow_remove_source: true` (boolean) enables the dangerous operation
         
     | 
| 
      
 35 
     | 
    
         
            +
            - Enhanced error messages guide users to fix type errors correctly
         
     | 
| 
      
 36 
     | 
    
         
            +
            - SECURITY.md updated with type confusion findings and mitigations
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            ## [0.5.0] - 2025-10-08
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            ### Added
         
     | 
| 
      
 41 
     | 
    
         
            +
            - **Hostname Validation**: Optional `hostname` configuration key to restrict backup operations to specific hosts
         
     | 
| 
      
 42 
     | 
    
         
            +
              - Can be configured globally or per-repository
         
     | 
| 
      
 43 
     | 
    
         
            +
              - Repository-specific hostname overrides global setting
         
     | 
| 
      
 44 
     | 
    
         
            +
              - Validates system hostname before backup, list, restore, check operations
         
     | 
| 
      
 45 
     | 
    
         
            +
              - Prevents accidental execution of backups on wrong machines
         
     | 
| 
      
 46 
     | 
    
         
            +
              - Displayed in `info` command output
         
     | 
| 
      
 47 
     | 
    
         
            +
            - Comprehensive test coverage for hostname validation (6 new test cases)
         
     | 
| 
      
 48 
     | 
    
         
            +
            - Documentation for hostname feature in example config and README
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            ### Changed
         
     | 
| 
      
 51 
     | 
    
         
            +
            - `info` command now displays hostname when configured (global or per-repository)
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
       10 
53 
     | 
    
         
             
            ## [0.4.0] - 2025-10-06
         
     | 
| 
       11 
54 
     | 
    
         | 
| 
       12 
55 
     | 
    
         
             
            ### Added
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -24,7 +24,8 @@ e- ⏰ **Retention Policies** - Configure backup retention (hourly, daily, weekl 
     | 
|
| 
       24 
24 
     | 
    
         
             
            - 📋 **Repository Descriptions** - Document each repository's purpose
         
     | 
| 
       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 (178+ tests)
         
     | 
| 
       28 
29 
     | 
    
         
             
            - 🔒 **Security-focused** - Path validation, safe YAML loading, command injection protection
         
     | 
| 
       29 
30 
     | 
    
         | 
| 
       30 
31 
     | 
    
         
             
            ## Prerequisites
         
     | 
| 
         @@ -127,6 +128,7 @@ repositories: 
     | 
|
| 
       127 
128 
     | 
    
         
             
              - name: databases
         
     | 
| 
       128 
129 
     | 
    
         
             
                description: "MySQL and PostgreSQL database dumps"
         
     | 
| 
       129 
130 
     | 
    
         
             
                path: /mnt/backup/databases
         
     | 
| 
      
 131 
     | 
    
         
            +
                hostname: dbserver.local  # Optional: repository-specific hostname override
         
     | 
| 
       130 
132 
     | 
    
         
             
                # Repository-specific passbolt (overrides global)
         
     | 
| 
       131 
133 
     | 
    
         
             
                passbolt:
         
     | 
| 
       132 
134 
     | 
    
         
             
                  resource_id: "db-specific-passbolt-id"
         
     | 
| 
         @@ -160,14 +162,55 @@ repositories: 
     | 
|
| 
       160 
162 
     | 
    
         
             
            ```
         
     | 
| 
       161 
163 
     | 
    
         | 
| 
       162 
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
         
     | 
| 
       163 
167 
     | 
    
         
             
            - **Descriptions**: Add `description` field to document each repository's purpose
         
     | 
| 
       164 
     | 
    
         
            -
            - ** 
     | 
| 
       165 
     | 
    
         
            -
            - ** 
     | 
| 
      
 168 
     | 
    
         
            +
            - **Hostname Validation**: Optional `hostname` field to restrict backups to specific hosts (global or per-repository)
         
     | 
| 
      
 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)
         
     | 
| 
       166 
173 
     | 
    
         
             
            - **Custom Borg Path**: Specify a custom Borg executable path if borg is not in PATH or to use a specific version
         
     | 
| 
       167 
174 
     | 
    
         
             
            - **Retention Policies**: Define how many backups to keep (hourly, daily, weekly, monthly, yearly)
         
     | 
| 
       168 
175 
     | 
    
         
             
            - **Multiple Sources**: Each repository can have multiple backup sources with their own exclude patterns
         
     | 
| 
       169 
176 
     | 
    
         
             
            - **Flexible Organization**: Organize backups by type (documents, databases, media) with different policies
         
     | 
| 
       170 
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 
     | 
    
         
            +
            - Boolean types (must be `true` or `false`, not strings like `'true'`)
         
     | 
| 
      
 192 
     | 
    
         
            +
            - Valid compression values (lz4, zstd, zlib, lzma, none)
         
     | 
| 
      
 193 
     | 
    
         
            +
            - Valid encryption modes
         
     | 
| 
      
 194 
     | 
    
         
            +
            - Required repository fields (name, path)
         
     | 
| 
      
 195 
     | 
    
         
            +
            - Correct borg_options values
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
            **Example validation output:**
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
            ```
         
     | 
| 
      
 200 
     | 
    
         
            +
            ✓ Configuration is valid
         
     | 
| 
      
 201 
     | 
    
         
            +
              No type errors or warnings found
         
     | 
| 
      
 202 
     | 
    
         
            +
            ```
         
     | 
| 
      
 203 
     | 
    
         
            +
             
     | 
| 
      
 204 
     | 
    
         
            +
            Or with errors:
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
            ```
         
     | 
| 
      
 207 
     | 
    
         
            +
            ❌ ERRORS FOUND (2):
         
     | 
| 
      
 208 
     | 
    
         
            +
              - global/auto_init: must be boolean (true or false), got String: "true"
         
     | 
| 
      
 209 
     | 
    
         
            +
              - test-repo/allow_remove_source: must be boolean (true or false), got Integer: 1
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
            Configuration has errors that must be fixed.
         
     | 
| 
      
 212 
     | 
    
         
            +
            ```
         
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
       171 
214 
     | 
    
         
             
            ## Usage
         
     | 
| 
       172 
215 
     | 
    
         | 
| 
       173 
216 
     | 
    
         
             
            ### Initialize a Repository
         
     | 
| 
         @@ -195,10 +238,48 @@ ruborg backup --repository databases --name "db-backup-2025-10-05" 
     | 
|
| 
       195 
238 
     | 
    
         
             
            # Using custom configuration file
         
     | 
| 
       196 
239 
     | 
    
         
             
            ruborg backup --config /path/to/config.yml --repository documents
         
     | 
| 
       197 
240 
     | 
    
         | 
| 
       198 
     | 
    
         
            -
            # Remove source files after successful backup
         
     | 
| 
      
 241 
     | 
    
         
            +
            # Remove source files after successful backup (requires allow_remove_source: true)
         
     | 
| 
       199 
242 
     | 
    
         
             
            ruborg backup --repository documents --remove-source
         
     | 
| 
       200 
243 
     | 
    
         
             
            ```
         
     | 
| 
       201 
244 
     | 
    
         | 
| 
      
 245 
     | 
    
         
            +
            **IMPORTANT: Source File Deletion Safety**
         
     | 
| 
      
 246 
     | 
    
         
            +
             
     | 
| 
      
 247 
     | 
    
         
            +
            The `--remove-source` option is disabled by default for safety. To use it, you must explicitly enable it in your configuration:
         
     | 
| 
      
 248 
     | 
    
         
            +
             
     | 
| 
      
 249 
     | 
    
         
            +
            ```yaml
         
     | 
| 
      
 250 
     | 
    
         
            +
            # Global setting - applies to all repositories
         
     | 
| 
      
 251 
     | 
    
         
            +
            allow_remove_source: true
         
     | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
      
 253 
     | 
    
         
            +
            # OR per-repository setting
         
     | 
| 
      
 254 
     | 
    
         
            +
            repositories:
         
     | 
| 
      
 255 
     | 
    
         
            +
              - name: temp-backups
         
     | 
| 
      
 256 
     | 
    
         
            +
                allow_remove_source: true  # Only for this repository
         
     | 
| 
      
 257 
     | 
    
         
            +
                ...
         
     | 
| 
      
 258 
     | 
    
         
            +
            ```
         
     | 
| 
      
 259 
     | 
    
         
            +
             
     | 
| 
      
 260 
     | 
    
         
            +
            **⚠️ TYPE SAFETY WARNING:** The value MUST be a boolean `true`, not a string:
         
     | 
| 
      
 261 
     | 
    
         
            +
             
     | 
| 
      
 262 
     | 
    
         
            +
            ```yaml
         
     | 
| 
      
 263 
     | 
    
         
            +
            # ✅ CORRECT - Boolean true
         
     | 
| 
      
 264 
     | 
    
         
            +
            allow_remove_source: true
         
     | 
| 
      
 265 
     | 
    
         
            +
             
     | 
| 
      
 266 
     | 
    
         
            +
            # ❌ WRONG - String 'true' (will be rejected)
         
     | 
| 
      
 267 
     | 
    
         
            +
            allow_remove_source: 'true'
         
     | 
| 
      
 268 
     | 
    
         
            +
            allow_remove_source: "true"
         
     | 
| 
      
 269 
     | 
    
         
            +
             
     | 
| 
      
 270 
     | 
    
         
            +
            # ❌ WRONG - Other truthy values (will be rejected)
         
     | 
| 
      
 271 
     | 
    
         
            +
            allow_remove_source: 1
         
     | 
| 
      
 272 
     | 
    
         
            +
            allow_remove_source: yes
         
     | 
| 
      
 273 
     | 
    
         
            +
            ```
         
     | 
| 
      
 274 
     | 
    
         
            +
             
     | 
| 
      
 275 
     | 
    
         
            +
            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.
         
     | 
| 
      
 276 
     | 
    
         
            +
             
     | 
| 
      
 277 
     | 
    
         
            +
            Without `allow_remove_source: true` configured, using `--remove-source` will result in an error:
         
     | 
| 
      
 278 
     | 
    
         
            +
            ```
         
     | 
| 
      
 279 
     | 
    
         
            +
            Error: Cannot use --remove-source: 'allow_remove_source' must be true (boolean).
         
     | 
| 
      
 280 
     | 
    
         
            +
            Current value: "true" (String). Set 'allow_remove_source: true' in configuration.
         
     | 
| 
      
 281 
     | 
    
         
            +
            ```
         
     | 
| 
      
 282 
     | 
    
         
            +
             
     | 
| 
       202 
283 
     | 
    
         
             
            ### List Archives
         
     | 
| 
       203 
284 
     | 
    
         | 
| 
       204 
285 
     | 
    
         
             
            ```bash
         
     | 
| 
         @@ -338,6 +419,70 @@ repositories: 
     | 
|
| 
       338 
419 
     | 
    
         | 
| 
       339 
420 
     | 
    
         
             
            When enabled, ruborg will automatically run `borg init` if the repository doesn't exist when you run `backup`, `list`, or `info` commands. The passphrase will be retrieved from Passbolt if configured.
         
     | 
| 
       340 
421 
     | 
    
         | 
| 
      
 422 
     | 
    
         
            +
            ## Hostname Validation
         
     | 
| 
      
 423 
     | 
    
         
            +
             
     | 
| 
      
 424 
     | 
    
         
            +
            Restrict backup operations to specific hosts using the optional `hostname` configuration key. This prevents accidental execution of backups on the wrong machine.
         
     | 
| 
      
 425 
     | 
    
         
            +
             
     | 
| 
      
 426 
     | 
    
         
            +
            ### Global Hostname
         
     | 
| 
      
 427 
     | 
    
         
            +
             
     | 
| 
      
 428 
     | 
    
         
            +
            Apply hostname restriction to all repositories:
         
     | 
| 
      
 429 
     | 
    
         
            +
             
     | 
| 
      
 430 
     | 
    
         
            +
            ```yaml
         
     | 
| 
      
 431 
     | 
    
         
            +
            # Global hostname - applies to all repositories
         
     | 
| 
      
 432 
     | 
    
         
            +
            hostname: myserver.local
         
     | 
| 
      
 433 
     | 
    
         
            +
             
     | 
| 
      
 434 
     | 
    
         
            +
            repositories:
         
     | 
| 
      
 435 
     | 
    
         
            +
              - name: documents
         
     | 
| 
      
 436 
     | 
    
         
            +
                path: /mnt/backup/documents
         
     | 
| 
      
 437 
     | 
    
         
            +
                sources:
         
     | 
| 
      
 438 
     | 
    
         
            +
                  - name: main
         
     | 
| 
      
 439 
     | 
    
         
            +
                    paths:
         
     | 
| 
      
 440 
     | 
    
         
            +
                      - /home/user/documents
         
     | 
| 
      
 441 
     | 
    
         
            +
            ```
         
     | 
| 
      
 442 
     | 
    
         
            +
             
     | 
| 
      
 443 
     | 
    
         
            +
            ### Per-Repository Hostname
         
     | 
| 
      
 444 
     | 
    
         
            +
             
     | 
| 
      
 445 
     | 
    
         
            +
            Override global hostname for specific repositories:
         
     | 
| 
      
 446 
     | 
    
         
            +
             
     | 
| 
      
 447 
     | 
    
         
            +
            ```yaml
         
     | 
| 
      
 448 
     | 
    
         
            +
            # Global hostname for most repositories
         
     | 
| 
      
 449 
     | 
    
         
            +
            hostname: mainserver.local
         
     | 
| 
      
 450 
     | 
    
         
            +
             
     | 
| 
      
 451 
     | 
    
         
            +
            repositories:
         
     | 
| 
      
 452 
     | 
    
         
            +
              # Uses global hostname (mainserver.local)
         
     | 
| 
      
 453 
     | 
    
         
            +
              - name: documents
         
     | 
| 
      
 454 
     | 
    
         
            +
                path: /mnt/backup/documents
         
     | 
| 
      
 455 
     | 
    
         
            +
                sources:
         
     | 
| 
      
 456 
     | 
    
         
            +
                  - name: main
         
     | 
| 
      
 457 
     | 
    
         
            +
                    paths:
         
     | 
| 
      
 458 
     | 
    
         
            +
                      - /home/user/documents
         
     | 
| 
      
 459 
     | 
    
         
            +
             
     | 
| 
      
 460 
     | 
    
         
            +
              # Override with repository-specific hostname
         
     | 
| 
      
 461 
     | 
    
         
            +
              - name: databases
         
     | 
| 
      
 462 
     | 
    
         
            +
                hostname: dbserver.local  # Only runs on dbserver.local
         
     | 
| 
      
 463 
     | 
    
         
            +
                path: /mnt/backup/databases
         
     | 
| 
      
 464 
     | 
    
         
            +
                sources:
         
     | 
| 
      
 465 
     | 
    
         
            +
                  - name: mysql
         
     | 
| 
      
 466 
     | 
    
         
            +
                    paths:
         
     | 
| 
      
 467 
     | 
    
         
            +
                      - /var/lib/mysql/dumps
         
     | 
| 
      
 468 
     | 
    
         
            +
            ```
         
     | 
| 
      
 469 
     | 
    
         
            +
             
     | 
| 
      
 470 
     | 
    
         
            +
            **How it works:**
         
     | 
| 
      
 471 
     | 
    
         
            +
            - Before backup, list, restore, or check operations, Ruborg validates the system hostname
         
     | 
| 
      
 472 
     | 
    
         
            +
            - If configured hostname doesn't match the current hostname, the operation fails with an error
         
     | 
| 
      
 473 
     | 
    
         
            +
            - Repository-specific hostname takes precedence over global hostname
         
     | 
| 
      
 474 
     | 
    
         
            +
            - If no hostname is configured, validation is skipped
         
     | 
| 
      
 475 
     | 
    
         
            +
             
     | 
| 
      
 476 
     | 
    
         
            +
            **Example error:**
         
     | 
| 
      
 477 
     | 
    
         
            +
            ```
         
     | 
| 
      
 478 
     | 
    
         
            +
            Error: Hostname mismatch: configuration is for 'dbserver.local' but current hostname is 'mainserver.local'
         
     | 
| 
      
 479 
     | 
    
         
            +
            ```
         
     | 
| 
      
 480 
     | 
    
         
            +
             
     | 
| 
      
 481 
     | 
    
         
            +
            **Use cases:**
         
     | 
| 
      
 482 
     | 
    
         
            +
            - **Multi-server environments**: Different servers backup to different repositories
         
     | 
| 
      
 483 
     | 
    
         
            +
            - **Development vs Production**: Prevent production config from running on dev machines
         
     | 
| 
      
 484 
     | 
    
         
            +
            - **Safety**: Avoid accidentally running wrong backups on shared configuration files
         
     | 
| 
      
 485 
     | 
    
         
            +
             
     | 
| 
       341 
486 
     | 
    
         
             
            ## Security Configuration
         
     | 
| 
       342 
487 
     | 
    
         | 
| 
       343 
488 
     | 
    
         
             
            Ruborg provides configurable security options via `borg_options`:
         
     | 
| 
         @@ -370,6 +515,7 @@ See [SECURITY.md](SECURITY.md) for detailed security information and best practi 
     | 
|
| 
       370 
515 
     | 
    
         
             
            | Command | Description | Options |
         
     | 
| 
       371 
516 
     | 
    
         
             
            |---------|-------------|---------|
         
     | 
| 
       372 
517 
     | 
    
         
             
            | `init REPOSITORY` | Initialize a new Borg repository | `--passphrase`, `--passbolt-id`, `--log` |
         
     | 
| 
      
 518 
     | 
    
         
            +
            | `validate` | Validate configuration file for type errors | `--config`, `--log` |
         
     | 
| 
       373 
519 
     | 
    
         
             
            | `backup` | Create a backup using config file | `--config`, `--repository`, `--all`, `--name`, `--remove-source`, `--log` |
         
     | 
| 
       374 
520 
     | 
    
         
             
            | `list` | List all archives in repository | `--config`, `--repository`, `--log` |
         
     | 
| 
       375 
521 
     | 
    
         
             
            | `restore ARCHIVE` | Restore files from archive | `--config`, `--repository`, `--destination`, `--path`, `--log` |
         
     | 
    
        data/SECURITY.md
    CHANGED
    
    | 
         @@ -66,6 +66,15 @@ 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 
     | 
    
         
            +
             
     | 
| 
       69 
78 
     | 
    
         
             
            ## Security Best Practices
         
     | 
| 
       70 
79 
     | 
    
         | 
| 
       71 
80 
     | 
    
         
             
            ### When Using `--remove-source`
         
     | 
| 
         @@ -76,6 +85,16 @@ Ruborg implements several security measures to protect your backup operations: 
     | 
|
| 
       76 
85 
     | 
    
         
             
            2. **Never use on symlinks** to critical system directories
         
     | 
| 
       77 
86 
     | 
    
         
             
            3. **Verify backups** before using this flag in production
         
     | 
| 
       78 
87 
     | 
    
         
             
            4. **Use absolute paths** in configuration to avoid ambiguity
         
     | 
| 
      
 88 
     | 
    
         
            +
            5. **Use boolean values** for `allow_remove_source` - NEVER use quoted strings:
         
     | 
| 
      
 89 
     | 
    
         
            +
               ```yaml
         
     | 
| 
      
 90 
     | 
    
         
            +
               # ✅ CORRECT
         
     | 
| 
      
 91 
     | 
    
         
            +
               allow_remove_source: true
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
               # ❌ WRONG - Will be rejected
         
     | 
| 
      
 94 
     | 
    
         
            +
               allow_remove_source: 'true'
         
     | 
| 
      
 95 
     | 
    
         
            +
               allow_remove_source: "true"
         
     | 
| 
      
 96 
     | 
    
         
            +
               allow_remove_source: 1
         
     | 
| 
      
 97 
     | 
    
         
            +
               ```
         
     | 
| 
       79 
98 
     | 
    
         | 
| 
       80 
99 
     | 
    
         
             
            ### Configuration File Security
         
     | 
| 
       81 
100 
     | 
    
         | 
| 
         @@ -144,6 +163,19 @@ We will respond within 48 hours and work with you to address the issue. 
     | 
|
| 
       144 
163 
     | 
    
         | 
| 
       145 
164 
     | 
    
         
             
            ## Security Audit History
         
     | 
| 
       146 
165 
     | 
    
         | 
| 
      
 166 
     | 
    
         
            +
            - **v0.6.0** (2025-10-08): Configuration validation and type confusion protection
         
     | 
| 
      
 167 
     | 
    
         
            +
              - **SECURITY FIX**: Implemented strict boolean type checking for `allow_remove_source`
         
     | 
| 
      
 168 
     | 
    
         
            +
              - Prevents type confusion attacks (CWE-843) where string values bypass safety checks
         
     | 
| 
      
 169 
     | 
    
         
            +
              - Added configuration validation command (`ruborg validate`) for proactive error detection
         
     | 
| 
      
 170 
     | 
    
         
            +
              - Automatic schema validation on config load catches type errors early
         
     | 
| 
      
 171 
     | 
    
         
            +
              - Added 10 comprehensive test cases for validation and type confusion scenarios
         
     | 
| 
      
 172 
     | 
    
         
            +
              - Enhanced error messages to show actual type vs expected type
         
     | 
| 
      
 173 
     | 
    
         
            +
              - Updated documentation with type safety warnings and examples
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
            - **v0.5.0** (2025-10-08): Hostname validation
         
     | 
| 
      
 176 
     | 
    
         
            +
              - Added hostname validation feature (optional global or per-repository)
         
     | 
| 
      
 177 
     | 
    
         
            +
              - Prevents accidental execution of backups on wrong machines
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
       147 
179 
     | 
    
         
             
            - **v0.4.0** (2025-10-06): Complete command injection elimination
         
     | 
| 
       148 
180 
     | 
    
         
             
              - **CRITICAL**: Fixed all remaining command injection vulnerabilities in repository.rb
         
     | 
| 
       149 
181 
     | 
    
         
             
              - Replaced all backtick execution with Open3.capture3/capture2e methods
         
     | 
| 
         @@ -210,3 +242,5 @@ Before deploying ruborg in production: 
     | 
|
| 
       210 
242 
     | 
    
         
             
            - [ ] Configure borg_options for your security requirements
         
     | 
| 
       211 
243 
     | 
    
         
             
            - [ ] Use default archive names or sanitized custom names only
         
     | 
| 
       212 
244 
     | 
    
         
             
            - [ ] Ensure backup paths don't contain empty or nil values
         
     | 
| 
      
 245 
     | 
    
         
            +
            - [ ] Use boolean `true` (not strings) for `allow_remove_source` configuration
         
     | 
| 
      
 246 
     | 
    
         
            +
            - [ ] Configure hostname validation for multi-server environments
         
     | 
    
        data/lib/ruborg/cli.rb
    CHANGED
    
    | 
         @@ -54,6 +54,7 @@ module Ruborg 
     | 
|
| 
       54 
54 
     | 
    
         
             
                def backup
         
     | 
| 
       55 
55 
     | 
    
         
             
                  @logger.info("Starting backup operation with config: #{options[:config]}")
         
     | 
| 
       56 
56 
     | 
    
         
             
                  config = Config.new(options[:config])
         
     | 
| 
      
 57 
     | 
    
         
            +
                  validate_hostname(config.global_settings)
         
     | 
| 
       57 
58 
     | 
    
         
             
                  backup_repositories(config)
         
     | 
| 
       58 
59 
     | 
    
         
             
                rescue Error => e
         
     | 
| 
       59 
60 
     | 
    
         
             
                  @logger.error("Backup failed: #{e.message}")
         
     | 
| 
         @@ -72,6 +73,7 @@ module Ruborg 
     | 
|
| 
       72 
73 
     | 
    
         | 
| 
       73 
74 
     | 
    
         
             
                  global_settings = config.global_settings
         
     | 
| 
       74 
75 
     | 
    
         
             
                  merged_config = global_settings.merge(repo_config)
         
     | 
| 
      
 76 
     | 
    
         
            +
                  validate_hostname(merged_config)
         
     | 
| 
       75 
77 
     | 
    
         
             
                  passphrase = fetch_passphrase_for_repo(merged_config)
         
     | 
| 
       76 
78 
     | 
    
         
             
                  borg_opts = merged_config["borg_options"] || {}
         
     | 
| 
       77 
79 
     | 
    
         
             
                  borg_path = merged_config["borg_path"]
         
     | 
| 
         @@ -79,7 +81,9 @@ module Ruborg 
     | 
|
| 
       79 
81 
     | 
    
         
             
                  repo = Repository.new(repo_config["path"], passphrase: passphrase, borg_options: borg_opts, borg_path: borg_path)
         
     | 
| 
       80 
82 
     | 
    
         | 
| 
       81 
83 
     | 
    
         
             
                  # Auto-initialize repository if configured
         
     | 
| 
       82 
     | 
    
         
            -
                   
     | 
| 
      
 84 
     | 
    
         
            +
                  # Use strict boolean checking: only true enables, everything else disables
         
     | 
| 
      
 85 
     | 
    
         
            +
                  auto_init = merged_config["auto_init"]
         
     | 
| 
      
 86 
     | 
    
         
            +
                  auto_init = false unless auto_init == true
         
     | 
| 
       83 
87 
     | 
    
         
             
                  if auto_init && !repo.exists?
         
     | 
| 
       84 
88 
     | 
    
         
             
                    @logger.info("Auto-initializing repository at #{repo_config["path"]}")
         
     | 
| 
       85 
89 
     | 
    
         
             
                    repo.create
         
     | 
| 
         @@ -108,6 +112,7 @@ module Ruborg 
     | 
|
| 
       108 
112 
     | 
    
         | 
| 
       109 
113 
     | 
    
         
             
                  global_settings = config.global_settings
         
     | 
| 
       110 
114 
     | 
    
         
             
                  merged_config = global_settings.merge(repo_config)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  validate_hostname(merged_config)
         
     | 
| 
       111 
116 
     | 
    
         
             
                  passphrase = fetch_passphrase_for_repo(merged_config)
         
     | 
| 
       112 
117 
     | 
    
         
             
                  borg_opts = merged_config["borg_options"] || {}
         
     | 
| 
       113 
118 
     | 
    
         
             
                  borg_path = merged_config["borg_path"]
         
     | 
| 
         @@ -154,7 +159,9 @@ module Ruborg 
     | 
|
| 
       154 
159 
     | 
    
         
             
                  repo = Repository.new(repo_config["path"], passphrase: passphrase, borg_options: borg_opts, borg_path: borg_path)
         
     | 
| 
       155 
160 
     | 
    
         | 
| 
       156 
161 
     | 
    
         
             
                  # Auto-initialize repository if configured
         
     | 
| 
       157 
     | 
    
         
            -
                   
     | 
| 
      
 162 
     | 
    
         
            +
                  # Use strict boolean checking: only true enables, everything else disables
         
     | 
| 
      
 163 
     | 
    
         
            +
                  auto_init = merged_config["auto_init"]
         
     | 
| 
      
 164 
     | 
    
         
            +
                  auto_init = false unless auto_init == true
         
     | 
| 
       158 
165 
     | 
    
         
             
                  if auto_init && !repo.exists?
         
     | 
| 
       159 
166 
     | 
    
         
             
                    @logger.info("Auto-initializing repository at #{repo_config["path"]}")
         
     | 
| 
       160 
167 
     | 
    
         
             
                    repo.create
         
     | 
| 
         @@ -168,6 +175,78 @@ module Ruborg 
     | 
|
| 
       168 
175 
     | 
    
         
             
                  error_exit(e)
         
     | 
| 
       169 
176 
     | 
    
         
             
                end
         
     | 
| 
       170 
177 
     | 
    
         | 
| 
      
 178 
     | 
    
         
            +
                desc "validate", "Validate configuration file for errors and type issues"
         
     | 
| 
      
 179 
     | 
    
         
            +
                def validate_config
         
     | 
| 
      
 180 
     | 
    
         
            +
                  @logger.info("Validating configuration file: #{options[:config]}")
         
     | 
| 
      
 181 
     | 
    
         
            +
                  config = Config.new(options[:config])
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                  puts "\n═══════════════════════════════════════════════════════════════"
         
     | 
| 
      
 184 
     | 
    
         
            +
                  puts "  CONFIGURATION VALIDATION"
         
     | 
| 
      
 185 
     | 
    
         
            +
                  puts "═══════════════════════════════════════════════════════════════\n\n"
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                  errors = []
         
     | 
| 
      
 188 
     | 
    
         
            +
                  warnings = []
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
      
 190 
     | 
    
         
            +
                  # Validate global boolean settings
         
     | 
| 
      
 191 
     | 
    
         
            +
                  global_settings = config.global_settings
         
     | 
| 
      
 192 
     | 
    
         
            +
                  errors.concat(validate_boolean_setting(global_settings, "auto_init", "global"))
         
     | 
| 
      
 193 
     | 
    
         
            +
                  errors.concat(validate_boolean_setting(global_settings, "auto_prune", "global"))
         
     | 
| 
      
 194 
     | 
    
         
            +
                  errors.concat(validate_boolean_setting(global_settings, "allow_remove_source", "global"))
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                  # Validate borg_options booleans
         
     | 
| 
      
 197 
     | 
    
         
            +
                  if global_settings["borg_options"]
         
     | 
| 
      
 198 
     | 
    
         
            +
                    warnings.concat(validate_borg_option(global_settings["borg_options"], "allow_relocated_repo", "global"))
         
     | 
| 
      
 199 
     | 
    
         
            +
                    warnings.concat(validate_borg_option(global_settings["borg_options"], "allow_unencrypted_repo", "global"))
         
     | 
| 
      
 200 
     | 
    
         
            +
                  end
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                  # Validate per-repository settings
         
     | 
| 
      
 203 
     | 
    
         
            +
                  config.repositories.each do |repo|
         
     | 
| 
      
 204 
     | 
    
         
            +
                    repo_name = repo["name"]
         
     | 
| 
      
 205 
     | 
    
         
            +
                    errors.concat(validate_boolean_setting(repo, "auto_init", repo_name))
         
     | 
| 
      
 206 
     | 
    
         
            +
                    errors.concat(validate_boolean_setting(repo, "auto_prune", repo_name))
         
     | 
| 
      
 207 
     | 
    
         
            +
                    errors.concat(validate_boolean_setting(repo, "allow_remove_source", repo_name))
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
                    if repo["borg_options"]
         
     | 
| 
      
 210 
     | 
    
         
            +
                      warnings.concat(validate_borg_option(repo["borg_options"], "allow_relocated_repo", repo_name))
         
     | 
| 
      
 211 
     | 
    
         
            +
                      warnings.concat(validate_borg_option(repo["borg_options"], "allow_unencrypted_repo", repo_name))
         
     | 
| 
      
 212 
     | 
    
         
            +
                    end
         
     | 
| 
      
 213 
     | 
    
         
            +
                  end
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
                  # Display results
         
     | 
| 
      
 216 
     | 
    
         
            +
                  if errors.empty? && warnings.empty?
         
     | 
| 
      
 217 
     | 
    
         
            +
                    puts "✓ Configuration is valid"
         
     | 
| 
      
 218 
     | 
    
         
            +
                    puts "  No type errors or warnings found\n\n"
         
     | 
| 
      
 219 
     | 
    
         
            +
                  else
         
     | 
| 
      
 220 
     | 
    
         
            +
                    unless errors.empty?
         
     | 
| 
      
 221 
     | 
    
         
            +
                      puts "❌ ERRORS FOUND (#{errors.size}):"
         
     | 
| 
      
 222 
     | 
    
         
            +
                      errors.each do |error|
         
     | 
| 
      
 223 
     | 
    
         
            +
                        puts "  - #{error}"
         
     | 
| 
      
 224 
     | 
    
         
            +
                      end
         
     | 
| 
      
 225 
     | 
    
         
            +
                      puts ""
         
     | 
| 
      
 226 
     | 
    
         
            +
                    end
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
                    unless warnings.empty?
         
     | 
| 
      
 229 
     | 
    
         
            +
                      puts "⚠️  WARNINGS (#{warnings.size}):"
         
     | 
| 
      
 230 
     | 
    
         
            +
                      warnings.each do |warning|
         
     | 
| 
      
 231 
     | 
    
         
            +
                        puts "  - #{warning}"
         
     | 
| 
      
 232 
     | 
    
         
            +
                      end
         
     | 
| 
      
 233 
     | 
    
         
            +
                      puts ""
         
     | 
| 
      
 234 
     | 
    
         
            +
                    end
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
                    if errors.any?
         
     | 
| 
      
 237 
     | 
    
         
            +
                      puts "Configuration has errors that must be fixed.\n\n"
         
     | 
| 
      
 238 
     | 
    
         
            +
                      exit 1
         
     | 
| 
      
 239 
     | 
    
         
            +
                    else
         
     | 
| 
      
 240 
     | 
    
         
            +
                      puts "Configuration is valid but has warnings.\n\n"
         
     | 
| 
      
 241 
     | 
    
         
            +
                    end
         
     | 
| 
      
 242 
     | 
    
         
            +
                  end
         
     | 
| 
      
 243 
     | 
    
         
            +
             
     | 
| 
      
 244 
     | 
    
         
            +
                  @logger.info("Configuration validation completed")
         
     | 
| 
      
 245 
     | 
    
         
            +
                rescue Error => e
         
     | 
| 
      
 246 
     | 
    
         
            +
                  @logger.error("Validation failed: #{e.message}")
         
     | 
| 
      
 247 
     | 
    
         
            +
                  error_exit(e)
         
     | 
| 
      
 248 
     | 
    
         
            +
                end
         
     | 
| 
      
 249 
     | 
    
         
            +
             
     | 
| 
       171 
250 
     | 
    
         
             
                desc "check", "Check repository integrity and compatibility"
         
     | 
| 
       172 
251 
     | 
    
         
             
                option :verify_data, type: :boolean, default: false, desc: "Verify repository data (slower)"
         
     | 
| 
       173 
252 
     | 
    
         
             
                option :all, type: :boolean, default: false, desc: "Check all repositories"
         
     | 
| 
         @@ -175,6 +254,7 @@ module Ruborg 
     | 
|
| 
       175 
254 
     | 
    
         
             
                  @logger.info("Checking repository compatibility")
         
     | 
| 
       176 
255 
     | 
    
         
             
                  config = Config.new(options[:config])
         
     | 
| 
       177 
256 
     | 
    
         
             
                  global_settings = config.global_settings
         
     | 
| 
      
 257 
     | 
    
         
            +
                  validate_hostname(global_settings)
         
     | 
| 
       178 
258 
     | 
    
         | 
| 
       179 
259 
     | 
    
         
             
                  # Show Borg version first
         
     | 
| 
       180 
260 
     | 
    
         
             
                  borg_version = Repository.borg_version
         
     | 
| 
         @@ -207,6 +287,7 @@ module Ruborg 
     | 
|
| 
       207 
287 
     | 
    
         
             
                  @logger.info("Checking repository: #{repo_name}")
         
     | 
| 
       208 
288 
     | 
    
         | 
| 
       209 
289 
     | 
    
         
             
                  merged_config = global_settings.merge(repo_config)
         
     | 
| 
      
 290 
     | 
    
         
            +
                  validate_hostname(merged_config)
         
     | 
| 
       210 
291 
     | 
    
         
             
                  passphrase = fetch_passphrase_for_repo(merged_config)
         
     | 
| 
       211 
292 
     | 
    
         
             
                  borg_opts = merged_config["borg_options"] || {}
         
     | 
| 
       212 
293 
     | 
    
         
             
                  borg_path = merged_config["borg_path"]
         
     | 
| 
         @@ -267,6 +348,7 @@ module Ruborg 
     | 
|
| 
       267 
348 
     | 
    
         | 
| 
       268 
349 
     | 
    
         
             
                  # Show global settings
         
     | 
| 
       269 
350 
     | 
    
         
             
                  puts "Global Settings:"
         
     | 
| 
      
 351 
     | 
    
         
            +
                  puts "  Hostname:       #{global_settings["hostname"]}" if global_settings["hostname"]
         
     | 
| 
       270 
352 
     | 
    
         
             
                  puts "  Compression:    #{global_settings["compression"] || "lz4 (default)"}"
         
     | 
| 
       271 
353 
     | 
    
         
             
                  puts "  Encryption:     #{global_settings["encryption"] || "repokey (default)"}"
         
     | 
| 
       272 
354 
     | 
    
         
             
                  puts "  Auto-init:      #{global_settings["auto_init"] || false}"
         
     | 
| 
         @@ -284,6 +366,7 @@ module Ruborg 
     | 
|
| 
       284 
366 
     | 
    
         
             
                    puts "   Description: #{repo["description"]}" if repo["description"]
         
     | 
| 
       285 
367 
     | 
    
         | 
| 
       286 
368 
     | 
    
         
             
                    # Show repo-specific overrides
         
     | 
| 
      
 369 
     | 
    
         
            +
                    puts "   Hostname:    #{repo["hostname"]}" if repo["hostname"]
         
     | 
| 
       287 
370 
     | 
    
         
             
                    puts "   Compression: #{repo["compression"]}" if repo["compression"]
         
     | 
| 
       288 
371 
     | 
    
         
             
                    puts "   Encryption:  #{repo["encryption"]}" if repo["encryption"]
         
     | 
| 
       289 
372 
     | 
    
         
             
                    puts "   Auto-init:   #{repo["auto_init"]}" unless repo["auto_init"].nil?
         
     | 
| 
         @@ -389,6 +472,7 @@ module Ruborg 
     | 
|
| 
       389 
472 
     | 
    
         | 
| 
       390 
473 
     | 
    
         
             
                  # Merge global settings with repo-specific settings (repo-specific takes precedence)
         
     | 
| 
       391 
474 
     | 
    
         
             
                  merged_config = global_settings.merge(repo_config)
         
     | 
| 
      
 475 
     | 
    
         
            +
                  validate_hostname(merged_config)
         
     | 
| 
       392 
476 
     | 
    
         | 
| 
       393 
477 
     | 
    
         
             
                  passphrase = fetch_passphrase_for_repo(merged_config)
         
     | 
| 
       394 
478 
     | 
    
         
             
                  borg_opts = merged_config["borg_options"] || {}
         
     | 
| 
         @@ -396,7 +480,9 @@ module Ruborg 
     | 
|
| 
       396 
480 
     | 
    
         
             
                  repo = Repository.new(repo_config["path"], passphrase: passphrase, borg_options: borg_opts, borg_path: borg_path)
         
     | 
| 
       397 
481 
     | 
    
         | 
| 
       398 
482 
     | 
    
         
             
                  # Auto-initialize if configured
         
     | 
| 
       399 
     | 
    
         
            -
                   
     | 
| 
      
 483 
     | 
    
         
            +
                  # Use strict boolean checking: only true enables, everything else disables
         
     | 
| 
      
 484 
     | 
    
         
            +
                  auto_init = merged_config["auto_init"]
         
     | 
| 
      
 485 
     | 
    
         
            +
                  auto_init = false unless auto_init == true
         
     | 
| 
       400 
486 
     | 
    
         
             
                  if auto_init && !repo.exists?
         
     | 
| 
       401 
487 
     | 
    
         
             
                    @logger.info("Auto-initializing repository at #{repo_config["path"]}")
         
     | 
| 
       402 
488 
     | 
    
         
             
                    repo.create
         
     | 
| 
         @@ -406,6 +492,17 @@ module Ruborg 
     | 
|
| 
       406 
492 
     | 
    
         
             
                  # Get retention mode (defaults to standard)
         
     | 
| 
       407 
493 
     | 
    
         
             
                  retention_mode = merged_config["retention_mode"] || "standard"
         
     | 
| 
       408 
494 
     | 
    
         | 
| 
      
 495 
     | 
    
         
            +
                  # Validate remove_source permission with strict type checking
         
     | 
| 
      
 496 
     | 
    
         
            +
                  if options[:remove_source]
         
     | 
| 
      
 497 
     | 
    
         
            +
                    allow_remove_source = merged_config["allow_remove_source"]
         
     | 
| 
      
 498 
     | 
    
         
            +
                    unless allow_remove_source.is_a?(TrueClass)
         
     | 
| 
      
 499 
     | 
    
         
            +
                      raise ConfigError,
         
     | 
| 
      
 500 
     | 
    
         
            +
                            "Cannot use --remove-source: 'allow_remove_source' must be true (boolean). " \
         
     | 
| 
      
 501 
     | 
    
         
            +
                            "Current value: #{allow_remove_source.inspect} (#{allow_remove_source.class}). " \
         
     | 
| 
      
 502 
     | 
    
         
            +
                            "Set 'allow_remove_source: true' in configuration to allow source deletion."
         
     | 
| 
      
 503 
     | 
    
         
            +
                    end
         
     | 
| 
      
 504 
     | 
    
         
            +
                  end
         
     | 
| 
      
 505 
     | 
    
         
            +
             
     | 
| 
       409 
506 
     | 
    
         
             
                  # Create backup config wrapper
         
     | 
| 
       410 
507 
     | 
    
         
             
                  backup_config = BackupConfig.new(repo_config, merged_config)
         
     | 
| 
       411 
508 
     | 
    
         
             
                  backup = Backup.new(repo, config: backup_config, retention_mode: retention_mode, repo_name: repo_name)
         
     | 
| 
         @@ -427,7 +524,9 @@ module Ruborg 
     | 
|
| 
       427 
524 
     | 
    
         
             
                  puts "  Sources removed" if options[:remove_source]
         
     | 
| 
       428 
525 
     | 
    
         | 
| 
       429 
526 
     | 
    
         
             
                  # Auto-prune if configured and retention policy exists
         
     | 
| 
       430 
     | 
    
         
            -
                   
     | 
| 
      
 527 
     | 
    
         
            +
                  # Use strict boolean checking: only true enables, everything else disables
         
     | 
| 
      
 528 
     | 
    
         
            +
                  auto_prune = merged_config["auto_prune"]
         
     | 
| 
      
 529 
     | 
    
         
            +
                  auto_prune = false unless auto_prune == true
         
     | 
| 
       431 
530 
     | 
    
         
             
                  retention_policy = merged_config["retention"]
         
     | 
| 
       432 
531 
     | 
    
         | 
| 
       433 
532 
     | 
    
         
             
                  return unless auto_prune && retention_policy && !retention_policy.empty?
         
     | 
| 
         @@ -460,6 +559,46 @@ module Ruborg 
     | 
|
| 
       460 
559 
     | 
    
         
             
                  name.gsub(/[^a-zA-Z0-9._-]/, "_")
         
     | 
| 
       461 
560 
     | 
    
         
             
                end
         
     | 
| 
       462 
561 
     | 
    
         | 
| 
      
 562 
     | 
    
         
            +
                def validate_hostname(config)
         
     | 
| 
      
 563 
     | 
    
         
            +
                  configured_hostname = config["hostname"]
         
     | 
| 
      
 564 
     | 
    
         
            +
                  return if configured_hostname.nil? || configured_hostname.empty?
         
     | 
| 
      
 565 
     | 
    
         
            +
             
     | 
| 
      
 566 
     | 
    
         
            +
                  current_hostname = `hostname`.strip
         
     | 
| 
      
 567 
     | 
    
         
            +
                  return if current_hostname == configured_hostname
         
     | 
| 
      
 568 
     | 
    
         
            +
             
     | 
| 
      
 569 
     | 
    
         
            +
                  raise ConfigError,
         
     | 
| 
      
 570 
     | 
    
         
            +
                        "Hostname mismatch: configuration is for '#{configured_hostname}' " \
         
     | 
| 
      
 571 
     | 
    
         
            +
                        "but current hostname is '#{current_hostname}'"
         
     | 
| 
      
 572 
     | 
    
         
            +
                end
         
     | 
| 
      
 573 
     | 
    
         
            +
             
     | 
| 
      
 574 
     | 
    
         
            +
                # Validate boolean configuration settings
         
     | 
| 
      
 575 
     | 
    
         
            +
                def validate_boolean_setting(config, key, context)
         
     | 
| 
      
 576 
     | 
    
         
            +
                  errors = []
         
     | 
| 
      
 577 
     | 
    
         
            +
                  value = config[key]
         
     | 
| 
      
 578 
     | 
    
         
            +
             
     | 
| 
      
 579 
     | 
    
         
            +
                  return errors if value.nil? # Not set is OK
         
     | 
| 
      
 580 
     | 
    
         
            +
             
     | 
| 
      
 581 
     | 
    
         
            +
                  unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
         
     | 
| 
      
 582 
     | 
    
         
            +
                    errors << "#{context}/#{key}: must be boolean (true/false), got #{value.class}: #{value.inspect}"
         
     | 
| 
      
 583 
     | 
    
         
            +
                  end
         
     | 
| 
      
 584 
     | 
    
         
            +
             
     | 
| 
      
 585 
     | 
    
         
            +
                  errors
         
     | 
| 
      
 586 
     | 
    
         
            +
                end
         
     | 
| 
      
 587 
     | 
    
         
            +
             
     | 
| 
      
 588 
     | 
    
         
            +
                # Validate borg_options boolean settings (these have different defaults)
         
     | 
| 
      
 589 
     | 
    
         
            +
                def validate_borg_option(borg_options, key, context)
         
     | 
| 
      
 590 
     | 
    
         
            +
                  warnings = []
         
     | 
| 
      
 591 
     | 
    
         
            +
                  value = borg_options[key]
         
     | 
| 
      
 592 
     | 
    
         
            +
             
     | 
| 
      
 593 
     | 
    
         
            +
                  return warnings if value.nil? # Not set is OK (uses default)
         
     | 
| 
      
 594 
     | 
    
         
            +
             
     | 
| 
      
 595 
     | 
    
         
            +
                  unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
         
     | 
| 
      
 596 
     | 
    
         
            +
                    warnings << "#{context}/borg_options/#{key}: should be boolean (true/false), got #{value.class}: #{value.inspect}"
         
     | 
| 
      
 597 
     | 
    
         
            +
                  end
         
     | 
| 
      
 598 
     | 
    
         
            +
             
     | 
| 
      
 599 
     | 
    
         
            +
                  warnings
         
     | 
| 
      
 600 
     | 
    
         
            +
                end
         
     | 
| 
      
 601 
     | 
    
         
            +
             
     | 
| 
       463 
602 
     | 
    
         
             
                # Wrapper class to adapt repository config to existing Backup class
         
     | 
| 
       464 
603 
     | 
    
         
             
                class BackupConfig
         
     | 
| 
       465 
604 
     | 
    
         
             
                  def initialize(repo_config, merged_settings)
         
     | 
    
        data/lib/ruborg/config.rb
    CHANGED
    
    | 
         @@ -9,10 +9,12 @@ module Ruborg 
     | 
|
| 
       9 
9 
     | 
    
         
             
              class Config
         
     | 
| 
       10 
10 
     | 
    
         
             
                attr_reader :data
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
                def initialize(config_path)
         
     | 
| 
      
 12 
     | 
    
         
            +
                def initialize(config_path, validate_types: true)
         
     | 
| 
       13 
13 
     | 
    
         
             
                  @config_path = config_path
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @validate_types = validate_types
         
     | 
| 
       14 
15 
     | 
    
         
             
                  load_config
         
     | 
| 
       15 
16 
     | 
    
         
             
                  validate_format
         
     | 
| 
      
 17 
     | 
    
         
            +
                  validate_schema if @validate_types
         
     | 
| 
       16 
18 
     | 
    
         
             
                end
         
     | 
| 
       17 
19 
     | 
    
         | 
| 
       18 
20 
     | 
    
         
             
                def load_config
         
     | 
| 
         @@ -39,7 +41,7 @@ module Ruborg 
     | 
|
| 
       39 
41 
     | 
    
         | 
| 
       40 
42 
     | 
    
         
             
                def global_settings
         
     | 
| 
       41 
43 
     | 
    
         
             
                  @data.slice("passbolt", "compression", "encryption", "auto_init", "borg_options", "log_file", "retention",
         
     | 
| 
       42 
     | 
    
         
            -
                              "auto_prune")
         
     | 
| 
      
 44 
     | 
    
         
            +
                              "auto_prune", "hostname", "allow_remove_source")
         
     | 
| 
       43 
45 
     | 
    
         
             
                end
         
     | 
| 
       44 
46 
     | 
    
         | 
| 
       45 
47 
     | 
    
         
             
                private
         
     | 
| 
         @@ -84,5 +86,78 @@ module Ruborg 
     | 
|
| 
       84 
86 
     | 
    
         | 
| 
       85 
87 
     | 
    
         
             
                  patterns
         
     | 
| 
       86 
88 
     | 
    
         
             
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                # Validate YAML schema for type correctness
         
     | 
| 
      
 91 
     | 
    
         
            +
                def validate_schema
         
     | 
| 
      
 92 
     | 
    
         
            +
                  errors = []
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                  # Validate global boolean settings
         
     | 
| 
      
 95 
     | 
    
         
            +
                  errors.concat(validate_boolean_config(@data, "auto_init", "global"))
         
     | 
| 
      
 96 
     | 
    
         
            +
                  errors.concat(validate_boolean_config(@data, "auto_prune", "global"))
         
     | 
| 
      
 97 
     | 
    
         
            +
                  errors.concat(validate_boolean_config(@data, "allow_remove_source", "global"))
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                  # Validate global borg_options
         
     | 
| 
      
 100 
     | 
    
         
            +
                  if @data["borg_options"]
         
     | 
| 
      
 101 
     | 
    
         
            +
                    errors.concat(validate_boolean_config(@data["borg_options"], "allow_relocated_repo", "global/borg_options"))
         
     | 
| 
      
 102 
     | 
    
         
            +
                    errors.concat(validate_boolean_config(@data["borg_options"], "allow_unencrypted_repo", "global/borg_options"))
         
     | 
| 
      
 103 
     | 
    
         
            +
                  end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                  # Validate compression and encryption if present
         
     | 
| 
      
 106 
     | 
    
         
            +
                  if @data["compression"] && !VALID_COMPRESSION.include?(@data["compression"])
         
     | 
| 
      
 107 
     | 
    
         
            +
                    errors << "global/compression: invalid value '#{@data["compression"]}'"
         
     | 
| 
      
 108 
     | 
    
         
            +
                  end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                  if @data["encryption"] && !VALID_ENCRYPTION.include?(@data["encryption"])
         
     | 
| 
      
 111 
     | 
    
         
            +
                    errors << "global/encryption: invalid value '#{@data["encryption"]}'"
         
     | 
| 
      
 112 
     | 
    
         
            +
                  end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                  # Validate per-repository settings
         
     | 
| 
      
 115 
     | 
    
         
            +
                  repositories.each do |repo|
         
     | 
| 
      
 116 
     | 
    
         
            +
                    repo_name = repo["name"] || "unnamed"
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                    errors.concat(validate_boolean_config(repo, "auto_init", repo_name))
         
     | 
| 
      
 119 
     | 
    
         
            +
                    errors.concat(validate_boolean_config(repo, "auto_prune", repo_name))
         
     | 
| 
      
 120 
     | 
    
         
            +
                    errors.concat(validate_boolean_config(repo, "allow_remove_source", repo_name))
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                    if repo["borg_options"]
         
     | 
| 
      
 123 
     | 
    
         
            +
                      errors.concat(validate_boolean_config(repo["borg_options"], "allow_relocated_repo",
         
     | 
| 
      
 124 
     | 
    
         
            +
                                                            "#{repo_name}/borg_options"))
         
     | 
| 
      
 125 
     | 
    
         
            +
                      errors.concat(validate_boolean_config(repo["borg_options"], "allow_unencrypted_repo",
         
     | 
| 
      
 126 
     | 
    
         
            +
                                                            "#{repo_name}/borg_options"))
         
     | 
| 
      
 127 
     | 
    
         
            +
                    end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                    # Validate compression and encryption if present
         
     | 
| 
      
 130 
     | 
    
         
            +
                    if repo["compression"] && !VALID_COMPRESSION.include?(repo["compression"])
         
     | 
| 
      
 131 
     | 
    
         
            +
                      errors << "#{repo_name}/compression: invalid value '#{repo["compression"]}'"
         
     | 
| 
      
 132 
     | 
    
         
            +
                    end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                    if repo["encryption"] && !VALID_ENCRYPTION.include?(repo["encryption"])
         
     | 
| 
      
 135 
     | 
    
         
            +
                      errors << "#{repo_name}/encryption: invalid value '#{repo["encryption"]}'"
         
     | 
| 
      
 136 
     | 
    
         
            +
                    end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                    # Validate repository structure
         
     | 
| 
      
 139 
     | 
    
         
            +
                    errors << "#{repo_name}: missing 'path' key" unless repo["path"]
         
     | 
| 
      
 140 
     | 
    
         
            +
                    errors << "#{repo_name}: 'sources' must be an array" if repo["sources"] && !repo["sources"].is_a?(Array)
         
     | 
| 
      
 141 
     | 
    
         
            +
                  end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                  return if errors.empty?
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                  raise ConfigError,
         
     | 
| 
      
 146 
     | 
    
         
            +
                        "Configuration validation failed:\n  - #{errors.join("\n  - ")}\n\n" \
         
     | 
| 
      
 147 
     | 
    
         
            +
                        "Run 'ruborg validate' for detailed validation information."
         
     | 
| 
      
 148 
     | 
    
         
            +
                end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                def validate_boolean_config(config, key, context)
         
     | 
| 
      
 151 
     | 
    
         
            +
                  errors = []
         
     | 
| 
      
 152 
     | 
    
         
            +
                  value = config[key]
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                  return errors if value.nil? # Not set is OK
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
                  unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
         
     | 
| 
      
 157 
     | 
    
         
            +
                    errors << "#{context}/#{key}: must be boolean (true or false), got #{value.class}: #{value.inspect}"
         
     | 
| 
      
 158 
     | 
    
         
            +
                  end
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                  errors
         
     | 
| 
      
 161 
     | 
    
         
            +
                end
         
     | 
| 
       87 
162 
     | 
    
         
             
              end
         
     | 
| 
       88 
163 
     | 
    
         
             
            end
         
     | 
    
        data/lib/ruborg/repository.rb
    CHANGED
    
    | 
         @@ -189,8 +189,12 @@ module Ruborg 
     | 
|
| 
       189 
189 
     | 
    
         
             
                  env = {}
         
     | 
| 
       190 
190 
     | 
    
         
             
                  env["BORG_PASSPHRASE"] = @passphrase if @passphrase
         
     | 
| 
       191 
191 
     | 
    
         | 
| 
      
 192 
     | 
    
         
            +
                  # Use strict boolean checking (only true/false allowed, default to true for backward compatibility)
         
     | 
| 
       192 
193 
     | 
    
         
             
                  allow_relocated = @borg_options.fetch("allow_relocated_repo", true)
         
     | 
| 
      
 194 
     | 
    
         
            +
                  allow_relocated = true unless allow_relocated == false # Normalize: only false disables, everything else enables
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
       193 
196 
     | 
    
         
             
                  allow_unencrypted = @borg_options.fetch("allow_unencrypted_repo", true)
         
     | 
| 
      
 197 
     | 
    
         
            +
                  allow_unencrypted = true unless allow_unencrypted == false # Normalize: only false disables, everything else enables
         
     | 
| 
       194 
198 
     | 
    
         | 
| 
       195 
199 
     | 
    
         
             
                  env["BORG_RELOCATED_REPO_ACCESS_IS_OK"] = allow_relocated ? "yes" : "no"
         
     | 
| 
       196 
200 
     | 
    
         
             
                  env["BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK"] = allow_unencrypted ? "yes" : "no"
         
     | 
| 
         @@ -327,8 +331,12 @@ module Ruborg 
     | 
|
| 
       327 
331 
     | 
    
         
             
                  env["BORG_PASSPHRASE"] = @passphrase if @passphrase
         
     | 
| 
       328 
332 
     | 
    
         | 
| 
       329 
333 
     | 
    
         
             
                  # Apply Borg environment options from config (defaults to yes for backward compatibility)
         
     | 
| 
      
 334 
     | 
    
         
            +
                  # Use strict boolean checking (only true/false allowed, default to true for backward compatibility)
         
     | 
| 
       330 
335 
     | 
    
         
             
                  allow_relocated = @borg_options.fetch("allow_relocated_repo", true)
         
     | 
| 
      
 336 
     | 
    
         
            +
                  allow_relocated = true unless allow_relocated == false # Normalize: only false disables, everything else enables
         
     | 
| 
      
 337 
     | 
    
         
            +
             
     | 
| 
       331 
338 
     | 
    
         
             
                  allow_unencrypted = @borg_options.fetch("allow_unencrypted_repo", true)
         
     | 
| 
      
 339 
     | 
    
         
            +
                  allow_unencrypted = true unless allow_unencrypted == false # Normalize: only false disables, everything else enables
         
     | 
| 
       332 
340 
     | 
    
         | 
| 
       333 
341 
     | 
    
         
             
                  env["BORG_RELOCATED_REPO_ACCESS_IS_OK"] = allow_relocated ? "yes" : "no"
         
     | 
| 
       334 
342 
     | 
    
         
             
                  env["BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK"] = allow_unencrypted ? "yes" : "no"
         
     | 
    
        data/lib/ruborg/version.rb
    CHANGED
    
    
    
        data/ruborg.gemspec
    ADDED
    
    | 
         @@ -0,0 +1,46 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative "lib/ruborg/version"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Gem::Specification.new do |spec|
         
     | 
| 
      
 6 
     | 
    
         
            +
              spec.name = "ruborg"
         
     | 
| 
      
 7 
     | 
    
         
            +
              spec.version = Ruborg::VERSION
         
     | 
| 
      
 8 
     | 
    
         
            +
              spec.authors = ["Michail Pantelelis"]
         
     | 
| 
      
 9 
     | 
    
         
            +
              spec.email = ["mpantel@aegean.gr"]
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              spec.summary = "A friendly Ruby frontend for Borg backup"
         
     | 
| 
      
 12 
     | 
    
         
            +
              spec.description = "Ruborg provides a user-friendly interface to Borg backup. " \
         
     | 
| 
      
 13 
     | 
    
         
            +
                                 "It reads YAML configuration files and orchestrates backup operations, " \
         
     | 
| 
      
 14 
     | 
    
         
            +
                                 "supporting repository creation, backup management, and Passbolt integration."
         
     | 
| 
      
 15 
     | 
    
         
            +
              spec.homepage = "https://github.com/mpantel/ruborg"
         
     | 
| 
      
 16 
     | 
    
         
            +
              spec.license = "MIT"
         
     | 
| 
      
 17 
     | 
    
         
            +
              spec.required_ruby_version = ">= 3.2.0"
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              spec.metadata["homepage_uri"] = spec.homepage
         
     | 
| 
      
 20 
     | 
    
         
            +
              spec.metadata["source_code_uri"] = "#{spec.homepage}.git"
         
     | 
| 
      
 21 
     | 
    
         
            +
              spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
         
     | 
| 
      
 22 
     | 
    
         
            +
              spec.metadata["rubygems_mfa_required"] = "true"
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              # Specify which files should be added to the gem when it is released.
         
     | 
| 
      
 25 
     | 
    
         
            +
              spec.files = Dir.chdir(__dir__) do
         
     | 
| 
      
 26 
     | 
    
         
            +
                `git ls-files -z`.split("\x0").reject do |f|
         
     | 
| 
      
 27 
     | 
    
         
            +
                  (File.expand_path(f) == __FILE__) ||
         
     | 
| 
      
 28 
     | 
    
         
            +
                    f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
              spec.bindir = "exe"
         
     | 
| 
      
 32 
     | 
    
         
            +
              spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
         
     | 
| 
      
 33 
     | 
    
         
            +
              spec.require_paths = ["lib"]
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              # Dependencies
         
     | 
| 
      
 36 
     | 
    
         
            +
              spec.add_dependency "psych", "~> 5.0"
         
     | 
| 
      
 37 
     | 
    
         
            +
              spec.add_dependency "thor", "~> 1.3"
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              # Development dependencies
         
     | 
| 
      
 40 
     | 
    
         
            +
              spec.add_development_dependency "bundler", "~> 2.0"
         
     | 
| 
      
 41 
     | 
    
         
            +
              spec.add_development_dependency "bundler-audit", "~> 0.9"
         
     | 
| 
      
 42 
     | 
    
         
            +
              spec.add_development_dependency "rake", "~> 13.0"
         
     | 
| 
      
 43 
     | 
    
         
            +
              spec.add_development_dependency "rspec", "~> 3.0"
         
     | 
| 
      
 44 
     | 
    
         
            +
              spec.add_development_dependency "rubocop", "~> 1.0"
         
     | 
| 
      
 45 
     | 
    
         
            +
              spec.add_development_dependency "rubocop-rspec", "~> 3.0"
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
    
        data/ruborg.yml.example
    CHANGED
    
    | 
         @@ -6,10 +6,12 @@ 
     | 
|
| 
       6 
6 
     | 
    
         
             
            #   # Edit ruborg.yml with your settings
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            # Global settings (applied to all repositories unless overridden)
         
     | 
| 
      
 9 
     | 
    
         
            +
            hostname: myserver.local  # Optional: restrict configuration to specific hostname
         
     | 
| 
       9 
10 
     | 
    
         
             
            compression: lz4          # Options: lz4 (fast), zstd (balanced), lzma (high compression), none
         
     | 
| 
       10 
11 
     | 
    
         
             
            encryption: repokey       # Options: repokey, keyfile, none (NOT recommended)
         
     | 
| 
       11 
12 
     | 
    
         
             
            auto_init: true           # Automatically initialize repositories on first use
         
     | 
| 
       12 
13 
     | 
    
         
             
            auto_prune: true          # Automatically prune old backups after each backup
         
     | 
| 
      
 14 
     | 
    
         
            +
            allow_remove_source: false # Allow --remove-source flag to delete source files after backup (default: false)
         
     | 
| 
       13 
15 
     | 
    
         
             
            log_file: ~/.ruborg/logs/ruborg.log  # Log file path (optional)
         
     | 
| 
       14 
16 
     | 
    
         | 
| 
       15 
17 
     | 
    
         
             
            # Custom Borg executable path (optional)
         
     | 
| 
         @@ -65,6 +67,7 @@ repositories: 
     | 
|
| 
       65 
67 
     | 
    
         
             
              - name: databases
         
     | 
| 
       66 
68 
     | 
    
         
             
                description: "MySQL and PostgreSQL database dumps"
         
     | 
| 
       67 
69 
     | 
    
         
             
                path: /mnt/backup/borg-databases
         
     | 
| 
      
 70 
     | 
    
         
            +
                hostname: dbserver.local  # Optional: repository-specific hostname override
         
     | 
| 
       68 
71 
     | 
    
         
             
                retention_mode: per_file  # Each file gets its own archive
         
     | 
| 
       69 
72 
     | 
    
         
             
                # Repository-specific passbolt (overrides global)
         
     | 
| 
       70 
73 
     | 
    
         
             
                passbolt:
         
     | 
    
        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. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.6.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Michail Pantelelis
         
     | 
| 
         @@ -148,6 +148,7 @@ files: 
     | 
|
| 
       148 
148 
     | 
    
         
             
            - lib/ruborg/passbolt.rb
         
     | 
| 
       149 
149 
     | 
    
         
             
            - lib/ruborg/repository.rb
         
     | 
| 
       150 
150 
     | 
    
         
             
            - lib/ruborg/version.rb
         
     | 
| 
      
 151 
     | 
    
         
            +
            - ruborg.gemspec
         
     | 
| 
       151 
152 
     | 
    
         
             
            - ruborg.yml.example
         
     | 
| 
       152 
153 
     | 
    
         
             
            homepage: https://github.com/mpantel/ruborg
         
     | 
| 
       153 
154 
     | 
    
         
             
            licenses:
         
     |