Sanitation of Vulnerable Data
This example shows how to setup a processor that will remove the password and secrets from a non-versioned .env file on pull.
- Define a file group
install, which includes a file called .env. - Next, map the file group to your local, e.g.,
environments.0.files.install - (You will need to also map it to the remote, but that's covered elsewhere.)
- Define a workflow:
development - Add to that workflow a processor item pointing to a class::method, in this case
RemoveSecrets::process - Configured the environment to use the
developmentworkflow by default onpull - Create the processor class::method as _./live_devporter/processors/RemoveSecrets.php. Notice the trait and the parent class and study those for more info.
Configuration
This is not a complete configuration, for example the remove environment is missing; just the items needed to illustrate this concept are shown.
_.live_devporter/config.yml
file_groups:
install:
include:
- /.env
workflows:
development:
-
processor: RemoveSecrets::process
environments:
local:
files:
install: install/default/scaffold
command_workflows:
pull: development
The .env file
FOO=BAR
HASH_SALT='x4XchzwQ`zk55n24r$\ZQ1P.qkcqZcGEiW7J1-K0jcC9|HC(Csl<;kwxPteegp7aS4iNq~to'
CLIENT_SECRET='TQiGdby59oBv3n$BqOZVxzkKX9ojztZX1hIIK6jIKog\q>iN*IDCbO8b$pbmT1BhMiijIHx4XchzwQ`zk55n24r$\ZQ1P.qkcqZcGEiW7J1-K0jcC9|HC(Csl<;kwxPteegp7aS4iNq~to'
BAR=BAZ
DATABASE_URL=mysql://drupal8:rock$ol1D@database/drupal8
After being sanitized:
FOO=BAR
HASH_SALT=REDACTED
CLIENT_SECRET=REDACTED
BAR=BAZ
DATABASE_URL=mysql://drupal8:PASSWORD@database/drupal8
The Processor File
_./live_devporter/processors/RemoveSecrets.php
class RemoveSecrets extends \AKlump\LiveDevPorter\Processors\ProcessorBase {
public function process() {
if (!$this->isWriteableEnvironment() || 'install' !== $this->filesGroupId || !$this->loadFile()) {
throw new \AKlump\LiveDevPorter\Processors\ProcessorSkippedException();
}
// We will apply sanitizing to the ".env" file.
if ($this->getBasename() === '.env') {
// This argument is passed by reference and is mutated by $redactor.
$redactor = (new \AKlump\LiveDevPorter\Security\Redactor($this->loadedFile['contents']));
// The default replacement will be used for these two keys.
$redactor->find(['CLIENT_SECRET', 'HASH_SALT'])->redact();
// A custom "PASSWORD" replacement will be used.
$redactor->find(['DATABASE_URL'])->replaceWith('PASSWORD')->redact();
// This will contain messages about what, if anything has been redacted. Or be an empty string if no redaction occurred.
$message = $redactor->getMessage();
if (!$message || $this->saveFile() !== FALSE) {
return $message;
}
throw new \Symfony\Component\Process\Exception\ProcessFailedException('Could not save %s', $this->getFilepath());
}
throw new \AKlump\LiveDevPorter\Processors\ProcessorSkippedException();
}
}
Redacting in a Text File
When redacting in a text file, you must provide a RegExp that captures one group, which is the portion that will be replaced. In the following example the redacted output will contain example.com/cron/{TOKEN_REDACTED}.
if ($this->getBasename() === 'crontab.bak') {
// A text file "crontab.bak", we can use a regex find and replace.
$message = (new \AKlump\LiveDevPorter\Security\Redactor($this->loadedFile['contents'], \AKlump\LiveDevPorter\Processors\ProcessorModes::TXT))
->find(['example.com/cron/(.+)'])
->replaceWith('{TOKEN_REDACTED}')
->redact()
->getMessage();
// Continue as above...
}