Skip to content

Commit

Permalink
[Feature] Add custom replacement value on a per-class basis (#42)
Browse files Browse the repository at this point in the history
* Add custom replacement value on a per-class basis

The updates in this commit allow for the return of a customizable, per-class, replacement value instead of always defaulting to the configured redaction setting.

- A `return ''` option has been placed in the stubs alongside example values.
- The .gitignore file has also been updated to ignore .idea files.
- Previous tests have been modified to incorporate the new feature
- The README and comments in the stubs have been updated to reflect the new feature
  • Loading branch information
AlexGodbehere authored Oct 3, 2023
1 parent cd084cf commit 0531f41
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 16 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.phpunit.result.cache
/coverage
/vendor
.idea
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ class.

### Opting Into Custom Extended Classes

> To create custom scrubbers, see the [Extending the Scrubber](#extending-the-scrubber) section.
The `regex_loader` array takes strings, not objects. To opt in to specific custom extended regex classes, define the
class name as a string.

Expand All @@ -167,7 +169,6 @@ class TestRegex implements RegexCollectionInterface
public function getPattern(): string
{
/**
* @todo
* @note return a regex pattern to detect a specific piece of sensitive data.
*/
return '(?<=basic) [a-zA-Z0-9=:\\+\/-]{5,100}';
Expand All @@ -176,11 +177,19 @@ class TestRegex implements RegexCollectionInterface
public function getTestableString(): string
{
/**
* @todo
* @note return a string that can be used to verify the regex pattern provided.
*/
return 'basic f9Iu+YwMiJEsQu/vBHlbUNZRkN/ihdB1sNTU';
}

public function getReplacementValue(): string
{

/**
* @note return a string that replaces the regex pattern provided.
*/
return config('scrubber.redaction');
}

public function isSecret(): bool
{
Expand Down Expand Up @@ -230,7 +239,7 @@ php artisan make:regex-class {name}

This command will create a stubbed out class in `App\Scrubber\RegexCollection`. The Scrubber package will autoload
everything from the `App\Scrubber\RegexCollection` folder with the wildcard value on the `regex_loader` array in the
scrubber config file. You will need to provide a `Regex Pattern` and a `Testable String` for the class.
scrubber config file. You will need to provide a `Regex Pattern` and a `Testable String` for the class and you may also provide a `Replacement Value` if you want to replace the detected value with something other than the default value in the config file.

## Testing

Expand All @@ -244,5 +253,6 @@ composer test
- [Whizboy-Arnold](https://github.com/Whizboy-Arnold)
- [majchrosoft](https://github.com/majchrosoft)
- [Lucaxue](https://github.com/lucaxue)
- [AlexGodbehere](https://github.com/AlexGodbehere)
- [All Contributors](../../contributors)

21 changes: 21 additions & 0 deletions src/Commands/Stubs/RegexCollectionClass.stub
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ class DummyClass implements RegexCollectionInterface
* @todo
* @note return a regex pattern to detect a specific piece of sensitive data.
*/

// e.g. 'sensitive_data=[a-zA-Z0-9]{5,100}'
return '';
}

public function getTestableString(): string
Expand All @@ -20,6 +23,24 @@ class DummyClass implements RegexCollectionInterface
* @todo
* @note return a string that can be used to verify the regex pattern provided.
*/

// e.g. 'sensitive_data=adfa734jwfsdkf234'
return '';
}

/**
* Optional. Remove this function to use the default replacement pattern.
*
* @return string
*/
public function getReplacementValue(): string {
/**
* @todo
* @note return a string that replaces the regex pattern provided.
*/

// e.g. '**redacted_key**=**redacted_data**'
return config('scrubber.redaction');
}

public function isSecret(): bool
Expand Down
4 changes: 2 additions & 2 deletions src/Repositories/RegexRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ public function __construct(
) {
}

public static function checkAndSanitize(string $regex, string $content, int &$hits = 0): string
public static function checkAndSanitize(string $regex, string $replace, string $content, int &$hits = 0): string
{
return preg_replace("~$regex~i", config('scrubber.redaction'), $content, -1, $hits);
return preg_replace("~$regex~i", $replace, $content, -1, $hits);
}

public static function check(string $regex, string $content): int
Expand Down
14 changes: 11 additions & 3 deletions src/Services/ScrubberService.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,22 @@ public static function autoSanitize(string &$jsonContent): void
? Secret::decrypt($regexClass->getPattern())
: $regexClass->getPattern();

self::patternChecker($pattern, $jsonContent);
if (method_exists($regexClass, 'getReplacementValue')) {
// Check if getReplacementValue() exists on the regex class and if it does, use it.
$replace = $regexClass->getReplacementValue();
} else {
// Otherwise, use the default replacement pattern.
$replace = config('scrubber.redaction');
}

self::patternChecker($pattern, $jsonContent, $replace);
});
}

protected static function patternChecker(string $regexPattern, string &$jsonContent): void
protected static function patternChecker(string $regexPattern, string &$jsonContent, string $replace): void
{
$hits = 0;
$jsonContent = RegexRepository::checkAndSanitize($regexPattern, $jsonContent, $hits);
$jsonContent = RegexRepository::checkAndSanitize($regexPattern, $replace, $jsonContent, $hits);

/**
* @todo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ trait ProcessArrayTrait
public function processArrayRecursively(array $content): array
{
foreach ($content as $key => $value) {
if (null !== $value) {
if ($value !== null) {
if (is_array($value)) {
$content[$key] = $this->processArray($value);
} elseif (is_object($value) && ! method_exists($value, '__toString')) {
Expand All @@ -30,7 +30,7 @@ public function processArrayRecursively(array $content): array
public function processArray(array $content): array
{
$jsonContent = ScrubberService::encodeRecord($content);
if ('' === $jsonContent) {
if ($jsonContent === '') {
// failed to convert array to JSON, so process array recursively
return $this->processArrayRecursively($content);
}
Expand Down
3 changes: 2 additions & 1 deletion tests/Unit/Repositories/RegexRepositoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function it_can_verify_that_all_regex_patterns_have_testable_counter_part

$this->assertStringContainsString(
config('scrubber.redaction'),
app(RegexRepository::class)->checkAndSanitize($regexClass->getPattern(), $regexClass->getTestableString(), $hits)
app(RegexRepository::class)->checkAndSanitize($regexClass->getPattern(), config('scrubber.redaction'), $regexClass->getTestableString(), $hits)
);

$this->assertEquals(1, $hits);
Expand All @@ -46,6 +46,7 @@ public function it_can_sanitize_a_string_with_multiple_sensitive_pieces()
config('scrubber.redaction'),
app(RegexRepository::class)->checkAndSanitize(
app(RegexRepository::class)->getRegexCollection()->get('google_api')->getPattern(),
config('scrubber.redaction'),
$content,
$hits
)
Expand Down
70 changes: 69 additions & 1 deletion tests/Unit/Services/ScrubberServiceTest.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?php

namespace YorCreative\Scrubber\Test\Unit\Services;
namespace YorCreative\Scrubber\Tests\Unit\Services;

use GuzzleHttp\Psr7\Response;
use YorCreative\Scrubber\Interfaces\RegexCollectionInterface;
use YorCreative\Scrubber\Repositories\RegexRepository;
use YorCreative\Scrubber\Services\ScrubberService;
use YorCreative\Scrubber\Tests\TestCase;
Expand Down Expand Up @@ -65,4 +66,71 @@ public function test_it_can_get_regex_repository()
{
$this->assertInstanceOf(RegexRepository::class, ScrubberService::getRegexRepository());
}

public function test_it_can_handle_get_replacement_value_on_custom_class()
{
$withReplacement = new class() implements RegexCollectionInterface
{
public function isSecret(): bool
{
return false;
}

public function getPattern(): string
{
return 'something_with';
}

public function getTestableString(): string
{
return 'something_with';
}

public function getReplacementValue(): string
{
return 'not_something';
}
};

$withoutReplacement = new class() implements RegexCollectionInterface
{
public function isSecret(): bool
{
return false;
}

public function getPattern(): string
{
return 'without_something';
}

public function getTestableString(): string
{
return 'without_something';
}
};

$regexCollection = collect([
'with_replacement' => $withReplacement,
'without_replacement' => $withoutReplacement,
]);

$regexRepository = new RegexRepository($regexCollection);
$this->app->instance(RegexRepository::class, $regexRepository);

$content = 'something_with';
ScrubberService::autoSanitize($content);
$this->assertEquals($withReplacement->getReplacementValue(), $content);

$content = 'without_something';
ScrubberService::autoSanitize($content);

$defaultReplacementValue = config('scrubber.redaction');
$this->assertEquals($defaultReplacementValue, $content);

$this->assertNotEquals(
$withReplacement->getReplacementValue(),
$defaultReplacementValue
);
}
}
2 changes: 1 addition & 1 deletion tests/Unit/Services/SecretServiceTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace YorCreative\Scrubber\Test\Unit\Services;
namespace YorCreative\Scrubber\Tests\Unit\Services;

use YorCreative\Scrubber\SecretManager\Providers\Gitlab;
use YorCreative\Scrubber\SecretManager\Secret;
Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/Strategies/ContentProcessingStrategyTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace YorCreative\Scrubber\Test\Unit\Strategies;
namespace YorCreative\Scrubber\Tests\Unit\Strategies;

use Carbon\Carbon;
use YorCreative\Scrubber\Strategies\ContentProcessingStrategy\ContentProcessingStrategy;
Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/Strategies/RegexLoaderStrategyTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace YorCreative\Scrubber\Test\Unit\Strategies;
namespace YorCreative\Scrubber\Tests\Unit\Strategies;

use Illuminate\Support\Facades\Config;
use YorCreative\Scrubber\Repositories\RegexCollection;
Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/Strategies/TapLoaderStrategyTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace YorCreative\Scrubber\Test\Unit\Strategies;
namespace YorCreative\Scrubber\Tests\Unit\Strategies;

use YorCreative\Scrubber\Strategies\TapLoader\TapLoaderStrategy;
use YorCreative\Scrubber\Tests\TestCase;
Expand Down

0 comments on commit 0531f41

Please sign in to comment.