Skip to content

Commit

Permalink
Merge pull request #19 from AmazeeLabs/feature/db-exposure-policy
Browse files Browse the repository at this point in the history
Adding database dump exposure policy
  • Loading branch information
Tim Clifford authored Jan 27, 2021
2 parents 288167b + 84f3b60 commit 1ebd3af
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 2 deletions.
34 changes: 34 additions & 0 deletions Policies/database_exposure.policy.yml
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'
3 changes: 2 additions & 1 deletion Profiles/algm_security.profile.yml
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 }
4 changes: 3 additions & 1 deletion Profiles/algm_sla_site_beta.profile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ policies:
'algm:SSLChecker': { severity: high }
'algm:StageFileProxy': { severity: medium }
'algm:StorageSpace': { severity: medium }
'algm:CdnCheck': { severity: high }
'algm:DevModulesEnabled': { severity: high}
# Security
'Security:DatabaseExposure': { severity: critical }
'algm:CdnCheck': { severity: high }
158 changes: 158 additions & 0 deletions src/Audit/DatabaseExposure.php
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);
}
}

0 comments on commit 1ebd3af

Please sign in to comment.