diff --git a/api/src/reportcreator_api/conf/settings.py b/api/src/reportcreator_api/conf/settings.py
index 022efa0a..4cff81fb 100644
--- a/api/src/reportcreator_api/conf/settings.py
+++ b/api/src/reportcreator_api/conf/settings.py
@@ -551,9 +551,9 @@
# Execute tasks locally, if no broker is configured
CELERY_TASK_ALWAYS_EAGER = not CELERY_BROKER_URL
-# Time limits are only enforced if a broker is configured and an external worker is used (but not in eager mode).
-# Self-hosted SysReptor instances use the eager mode by default, resulting in no PDF rendering time limits being applied.
-PDF_RENDERING_TIME_LIMIT = config('PDF_RENDERING_TIME_LIMIT', cast=int, default=60)
+# Maximum time a PDF rendering task is allowed to run. If a task takes longer, it gets cancelled.
+# Set to 0 to disable the time limit
+PDF_RENDERING_TIME_LIMIT = config('PDF_RENDERING_TIME_LIMIT', cast=int, default=5 * 60)
# History
diff --git a/api/src/reportcreator_api/tasks/rendering/entry.py b/api/src/reportcreator_api/tasks/rendering/entry.py
index 96589f21..0cf596ce 100644
--- a/api/src/reportcreator_api/tasks/rendering/entry.py
+++ b/api/src/reportcreator_api/tasks/rendering/entry.py
@@ -186,7 +186,8 @@ async def get_celery_result_async(task, timeout=None):
async def _render_pdf_task_async(timeout=None, **kwargs):
- timeout = timeout or timedelta(seconds=settings.PDF_RENDERING_TIME_LIMIT + 5)
+ if not timeout and settings.PDF_RENDERING_TIME_LIMIT:
+ timeout = timedelta(seconds=settings.PDF_RENDERING_TIME_LIMIT + 5)
try:
if settings.CELERY_TASK_ALWAYS_EAGER:
@@ -224,7 +225,7 @@ def format_resources():
resources |= {'/images/name/' + i.name: b64encode(i.file.read()).decode() for i in project.images.all() if project.is_file_referenced(i, sections=True, findings=True, notes=False)}
return resources
- with res.add_timing('collect data'):
+ with res.add_timing('collect_data'):
resources = await format_resources()
timing_total_before = sum(res.timings.values())
@@ -325,7 +326,7 @@ async def render_note_to_pdf(note: Union[ProjectNotebookPage, UserNotebookPage],
parent_obj = note.project if is_project_note else note.user
res = RenderStageResult()
- with res.add_timing('collect data'):
+ with res.add_timing('collect_data'):
# Prevent sending unreferenced images to rendering task to reduce memory consumption
resources = {}
async for i in parent_obj.images.all():
@@ -343,7 +344,7 @@ async def render_note_to_pdf(note: Union[ProjectNotebookPage, UserNotebookPage],
absolute_file_url = request.build_absolute_uri(reverse('uploadedusernotebookfile-retrieve-by-name', kwargs={'pentestuser_pk': note.user.id, 'filename': f.name}))
note_text = note_text.replace(f'/files/name/{f.name}', absolute_file_url)
- return await _render_pdf_task_async(
+ res |= await _render_pdf_task_async(
template="""
{{ data.note.title }}
""",
styles="""@import "/assets/global/base.css";""",
data={
@@ -355,8 +356,8 @@ async def render_note_to_pdf(note: Union[ProjectNotebookPage, UserNotebookPage],
},
language=note.project.language if is_project_note else Language.ENGLISH_US,
resources=resources,
- timings=res.timings,
)
+ return res
async def render_pdf(
@@ -373,7 +374,7 @@ async def render_pdf(
res = RenderStageResult()
- with res.add_timing('collect data'):
+ with res.add_timing('collect_data'):
data = await format_project_template_data(project=project, project_type=project_type)
return await render_pdf_task(
project=project,
@@ -389,7 +390,7 @@ async def render_pdf(
async def render_pdf_preview(project_type: ProjectType, report_template: str, report_styles: str, report_preview_data: dict) -> RenderStageResult:
res = RenderStageResult()
- with res.add_timing('collect data'):
+ with res.add_timing('collect_data'):
preview_data = report_preview_data.copy()
data = await sync_to_async(format_template_data)(data=preview_data, project_type=project_type)
diff --git a/api/src/reportcreator_api/tasks/rendering/render.py b/api/src/reportcreator_api/tasks/rendering/render.py
index e3d86de1..86223721 100644
--- a/api/src/reportcreator_api/tasks/rendering/render.py
+++ b/api/src/reportcreator_api/tasks/rendering/render.py
@@ -110,7 +110,7 @@ def encrypt_pdf(pdf_data: bytes, password: Optional[str]) -> RenderStageResult:
if not password:
return out
- with out.add_timing('compress_pdf'), \
+ with out.add_timing('encrypt_pdf'), \
Pdf.open(BytesIO(pdf_data)) as pdf:
out_data = BytesIO()
# Encrypt PDF with AES-256
diff --git a/api/src/reportcreator_api/tasks/rendering/render_chromium.py b/api/src/reportcreator_api/tasks/rendering/render_chromium.py
index 1485c862..ce6f64a6 100644
--- a/api/src/reportcreator_api/tasks/rendering/render_chromium.py
+++ b/api/src/reportcreator_api/tasks/rendering/render_chromium.py
@@ -57,12 +57,12 @@ async def chromium_request_handler(route):
await route.abort()
try:
- chromium_startup_timer = out.add_timing('chromium startup')
+ chromium_startup_timer = out.add_timing('chromium_startup')
chromium_startup_timer.__enter__()
async with get_page() as page:
chromium_startup_timer.__exit__(None, None, None)
- with out.add_timing('chromium render'):
+ with out.add_timing('chromium_render'):
console_output = []
page.on('console', lambda l: console_output.append(l))
page.on('pageerror', lambda exc: out.messages.append(ErrorMessage(
diff --git a/api/src/reportcreator_api/tasks/rendering/tasks.py b/api/src/reportcreator_api/tasks/rendering/tasks.py
index fcecb440..02a388cf 100644
--- a/api/src/reportcreator_api/tasks/rendering/tasks.py
+++ b/api/src/reportcreator_api/tasks/rendering/tasks.py
@@ -8,9 +8,9 @@
@shared_task(
name='reportcreator.render_pdf',
- soft_time_limit=settings.PDF_RENDERING_TIME_LIMIT,
- time_limit=settings.PDF_RENDERING_TIME_LIMIT + 5,
- expires=settings.PDF_RENDERING_TIME_LIMIT + 5,
+ soft_time_limit=settings.PDF_RENDERING_TIME_LIMIT or None,
+ time_limit=settings.PDF_RENDERING_TIME_LIMIT + 5 if settings.PDF_RENDERING_TIME_LIMIT else None,
+ expires=settings.PDF_RENDERING_TIME_LIMIT + 5 if settings.PDF_RENDERING_TIME_LIMIT else None,
)
def render_pdf_task_celery(*args, **kwargs) -> dict:
return asyncio.run(render_pdf_task_async(*args, **kwargs))
diff --git a/api/src/reportcreator_api/tests/test_rendering.py b/api/src/reportcreator_api/tests/test_rendering.py
index ba82a595..9f9fb8e1 100644
--- a/api/src/reportcreator_api/tests/test_rendering.py
+++ b/api/src/reportcreator_api/tests/test_rendering.py
@@ -1,6 +1,5 @@
import io
import re
-from base64 import b64decode
from unittest import mock
import pikepdf
@@ -121,8 +120,8 @@ def test_variables_rendering(self, template, html):
def test_error_messages(self, template, expected):
self.project_type.report_template = template
res = async_to_sync(render_pdf)(project=self.project)
- assert len(res['messages']) >= 1
- assert expected in [copy_keys(m, expected.keys()) for m in res['messages']]
+ assert len(res.messages) >= 1
+ assert expected in [copy_keys(m.to_dict(), expected.keys()) for m in res.messages]
def test_markdown_rendering(self):
assertHTMLEqual(
@@ -362,7 +361,7 @@ def test_mermaid_rendering(self):
('', False),
])
def test_pdf_encryption(self, password, encrypted):
- pdf_data = b64decode(async_to_sync(render_pdf)(project=self.project, password=password)['pdf'])
+ pdf_data = async_to_sync(render_pdf)(project=self.project, password=password).pdf
with pikepdf.Pdf.open(io.BytesIO(pdf_data), password=password) as pdf:
assert pdf.is_encrypted == encrypted
diff --git a/frontend/src/components/PdfPreview.vue b/frontend/src/components/PdfPreview.vue
index 7d3fe25d..d630fcca 100644
--- a/frontend/src/components/PdfPreview.vue
+++ b/frontend/src/components/PdfPreview.vue
@@ -13,7 +13,7 @@
- {{ key }} |
+ {{ key.replaceAll('_', ' ') }} |
{{ formatTiming(timing) }} |