{"id":29425729,"url":"https://github.com/kirschbaum-development/redactor","last_synced_at":"2025-07-12T10:07:36.836Z","repository":{"id":300043933,"uuid":"998907370","full_name":"kirschbaum-development/redactor","owner":"kirschbaum-development","description":null,"archived":false,"fork":false,"pushed_at":"2025-06-19T08:47:46.000Z","size":85,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-12T05:40:01.328Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kirschbaum-development.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-09T12:50:27.000Z","updated_at":"2025-06-19T15:01:51.000Z","dependencies_parsed_at":"2025-06-19T16:06:19.854Z","dependency_job_id":"993d6310-9e8c-44d5-88ab-356900224766","html_url":"https://github.com/kirschbaum-development/redactor","commit_stats":null,"previous_names":["kirschbaum-development/redactor"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/kirschbaum-development/redactor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kirschbaum-development%2Fredactor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kirschbaum-development%2Fredactor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kirschbaum-development%2Fredactor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kirschbaum-development%2Fredactor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kirschbaum-development","download_url":"https://codeload.github.com/kirschbaum-development/redactor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kirschbaum-development%2Fredactor/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264973189,"owners_count":23691480,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-07-12T10:07:33.720Z","updated_at":"2025-07-12T10:07:36.829Z","avatar_url":"https://github.com/kirschbaum-development.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Kirschbaum Redactor\n\n![Laravel Supported Versions](https://img.shields.io/badge/laravel-10.x/11.x/12.x-green.svg)\n[![MIT Licensed](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)\n[![Latest Version on Packagist](https://img.shields.io/packagist/v/kirschbaum-development/redactor.svg?style=flat-square)](https://packagist.org/packages/kirschbaum-development/redactor)\n![Application Testing](https://github.com/kirschbaum-development/redactor/actions/workflows/php-tests.yml/badge.svg)\n![Static Analysis](https://github.com/kirschbaum-development/redactor/actions/workflows/static-analysis.yml/badge.svg)\n![Code Style](https://github.com/kirschbaum-development/redactor/actions/workflows/style-check.yml/badge.svg)\n\nAutomatically redact sensitive data from arrays, objects, and strings before logging or exporting. Features a class-based strategy system with profile-based configurations, Shannon entropy detection.\n\n\u003e This package is in active development and its API can change abruptly without any notice. Please reach out if you plan to use it in a production environment.\n\n\n## Quick Start\n\n```bash\ncomposer require kirschbaum-development/redactor\nphp artisan vendor:publish --tag=redactor-config\n```\n\nThe package automatically registers the service provider and facade. Use it directly:\n\n```php\nuse Kirschbaum\\Redactor\\Facades\\Redactor;\n\n// Basic usage\n$data = [\n    'user_id' =\u003e 123,\n    'password' =\u003e 'secret123',\n    'api_key' =\u003e 'sk-1234567890abcdef1234567890abcdef12345678',\n    'email' =\u003e 'user@example.com'\n];\n\n$redacted = Redactor::redact($data);\n// Result:\n// [\n//     'user_id' =\u003e 123,                    // Safe key - preserved\n//     'password' =\u003e '[REDACTED]',          // Blocked key - redacted\n//     'api_key' =\u003e '[REDACTED]',           // High entropy - redacted\n//     'email' =\u003e '[REDACTED]',             // Email pattern - redacted\n//     '_redacted' =\u003e true                  // Metadata added\n// ]\n```\n\n## Core Concepts\n\n### Redaction Strategies\n\nThe package uses a class-based configuration:\n\n1. **SafeKeysStrategy** - Preserves safe keys like `id`, `user_id`\n2. **BlockedKeysStrategy** - Always redacts blocked keys like `password`, `secret`\n3. **LargeObjectStrategy** - Redacts objects/arrays exceeding size limits\n4. **LargeStringStrategy** - Redacts strings exceeding length limits\n5. **RegexPatternsStrategy** - Custom regex patterns for emails, credit cards, etc.\n6. **ShannonEntropyStrategy** - Detects high-entropy strings (API keys, tokens)\n\n### Profiles\n\nProfiles provide different redaction configurations for different contexts:\n\n```php\n// Use built-in profiles\n$logData = Redactor::redact($data, 'default');       // Balanced redaction\n$auditData = Redactor::redact($data, 'strict');      // Aggressive redaction  \n$debugData = Redactor::redact($data, 'performance'); // Minimal redaction for speed\n```\n\n## Configuration\n\nThe config file (`config/redactor.php`) uses a class-based approach:\n\n```php\nreturn [\n    'default_profile' =\u003e 'default',\n    \n    'profiles' =\u003e [\n        'default' =\u003e [\n            'enabled' =\u003e true,\n            \n            // Strategies executed in array order (top-to-bottom priority)\n            'strategies' =\u003e [\n                \\Kirschbaum\\Redactor\\Strategies\\SafeKeysStrategy::class,\n                \\Kirschbaum\\Redactor\\Strategies\\BlockedKeysStrategy::class,\n                \\Kirschbaum\\Redactor\\Strategies\\LargeObjectStrategy::class,\n                \\Kirschbaum\\Redactor\\Strategies\\LargeStringStrategy::class,\n                \\Kirschbaum\\Redactor\\Strategies\\RegexPatternsStrategy::class,\n                \\Kirschbaum\\Redactor\\Strategies\\ShannonEntropyStrategy::class,\n            ],\n            \n            'safe_keys' =\u003e ['id', 'user_id', 'uuid', 'created_at', 'updated_at'],\n            'blocked_keys' =\u003e ['password', 'secret', 'token', 'api_key', 'authorization'],\n            'patterns' =\u003e [\n                'email' =\u003e '/[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+/',\n                'credit_card' =\u003e '/\\b(?:\\d[ -]*?){13,16}\\b/',\n                'ssn' =\u003e '/\\b\\d{3}-?\\d{2}-?\\d{4}\\b/',\n                'phone_simple' =\u003e '/\\b\\d{3}[.-]?\\d{3}[.-]?\\d{4}\\b/',\n                'url_with_auth' =\u003e '/https?:\\/\\/[^:\\/\\s]+:[^@\\/\\s]+@[^\\s]+/',\n            ],\n            'replacement' =\u003e '[REDACTED]',\n            'mark_redacted' =\u003e true,\n            'track_redacted_keys' =\u003e false,\n            'non_redactable_object_behavior' =\u003e 'preserve', // 'preserve', 'remove', 'redact', 'empty_array'\n            'max_value_length' =\u003e 5000,\n            'redact_large_objects' =\u003e true,\n            'max_object_size' =\u003e 100,\n            \n            'shannon_entropy' =\u003e [\n                'enabled' =\u003e true,\n                'threshold' =\u003e 4.8,  // Higher = more selective\n                'min_length' =\u003e 25,  // Only analyze strings this long or longer\n                'exclusion_patterns' =\u003e [\n                    '/^https?:\\/\\//', // URLs\n                    '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', // UUIDs\n                    '/^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/',      // IP addresses\n                    '/^[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}$/i', // MAC addresses\n                ],\n            ],\n        ],\n    ],\n];\n```\n\n## Wildcard Patterns\n\nThe `BlockedKeysStrategy` and `SafeKeysStrategy` support powerful wildcard patterns using the `*` character. This allows you to match multiple key variations without listing each one explicitly.\n\n### Basic Wildcard Usage\n\n```php\n// config/redactor.php\n'profiles' =\u003e [\n    'wildcard_example' =\u003e [\n        'enabled' =\u003e true,\n        'strategies' =\u003e [\n            \\Kirschbaum\\Redactor\\Strategies\\BlockedKeysStrategy::class,\n        ],\n        'blocked_keys' =\u003e [\n            '*token*',        // Matches any key containing \"token\"\n            '*key*',          // Matches any key containing \"key\"  \n            'password',       // Exact match (no wildcards)\n            'user_*_data',    // Matches keys like \"user_profile_data\", \"user_settings_data\"\n        ],\n        // ... other config\n    ],\n];\n\n// Usage example\n$data = [\n    'user_id' =\u003e 123,\n    'api_token' =\u003e 'secret123',           // Matched by *token*\n    'access_token' =\u003e 'abc123',           // Matched by *token*\n    'my_custom_token' =\u003e 'xyz789',        // Matched by *token*\n    'user_api_key' =\u003e 'key123',           // Matched by *key*\n    'private_key_data' =\u003e 'private',      // Matched by *key*\n    'password' =\u003e 'secret',               // Matched by exact \"password\"\n    'user_profile_data' =\u003e 'profile',     // Matched by user_*_data\n    'user_settings_data' =\u003e 'settings',   // Matched by user_*_data\n    'normal_field' =\u003e 'safe_value',       // Not matched - preserved\n];\n\n$redacted = Redactor::redact($data, 'wildcard_example');\n```\n\n### Wildcard Pattern Types\n\n#### Contains Pattern (`*word*`)\nMatches any key that contains the specified word anywhere:\n\n```php\n'blocked_keys' =\u003e ['*token*', '*secret*', '*auth*'],\n\n// Matches:\n// - api_token, access_token, token_data, my_token_field\n// - user_secret, secret_key, app_secret_config  \n// - auth_header, oauth_token, authentication_data\n```\n\n#### Prefix Pattern (`word*`)\nMatches any key that starts with the specified word:\n\n```php\n'blocked_keys' =\u003e ['password*', 'secret*', 'api*'],\n\n// Matches:\n// - password, password_hash, password_confirmation\n// - secret, secret_key, secret_data\n// - api, api_key, api_token, api_endpoint\n```\n\n#### Suffix Pattern (`*word`)\nMatches any key that ends with the specified word:\n\n```php\n'blocked_keys' =\u003e ['*token', '*key', '*secret'],\n\n// Matches:\n// - access_token, api_token, user_token\n// - private_key, public_key, encryption_key  \n// - user_secret, app_secret, database_secret\n```\n\n#### Multi-Wildcard Patterns (`word*middle*word`)\nUse multiple wildcards for complex patterns:\n\n```php\n'blocked_keys' =\u003e [\n    'user_*_token',     // user_api_token, user_auth_token\n    'app_*_*_key',      // app_private_encryption_key, app_public_signing_key\n    '*_key_*',          // my_key_data, the_key_value, user_key_config\n],\n```\n\n### Case-Insensitive Matching\n\nAll wildcard patterns are case-insensitive by default:\n\n```php\n'blocked_keys' =\u003e ['*TOKEN*'],\n\n// Matches all of these:\n// - API_TOKEN, api_token, Api_Token, MyTokenData, user_token_field\n```\n\n### Combining Exact and Wildcard Patterns\n\nYou can mix exact matches with wildcard patterns in the same configuration:\n\n```php\n'blocked_keys' =\u003e [\n    'password',           // Exact match\n    'secret',            // Exact match\n    '*token*',           // Wildcard pattern\n    '*_key_*',           // Complex wildcard\n    'user_*_data',       // Specific structure\n],\n\n'safe_keys' =\u003e [\n    'id',                // Exact match - always preserved\n    'user_id',           // Exact match - always preserved  \n    '*_count',           // Wildcard pattern - preserve counting fields\n    'meta_*',            // Wildcard pattern - preserve metadata fields\n],\n```\n\n### Performance Considerations\n\n- Exact matches are faster than wildcard patterns\n- Simple wildcards (`*word*`) are faster than complex multi-wildcard patterns\n- Consider placing more specific patterns before broader ones\n- Use exact matches when you know the specific key names\n\n## Common Use Cases\n\n### Logging Context\n\n```php\nuse Kirschbaum\\Redactor\\Facades\\Redactor;\n\n// Before logging user actions\nLog::info('User action', Redactor::redact([\n    'user_id' =\u003e 123,\n    'action' =\u003e 'login',\n    'ip_address' =\u003e '192.168.1.1',\n    'session_token' =\u003e 'abc123def456...',\n    'user_agent' =\u003e 'Mozilla/5.0...',\n    'api_response' =\u003e $sensitiveApiData,\n]));\n```\n\n### Laravel Logging Integration\n\nFor automatic redaction of all log entries, use the `CustomLogTap` with Laravel's logging configuration. In your `config/logging.php`, add the tap to any channel:\n\n```php\n'channels' =\u003e [\n    'stack' =\u003e [\n        'driver' =\u003e 'stack',\n        'channels' =\u003e explode(',', env('LOG_STACK', 'single')),\n        'ignore_exceptions' =\u003e false,\n        'tap' =\u003e [Kirschbaum\\Redactor\\Logging\\CustomLogTap::class],\n    ],\n    \n    'single' =\u003e [\n        'driver' =\u003e 'single',\n        'path' =\u003e storage_path('logs/laravel.log'),\n        'level' =\u003e env('LOG_LEVEL', 'debug'),\n        'tap' =\u003e [Kirschbaum\\Redactor\\Logging\\CustomLogTap::class],\n    ],\n    \n    'daily' =\u003e [\n        'driver' =\u003e 'daily',\n        'path' =\u003e storage_path('logs/laravel.log'),\n        'level' =\u003e env('LOG_LEVEL', 'debug'),\n        'days' =\u003e 14,\n        'tap' =\u003e [Kirschbaum\\Redactor\\Logging\\CustomLogTap::class],\n    ],\n],\n```\n\nWith this configuration, all log entries will automatically have their context data redacted before being written to logs. The tap uses the `default` redaction profile unless otherwise configured.\n\n### API Response Sanitization\n\n```php\nuse Kirschbaum\\Redactor\\Facades\\Redactor;\n\n// Before returning debug information\nreturn response()-\u003ejson([\n    'debug' =\u003e Redactor::redact($requestData, 'performance'),\n    'status' =\u003e 'processed'\n]);\n```\n\n### Database Export \u0026 Auditing\n\n```php\nuse Kirschbaum\\Redactor\\Facades\\Redactor;\n\n// Before exporting user data\n$users = User::all()-\u003emap(function ($user) {\n    return Redactor::redact($user-\u003etoArray(), 'strict');\n});\n\n// Audit trail with sensitive data redacted\n$auditLog = Redactor::redact([\n    'user_id' =\u003e $user-\u003eid,\n    'changes' =\u003e $changes,\n    'request_data' =\u003e request()-\u003eall(),\n], 'audit');\n```\n\n### PCI Compliance Example\n\n```php\n// config/redactor.php\n'profiles' =\u003e [\n    'pci_compliant' =\u003e [\n        'enabled' =\u003e true,\n        'strategies' =\u003e [\n            \\Kirschbaum\\Redactor\\Strategies\\SafeKeysStrategy::class,\n            \\Kirschbaum\\Redactor\\Strategies\\BlockedKeysStrategy::class,\n            \\Kirschbaum\\Redactor\\Strategies\\RegexPatternsStrategy::class,\n        ],\n        'safe_keys' =\u003e ['order_id', 'customer_id', 'amount', 'currency'],\n        'blocked_keys' =\u003e [\n            'credit_card', 'cc_number', 'card_number', 'pan',\n            'cvv', 'cvc', 'cvn', 'expiry', 'exp_date', 'security_code'\n        ],\n        'patterns' =\u003e [\n            'credit_card' =\u003e '/\\b(?:\\d[ -]*?){13,16}\\b/',\n            'ssn' =\u003e '/\\b\\d{3}-?\\d{2}-?\\d{4}\\b/',\n            'routing_number' =\u003e '/\\b\\d{9}\\b/',\n        ],\n        'replacement' =\u003e '[PCI_REDACTED]',\n        'non_redactable_object_behavior' =\u003e 'redact',\n    ],\n];\n\n// Usage\n$orderData = Redactor::redact($order-\u003etoArray(), 'pci_compliant');\n```\n\n## Advanced Features\n\n### Object Handling\n\nThe package handles various object types:\n\n```php\nuse Kirschbaum\\Redactor\\Facades\\Redactor;\n\n// Laravel models (uses toArray())\n$user = User::find(1);\n$redacted = Redactor::redact($user);\n\n// Plain objects (uses JSON serialization)\n$object = new stdClass();\n$object-\u003esecret = 'sensitive';\n$redacted = Redactor::redact($object);\n\n// Non-serializable objects (configurable behavior)\n$resource = fopen('file.txt', 'r');\n$redacted = Redactor::redact(['file' =\u003e $resource]);\n// Behavior controlled by 'non_redactable_object_behavior' setting\n```\n\n### Custom Strategies\n\nCreate your own redaction logic with full type safety:\n\n```php\nuse Kirschbaum\\Redactor\\Strategies\\RedactionStrategyInterface;\nuse Kirschbaum\\Redactor\\RedactionContext;\n\nclass InternalDataStrategy implements RedactionStrategyInterface\n{\n    public function shouldHandle(mixed $value, string $key, RedactionContext $context): bool\n    {\n        return str_contains($key, 'internal_') || str_contains($key, 'debug_');\n    }\n\n    public function handle(mixed $value, string $key, RedactionContext $context): mixed\n    {\n        $context-\u003emarkRedacted();\n        return '[INTERNAL]';\n    }\n}\n\n// Register and use\nuse Kirschbaum\\Redactor\\Facades\\Redactor;\n\nRedactor::registerCustomStrategy('internal_data', new InternalDataStrategy());\n\n// Add to profile configuration\n'strategies' =\u003e [\n    'internal_data', // Custom strategy by registered name\n    \\Kirschbaum\\Redactor\\Strategies\\SafeKeysStrategy::class,\n    // ... other strategies\n],\n```\n\n### Multiple Usage Patterns\n\n```php\n// Via Facade (recommended)\nuse Kirschbaum\\Redactor\\Facades\\Redactor;\n$result = Redactor::redact($data, 'profile_name');\n\n// Via Service Container\n$redactor = app(\\Kirschbaum\\Redactor\\Redactor::class);\n$result = $redactor-\u003eredact($data, 'profile_name');\n\n// Direct Instantiation (gets fresh instance - no state conflicts)\n$redactor = new \\Kirschbaum\\Redactor\\Redactor();\n$result = $redactor-\u003eredact($data, 'profile_name');\n\n// Check available profiles\n$profiles = Redactor::getAvailableProfiles();\n$exists = Redactor::profileExists('custom_profile');\n```\n\n## Built-in Profiles\n\n- **`default`**: Balanced redaction for general logging and debugging\n- **`strict`**: Aggressive redaction for sensitive contexts and audit trails  \n- **`performance`**: Minimal redaction optimized for high-throughput scenarios\n\n## Environment Configuration\n\nMany settings can be controlled via environment variables:\n\n```env\nREDACTOR_ENABLED=true\nREDACTOR_DEFAULT_PROFILE=default\nREDACTOR_REPLACEMENT=\"[REDACTED]\"\nREDACTOR_MARK_REDACTED=true\nREDACTOR_TRACK_KEYS=false\nREDACTOR_OBJECT_BEHAVIOR=preserve\nREDACTOR_MAX_VALUE_LENGTH=5000\nREDACTOR_LARGE_OBJECTS=true\nREDACTOR_MAX_OBJECT_SIZE=100\nREDACTOR_SHANNON_ENABLED=true\nREDACTOR_SHANNON_THRESHOLD=4.8\nREDACTOR_SHANNON_MIN_LENGTH=25\n```\n\n## File Scanning Command\n\nThe package includes a console command to scan files and directories for sensitive content:\n\n```bash\n# Scan specific files\nphp artisan redactor:scan path/to/sensitive-file.txt\n\n# Scan directories (scans entire project by default)\nphp artisan redactor:scan app/ config/\n\n# Scan with custom profile\nphp artisan redactor:scan --profile=strict app/\n\n# Exit with error code if sensitive content found (useful for CI)\nphp artisan redactor:scan --bail app/\n\n# JSON output for programmatic use\nphp artisan redactor:scan --output=json config/\n\n# Summary only (no per-file details)\nphp artisan redactor:scan --summary-only\n```\n\nThe scanner uses the `file_scan` profile by default, which is optimized for plain text content and detects:\n- API keys, tokens, and secrets\n- Email addresses and personal information\n- High-entropy strings (potential keys/tokens)\n- Credit cards, SSNs, phone numbers\n- Passwords and authentication strings\n\nResults show **CLEAN**, **FINDINGS**, or **SKIPPED** status for each file, with a summary of total files scanned and findings detected.\n\n## Requirements\n\n- PHP 8.3+\n- Laravel 11.x or 12.x\n\n## Installation\n\n```bash\ncomposer require kirschbaum-development/redactor\nphp artisan vendor:publish --tag=redactor-config\n```\n\n## Testing\n\n```bash\n# Run tests\n./vendor/bin/pest\n\n# Run tests with coverage\n./vendor/bin/pest --coverage\n```\n\n## Roadmap\n- Add Laravel custom log formatter to tap logs and automatically redact sensitive data\n- Add supoprt for partial replacement of sensitive data (low priority)\n\n## License\n\nMIT License. See [LICENSE.md](LICENSE.md) for details.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkirschbaum-development%2Fredactor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkirschbaum-development%2Fredactor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkirschbaum-development%2Fredactor/lists"}