-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #19 from AmazeeLabs/feature/db-exposure-policy
Adding database dump exposure policy
- Loading branch information
Showing
4 changed files
with
197 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
title: "Database exposure check" | ||
class: \Drutiny\algm\Audit\DatabaseExposure | ||
name: Security:DatabaseExposure | ||
tags: | ||
- Drupal 8 | ||
- Drupal 7 | ||
- Security | ||
description: | | ||
A policy that will check for database dumps that are publicly exposed and readable by the web server. | ||
remediation: | | ||
Due to the severity of this, the following databases were removed: | ||
{{#cleaned}} | ||
- {{ . }} | ||
{{/cleaned}} | ||
failure: | | ||
Sensitive database{{ plural }} found: {{ results.found }} | ||
{{#results.findings}}{{ markdown_display }}{{/results.findings}} | ||
success: No leaked/exposed databases were found | ||
parameters: | ||
root: | ||
default: "%root" | ||
filetypes: | ||
default: | ||
- sql | ||
- sql.gz | ||
description: 'Database file extensions to look for.' | ||
type: string | ||
exclude: | ||
default: | ||
- core | ||
description: 'Directories to exclude from find' | ||
severity: 'critical' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
title: 'ALGM Security audit' | ||
description: 'This profile is for sites which contain security policies that need to be rolled out ad-hoc' | ||
policies: | ||
'Security:DoubleFileExtension': { severity: critical } | ||
'Security:DoubleFileExtension': { severity: critical } | ||
'Security:DatabaseExposure': { severity: critical } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
<?php | ||
|
||
namespace Drutiny\algm\Audit; | ||
|
||
use Drutiny\Audit; | ||
use Drutiny\Profile\ProfileSource; | ||
use Drutiny\Sandbox\Sandbox; | ||
use Drutiny\AuditResponse\AuditResponse; | ||
use Drutiny\RemediableInterface; | ||
use Drutiny\Annotation\Param; | ||
use Drutiny\Annotation\Token; | ||
use Drutiny\algm\Utils\MarkdownTableGenerator; | ||
use Drutiny\Report\Format; | ||
|
||
/** | ||
* Database exposure checks | ||
* @Param( | ||
* name = "filetypes", | ||
* description = "Database extensions to include in the check", | ||
* type = "array", | ||
* default = {} | ||
* ) | ||
* @Param( | ||
* name = "root", | ||
* description = "Root directory of app", | ||
* type = "string", | ||
* default = "%root" | ||
* ) | ||
* @Param( | ||
* name = "exclude", | ||
* description = "Directories to be exlcuded from the find.", | ||
* type = "array", | ||
* default = {} | ||
* ) | ||
* @Param( | ||
* name = "results", | ||
* description = "An array of results matching the scan criteria. Each match is an assoc array with the following keys: filepath, line, code, basename.", | ||
* type = "array", | ||
* default = {} | ||
* ) | ||
* @Token( | ||
* name = "plural", | ||
* description = "Determines if single or multi results are returned.", | ||
* type = "bool", | ||
* default = false | ||
* ) | ||
* @Token( | ||
* name = "cleaned", | ||
* description = "Returns files than have been removed", | ||
* type = "array", | ||
* default = {} | ||
* ) | ||
*/ | ||
class DatabaseExposure extends Audit implements RemediableInterface { | ||
|
||
public function audit(Sandbox $sandbox) { | ||
$root = $sandbox->getParameter('root', '%root'); | ||
$stat = $sandbox->drush(['format' => 'json'])->status(); | ||
$root = strtr($root, $stat['%paths']); | ||
|
||
$command = ['find', $root]; | ||
|
||
$filepathConditions = []; | ||
foreach ($sandbox->getParameter('exclude', []) as $filepath) { | ||
$filepath = strtr($filepath, $stat['%paths']); | ||
$format = "-path %s/%s"; | ||
$filepathConditions[] = sprintf($format, $root, $filepath); | ||
} | ||
|
||
$filepathConditions = '\( ' . implode(' -o ', $filepathConditions) . ' \)'; | ||
|
||
$command[] = sprintf("-type d %s", $filepathConditions); | ||
|
||
$command[] = "-prune -false -o -type f"; | ||
|
||
$types = $sandbox->getParameter('filetypes', []); | ||
|
||
if (!empty($types)) { | ||
$conditions = []; | ||
foreach ($types as $type) { | ||
$format = '-iname \*.%s'; | ||
$conditions[] = sprintf($format, $type); | ||
} | ||
|
||
$command[] = '\( ' . implode(' -o ', $conditions) . ' \) -readable 2> /dev/null'; | ||
} | ||
|
||
$command = implode(' ', $command); | ||
$sandbox->logger()->info('[' . __CLASS__ . '] ' . $command); | ||
|
||
// Execute | ||
$output = $sandbox->exec($command); | ||
|
||
if (empty($output)) { | ||
return TRUE; | ||
} | ||
|
||
$matches = array_filter(explode(PHP_EOL, $output)); | ||
$matches = array_map(function ($line) { | ||
return [ | ||
'file' => $line, | ||
'permission' => 'readable' | ||
]; | ||
}, $matches); | ||
|
||
// Filters | ||
// $matches = array_filter($matches, function($line) { | ||
// return !strpos($line['file'], '\/core\/') !== false; | ||
// }); | ||
|
||
$results = [ | ||
'found' => count($matches), | ||
'findings' => $matches, | ||
'filepaths' => array_values(array_unique(array_map(function ($match) use ($stat) { | ||
return str_replace($stat['%paths']['%root'], '', $match['file']); | ||
}, $matches))) | ||
]; | ||
|
||
//TODO: Add a conditional check for Markdown format | ||
$columns = ['File', 'Permission']; | ||
$rows = []; | ||
foreach ($results['findings'] as $key => $file) { | ||
$rows[] = [$file['file'], $file["permission"]]; | ||
} | ||
|
||
$md_table = new MarkdownTableGenerator($columns, $rows); | ||
$results['findings'] = ['markdown_display' => $md_table->render()]; | ||
|
||
$sandbox->setParameter('results', $results); | ||
$sandbox->setParameter('plural', count($results) > 1 ? 's' : ''); | ||
|
||
if (empty($matches)) { | ||
return Audit::SUCCESS; | ||
} | ||
return Audit::FAIL; | ||
} | ||
|
||
// This remediation step is run if the audit fails/returns false. | ||
public function remediate(Sandbox $sandbox) { | ||
$root = $sandbox->getParameter('root', '%root'); | ||
$list = $sandbox->getParameter('results'); | ||
|
||
$stat = $sandbox->drush(['format' => 'json'])->status(); | ||
$root = strtr($root, $stat['%paths']); | ||
|
||
$output = ''; | ||
if (!empty($list['filepaths'])) { | ||
foreach ($list['filepaths'] as $file) { | ||
$fileToRemove = sprintf('%s%s', $root, $file); | ||
$output = $sandbox->exec('rm -rf ' . $fileToRemove); | ||
} | ||
|
||
$sandbox->setParameter('cleaned', $list['filepaths']); | ||
} | ||
|
||
return $this->audit($sandbox); | ||
} | ||
} |