Skip to content

Commit

Permalink
Optimize docker image and add BIMI svg validation (#126)
Browse files Browse the repository at this point in the history
  • Loading branch information
antedebaas authored Jul 15, 2024
1 parent f2f08eb commit b483dda
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 61 deletions.
File renamed without changes.
41 changes: 41 additions & 0 deletions .github/workflows/docker-testing.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Docker (Testing) build and push

on:
push:
branches-ignore:
- main

jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
# list of Docker images to use as base name for tags
images: |
${{ secrets.DOCKERHUB_USERNAME }}/dmarc-reports-testing
# generate Docker tags based on the following events/attributes
tags: |
type=sha,pattern=${{ github.sha }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
platforms: linux/amd64,linux/arm64
labels: ${{ steps.meta.outputs.labels }}
6 changes: 0 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,8 @@ RUN apk --update add ca-certificates && \
COPY dockerfiles/ /

RUN chmod +x /usr/local/bin/containerstartup.sh && \
dos2unix /usr/local/bin/containerstartup.sh && \
chmod +x /usr/local/bin/phpstartup.sh && \
dos2unix /usr/local/bin/phpstartup.sh && \
chmod +x /usr/local/bin/checkmail.sh && \
dos2unix /etc/nginx/nginx.conf && \
dos2unix /etc/php83/php-fpm.d/www.conf && \
dos2unix /etc/php83/conf.d/custom.ini && \
dos2unix /etc/supervisor/conf.d/supervisord.conf && \
mkdir -p /var/www/html && \
ln -s /usr/sbin/php-fpm83 /usr/sbin/php-fpm

Expand Down
2 changes: 1 addition & 1 deletion dockerfiles/usr/local/bin/checkmail.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/bin/sh
php /var/www/html/bin/console app:getreportsfrommailbox
php /var/www/html/bin/console app:getreportsfrommailbox
4 changes: 4 additions & 0 deletions dockerfiles/usr/local/bin/containerstartup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,9 @@ else
grep "/usr/local/bin/checkmail.sh" /etc/crontabs/root || echo "$MAILCHECK_SCHEDULE /usr/local/bin/checkmail.sh" >> /etc/crontabs/root
fi fi fi fi fi

echo "Mainenance"
php /var/www/html/bin/console app:removemaillock --quiet
php /var/www/html/bin/console cache:clear --quiet

echo "Run migrations"
php /var/www/html/bin/console doctrine:migrations:migrate --no-interaction
2 changes: 1 addition & 1 deletion src/Command/ClearLogsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int
foreach ($logs as $log) {
$this->em->remove($log);
}

$log = new Logs();
$log->setTime(new \DateTime());
$log->setSuccess(true);
$log->setMessage("Logs cleared");
$this->em->persist($log);

$this->em->flush();

$io->success('Logs have been cleared');
Expand Down
88 changes: 48 additions & 40 deletions src/Command/GetReportsFromMailboxCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,49 +70,57 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$this->em->flush();
}

if($lock->getValue() == 'true') {
$log = new Logs();
$log->setTime(new \DateTime());
$log->setSuccess(false);
$log->setMessage("GetReportsFromMailbox command was already running.");
$this->em->persist($log);
$this->em->flush();

$io->error('GetReportsFromMailbox command is already running.');
return Command::FAILURE;
} else {
$lock->setValue('true');
$this->em->persist($lock);
$this->em->flush();

$result = $this->open_mailbox($this->mailbox);
if($this->mailbox_secondary->isEnabled()) {
$result = $this->open_mailbox($this->mailbox_secondary);
}

$log = new Logs();
$log->setTime(new \DateTime());
$log->setSuccess($result->getSuccess());
$log->setMessage($result->getMessage());
try {
if($lock->getValue() == 'true') {
$log = new Logs();
$log->setTime(new \DateTime());
$log->setSuccess(false);
$log->setMessage("GetReportsFromMailbox command was already running.");
$this->em->persist($log);
$this->em->flush();

foreach ($result->getDetails()["reports"] as $report) {
$report->setReport(null);
}
$log->setDetails(serialize($result->getDetails()));
$this->em->persist($log);
$this->em->flush();

$lock->setValue('false');
$this->em->persist($lock);
$this->em->flush();

if($result->getSuccess() == true) {
$io->success($result->getMessage());
return Command::SUCCESS;
} else {
$io->error($result->getMessage());
$io->error('GetReportsFromMailbox command is already running.');
return Command::FAILURE;
} else {
$lock->setValue('true');
$this->em->persist($lock);
$this->em->flush();

$result = $this->open_mailbox($this->mailbox);
if($this->mailbox_secondary->isEnabled()) {
$result = $this->open_mailbox($this->mailbox_secondary);
}

$log = new Logs();
$log->setTime(new \DateTime());
$log->setSuccess($result->getSuccess());
$log->setMessage($result->getMessage());

foreach ($result->getDetails()["reports"] as $report) {
$report->setReport(null);
}
$log->setDetails(serialize($result->getDetails()));
$this->em->persist($log);
$this->em->flush();

$lock->setValue('false');
$this->em->persist($lock);
$this->em->flush();

if($result->getSuccess() == true) {
$io->success($result->getMessage());
return Command::SUCCESS;
} else {
$io->error($result->getMessage());
return Command::FAILURE;
}
}
} catch (\Exception $e) {
$io->error($e->getMessage());
return Command::FAILURE;
} catch (\Error $e) {
$io->error($e->getMessage());
return Command::FAILURE;
}
}

Expand Down
18 changes: 10 additions & 8 deletions src/Command/RemoveMailLock.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$lock->setValue('false');
$this->em->persist($lock);

$log = new Logs();
$log->setTime(new \DateTime());
$log->setSuccess(true);
$log->setMessage("Lock manually removed");
$this->em->persist($log);

if (!$input->getOption('quiet')) {
$log = new Logs();
$log->setTime(new \DateTime());
$log->setSuccess(true);
$log->setMessage("Lock manually removed");
$this->em->persist($log);

$io->success('Lock manually removed');
}
$this->em->flush();

$io->success('Lock manually removed');


return Command::SUCCESS;
}
Expand Down
23 changes: 19 additions & 4 deletions src/Controller/DomainsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ class DomainsController extends AbstractController
private $em;
private $router;
private $translator;
private DomainsRepository $DomainsRepository;

public function __construct(EntityManagerInterface $em, UrlGeneratorInterface $router, TranslatorInterface $translator)
public function __construct(EntityManagerInterface $em, UrlGeneratorInterface $router, TranslatorInterface $translator, DomainsRepository $domainsRepository)
{
$this->em = $em;
$this->router = $router;
$this->translator = $translator;
$this->DomainsRepository = $domainsRepository;
}

#[Route('/domains', name: 'app_domains')]
Expand Down Expand Up @@ -68,7 +70,6 @@ public function index(): Response
$bimivmcinfo[$domain->getId()] = openssl_x509_parse($domain->getBimivmcfile());
}
}
#dd($bimivmcinfo[9]);

$totaldomains = $repository->getTotalRows();

Expand Down Expand Up @@ -107,7 +108,14 @@ public function add(Request $request): Response

$bimisvgfile = $form->get('bimisvgfile')->getData();
if ($bimisvgfile) {
$formdata->setBimisvgfile(file_get_contents($bimisvgfile));
$validate = $this->DomainsRepository->validate_bimiv1_svg_file(file_get_contents($bimisvgfile));
if (!$validate['result']) {
foreach($validate['errors'] as $error) {
$this->addFlash('danger', $error);
}
} else {
$formdata->setBimisvgfile(file_get_contents($bimisvgfile));
}
}
$bimivmcfile = $form->get('bimivmcfile')->getData();
if ($bimivmcfile) {
Expand Down Expand Up @@ -167,7 +175,14 @@ public function edit(Domains $domain, Request $request): Response

$bimisvgfile = $form->get('bimisvgfile')->getData();
if ($bimisvgfile) {
$formdata->setBimisvgfile(file_get_contents($bimisvgfile));
$validate = $this->DomainsRepository->validate_bimiv1_svg_file(file_get_contents($bimisvgfile));
if (!$validate['result']) {
foreach($validate['errors'] as $error) {
$this->addFlash('danger', $error);
}
} else {
$formdata->setBimisvgfile(file_get_contents($bimisvgfile));
}
}
$bimivmcfile = $form->get('bimivmcfile')->getData();
if ($bimivmcfile) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public function process(ContainerBuilder $container)
} else {
$dsn = parse_url($container->resolveEnvPlaceholders($connectionConfig['url'], true));
}

// Determine the appropriate migrations path
if ($dsn['scheme'] == 'mysql') {
$migrationPath = '%kernel.project_dir%/migrations/mysql';
Expand Down
91 changes: 91 additions & 0 deletions src/Repository/DomainsRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,97 @@ public function __construct(ManagerRegistry $registry, UsersRepository $usersRep
// ;
// }

public function validate_bimiv1_svg_file($svgContent) {
$result = ['result' => true, 'errors' => []];

// Check for '<?xml version' tag
if (!preg_match('/^\<\?xml version="([^\"]+)"/', $svgContent)) {
$result['result'] = false;
$result['errors'][] = 'Invalid SVG: Missing <?xml version> tag';
}

// Check for root element '<svg>'
if (!preg_match('/\<svg/', $svgContent)) {
$result['result'] = false;
$result['errors'][] = 'Invalid SVG: Missing <svg> root element';
}

// Check for mandatory attributes on '<svg>' element (baseProfile, version, no x,y)
if (!preg_match('/<svg\s+[^>]*?(baseProfile|version|x|y)\s*=\s*["\']([^>]*?)["\']/i', $svgContent, $matches)) {
if (!isset($matches[1]) || (isset($matches[1]) && ($matches[1] == 'baseProfile' || $matches[1] == 'version' || $matches[1] == 'x' || $matches[1] == 'y'))) {
$result['result'] = false;
$result['errors'][] = 'Invalid SVG: Missing mandatory attributes (baseProfile="tiny-ps", version="1.2") or unexpected attributes (x, y) on <svg> element';
}
} else {
// Check baseProfile and version values
if (isset($matches[1]) && isset($matches[3])) {
dd($matches);
if ($matches[1] == 'baseProfile' && strtolower($matches[3]) != 'tiny-ps') {
$result['result'] = false;
$result['errors'][] = 'Invalid SVG: baseProfile attribute on <svg> element should be set to "tiny-ps".';
} else if ($matches[1] == 'version' && $matches[3] != '1.2') {
$result['result'] = false;
$result['errors'][] = 'Invalid SVG: version attribute on <svg> element should be set to "1.2".';
}
}
}

// Check for required elements (<title>) and optional (<desc>)
if (!preg_match('/<title[^>]*>(.*?)<\/title>/is', $svgContent, $titleMatches)) {
$result['result'] = false;
$result['errors'][] = 'Invalid SVG: Missing required <title> element.';
} else {
// Check for content in <title> (assuming it reflects company name)
if (empty(trim($titleMatches[1]))) {
$result['result'] = false;
$result['errors'][] = 'Invalid SVG: <title> element should contain company name.';
}
}

// Presence of <desc> is not mandatory but recommended
if (!preg_match('/<desc[^>]*>(.*?)<\/desc>/is', $svgContent)) {
$result['result'] = false;
$result['errors'][] = 'Invalid SVG: <desc> element is not present for accessibility.';
}

// Check for square aspect ratio (basic check)
if (preg_match('/<svg\s+[^>]*?(width|height)\s*=\s*(["\'])(\d+)(["\'])/i', $svgContent, $matches)) {
if (isset($matches[3]) && isset($matches[5]) && $matches[3] != $matches[5]) {
$result['result'] = false;
$result['errors'][] = 'Invalid SVG: Image is not a square aspect ratio (width and height should be equal).';
}
}

// File size check (basic - can be improved for accuracy)
if (strlen($svgContent) > 32 * 1024) {
$result['result'] = false; // Can be a separate key for warnings
$result['errors'][] = 'Invalid SVG: SVG file size exceeds recommended limit of 32 KB.';
}

// Check for external links or references
if (preg_match('/<\w+\s+[^>]*?(href|xlink:href)\s*=\s*(["\'])[^>]*?(["\'])/i', $svgContent)) {
$result['result'] = false;
$result['errors'][] = 'Invalid SVG: External links or references are not allowed.';
}
// Check for scripts
if (preg_match('/<script|<\/script>/is', $svgContent)) {
$result['result'] = false;
$result['errors'][] = 'Invalid SVG: Scripts are not allowed.';
}
// Check for animation elements (consider adding more as needed)
if (preg_match('/<animate|<\/animate>/is', $svgContent)) {
$result['result'] = false;
$result['errors'][] = 'Invalid SVG: Animations are not allowed.';
}
// Check for x and y attributes on the <svg> element
if (preg_match('/<svg\s+[^>]*?\W(x|y)\s*=\s*["\'][^>]*?["\']/i', $svgContent)) {
$result['result'] = false;
$result['errors'][] = 'Invalid SVG: "x" and "y" attributes are not allowed on the <svg> element.';
}

return $result;
}

public function findOwnedBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null)
{
$domains = array();
Expand Down
12 changes: 11 additions & 1 deletion templates/base.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,17 @@
</span>
</div>
<div class="d-all d-lg-none mt-5 mb-5">&nbsp;</div>
{% block body %}{% endblock %}
{% for message in app.flashes('success') %}
<div class="alert alert-success" role="alert">
{{ message }}
</div>
{% endfor %}
{% for message in app.flashes('danger') %}
<div class="alert alert-danger" role="alert">
{{ message }}
</div>
{% endfor %}
{% block body %}{% endblock %}
</main>
</div>
</div>
Expand Down

0 comments on commit b483dda

Please sign in to comment.