mirror of
https://github.com/ivuorinen/monolog-gdpr-filter.git
synced 2026-03-12 19:01:12 +00:00
refactor: replace hardcoded strings with constant references (#100)
* fix(tests): remove error_log calls and clean up ComprehensiveValidationTest * refactor: replace hardcoded strings with MaskConstants and TestConstants references * fix(streaming): replace overcounting '[' heuristic with proper mask detection StreamingProcessor::getStatistics() was counting any message containing '[' as masked, causing false positives. Now checks for specific mask constants (MASK_GENERIC, MASK_BRACKETS, MASK_REDACTED_BRACKETS) instead. Also adds MASK_REDACTED_BRACKETS constant to MaskConstants and removes the now-unnecessary UnusedFunctionCall psalm suppression. * refactor(tests): replace remaining hardcoded literals with constant references Add new constants to TestConstants (MASK_REDACTED_PLAIN, MASK_SECRET_BRACKETS, MASK_SSN_BRACKETS, PATTERN_REDOS_NESTED_STAR, FIELD_USER_SSN, FIELD_USER_DATA) and replace all matching literals across 21 test files. Also removes dead memory_get_usage() call and uses existing TestConstants::IP_ADDRESS_PUBLIC for hardcoded IP. * fix(streaming): replace mask-token heuristic with accurate record comparison in getStatistics() The previous implementation only detected masking when specific mask tokens appeared in the message, missing cases where context was masked or different mask values were used. Compare original vs processed records instead. * refactor(tests): add PATTERN_EMAIL_SIMPLE, MASK_CARD_BRACKETS, EXPECTED_SSN_MASKED constants Replace cross-file duplicate literals with TestConstants references: - Email regex (4 files), '[CARD]' (2 files), 'SSN: [SSN]' (2 files) * fix(streaming): bypass audit logger in getStatistics() by calling orchestrator directly getStatistics() previously routed through processStream()/processChunk() which triggered the audit logger for each record. A read-only statistics method should not produce audit side-effects. Now calls orchestrator.process() directly and processes records one at a time without materializing the entire iterable. * refactor(tests): fix test quality issues and add PATTERN_CREDIT_CARD constant - Replace fail() message that leaked sensitive terms with count-only message - Replace bare 'EMAIL' string with MaskConstants::MASK_EMAIL for consistency - Remove error_log() debug output from CriticalBugRegressionTest - Add TestConstants::PATTERN_CREDIT_CARD and replace inline regex in 3 files
This commit is contained in:
@@ -58,7 +58,7 @@ final class AbstractMaskingStrategyTest extends TestCase
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Test Strategy';
|
||||
return TestConstants::STRATEGY_TEST;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,8 +123,8 @@ final class AbstractMaskingStrategyTest extends TestCase
|
||||
#[Test]
|
||||
public function valueToStringConvertsStringAsIs(): void
|
||||
{
|
||||
$result = $this->strategy->testValueToString('test string');
|
||||
$this->assertSame('test string', $result);
|
||||
$result = $this->strategy->testValueToString(TestConstants::MESSAGE_TEST_STRING);
|
||||
$this->assertSame(TestConstants::MESSAGE_TEST_STRING, $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -159,7 +159,7 @@ final class AbstractMaskingStrategyTest extends TestCase
|
||||
public function valueToStringConvertsArray(): void
|
||||
{
|
||||
$result = $this->strategy->testValueToString(['key' => 'value']);
|
||||
$this->assertSame('{"key":"value"}', $result);
|
||||
$this->assertSame(TestConstants::JSON_KEY_VALUE, $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -256,7 +256,7 @@ final class AbstractMaskingStrategyTest extends TestCase
|
||||
$conditions = [
|
||||
'level' => 'Error',
|
||||
'channel' => 'test-channel',
|
||||
'message' => TestConstants::MESSAGE_TEST_LOWERCASE,
|
||||
TestConstants::FIELD_MESSAGE => TestConstants::MESSAGE_TEST_LOWERCASE,
|
||||
TestConstants::CONTEXT_USER_ID => 123,
|
||||
];
|
||||
|
||||
@@ -323,7 +323,7 @@ final class AbstractMaskingStrategyTest extends TestCase
|
||||
public function generateValuePreviewHandlesNonStringValues(): void
|
||||
{
|
||||
$preview = $this->strategy->testGenerateValuePreview(['key' => 'value']);
|
||||
$this->assertSame('{"key":"value"}', $preview);
|
||||
$this->assertSame(TestConstants::JSON_KEY_VALUE, $preview);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -383,8 +383,8 @@ final class AbstractMaskingStrategyTest extends TestCase
|
||||
#[Test]
|
||||
public function preserveValueTypeConvertsBackToArray(): void
|
||||
{
|
||||
$result = $this->strategy->testPreserveValueType(['original' => 'value'], '{"masked":"data"}');
|
||||
$this->assertSame(['masked' => 'data'], $result);
|
||||
$result = $this->strategy->testPreserveValueType(['original' => 'value'], '{"' . TestConstants::DATA_MASKED . '":"data"}');
|
||||
$this->assertSame([TestConstants::DATA_MASKED => 'data'], $result);
|
||||
$this->assertIsArray($result);
|
||||
}
|
||||
|
||||
@@ -392,10 +392,10 @@ final class AbstractMaskingStrategyTest extends TestCase
|
||||
public function preserveValueTypeConvertsBackToObject(): void
|
||||
{
|
||||
$original = (object) ['original' => 'value'];
|
||||
$result = $this->strategy->testPreserveValueType($original, '{"masked":"data"}');
|
||||
$result = $this->strategy->testPreserveValueType($original, '{"' . TestConstants::DATA_MASKED . '":"data"}');
|
||||
|
||||
$this->assertIsObject($result);
|
||||
$this->assertEquals((object) ['masked' => 'data'], $result);
|
||||
$this->assertEquals((object) [TestConstants::DATA_MASKED => 'data'], $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Tests\Strategies;
|
||||
|
||||
use Ivuorinen\MonologGdprFilter\Exceptions\MaskingOperationFailedException;
|
||||
use Ivuorinen\MonologGdprFilter\MaskConstants;
|
||||
use Ivuorinen\MonologGdprFilter\Exceptions\RuleExecutionException;
|
||||
use Ivuorinen\MonologGdprFilter\Strategies\CallbackMaskingStrategy;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
@@ -23,9 +24,9 @@ final class CallbackMaskingStrategyTest extends TestCase
|
||||
public function testBasicConstruction(): void
|
||||
{
|
||||
$callback = fn(mixed $value): string => TestConstants::MASK_MASKED_BRACKETS;
|
||||
$strategy = new CallbackMaskingStrategy('user.email', $callback);
|
||||
$strategy = new CallbackMaskingStrategy(TestConstants::FIELD_USER_EMAIL, $callback);
|
||||
|
||||
$this->assertSame('user.email', $strategy->getFieldPath());
|
||||
$this->assertSame(TestConstants::FIELD_USER_EMAIL, $strategy->getFieldPath());
|
||||
$this->assertTrue($strategy->isExactMatch());
|
||||
$this->assertSame(50, $strategy->getPriority());
|
||||
}
|
||||
@@ -33,10 +34,10 @@ final class CallbackMaskingStrategyTest extends TestCase
|
||||
public function testMaskWithSimpleCallback(): void
|
||||
{
|
||||
$callback = fn(mixed $value): string => TestConstants::MASK_MASKED_BRACKETS;
|
||||
$strategy = new CallbackMaskingStrategy('user.email', $callback);
|
||||
$strategy = new CallbackMaskingStrategy(TestConstants::FIELD_USER_EMAIL, $callback);
|
||||
$record = $this->createLogRecord();
|
||||
|
||||
$result = $strategy->mask('john@example.com', 'user.email', $record);
|
||||
$result = $strategy->mask(TestConstants::EMAIL_JOHN, TestConstants::FIELD_USER_EMAIL, $record);
|
||||
|
||||
$this->assertSame(TestConstants::MASK_MASKED_BRACKETS, $result);
|
||||
}
|
||||
@@ -44,10 +45,10 @@ final class CallbackMaskingStrategyTest extends TestCase
|
||||
public function testMaskWithTransformingCallback(): void
|
||||
{
|
||||
$callback = fn(mixed $value): string => strtoupper((string) $value);
|
||||
$strategy = new CallbackMaskingStrategy('user.name', $callback);
|
||||
$strategy = new CallbackMaskingStrategy(TestConstants::FIELD_USER_NAME, $callback);
|
||||
$record = $this->createLogRecord();
|
||||
|
||||
$result = $strategy->mask('john', 'user.name', $record);
|
||||
$result = $strategy->mask('john', TestConstants::FIELD_USER_NAME, $record);
|
||||
|
||||
$this->assertSame('JOHN', $result);
|
||||
}
|
||||
@@ -57,27 +58,27 @@ final class CallbackMaskingStrategyTest extends TestCase
|
||||
$callback = function (): never {
|
||||
throw new RuleExecutionException('Callback failed');
|
||||
};
|
||||
$strategy = new CallbackMaskingStrategy('user.data', $callback);
|
||||
$strategy = new CallbackMaskingStrategy(TestConstants::FIELD_USER_DATA, $callback);
|
||||
$record = $this->createLogRecord();
|
||||
|
||||
$this->expectException(MaskingOperationFailedException::class);
|
||||
$this->expectExceptionMessage('Callback threw exception');
|
||||
|
||||
$strategy->mask('value', 'user.data', $record);
|
||||
$strategy->mask('value', TestConstants::FIELD_USER_DATA, $record);
|
||||
}
|
||||
|
||||
public function testShouldApplyWithExactMatch(): void
|
||||
{
|
||||
$callback = fn(mixed $value): string => TestConstants::MASK_MASKED_BRACKETS;
|
||||
$strategy = new CallbackMaskingStrategy(
|
||||
'user.email',
|
||||
TestConstants::FIELD_USER_EMAIL,
|
||||
$callback,
|
||||
exactMatch: true
|
||||
);
|
||||
$record = $this->createLogRecord();
|
||||
|
||||
$this->assertTrue($strategy->shouldApply('value', 'user.email', $record));
|
||||
$this->assertFalse($strategy->shouldApply('value', 'user.name', $record));
|
||||
$this->assertTrue($strategy->shouldApply('value', TestConstants::FIELD_USER_EMAIL, $record));
|
||||
$this->assertFalse($strategy->shouldApply('value', TestConstants::FIELD_USER_NAME, $record));
|
||||
$this->assertFalse($strategy->shouldApply('value', 'user.email.work', $record));
|
||||
}
|
||||
|
||||
@@ -85,32 +86,32 @@ final class CallbackMaskingStrategyTest extends TestCase
|
||||
{
|
||||
$callback = fn(mixed $value): string => TestConstants::MASK_MASKED_BRACKETS;
|
||||
$strategy = new CallbackMaskingStrategy(
|
||||
'user.*',
|
||||
TestConstants::PATH_USER_WILDCARD,
|
||||
$callback,
|
||||
exactMatch: false
|
||||
);
|
||||
$record = $this->createLogRecord();
|
||||
|
||||
$this->assertTrue($strategy->shouldApply('value', 'user.email', $record));
|
||||
$this->assertTrue($strategy->shouldApply('value', 'user.name', $record));
|
||||
$this->assertTrue($strategy->shouldApply('value', TestConstants::FIELD_USER_EMAIL, $record));
|
||||
$this->assertTrue($strategy->shouldApply('value', TestConstants::FIELD_USER_NAME, $record));
|
||||
$this->assertFalse($strategy->shouldApply('value', 'admin.email', $record));
|
||||
}
|
||||
|
||||
public function testGetName(): void
|
||||
{
|
||||
$callback = fn(mixed $value): string => TestConstants::MASK_MASKED_BRACKETS;
|
||||
$strategy = new CallbackMaskingStrategy('user.email', $callback);
|
||||
$strategy = new CallbackMaskingStrategy(TestConstants::FIELD_USER_EMAIL, $callback);
|
||||
|
||||
$name = $strategy->getName();
|
||||
|
||||
$this->assertStringContainsString('Callback Masking', $name);
|
||||
$this->assertStringContainsString('user.email', $name);
|
||||
$this->assertStringContainsString(TestConstants::FIELD_USER_EMAIL, $name);
|
||||
}
|
||||
|
||||
public function testValidate(): void
|
||||
{
|
||||
$callback = fn(mixed $value): string => TestConstants::MASK_MASKED_BRACKETS;
|
||||
$strategy = new CallbackMaskingStrategy('user.email', $callback);
|
||||
$strategy = new CallbackMaskingStrategy(TestConstants::FIELD_USER_EMAIL, $callback);
|
||||
|
||||
$this->assertTrue($strategy->validate());
|
||||
}
|
||||
@@ -119,7 +120,7 @@ final class CallbackMaskingStrategyTest extends TestCase
|
||||
{
|
||||
$callback = fn(mixed $value): string => TestConstants::MASK_MASKED_BRACKETS;
|
||||
$strategy = new CallbackMaskingStrategy(
|
||||
'user.email',
|
||||
TestConstants::FIELD_USER_EMAIL,
|
||||
$callback,
|
||||
75,
|
||||
false
|
||||
@@ -130,7 +131,7 @@ final class CallbackMaskingStrategyTest extends TestCase
|
||||
$this->assertArrayHasKey('field_path', $config);
|
||||
$this->assertArrayHasKey('exact_match', $config);
|
||||
$this->assertArrayHasKey('priority', $config);
|
||||
$this->assertSame('user.email', $config['field_path']);
|
||||
$this->assertSame(TestConstants::FIELD_USER_EMAIL, $config['field_path']);
|
||||
$this->assertFalse($config['exact_match']);
|
||||
$this->assertSame(75, $config['priority']);
|
||||
}
|
||||
@@ -138,7 +139,7 @@ final class CallbackMaskingStrategyTest extends TestCase
|
||||
public function testForPathsFactoryMethod(): void
|
||||
{
|
||||
$callback = fn(mixed $value): string => TestConstants::MASK_MASKED_BRACKETS;
|
||||
$paths = ['user.email', 'admin.email', 'contact.email'];
|
||||
$paths = [TestConstants::FIELD_USER_EMAIL, 'admin.email', 'contact.email'];
|
||||
|
||||
$strategies = CallbackMaskingStrategy::forPaths($paths, $callback);
|
||||
|
||||
@@ -152,20 +153,20 @@ final class CallbackMaskingStrategyTest extends TestCase
|
||||
|
||||
public function testConstantFactoryMethod(): void
|
||||
{
|
||||
$strategy = CallbackMaskingStrategy::constant('user.ssn', '***-**-****');
|
||||
$strategy = CallbackMaskingStrategy::constant(TestConstants::FIELD_USER_SSN, MaskConstants::MASK_SSN_PATTERN);
|
||||
$record = $this->createLogRecord();
|
||||
|
||||
$result = $strategy->mask('123-45-6789', 'user.ssn', $record);
|
||||
$result = $strategy->mask(TestConstants::SSN_US, TestConstants::FIELD_USER_SSN, $record);
|
||||
|
||||
$this->assertSame('***-**-****', $result);
|
||||
$this->assertSame(MaskConstants::MASK_SSN_PATTERN, $result);
|
||||
}
|
||||
|
||||
public function testHashFactoryMethod(): void
|
||||
{
|
||||
$strategy = CallbackMaskingStrategy::hash('user.password', 'sha256', 8);
|
||||
$strategy = CallbackMaskingStrategy::hash(TestConstants::FIELD_USER_PASSWORD, 'sha256', 8);
|
||||
$record = $this->createLogRecord();
|
||||
|
||||
$result = $strategy->mask('secret123', 'user.password', $record);
|
||||
$result = $strategy->mask('secret123', TestConstants::FIELD_USER_PASSWORD, $record);
|
||||
|
||||
$this->assertIsString($result);
|
||||
$this->assertSame(11, strlen($result));
|
||||
@@ -174,24 +175,24 @@ final class CallbackMaskingStrategyTest extends TestCase
|
||||
|
||||
public function testHashWithNoTruncation(): void
|
||||
{
|
||||
$strategy = CallbackMaskingStrategy::hash('user.password', 'md5', 0);
|
||||
$strategy = CallbackMaskingStrategy::hash(TestConstants::FIELD_USER_PASSWORD, 'md5', 0);
|
||||
$record = $this->createLogRecord();
|
||||
|
||||
$result = $strategy->mask('test', 'user.password', $record);
|
||||
$result = $strategy->mask('test', TestConstants::FIELD_USER_PASSWORD, $record);
|
||||
|
||||
$this->assertSame(32, strlen((string) $result));
|
||||
}
|
||||
|
||||
public function testPartialFactoryMethod(): void
|
||||
{
|
||||
$strategy = CallbackMaskingStrategy::partial('user.email', 2, 4);
|
||||
$strategy = CallbackMaskingStrategy::partial(TestConstants::FIELD_USER_EMAIL, 2, 4);
|
||||
$record = $this->createLogRecord();
|
||||
|
||||
$result = $strategy->mask('john@example.com', 'user.email', $record);
|
||||
$result = $strategy->mask(TestConstants::EMAIL_JOHN, TestConstants::FIELD_USER_EMAIL, $record);
|
||||
|
||||
$this->assertStringStartsWith('jo', $result);
|
||||
$this->assertStringEndsWith('.com', $result);
|
||||
$this->assertStringContainsString('***', $result);
|
||||
$this->assertStringContainsString(MaskConstants::MASK_GENERIC, $result);
|
||||
}
|
||||
|
||||
public function testPartialWithShortString(): void
|
||||
@@ -201,7 +202,7 @@ final class CallbackMaskingStrategyTest extends TestCase
|
||||
|
||||
$result = $strategy->mask('abc', 'user.code', $record);
|
||||
|
||||
$this->assertSame('***', $result);
|
||||
$this->assertSame(MaskConstants::MASK_GENERIC, $result);
|
||||
}
|
||||
|
||||
public function testPartialWithCustomMaskChar(): void
|
||||
@@ -224,29 +225,29 @@ final class CallbackMaskingStrategyTest extends TestCase
|
||||
return TestConstants::MASK_MASKED_BRACKETS;
|
||||
};
|
||||
|
||||
$strategy = new CallbackMaskingStrategy('user.data', $callback);
|
||||
$strategy = new CallbackMaskingStrategy(TestConstants::FIELD_USER_DATA, $callback);
|
||||
$record = $this->createLogRecord();
|
||||
|
||||
$strategy->mask(['key' => 'value'], 'user.data', $record);
|
||||
$strategy->mask(['key' => 'value'], TestConstants::FIELD_USER_DATA, $record);
|
||||
|
||||
$this->assertSame($receivedValue, ['key' => 'value']);
|
||||
}
|
||||
|
||||
public function testCallbackCanReturnNonString(): void
|
||||
{
|
||||
$callback = fn(mixed $value): array => ['masked' => true];
|
||||
$strategy = new CallbackMaskingStrategy('user.data', $callback);
|
||||
$callback = fn(mixed $value): array => [TestConstants::DATA_MASKED => true];
|
||||
$strategy = new CallbackMaskingStrategy(TestConstants::FIELD_USER_DATA, $callback);
|
||||
$record = $this->createLogRecord();
|
||||
|
||||
$result = $strategy->mask(['key' => 'value'], 'user.data', $record);
|
||||
$result = $strategy->mask(['key' => 'value'], TestConstants::FIELD_USER_DATA, $record);
|
||||
|
||||
$this->assertSame(['masked' => true], $result);
|
||||
$this->assertSame([TestConstants::DATA_MASKED => true], $result);
|
||||
}
|
||||
|
||||
public function testCustomPriority(): void
|
||||
{
|
||||
$callback = fn(mixed $value): string => TestConstants::MASK_MASKED_BRACKETS;
|
||||
$strategy = new CallbackMaskingStrategy('user.email', $callback, 100);
|
||||
$strategy = new CallbackMaskingStrategy(TestConstants::FIELD_USER_EMAIL, $callback, 100);
|
||||
|
||||
$this->assertSame(100, $strategy->getPriority());
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ final class ConditionalMaskingStrategyComprehensiveTest extends TestCase
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Test Strategy';
|
||||
return TestConstants::STRATEGY_TEST;
|
||||
}
|
||||
|
||||
public function validate(): bool
|
||||
|
||||
@@ -146,14 +146,14 @@ final class DataTypeMaskingStrategyComprehensiveTest extends TestCase
|
||||
|
||||
public function testMaskWithObjectValueJsonMask(): void
|
||||
{
|
||||
$strategy = new DataTypeMaskingStrategy(['object' => '{"masked":"data"}']);
|
||||
$strategy = new DataTypeMaskingStrategy(['object' => '{"' . TestConstants::DATA_MASKED . '":"data"}']);
|
||||
|
||||
$record = $this->createLogRecord('Test');
|
||||
|
||||
$obj = (object)['original' => 'value'];
|
||||
$result = $strategy->mask($obj, 'field', $record);
|
||||
|
||||
$expected = (object)['masked' => 'data'];
|
||||
$expected = (object)[TestConstants::DATA_MASKED => 'data'];
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
@@ -411,7 +411,7 @@ final class DataTypeMaskingStrategyComprehensiveTest extends TestCase
|
||||
'double' => 'D',
|
||||
'boolean' => '1', // Boolean uses filter_var, so '1' becomes true
|
||||
'array' => '["MASKED"]', // JSON array
|
||||
'object' => '{"masked":"value"}', // JSON object
|
||||
'object' => '{"' . TestConstants::DATA_MASKED . '":"value"}', // JSON object
|
||||
'NULL' => 'N',
|
||||
]);
|
||||
|
||||
@@ -422,7 +422,7 @@ final class DataTypeMaskingStrategyComprehensiveTest extends TestCase
|
||||
$this->assertSame('D', $strategy->mask(3.14, 'f', $record));
|
||||
$this->assertTrue($strategy->mask(true, 'f', $record)); // Boolean conversion
|
||||
$this->assertSame(['MASKED'], $strategy->mask([], 'f', $record));
|
||||
$this->assertEquals((object)['masked' => 'value'], $strategy->mask((object)[], 'f', $record));
|
||||
$this->assertEquals((object)[TestConstants::DATA_MASKED => 'value'], $strategy->mask((object)[], 'f', $record));
|
||||
$this->assertSame('N', $strategy->mask(null, 'f', $record));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ final class DataTypeMaskingStrategyTest extends TestCase
|
||||
{
|
||||
$strategy = new DataTypeMaskingStrategy(['string' => MaskConstants::MASK_GENERIC]);
|
||||
|
||||
$this->assertTrue($strategy->shouldApply('test string', 'field', $this->logRecord));
|
||||
$this->assertTrue($strategy->shouldApply(TestConstants::MESSAGE_TEST_STRING, 'field', $this->logRecord));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -107,11 +107,11 @@ final class DataTypeMaskingStrategyTest extends TestCase
|
||||
#[Test]
|
||||
public function maskAppliesStringMask(): void
|
||||
{
|
||||
$strategy = new DataTypeMaskingStrategy(['string' => 'REDACTED']);
|
||||
$strategy = new DataTypeMaskingStrategy(['string' => TestConstants::MASK_REDACTED_PLAIN]);
|
||||
|
||||
$result = $strategy->mask('sensitive data', 'field', $this->logRecord);
|
||||
|
||||
$this->assertSame('REDACTED', $result);
|
||||
$this->assertSame(TestConstants::MASK_REDACTED_PLAIN, $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -177,7 +177,7 @@ final class DataTypeMaskingStrategyTest extends TestCase
|
||||
#[Test]
|
||||
public function maskAppliesArrayMaskJsonArray(): void
|
||||
{
|
||||
$strategy = new DataTypeMaskingStrategy(['array' => '["masked"]']);
|
||||
$strategy = new DataTypeMaskingStrategy(['array' => '["' . TestConstants::DATA_MASKED . '"]']);
|
||||
|
||||
$result = $strategy->mask(['original'], 'field', $this->logRecord);
|
||||
|
||||
@@ -209,13 +209,13 @@ final class DataTypeMaskingStrategyTest extends TestCase
|
||||
#[Test]
|
||||
public function maskAppliesObjectMaskJsonObject(): void
|
||||
{
|
||||
$strategy = new DataTypeMaskingStrategy(['object' => '{"masked":"data"}']);
|
||||
$strategy = new DataTypeMaskingStrategy(['object' => '{"' . TestConstants::DATA_MASKED . '":"data"}']);
|
||||
|
||||
$obj = (object) ['original' => 'value'];
|
||||
$result = $strategy->mask($obj, 'field', $this->logRecord);
|
||||
|
||||
$this->assertIsObject($result);
|
||||
$this->assertEquals((object) ['masked' => 'data'], $result);
|
||||
$this->assertEquals((object) [TestConstants::DATA_MASKED => 'data'], $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -227,7 +227,7 @@ final class DataTypeMaskingStrategyTest extends TestCase
|
||||
$result = $strategy->mask($obj, 'field', $this->logRecord);
|
||||
|
||||
$this->assertIsObject($result);
|
||||
$this->assertEquals((object) ['masked' => 'MASKED'], $result);
|
||||
$this->assertEquals((object) [TestConstants::DATA_MASKED => 'MASKED'], $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
|
||||
@@ -30,7 +30,7 @@ final class FieldPathMaskingStrategyEnhancedTest extends TestCase
|
||||
$this->expectException(MaskingOperationFailedException::class);
|
||||
$this->expectExceptionMessage('Regex pattern is null');
|
||||
|
||||
$strategy->mask('test value', 'field', $record);
|
||||
$strategy->mask(TestConstants::VALUE_TEST, 'field', $record);
|
||||
}
|
||||
|
||||
public function testApplyStaticReplacementWithNullReplacement(): void
|
||||
|
||||
@@ -138,9 +138,9 @@ final class FieldPathMaskingStrategyTest extends TestCase
|
||||
TestConstants::PATTERN_SSN_FORMAT,
|
||||
MaskConstants::MASK_SSN_PATTERN
|
||||
);
|
||||
$strategy = new FieldPathMaskingStrategy(['user.ssn' => $ssnConfig]);
|
||||
$strategy = new FieldPathMaskingStrategy([TestConstants::FIELD_USER_SSN => $ssnConfig]);
|
||||
|
||||
$result = $strategy->mask(TestConstants::SSN_US, 'user.ssn', $this->logRecord);
|
||||
$result = $strategy->mask(TestConstants::SSN_US, TestConstants::FIELD_USER_SSN, $this->logRecord);
|
||||
|
||||
$this->assertSame(MaskConstants::MASK_SSN_PATTERN, $result);
|
||||
}
|
||||
@@ -149,12 +149,12 @@ final class FieldPathMaskingStrategyTest extends TestCase
|
||||
public function maskAppliesStaticReplacementFromConfig(): void
|
||||
{
|
||||
$strategy = new FieldPathMaskingStrategy([
|
||||
TestConstants::FIELD_USER_NAME => FieldMaskConfig::replace('[REDACTED]'),
|
||||
TestConstants::FIELD_USER_NAME => FieldMaskConfig::replace(TestConstants::MASK_REDACTED_BRACKETS),
|
||||
]);
|
||||
|
||||
$result = $strategy->mask(TestConstants::NAME_FULL, TestConstants::FIELD_USER_NAME, $this->logRecord);
|
||||
|
||||
$this->assertSame('[REDACTED]', $result);
|
||||
$this->assertSame(TestConstants::MASK_REDACTED_BRACKETS, $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -238,7 +238,7 @@ final class FieldPathMaskingStrategyTest extends TestCase
|
||||
$strategy = new FieldPathMaskingStrategy([
|
||||
TestConstants::FIELD_USER_EMAIL => MaskConstants::MASK_EMAIL_PATTERN,
|
||||
TestConstants::FIELD_USER_PASSWORD => FieldMaskConfig::remove(),
|
||||
'user.ssn' => $ssnConfig,
|
||||
TestConstants::FIELD_USER_SSN => $ssnConfig,
|
||||
]);
|
||||
|
||||
$this->assertTrue($strategy->validate());
|
||||
@@ -328,7 +328,7 @@ final class FieldPathMaskingStrategyTest extends TestCase
|
||||
public function maskHandlesMultipleReplacementsInSameValue(): void
|
||||
{
|
||||
$strategy = new FieldPathMaskingStrategy([
|
||||
'message' => FieldMaskConfig::regexMask(TestConstants::PATTERN_SSN_FORMAT, MaskConstants::MASK_SSN_PATTERN),
|
||||
TestConstants::FIELD_MESSAGE => FieldMaskConfig::regexMask(TestConstants::PATTERN_SSN_FORMAT, MaskConstants::MASK_SSN_PATTERN),
|
||||
]);
|
||||
|
||||
$input = 'SSNs: 123-45-6789 and 987-65-4321';
|
||||
|
||||
@@ -127,11 +127,11 @@ class MaskingStrategiesTest extends TestCase
|
||||
$this->assertSame(80, $strategy->getPriority());
|
||||
|
||||
// Test shouldApply
|
||||
$this->assertTrue($strategy->shouldApply('john@example.com', TestConstants::FIELD_USER_EMAIL, $logRecord));
|
||||
$this->assertTrue($strategy->shouldApply(TestConstants::EMAIL_JOHN, TestConstants::FIELD_USER_EMAIL, $logRecord));
|
||||
$this->assertFalse($strategy->shouldApply('some value', 'other.field', $logRecord));
|
||||
|
||||
// Test static replacement
|
||||
$masked = $strategy->mask('john@example.com', TestConstants::FIELD_USER_EMAIL, $logRecord);
|
||||
$masked = $strategy->mask(TestConstants::EMAIL_JOHN, TestConstants::FIELD_USER_EMAIL, $logRecord);
|
||||
$this->assertEquals(MaskConstants::MASK_EMAIL, $masked);
|
||||
|
||||
// Test removal (returns null)
|
||||
@@ -336,7 +336,7 @@ class MaskingStrategiesTest extends TestCase
|
||||
#[\Override]
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Test Strategy';
|
||||
return TestConstants::STRATEGY_TEST;
|
||||
}
|
||||
|
||||
// Expose protected methods for testing
|
||||
|
||||
@@ -64,7 +64,7 @@ final class RegexMaskingStrategyTest extends TestCase
|
||||
$this->expectException(InvalidRegexPatternException::class);
|
||||
$this->expectExceptionMessage('catastrophic backtracking');
|
||||
|
||||
new RegexMaskingStrategy(['/^(a+)+$/' => MaskConstants::MASK_GENERIC]);
|
||||
new RegexMaskingStrategy([TestConstants::PATTERN_REDOS_VULNERABLE => MaskConstants::MASK_GENERIC]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -124,7 +124,7 @@ final class RegexMaskingStrategyTest extends TestCase
|
||||
public function maskHandlesArrayValues(): void
|
||||
{
|
||||
$strategy = new RegexMaskingStrategy([
|
||||
'/"email":"[^"]+"/' => '"email":"' . MaskConstants::MASK_EMAIL_PATTERN . '"',
|
||||
'/"' . TestConstants::CONTEXT_EMAIL . '":"[^"]+"/' => '"' . TestConstants::CONTEXT_EMAIL . '":"' . MaskConstants::MASK_EMAIL_PATTERN . '"',
|
||||
]);
|
||||
|
||||
$input = [TestConstants::CONTEXT_EMAIL => TestConstants::EMAIL_TEST];
|
||||
@@ -208,12 +208,12 @@ final class RegexMaskingStrategyTest extends TestCase
|
||||
{
|
||||
$strategy = new RegexMaskingStrategy(
|
||||
patterns: [TestConstants::PATTERN_DIGITS => MaskConstants::MASK_GENERIC],
|
||||
includePaths: ['user.ssn', 'user.phone']
|
||||
includePaths: [TestConstants::FIELD_USER_SSN, 'user.phone']
|
||||
);
|
||||
|
||||
$this->assertTrue($strategy->shouldApply(
|
||||
TestConstants::DATA_NUMBER_STRING,
|
||||
'user.ssn',
|
||||
TestConstants::FIELD_USER_SSN,
|
||||
$this->logRecord
|
||||
));
|
||||
$this->assertTrue($strategy->shouldApply(
|
||||
@@ -236,7 +236,7 @@ final class RegexMaskingStrategyTest extends TestCase
|
||||
includePaths: [TestConstants::PATH_USER_WILDCARD]
|
||||
);
|
||||
|
||||
$this->assertTrue($strategy->shouldApply(TestConstants::DATA_NUMBER_STRING, 'user.ssn', $this->logRecord));
|
||||
$this->assertTrue($strategy->shouldApply(TestConstants::DATA_NUMBER_STRING, TestConstants::FIELD_USER_SSN, $this->logRecord));
|
||||
$this->assertTrue($strategy->shouldApply(TestConstants::DATA_NUMBER_STRING, 'user.phone', $this->logRecord));
|
||||
$this->assertFalse($strategy->shouldApply(TestConstants::DATA_NUMBER_STRING, 'admin.id', $this->logRecord));
|
||||
}
|
||||
@@ -277,7 +277,7 @@ final class RegexMaskingStrategyTest extends TestCase
|
||||
{
|
||||
$strategy = new RegexMaskingStrategy([
|
||||
TestConstants::PATTERN_SSN_FORMAT => MaskConstants::MASK_SSN_PATTERN,
|
||||
'/[a-z]+/' => 'REDACTED',
|
||||
TestConstants::PATTERN_SAFE => TestConstants::MASK_REDACTED_PLAIN,
|
||||
]);
|
||||
|
||||
$this->assertTrue($strategy->validate());
|
||||
@@ -297,7 +297,7 @@ final class RegexMaskingStrategyTest extends TestCase
|
||||
public function getConfigurationReturnsFullConfiguration(): void
|
||||
{
|
||||
$patterns = [TestConstants::PATTERN_DIGITS => MaskConstants::MASK_GENERIC];
|
||||
$includePaths = ['user.ssn'];
|
||||
$includePaths = [TestConstants::FIELD_USER_SSN];
|
||||
$excludePaths = ['debug.*'];
|
||||
|
||||
$strategy = new RegexMaskingStrategy(
|
||||
@@ -334,7 +334,7 @@ final class RegexMaskingStrategyTest extends TestCase
|
||||
'/REPLACED/' => 'FINAL',
|
||||
]);
|
||||
|
||||
$result = $strategy->mask('test value', 'field', $this->logRecord);
|
||||
$result = $strategy->mask(TestConstants::VALUE_TEST, 'field', $this->logRecord);
|
||||
|
||||
$this->assertSame('FINAL value', $result);
|
||||
}
|
||||
@@ -367,13 +367,13 @@ final class RegexMaskingStrategyTest extends TestCase
|
||||
public function maskHandlesMultilinePatterns(): void
|
||||
{
|
||||
$strategy = new RegexMaskingStrategy([
|
||||
'/^line\d+$/m' => 'REDACTED',
|
||||
'/^line\d+$/m' => TestConstants::MASK_REDACTED_PLAIN,
|
||||
]);
|
||||
|
||||
$input = "line1\nother\nline2";
|
||||
$result = $strategy->mask($input, 'field', $this->logRecord);
|
||||
|
||||
$this->assertStringContainsString('REDACTED', $result);
|
||||
$this->assertStringContainsString(TestConstants::MASK_REDACTED_PLAIN, $result);
|
||||
$this->assertStringContainsString('other', $result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Tests\TestConstants;
|
||||
|
||||
/**
|
||||
* Edge case tests for masking strategies to improve coverage.
|
||||
@@ -35,7 +36,7 @@ final class StrategyEdgeCasesTest extends TestCase
|
||||
datetime: new DateTimeImmutable(),
|
||||
channel: 'test',
|
||||
level: Level::Info,
|
||||
message: 'Test message',
|
||||
message: TestConstants::MESSAGE_DEFAULT,
|
||||
context: [],
|
||||
);
|
||||
}
|
||||
@@ -61,8 +62,8 @@ final class StrategyEdgeCasesTest extends TestCase
|
||||
public static function redosPatternProvider(): array
|
||||
{
|
||||
return [
|
||||
'nested plus quantifier' => ['/^(a+)+$/'],
|
||||
'nested star quantifier' => ['/^(a*)*$/'],
|
||||
'nested plus quantifier' => [TestConstants::PATTERN_REDOS_VULNERABLE],
|
||||
'nested star quantifier' => [TestConstants::PATTERN_REDOS_NESTED_STAR],
|
||||
'plus with repetition' => ['/^(a+){1,10}$/'],
|
||||
'star with repetition' => ['/^(a*){1,10}$/'],
|
||||
'identical alternation with star' => ['/(.*|.*)x/'],
|
||||
@@ -76,9 +77,9 @@ final class StrategyEdgeCasesTest extends TestCase
|
||||
public function regexStrategySafePatternsPasses(): void
|
||||
{
|
||||
$strategy = new RegexMaskingStrategy([
|
||||
'/\d{3}-\d{2}-\d{4}/' => '[SSN]',
|
||||
'/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/' => '[EMAIL]',
|
||||
'/\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/' => '[CARD]',
|
||||
TestConstants::PATTERN_SSN_FORMAT => TestConstants::MASK_SSN_BRACKETS,
|
||||
TestConstants::PATTERN_EMAIL_SIMPLE => TestConstants::MASK_EMAIL_BRACKETS,
|
||||
TestConstants::PATTERN_CREDIT_CARD => TestConstants::MASK_CARD_BRACKETS,
|
||||
]);
|
||||
|
||||
$this->assertInstanceOf(RegexMaskingStrategy::class, $strategy);
|
||||
@@ -153,7 +154,7 @@ final class StrategyEdgeCasesTest extends TestCase
|
||||
$result = $strategy->mask($obj, 'field', $this->logRecord);
|
||||
|
||||
$this->assertIsObject($result);
|
||||
$this->assertEquals((object) ['masked' => '{invalid json'], $result);
|
||||
$this->assertEquals((object) [TestConstants::DATA_MASKED => '{invalid json'], $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -165,7 +166,7 @@ final class StrategyEdgeCasesTest extends TestCase
|
||||
$result = $strategy->mask($obj, 'field', $this->logRecord);
|
||||
|
||||
$this->assertIsObject($result);
|
||||
$this->assertEquals((object) ['masked' => '["array"]'], $result);
|
||||
$this->assertEquals((object) [TestConstants::DATA_MASKED => '["array"]'], $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -256,7 +257,7 @@ final class StrategyEdgeCasesTest extends TestCase
|
||||
public function fieldPathStrategyValidateWithValidStringConfig(): void
|
||||
{
|
||||
$strategy = new FieldPathMaskingStrategy([
|
||||
'user.email' => MaskConstants::MASK_EMAIL_PATTERN,
|
||||
TestConstants::FIELD_USER_EMAIL => MaskConstants::MASK_EMAIL_PATTERN,
|
||||
]);
|
||||
|
||||
$this->assertTrue($strategy->validate());
|
||||
@@ -266,8 +267,8 @@ final class StrategyEdgeCasesTest extends TestCase
|
||||
public function fieldPathStrategyValidateWithFieldMaskConfig(): void
|
||||
{
|
||||
$strategy = new FieldPathMaskingStrategy([
|
||||
'user.email' => FieldMaskConfig::replace(MaskConstants::MASK_EMAIL_PATTERN),
|
||||
'user.ssn' => FieldMaskConfig::remove(),
|
||||
TestConstants::FIELD_USER_EMAIL => FieldMaskConfig::replace(MaskConstants::MASK_EMAIL_PATTERN),
|
||||
TestConstants::FIELD_USER_SSN => FieldMaskConfig::remove(),
|
||||
]);
|
||||
|
||||
$this->assertTrue($strategy->validate());
|
||||
@@ -277,7 +278,7 @@ final class StrategyEdgeCasesTest extends TestCase
|
||||
public function fieldPathStrategyValidateWithValidRegexConfig(): void
|
||||
{
|
||||
$strategy = new FieldPathMaskingStrategy([
|
||||
'user.data' => FieldMaskConfig::regexMask('/\d+/', '[MASKED]'),
|
||||
TestConstants::FIELD_USER_DATA => FieldMaskConfig::regexMask(TestConstants::PATTERN_DIGITS, MaskConstants::MASK_BRACKETS),
|
||||
]);
|
||||
|
||||
$this->assertTrue($strategy->validate());
|
||||
@@ -338,7 +339,7 @@ final class StrategyEdgeCasesTest extends TestCase
|
||||
public function fieldPathStrategyShouldApplyReturnsFalseForMissingPath(): void
|
||||
{
|
||||
$strategy = new FieldPathMaskingStrategy([
|
||||
'user.email' => MaskConstants::MASK_EMAIL_PATTERN,
|
||||
TestConstants::FIELD_USER_EMAIL => MaskConstants::MASK_EMAIL_PATTERN,
|
||||
]);
|
||||
|
||||
$this->assertFalse($strategy->shouldApply('value', 'other.path', $this->logRecord));
|
||||
@@ -348,11 +349,11 @@ final class StrategyEdgeCasesTest extends TestCase
|
||||
public function fieldPathStrategyShouldApplyReturnsTrueForWildcardMatch(): void
|
||||
{
|
||||
$strategy = new FieldPathMaskingStrategy([
|
||||
'user.*' => MaskConstants::MASK_GENERIC,
|
||||
TestConstants::PATH_USER_WILDCARD => MaskConstants::MASK_GENERIC,
|
||||
]);
|
||||
|
||||
$this->assertTrue($strategy->shouldApply('value', 'user.email', $this->logRecord));
|
||||
$this->assertTrue($strategy->shouldApply('value', 'user.name', $this->logRecord));
|
||||
$this->assertTrue($strategy->shouldApply('value', TestConstants::FIELD_USER_EMAIL, $this->logRecord));
|
||||
$this->assertTrue($strategy->shouldApply('value', TestConstants::FIELD_USER_NAME, $this->logRecord));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
@@ -371,19 +372,19 @@ final class StrategyEdgeCasesTest extends TestCase
|
||||
public function fieldPathStrategyMaskAppliesRegexConfig(): void
|
||||
{
|
||||
$strategy = new FieldPathMaskingStrategy([
|
||||
'user.ssn' => FieldMaskConfig::regexMask('/\d{3}-\d{2}-\d{4}/', '[SSN]'),
|
||||
TestConstants::FIELD_USER_SSN => FieldMaskConfig::regexMask(TestConstants::PATTERN_SSN_FORMAT, TestConstants::MASK_SSN_BRACKETS),
|
||||
]);
|
||||
|
||||
$result = $strategy->mask('SSN: 123-45-6789', 'user.ssn', $this->logRecord);
|
||||
$result = $strategy->mask('SSN: 123-45-6789', TestConstants::FIELD_USER_SSN, $this->logRecord);
|
||||
|
||||
$this->assertSame('SSN: [SSN]', $result);
|
||||
$this->assertSame(TestConstants::EXPECTED_SSN_MASKED, $result);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function fieldPathStrategyMaskHandlesArrayValue(): void
|
||||
{
|
||||
$strategy = new FieldPathMaskingStrategy([
|
||||
'data' => FieldMaskConfig::regexMask('/\d+/', '[NUM]'),
|
||||
'data' => FieldMaskConfig::regexMask(TestConstants::PATTERN_DIGITS, '[NUM]'),
|
||||
]);
|
||||
|
||||
$result = $strategy->mask(['count' => '123 items'], 'data', $this->logRecord);
|
||||
@@ -396,7 +397,7 @@ final class StrategyEdgeCasesTest extends TestCase
|
||||
public function fieldPathStrategyMaskReturnsValueWhenNoConfigMatch(): void
|
||||
{
|
||||
$strategy = new FieldPathMaskingStrategy([
|
||||
'user.email' => MaskConstants::MASK_EMAIL_PATTERN,
|
||||
TestConstants::FIELD_USER_EMAIL => MaskConstants::MASK_EMAIL_PATTERN,
|
||||
]);
|
||||
|
||||
$result = $strategy->mask('original', 'other.field', $this->logRecord);
|
||||
@@ -408,7 +409,7 @@ final class StrategyEdgeCasesTest extends TestCase
|
||||
public function fieldPathStrategyGetNameReturnsCorrectFormat(): void
|
||||
{
|
||||
$strategy = new FieldPathMaskingStrategy([
|
||||
'user.email' => MaskConstants::MASK_EMAIL_PATTERN,
|
||||
TestConstants::FIELD_USER_EMAIL => MaskConstants::MASK_EMAIL_PATTERN,
|
||||
'user.phone' => MaskConstants::MASK_PHONE,
|
||||
]);
|
||||
|
||||
@@ -421,7 +422,7 @@ final class StrategyEdgeCasesTest extends TestCase
|
||||
public function fieldPathStrategyGetConfigurationReturnsAllSettings(): void
|
||||
{
|
||||
$config = [
|
||||
'user.email' => FieldMaskConfig::replace('[EMAIL]'),
|
||||
TestConstants::FIELD_USER_EMAIL => FieldMaskConfig::replace(TestConstants::MASK_EMAIL_BRACKETS),
|
||||
];
|
||||
$strategy = new FieldPathMaskingStrategy($config);
|
||||
|
||||
|
||||
@@ -122,9 +122,9 @@ final class StrategyManagerComprehensiveTest extends TestCase
|
||||
$manager = new StrategyManager();
|
||||
$record = $this->createLogRecord('Test');
|
||||
|
||||
$result = $manager->maskValue('test value', 'field', $record);
|
||||
$result = $manager->maskValue(TestConstants::VALUE_TEST, 'field', $record);
|
||||
|
||||
$this->assertSame('test value', $result);
|
||||
$this->assertSame(TestConstants::VALUE_TEST, $result);
|
||||
}
|
||||
|
||||
public function testMaskValueAppliesFirstApplicableStrategy(): void
|
||||
@@ -137,7 +137,7 @@ final class StrategyManagerComprehensiveTest extends TestCase
|
||||
$manager = new StrategyManager([$lowPrio, $highPrio]);
|
||||
$record = $this->createLogRecord('Test');
|
||||
|
||||
$result = $manager->maskValue('secret data', 'field', $record);
|
||||
$result = $manager->maskValue(TestConstants::MESSAGE_SECRET_DATA, 'field', $record);
|
||||
|
||||
// High priority strategy should be applied
|
||||
$this->assertStringContainsString('HIGH', $result);
|
||||
@@ -206,7 +206,7 @@ final class StrategyManagerComprehensiveTest extends TestCase
|
||||
$manager = new StrategyManager([$strategy]);
|
||||
$record = $this->createLogRecord('Test');
|
||||
|
||||
$result = $manager->hasApplicableStrategy('secret data', 'field', $record);
|
||||
$result = $manager->hasApplicableStrategy(TestConstants::MESSAGE_SECRET_DATA, 'field', $record);
|
||||
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user