Files
monolog-gdpr-filter/src/SecuritySanitizer.php
Ismo Vuorinen 8866daaf33 feat: add advanced architecture, documentation, and coverage improvements (#65)
* fix(style): resolve PHPCS line-length warnings in source files

* fix(style): resolve PHPCS line-length warnings in test files

* feat(audit): add structured audit logging with ErrorContext and AuditContext

- ErrorContext: standardized error information with sensitive data sanitization
- AuditContext: structured context for audit entries with operation types
- StructuredAuditLogger: enhanced audit logger wrapper with timing support

* feat(recovery): add recovery mechanism for failed masking operations

- FailureMode enum: FAIL_OPEN, FAIL_CLOSED, FAIL_SAFE modes
- RecoveryStrategy interface and RecoveryResult value object
- RetryStrategy: exponential backoff with configurable attempts
- FallbackMaskStrategy: type-aware fallback values

* feat(strategies): add CallbackMaskingStrategy for custom masking logic

- Wraps custom callbacks as MaskingStrategy implementations
- Factory methods: constant(), hash(), partial() for common use cases
- Supports exact match and prefix match for field paths

* docs: add framework integration guides and examples

- symfony-integration.md: Symfony service configuration and Monolog setup
- psr3-decorator.md: PSR-3 logger decorator pattern implementation
- framework-examples.md: CakePHP, CodeIgniter 4, Laminas, Yii2, PSR-15
- docker-development.md: Docker development environment guide

* chore(docker): add Docker development environment

- Dockerfile: PHP 8.2-cli-alpine with Xdebug for coverage
- docker-compose.yml: development services with volume mounts

* feat(demo): add interactive GDPR pattern tester playground

- PatternTester.php: pattern testing utility with strategy support
- index.php: web API endpoint with JSON response handling
- playground.html: interactive web interface for testing patterns

* docs(todo): update with completed medium priority items

- Mark all PHPCS warnings as fixed (81 → 0)
- Document new Audit and Recovery features
- Update test count to 1,068 tests with 2,953 assertions
- Move remaining items to low priority

* feat: add advanced architecture, documentation, and coverage improvements

- Add architecture improvements:
  - ArrayAccessorInterface and DotArrayAccessor for decoupled array access
  - MaskingOrchestrator for single-responsibility masking coordination
  - GdprProcessorBuilder for fluent configuration
  - MaskingPluginInterface and AbstractMaskingPlugin for plugin architecture
  - PluginAwareProcessor for plugin hook execution
  - AuditLoggerFactory for instance-based audit logger creation

- Add advanced features:
  - SerializedDataProcessor for handling print_r/var_export/serialize output
  - KAnonymizer with GeneralizationStrategy for GDPR k-anonymity
  - RetentionPolicy for configurable data retention periods
  - StreamingProcessor for memory-efficient large log processing

- Add comprehensive documentation:
  - docs/performance-tuning.md - benchmarking, optimization, caching
  - docs/troubleshooting.md - common issues and solutions
  - docs/logging-integrations.md - ELK, Graylog, Datadog, etc.
  - docs/plugin-development.md - complete plugin development guide

- Improve test coverage (84.41% → 85.07%):
  - ConditionalRuleFactoryInstanceTest (100% coverage)
  - GdprProcessorBuilderEdgeCasesTest (100% coverage)
  - StrategyEdgeCasesTest for ReDoS detection and type parsing
  - 78 new tests, 119 new assertions

- Update TODO.md with current statistics:
  - 141 PHP files, 1,346 tests, 85.07% line coverage

* chore: tests, update actions, sonarcloud issues

* chore: rector

* fix: more sonarcloud fixes

* chore: more fixes

* refactor: copilot review fix

* chore: rector
2025-12-22 13:38:18 +02:00

91 lines
3.3 KiB
PHP

<?php
declare(strict_types=1);
namespace Ivuorinen\MonologGdprFilter;
use Ivuorinen\MonologGdprFilter\MaskConstants as Mask;
/**
* Sanitizes error messages to prevent information disclosure.
*
* This class removes sensitive information from error messages
* before they are logged to prevent security vulnerabilities.
*/
final class SecuritySanitizer
{
/**
* Sanitize error messages to prevent information disclosure.
*
* @param string $message The original error message
* @return string The sanitized error message
*/
public static function sanitizeErrorMessage(string $message): string
{
// List of sensitive patterns to remove or mask
$sensitivePatterns = [
// Database credentials
'/password=\S+/i' => 'password=***',
'/pwd=\S+/i' => 'pwd=***',
'/pass=\S+/i' => 'pass=***',
// Database hosts and connection strings
'/host=[\w\.-]+/i' => 'host=***',
'/server=[\w\.-]+/i' => 'server=***',
'/hostname=[\w\.-]+/i' => 'hostname=***',
// User credentials
'/user=\S+/i' => 'user=***',
'/username=\S+/i' => 'username=***',
'/uid=\S+/i' => 'uid=***',
// API keys and tokens
'/api[_-]?key[=:]\s*\S+/i' => 'api_key=***',
'/token[=:]\s*\S+/i' => 'token=***',
'/bearer\s+\S+/i' => 'bearer ***',
'/sk_\w+/i' => 'sk_***',
'/pk_\w+/i' => 'pk_***',
// File paths (potential information disclosure)
'/\/[\w\/\.-]*\/(config|secret|private|key)[\w\/\.-]*/i' => '/***/$1/***',
'/[a-zA-Z]:\\\\[\w\\\\.-]*\\\\(config|secret|private|key)[\w\\\\.-]*/i' => 'C:\\***\\$1\\***',
// Connection strings
'/redis:\/\/[^@]*@[\w\.-]+:\d+/i' => 'redis://***:***@***:***',
'/mysql:\/\/[^@]*@[\w\.-]+:\d+/i' => 'mysql://***:***@***:***',
'/postgresql:\/\/[^@]*@[\w\.-]+:\d+/i' => 'postgresql://***:***@***:***',
// JWT secrets and other secrets (enhanced to catch more patterns)
'/secret[_-]?key[=:\s]+\S+/i' => 'secret_key=***',
'/jwt[_-]?secret[=:\s]+\S+/i' => 'jwt_secret=***',
'/\bsuper_secret_\w+/i' => Mask::MASK_SECRET,
// Generic secret-like patterns (alphanumeric keys that look sensitive)
'/\b[a-z_]*secret[a-z_]*[=:\s]+[\w\d_-]{10,}/i' => 'secret=***',
'/\b[a-z_]*key[a-z_]*[=:\s]+[\w\d_-]{10,}/i' => 'key=***',
// IP addresses in internal ranges (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
'/\b(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|'
. '172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}|'
. '192\.168\.\d{1,3}\.\d{1,3})\b/' => '***.***.***',
];
$sanitized = $message;
foreach ($sensitivePatterns as $pattern => $replacement) {
$sanitized = preg_replace($pattern, $replacement, $sanitized) ?? $sanitized;
}
// Truncate very long messages to prevent log flooding
if (strlen($sanitized) > 500) {
return substr($sanitized, 0, 500) . '... (truncated for security)';
}
return $sanitized;
}
/** @psalm-suppress UnusedConstructor */
private function __construct()
{}
}