diff --git a/compose/production/postgres/maintenance/restore b/compose/production/postgres/maintenance/restore index 9661ca7f1..8d41a7080 100644 --- a/compose/production/postgres/maintenance/restore +++ b/compose/production/postgres/maintenance/restore @@ -43,6 +43,12 @@ export PGUSER="${POSTGRES_USER}" export PGPASSWORD="${POSTGRES_PASSWORD}" export PGDATABASE="${POSTGRES_DB}" + +### It's Required to terminate current connections to delete the database +message_info "Cleaning connections to the database..." + +psql -c "REVOKE CONNECT ON DATABASE ${PGDATABASE} FROM public; ALTER database ${POSTGRES_DB} allow_connections = off; SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '${PGDATABASE}';" postgres + message_info "Dropping the database..." dropdb "${PGDATABASE}" diff --git a/ghostwriter/modules/reportwriter.py b/ghostwriter/modules/reportwriter.py index 5f702fbc6..267d6ba20 100644 --- a/ghostwriter/modules/reportwriter.py +++ b/ghostwriter/modules/reportwriter.py @@ -1325,7 +1325,7 @@ def _parse_html_lists(self, tag, prev_p, num, finding, level=0): # Return last paragraph created return p - def _process_text_xml(self, text, finding=None): + def _process_text_xml(self, text, finding=None, p_style=None): """ Process the provided text from the specified finding to parse keywords for evidence placement and formatting for Office XML. @@ -1379,7 +1379,7 @@ def _process_text_xml(self, text, finding=None): p = self.finding_body_shape.text_frame.add_paragraph() ALIGNMENT = PP_ALIGN else: - p = self.sacrificial_doc.add_paragraph() + p = self.sacrificial_doc.add_paragraph(style=p_style) ALIGNMENT = WD_ALIGN_PARAGRAPH # Check for alignment classes on the ``p`` tag @@ -1565,92 +1565,129 @@ def _process_richtext(self, context: dict) -> dict: Pre-defined template context """ - def render_subdocument(section, finding): + def render_subdocument(section, finding, p_style=None): if section: self.sacrificial_doc = self.word_doc.new_subdoc() - self._process_text_xml(section, finding) + self._process_text_xml(section, finding, p_style) return self.sacrificial_doc return None # Findings for finding in context["findings"]: + logger.info("Processing %s", finding["title"]) # Create ``RichText()`` object for a colored severity category finding["severity_rt"] = RichText(finding["severity"], color=finding["severity_color"]) finding["cvss_score_rt"] = RichText(finding["cvss_score"], color=finding["severity_color"]) finding["cvss_vector_rt"] = RichText(finding["cvss_vector"], color=finding["severity_color"]) # Create subdocuments for each finding section - finding["affected_entities_rt"] = render_subdocument(finding["affected_entities"], finding) - finding["description_rt"] = render_subdocument(finding["description"], finding) - finding["impact_rt"] = render_subdocument(finding["impact"], finding) + finding["affected_entities_rt"] = render_subdocument( + finding["affected_entities"], finding, self.report_queryset.docx_template.p_style + ) + finding["description_rt"] = render_subdocument( + finding["description"], finding, self.report_queryset.docx_template.p_style + ) + finding["impact_rt"] = render_subdocument( + finding["impact"], finding, self.report_queryset.docx_template.p_style + ) # Include a copy of ``mitigation`` as ``recommendation`` to match legacy context - mitigation_section = render_subdocument(finding["mitigation"], finding) + mitigation_section = render_subdocument( + finding["mitigation"], finding, self.report_queryset.docx_template.p_style + ) finding["mitigation_rt"] = mitigation_section finding["recommendation_rt"] = mitigation_section - finding["replication_steps_rt"] = render_subdocument(finding["replication_steps"], finding) - finding["host_detection_techniques_rt"] = render_subdocument(finding["host_detection_techniques"], finding) + finding["replication_steps_rt"] = render_subdocument( + finding["replication_steps"], finding, self.report_queryset.docx_template.p_style + ) + finding["host_detection_techniques_rt"] = render_subdocument( + finding["host_detection_techniques"], finding, self.report_queryset.docx_template.p_style + ) finding["network_detection_techniques_rt"] = render_subdocument( - finding["network_detection_techniques"], finding + finding["network_detection_techniques"], finding, self.report_queryset.docx_template.p_style + ) + finding["references_rt"] = render_subdocument( + finding["references"], finding, self.report_queryset.docx_template.p_style ) - finding["references_rt"] = render_subdocument(finding["references"], finding) # Client Notes - context["client"]["note_rt"] = render_subdocument(context["client"]["note"], finding=None) - context["client"]["address_rt"] = render_subdocument(context["client"]["address"], finding=None) + context["client"]["note_rt"] = render_subdocument( + context["client"]["note"], finding=None, p_style=self.report_queryset.docx_template.p_style + ) + context["client"]["address_rt"] = render_subdocument( + context["client"]["address"], finding=None, p_style=self.report_queryset.docx_template.p_style + ) # Project Notes - context["project"]["note_rt"] = render_subdocument(context["project"]["note"], finding=None) + context["project"]["note_rt"] = render_subdocument( + context["project"]["note"], finding=None, p_style=self.report_queryset.docx_template.p_style + ) # Assignments for assignment in context["team"]: if isinstance(assignment, dict): if assignment["note"]: - assignment["note_rt"] = render_subdocument(assignment["note"], finding=None) + assignment["note_rt"] = render_subdocument( + assignment["note"], finding=None, p_style=self.report_queryset.docx_template.p_style + ) # Contacts for contact in context["client"]["contacts"]: if isinstance(contact, dict): if contact["note"]: - contact["note_rt"] = render_subdocument(contact["note"], finding=None) + contact["note_rt"] = render_subdocument( + contact["note"], finding=None, p_style=self.report_queryset.docx_template.p_style + ) # Objectives for objective in context["objectives"]: if isinstance(objective, dict): if objective["description"]: - objective["description_rt"] = render_subdocument(objective["description"], finding=None) + objective["description_rt"] = render_subdocument( + objective["description"], finding=None, p_style=self.report_queryset.docx_template.p_style + ) # Scope Lists for scope_list in context["scope"]: if isinstance(scope_list, dict): if scope_list["description"]: - scope_list["description_rt"] = render_subdocument(scope_list["description"], finding=None) + scope_list["description_rt"] = render_subdocument( + scope_list["description"], finding=None, p_style=self.report_queryset.docx_template.p_style + ) # Targets for target in context["targets"]: if isinstance(target, dict): if target["note"]: - target["note_rt"] = render_subdocument(target["note"], finding=None) + target["note_rt"] = render_subdocument( + target["note"], finding=None, p_style=self.report_queryset.docx_template.p_style + ) # Deconfliction Events for event in context["deconflictions"]: if isinstance(event, dict): if event["description"]: - event["description_rt"] = render_subdocument(event["description"], finding=None) + event["description_rt"] = render_subdocument( + event["description"], finding=None, p_style=self.report_queryset.docx_template.p_style + ) # White Cards for card in context["whitecards"]: if isinstance(card, dict): if card["description"]: - card["description_rt"] = render_subdocument(card["description"], finding=None) + card["description_rt"] = render_subdocument( + card["description"], finding=None, p_style=self.report_queryset.docx_template.p_style + ) # Infrastructure for asset_type in context["infrastructure"]: for asset in context["infrastructure"][asset_type]: if isinstance(asset, dict): if asset["note"]: - asset["note_rt"] = render_subdocument(asset["note"], finding=None) + asset["note_rt"] = render_subdocument( + asset["note"], finding=None, p_style=self.report_queryset.docx_template.p_style + ) return context diff --git a/ghostwriter/reporting/admin.py b/ghostwriter/reporting/admin.py index 1b24e3901..82ef29252 100644 --- a/ghostwriter/reporting/admin.py +++ b/ghostwriter/reporting/admin.py @@ -234,6 +234,7 @@ class ReportTemplateAdmin(admin.ModelAdmin): "description", "client", "doc_type", + "p_style", ) }, ), diff --git a/ghostwriter/reporting/forms.py b/ghostwriter/reporting/forms.py index a089e235f..4b0d9b15b 100644 --- a/ghostwriter/reporting/forms.py +++ b/ghostwriter/reporting/forms.py @@ -765,6 +765,7 @@ def __init__(self, *args, **kwargs): self.fields["doc_type"].empty_label = "-- Select a Matching Filetype --" self.fields["client"].empty_label = "-- Attach to a Client (Optional) --" self.fields["tags"].widget.attrs["placeholder"] = "language:en_US, cvss, ..." + self.fields["p_style"].widget.attrs["placeholder"] = "Style for new paragraph (Optional, Word only)" # Design form layout with Crispy FormHelper self.helper = FormHelper() self.helper.form_method = "post" @@ -787,6 +788,10 @@ def __init__(self, *args, **kwargs): Column("client", css_class="form-group col-md-6 mb-0"), css_class="form-row", ), + Row( + Column("p_style", css_class="form-group col-md-6 mb-0"), + css_class="form-row", + ), Row( Column( SwitchToggle( diff --git a/ghostwriter/reporting/migrations/0037_reporttemplate_p_style.py b/ghostwriter/reporting/migrations/0037_reporttemplate_p_style.py new file mode 100644 index 000000000..184071068 --- /dev/null +++ b/ghostwriter/reporting/migrations/0037_reporttemplate_p_style.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.16 on 2023-04-14 10:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reporting', '0036_reporttemplate_landscape'), + ] + + operations = [ + migrations.AddField( + model_name='reporttemplate', + name='p_style', + field=models.CharField(blank=True, default=None, help_text='Provide the name of the style of new paragraphs. The style must be present in the template. Leave empty to use default Normal style.', max_length=255, null=True, verbose_name='Style of new paragraphs (leave empty for default - Normal)'), + ), + ] diff --git a/ghostwriter/reporting/models.py b/ghostwriter/reporting/models.py index 4e1622d39..3758f2e76 100644 --- a/ghostwriter/reporting/models.py +++ b/ghostwriter/reporting/models.py @@ -338,6 +338,16 @@ class ReportTemplate(models.Model): help_text="Select the filetype for this template", ) + p_style = models.CharField( + "Style of new paragraphs - Word only (leave empty for default - Normal)", + max_length=255, + null=True, + blank=True, + default=None, + help_text="Provide the name of the style of new paragraphs. The style must be present in the template. " + \ + "Leave empty to use default Normal style (Word only).", + ) + class Meta: ordering = ["doc_type", "client", "name"] verbose_name = "Report template" diff --git a/ghostwriter/reporting/templates/reporting/report_template_detail.html b/ghostwriter/reporting/templates/reporting/report_template_detail.html index 1cf82c06c..30fc2bf19 100644 --- a/ghostwriter/reporting/templates/reporting/report_template_detail.html +++ b/ghostwriter/reporting/templates/reporting/report_template_detail.html @@ -85,6 +85,10 @@