Skip to content

Commit

Permalink
Merge pull request #120 from nichtsfrei/add_inline_svg
Browse files Browse the repository at this point in the history
adds a workaround for inline svg and WeasyPrint
  • Loading branch information
y0urself authored Nov 26, 2020
2 parents b233085 + 468b7af commit 54b7d1e
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- nvt threat information in host result [114](https://github.com/greenbone/pheme/pull/114)
### Changed
- remove pandas due to too old debian version [112](https://github.com/greenbone/pheme/pull/112)
- add workaround for svg in pdf with wasyprint [120](https://github.com/greenbone/pheme/pull/120)

### Deprecated
### Removed
- libsass support: https://sass-lang.com/blog/libsass-is-deprecated [111](https://github.com/greenbone/pheme/pull/111)
Expand Down
61 changes: 60 additions & 1 deletion pheme/transformation/scanreport/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from typing import Dict
from typing import Dict, Generator, Tuple

from base64 import b64encode

from django.core.cache import cache
from django.template import Template, Context
Expand Down Expand Up @@ -108,7 +109,64 @@ def apply(self, name: str, data: Dict, parameter: Dict):
)


def __find_all_tags_in(
html: str, open_tag: str, close_tag: str
) -> Generator[Tuple[int, int], None, None]:

"""
Is used within __replace_inline_svg_with_img_tag
It searches for open and close tags and returns the start of the opening
and the end of the close_tag so that it is easy to extract and replace an
svg image.
"""
open_index = 0
close_end_index = 0
# abort conditions are: open or close_tag not found (-1)
while True:
open_index = html.find(open_tag, open_index)
if open_index == -1:
return
close_end_index = html.find(close_tag, open_index + len(open_tag))
if close_end_index == -1:
return
close_end_index += len(close_tag)
yield (open_index, close_end_index)
open_index = close_end_index


def _replace_inline_svg_with_img_tags(html: str) -> str:
"""
Is a workaround because WeasyPrint is not capable of dealing with inline svg
It searches for svg tags and replaces the inline svg within a given
html document with img containing a base64 encoded data link.
It is known that we lose the css styling with that hack, but in the moment
it is considered better than losing the image altogether or trying to
produce the images before rendering the template.
Please replace this method as soon as possible with a proper solution.
"""
for from_index, to_index in __find_all_tags_in(html, '<svg ', '</svg>'):
to_encode = html[from_index:to_index]
encoded = b64encode(to_encode.encode()).decode()
img = (
'<img src="data:image/svg+xml;charset=utf-8;base64, {}" />'.format(
encoded
)
)
html = html[:from_index] + img + html[to_index:]
return html


class VulnerabilityPDFReport(Report):
"""
Is used to generate vulnerability reports in PDF.
It renders html and css templates given data struct and then translate
the html document to PDF
"""

__template = 'vulnerability_report_pdf_template'
__css_template = 'vulnerability_report_pdf_css'
media_type = 'application/pdf'
Expand All @@ -121,6 +179,7 @@ def apply(self, name: str, data: Dict, parameter: Dict):
)
html_template = _load_template(self.__template)
html = html_template.render(Context(_enrich(name, data, parameter)))
html = _replace_inline_svg_with_img_tags(html)
logger.debug("created html")
pdf = HTML(string=html).write_pdf(stylesheets=[CSS(string=css)])
logger.debug("created pdf")
Expand Down
17 changes: 17 additions & 0 deletions tests/test_report_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

from pheme.datalink import as_datalink
from pheme.settings import SECRET_KEY
from pheme.transformation.scanreport import renderer

from tests.generate_test_data import gen_report


Expand Down Expand Up @@ -65,6 +67,21 @@ def test_report_contains_charts():
# assert result['overview']['vulnerable_equipment'] is not None


@pytest.mark.parametrize(
"html_contains",
[
('<svg width="343"><g id="severity_arrows">', "svg"), # missing end tag
('<svg width="343"><g id="severity_arrows"></svg>', "img"), # replaced
('<div width="343"><g id="severity_arrows"></div>', "div"), # not svg
],
)
def test_workaround_for_inline_svg_and_weasyprint(html_contains):
# pylint: disable=W0212
html, contains = html_contains
result = renderer._replace_inline_svg_with_img_tags(html)
assert contains in result


@pytest.mark.parametrize(
"http_accept",
[
Expand Down

0 comments on commit 54b7d1e

Please sign in to comment.