Skip to content
This repository has been archived by the owner on Apr 25, 2024. It is now read-only.

Commit

Permalink
Tests updated to work with hashing
Browse files Browse the repository at this point in the history
  • Loading branch information
Sjustein committed Feb 3, 2024
1 parent 358e1da commit 2416991
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 139 deletions.
17 changes: 11 additions & 6 deletions src/HashSensitiveProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ private function hash(string $value): ?string

// Cut the input to the length limit specified
$cutInput = substr($value, 0, $this->lengthLimit);

if (strlen($cutInput) === 0) {
return null;
}

return hash($this->algorithm, $cutInput);
}

Expand Down Expand Up @@ -106,18 +111,18 @@ public function traverseInputArray(array $inputArray, array $sensitiveKeys): arr

// If the value is not an array or an object, hash it if it is a sensitive key
if (is_scalar($value)) {
if (in_array($key, $sensitiveKeys)) {
if (in_array($key, $sensitiveKeys) || array_key_exists($key, $sensitiveKeys)) {
$inputArray[$key] = $this->hash((string) $value);
}

continue;
}

// The value is either an array or an object, let traverse handle the specifics
if (array_key_exists($key, $sensitiveKeys)) {
if (in_array($key, $sensitiveKeys) || array_key_exists($key, $sensitiveKeys)) {
$inputArray[$key] = $this->traverse($key, $value, $sensitiveKeys[$key]);

// ExclusiveSubtree turned off means that subkeys should be checked according to ALL keys, not just
// ExclusiveSubtree turned off means that sub keys should be checked according to ALL keys, not just
// the keys in their sensitive keys subtree
if (!$this->exclusiveSubtree) {
$inputArray[$key] = $this->traverse($key, $inputArray[$key], $sensitiveKeys);
Expand All @@ -143,19 +148,19 @@ private function traverseObject(object $object, array $sensitiveKeys): object
foreach (get_object_vars($object) as $key => $value) {
// If the value is not an array or an object, hash it if it is a sensitive key
if (is_scalar($value)) {
if (array_key_exists($key, $sensitiveKeys)) {
if (in_array($key, $sensitiveKeys) || array_key_exists($key, $sensitiveKeys)) {
$object->{$key} = $this->hash((string) $value);
}

continue;
}

// The value is either an array or an object, let traverse handle the specifics
if (array_key_exists($key, $sensitiveKeys)) {
if (in_array($key, $sensitiveKeys) || array_key_exists($key, $sensitiveKeys)) {
$object->{$key} = $this->traverse($key, $value, $sensitiveKeys[$key]);

if (!$this->exclusiveSubtree) {
$object->{$key} = $this->traverse($key, $object->{key}, $sensitiveKeys);
$object->{$key} = $this->traverse($key, $object->{$key}, $sensitiveKeys);
}
} else {
$object->{$key} = $this->traverse($key, $value, $sensitiveKeys);
Expand Down
259 changes: 126 additions & 133 deletions tests/HashSensitiveTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,137 +5,130 @@
namespace HashSensitiveTests;

use HashSensitive\HashSensitiveProcessor;
use TypeError;

//it('redacts records contexts', function (): void {
// $sensitive_keys = ['test' => 3];
// $processor = new HashSensitiveProcessor($sensitive_keys);
//
// $record = $this->getRecord(context: ['test' => 'foobar']);
// expect($processor($record)->context)->toBe(['test' => 'foo***']);
//});
//
//it('redacts using template', function (): void {
// $sensitive_keys = ['test' => 2];
// $processor = new HashSensitiveProcessor($sensitive_keys, template: '%s(redacted)');
//
// $record = $this->getRecord(context: ['test' => 'foobar']);
// expect($processor($record)->context)->toBe(['test' => 'fo****(redacted)']);
//});
//
//it('redacts discarding masked', function (): void {
// $sensitive_keys = ['test' => 1];
// $processor = new HashSensitiveProcessor($sensitive_keys, template: '...');
//
// $record = $this->getRecord(context: ['test' => 'foobar123']);
// expect($processor($record)->context)->toBe(['test' => 'f...']);
//});
//
//it('truncates masked characters', function (): void {
// $sensitive_keys = ['test' => 3];
// $processor = new HashSensitiveProcessor($sensitive_keys, lengthLimit: 5);
//
// $record = $this->getRecord(context: ['test' => 'foobar']);
// expect($processor($record)->context)->toBe(['test' => 'foo**']);
//});
//
//it('truncates visible characters', function (): void {
// $sensitive_keys = ['test' => 3];
// $processor = new HashSensitiveProcessor($sensitive_keys, lengthLimit: 2);
//
// $record = $this->getRecord(context: ['test' => 'foobar']);
// expect($processor($record)->context)->toBe(['test' => 'fo']);
//});
//
//it('overrides default replacement', function (): void {
// $sensitive_keys = ['test' => 3];
// $processor = new HashSensitiveProcessor($sensitive_keys, '_');
//
// $record = $this->getRecord(context: ['test' => 'foobar']);
// expect($processor($record)->context)->toBe(['test' => 'foo___']);
//});
//
//it('redacts from right to left', function (): void {
// $sensitive_keys = ['test' => -3];
// $processor = new HashSensitiveProcessor($sensitive_keys);
//
// $record = $this->getRecord(context: ['test' => 'foobar']);
// expect($processor($record)->context)->toBe(['test' => '***bar']);
//});
//
//it('truncates masked from right to left', function (): void {
// $sensitive_keys = ['test' => -3];
// $processor = new HashSensitiveProcessor($sensitive_keys, lengthLimit: 4);
//
// $record = $this->getRecord(context: ['test' => 'foobar']);
// expect($processor($record)->context)->toBe(['test' => '*bar']);
//});
//
//it('truncates visible from right to left', function (): void {
// $sensitive_keys = ['test' => -3];
// $processor = new HashSensitiveProcessor($sensitive_keys, lengthLimit: 2);
//
// $record = $this->getRecord(context: ['test' => 'foobar']);
// expect($processor($record)->context)->toBe(['test' => 'ar']);
//});
//
//it('redacts nested arrays', function (): void {
// $sensitive_keys = ['test' => ['nested' => 3]];
// $processor = new HashSensitiveProcessor($sensitive_keys);
//
// $record = $this->getRecord(context: ['test' => ['nested' => 'foobar']]);
// expect($processor($record)->context)->toBe(['test' => ['nested' => 'foo***']]);
//});
//
//it('redacts inside nested arrays', function (): void {
// $sensitive_keys = ['nested' => 3];
// $processor = new HashSensitiveProcessor($sensitive_keys);
//
// $record = $this->getRecord(context: ['test' => ['nested' => 'foobar']]);
// expect($processor($record)->context)->toBe(['test' => ['nested' => 'foo***']]);
//});
//
//it('redacts nested objects', function (): void {
// $nested = new \stdClass();
// $nested->value = 'foobar';
// $nested->nested = ['value' => 'bazqux'];
//
// $sensitive_keys = ['test' => ['nested' => ['value' => 3, 'nested' => ['value' => -3]]]];
// $processor = new HashSensitiveProcessor($sensitive_keys);
//
// $record = $this->getRecord(context: ['test' => ['nested' => $nested]]);
//
// expect($processor($record)->context)->toBe(['test' => ['nested' => $nested]]);
// expect($nested->value)->toBe('foo***');
// expect($nested->nested['value'])->toBe('***qux');
//});
//
//it('redacts inside nested objects', function (): void {
// $nested = new \stdClass();
// $nested->value = 'foobar';
// $nested->nested = ['value' => 'bazqux'];
//
// $sensitive_keys = ['nested' => ['value' => -3]];
// $processor = new HashSensitiveProcessor($sensitive_keys);
//
// $record = $this->getRecord(context: ['test' => ['nested' => $nested]]);
//
// expect($processor($record)->context)->toBe(['test' => ['nested' => $nested]]);
// expect($nested->value)->toBe('***bar');
// expect($nested->nested['value'])->toBe('***qux');
//});
//
//it('preserves empty values', function (): void {
// $sensitive_keys = ['test' => 3, 'optionalKey' => 10];
// $processor = new HashSensitiveProcessor($sensitive_keys);
//
// $record = $this->getRecord(context: ['test' => 'foobar', 'optionalKey' => '']);
// expect($processor($record)->context)->toBe(['test' => 'foo***', 'optionalKey' => '']);
//});
//
//it('throws when finds an un-traversable value', function (): void {
// $sensitive_keys = ['test' => 3];
// $processor = new HashSensitiveProcessor($sensitive_keys);
//
// $record = $this->getRecord(context: ['test' => fopen(__FILE__, 'rb')]);
// $processor($record);
//})->throws(\UnexpectedValueException::class, 'Don\'t know how to traverse value at key test');
it('redacts records contexts', function (): void {
$sensitive_keys = ['test'];
$processor = new HashSensitiveProcessor($sensitive_keys);

$record = $this->getRecord(context: ['test' => 'foobar']);
expect($processor($record)->context)->toBe(['test' => 'c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2']);
});

it('truncates masked characters', function (): void {
$sensitive_keys = ['test'];
$processor = new HashSensitiveProcessor($sensitive_keys, lengthLimit: 5);

$record = $this->getRecord(context: ['test' => 'foobar']);
// Only `fooba` should be hashed, the first 5 characters of `foobar`
expect($processor($record)->context)->toBe(['test' => '41cbe1a87981490351ccad5346d96da0ac10678670b31fc0ab209aed1b5bc515']);
});

it('doesn\'t truncate more than the string length', function (): void {
$sensitive_keys = ['test'];
$processor = new HashSensitiveProcessor($sensitive_keys, lengthLimit: 10);

$record = $this->getRecord(context: ['test' => 'foobar']);
expect($processor($record)->context)->toBe(['test' => 'c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2']);
});

it('doesn\'t truncate when length limit is 0', function (): void {
$sensitive_keys = ['test'];
$processor = new HashSensitiveProcessor($sensitive_keys, lengthLimit: 0);

$record = $this->getRecord(context: ['test' => 'foobar']);
expect($processor($record)->context)->toBe(['test' => null]);
});

it('doesn\'t truncate when length limit is not set', function (): void {
$sensitive_keys = ['test'];
$processor = new HashSensitiveProcessor($sensitive_keys);

$record = $this->getRecord(context: ['test' => 'foobar']);
expect($processor($record)->context)->toBe(['test' => 'c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2']);
});


it('redacts nested arrays', function (): void {
$sensitive_keys = ['test' => ['nested']];
$processor = new HashSensitiveProcessor($sensitive_keys);

$record = $this->getRecord(context: ['test' => ['nested' => 'foobar']]);
expect($processor($record)->context)->toBe(['test' => ['nested' => 'c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2']]);
});

it('keeps non redacted nested arrays intact', function (): void {
$sensitive_keys = ['test' => ['nested']];
$processor = new HashSensitiveProcessor($sensitive_keys);

$record = $this->getRecord(context: ['test' => ['nested' => 'foobar', 'no_hash' => 'foobar']]);
expect($processor($record)->context)->toBe(['test' => ['nested' => 'c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2', 'no_hash' => 'foobar']]);
});

it('redacts inside nested arrays', function (): void {
$sensitive_keys = ['nested'];
$processor = new HashSensitiveProcessor($sensitive_keys);

$record = $this->getRecord(context: ['test' => ['nested' => 'foobar']]);
expect($processor($record)->context)->toBe(['test' => ['nested' => 'c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2']]);
});

it('redacts nested objects', function (): void {
$nested = new \stdClass();
$nested->value = 'foobar';
$nested->nested = ['value' => 'bazqux'];

$sensitive_keys = ['test' => ['nested' => ['value', 'nested' => ['value']]]];
$processor = new HashSensitiveProcessor($sensitive_keys);

$record = $this->getRecord(context: ['test' => ['nested' => $nested]]);

expect($processor($record)->context)->toBe(['test' => ['nested' => $nested]])
->and($nested->value)->toBe('c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2')
->and($nested->nested['value'])->toBe('972c5e1203896784a7cf9dd60acd443a1065e19ad5f92e59a9180c185f065c04');
});

it('keeps non redacted nested objects intact', function (): void {
$nested = new \stdClass();
$nested->value = 'foobar';
$nested->nested = ['value' => 'bazqux', 'no_hash' => 'foobar'];

$sensitive_keys = ['test' => ['nested' => ['value', 'nested' => ['value']]]];
$processor = new HashSensitiveProcessor($sensitive_keys);

$record = $this->getRecord(context: ['test' => ['nested' => $nested]]);

expect($processor($record)->context)->toBe(['test' => ['nested' => $nested]])
->and($nested->nested['no_hash'])->toBe('foobar');
});

it('redacts inside nested objects', function (): void {
$nested = new \stdClass();
$nested->value = 'foobar';
$nested->nested = ['value' => 'bazqux'];

$sensitive_keys = ['nested' => ['value']];
$processor = new HashSensitiveProcessor($sensitive_keys);

$record = $this->getRecord(context: ['test' => ['nested' => $nested]]);

expect($processor($record)->context)->toBe(['test' => ['nested' => $nested]])
->and($nested->value)->toBe('c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2')
->and($nested->nested['value'])->toBe('972c5e1203896784a7cf9dd60acd443a1065e19ad5f92e59a9180c185f065c04');
});

it('preserves empty values', function (): void {
$sensitive_keys = ['test', 'optionalKey'];
$processor = new HashSensitiveProcessor($sensitive_keys);

$record = $this->getRecord(context: ['test' => 'foobar', 'optionalKey' => '']);
expect($processor($record)->context)->toBe(['test' => 'c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2', 'optionalKey' => null]);
});

it('throws when finds an un-traversable value', function (): void {
$sensitive_keys = ['test'];
$processor = new HashSensitiveProcessor($sensitive_keys);

$record = $this->getRecord(context: ['test' => fopen(__FILE__, 'rb')]);
$processor($record);
})->throws(TypeError::class, 'Argument #2 ($value) must be of type object|array, resource given');

0 comments on commit 2416991

Please sign in to comment.