fix(outputs): refresh scan timestamps per run (#9272)

This commit is contained in:
Hugo Pereira Brito authored and GitHub committed 2025-11-20 13:12:39 +01:00
1 parent b6ba6c6e31
commit dc7d2d5aeb
5 files changed
+65 -4

No files matched your search

+1
View File
@@ -27,6 +27,7 @@ All notable changes to the **Prowler API** are documented in this file.
### Fixed ### Fixed
- Scans no longer fail when findings have UIDs exceeding 300 characters; such findings are now skipped with detailed logging [(#9246)](https://github.com/prowler-cloud/prowler/pull/9246) - Scans no longer fail when findings have UIDs exceeding 300 characters; such findings are now skipped with detailed logging [(#9246)](https://github.com/prowler-cloud/prowler/pull/9246)
- Refresh output report timestamps for each scan [(#9272)](https://github.com/prowler-cloud/prowler/pull/9272)
### Security ### Security
- Django updated to the latest 5.1 security release, 5.1.14, due to problems with potential [SQL injection](https://github.com/prowler-cloud/prowler/security/dependabot/113) and [denial-of-service vulnerability](https://github.com/prowler-cloud/prowler/security/dependabot/114) [(#9176)](https://github.com/prowler-cloud/prowler/pull/9176) - Django updated to the latest 5.1 security release, 5.1.14, due to problems with potential [SQL injection](https://github.com/prowler-cloud/prowler/security/dependabot/113) and [denial-of-service vulnerability](https://github.com/prowler-cloud/prowler/security/dependabot/114) [(#9176)](https://github.com/prowler-cloud/prowler/pull/9176)
+3
View File
@@ -15,6 +15,7 @@ from prowler.config.config import (
html_file_suffix, html_file_suffix,
json_asff_file_suffix, json_asff_file_suffix,
json_ocsf_file_suffix, json_ocsf_file_suffix,
set_output_timestamp,
) )
from prowler.lib.outputs.asff.asff import ASFF from prowler.lib.outputs.asff.asff import ASFF
from prowler.lib.outputs.compliance.aws_well_architected.aws_well_architected import ( from prowler.lib.outputs.compliance.aws_well_architected.aws_well_architected import (
@@ -275,6 +276,8 @@ def _build_output_path(
with rls_transaction(tenant_id): with rls_transaction(tenant_id):
started_at = Scan.objects.get(id=scan_id).started_at started_at = Scan.objects.get(id=scan_id).started_at
set_output_timestamp(started_at)
timestamp = started_at.strftime("%Y%m%d%H%M%S") timestamp = started_at.strftime("%Y%m%d%H%M%S")
if subdirectory: if subdirectory:
+4 -2
View File
@@ -148,10 +148,11 @@ class TestOutputs:
) )
mock_logger.assert_called() mock_logger.assert_called()
@patch("tasks.jobs.export.set_output_timestamp")
@patch("tasks.jobs.export.rls_transaction") @patch("tasks.jobs.export.rls_transaction")
@patch("tasks.jobs.export.Scan") @patch("tasks.jobs.export.Scan")
def test_generate_output_directory_creates_paths( def test_generate_output_directory_creates_paths(
self, mock_scan, mock_rls_transaction, tmpdir self, mock_scan, mock_rls_transaction, mock_set_timestamp, tmpdir
): ):
# Mock the scan object with a started_at timestamp # Mock the scan object with a started_at timestamp
mock_scan_instance = MagicMock() mock_scan_instance = MagicMock()
@@ -198,10 +199,11 @@ class TestOutputs:
assert ens.endswith(f"{provider}-{expected_timestamp}") assert ens.endswith(f"{provider}-{expected_timestamp}")
assert "/ens/" in ens assert "/ens/" in ens
@patch("tasks.jobs.export.set_output_timestamp")
@patch("tasks.jobs.export.rls_transaction") @patch("tasks.jobs.export.rls_transaction")
@patch("tasks.jobs.export.Scan") @patch("tasks.jobs.export.Scan")
def test_generate_output_directory_invalid_character( def test_generate_output_directory_invalid_character(
self, mock_scan, mock_rls_transaction, tmpdir self, mock_scan, mock_rls_transaction, mock_set_timestamp, tmpdir
): ):
# Mock the scan object with a started_at timestamp # Mock the scan object with a started_at timestamp
mock_scan_instance = MagicMock() mock_scan_instance = MagicMock()
+1
View File
@@ -74,6 +74,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
- Rename `get_oci_assessment_summary` to `get_oraclecloud_assessment_summary` in HTML output [(#9200)](https://github.com/prowler-cloud/prowler/pull/9200) - Rename `get_oci_assessment_summary` to `get_oraclecloud_assessment_summary` in HTML output [(#9200)](https://github.com/prowler-cloud/prowler/pull/9200)
- Fix Validation and other errors in Azure provider [(#8915)](https://github.com/prowler-cloud/prowler/pull/8915) - Fix Validation and other errors in Azure provider [(#8915)](https://github.com/prowler-cloud/prowler/pull/8915)
- Update documentation URLs from docs.prowler.cloud to docs.prowler.com [(#9240)](https://github.com/prowler-cloud/prowler/pull/9240) - Update documentation URLs from docs.prowler.cloud to docs.prowler.com [(#9240)](https://github.com/prowler-cloud/prowler/pull/9240)
- Refresh output report timestamps for each scan [(#9272)](https://github.com/prowler-cloud/prowler/pull/9272)
- Fix file name parsing for checks on Windows [(#9268)](https://github.com/prowler-cloud/prowler/pull/9268) - Fix file name parsing for checks on Windows [(#9268)](https://github.com/prowler-cloud/prowler/pull/9268)
- Remove typo for Prowler ThreatScore - M365 [(#9274)](https://github.com/prowler-cloud/prowler/pull/9274) - Remove typo for Prowler ThreatScore - M365 [(#9274)](https://github.com/prowler-cloud/prowler/pull/9274)
+56 -2
View File
@@ -3,6 +3,7 @@ import pathlib
from datetime import datetime, timezone from datetime import datetime, timezone
from enum import Enum from enum import Enum
from os import getcwd from os import getcwd
from typing import Tuple
import requests import requests
import yaml import yaml
@@ -10,8 +11,33 @@ from packaging import version
from prowler.lib.logger import logger from prowler.lib.logger import logger
timestamp = datetime.today()
timestamp_utc = datetime.now(timezone.utc).replace(tzinfo=timezone.utc) class _MutableTimestamp:
"""Lightweight proxy to keep timestamp references in sync across modules."""
def __init__(self, value: datetime) -> None:
self.value = value
def set(self, value: datetime) -> None:
self.value = value
def __getattr__(self, name):
return getattr(self.value, name)
def __str__(self) -> str: # pragma: no cover - trivial forwarder
return str(self.value)
def __repr__(self) -> str: # pragma: no cover - trivial forwarder
return repr(self.value)
def __eq__(self, other) -> bool:
if isinstance(other, _MutableTimestamp):
return self.value == other.value
return self.value == other
timestamp = _MutableTimestamp(datetime.today())
timestamp_utc = _MutableTimestamp(datetime.now(timezone.utc))
prowler_version = "5.14.0" prowler_version = "5.14.0"
html_logo_url = "https://github.com/prowler-cloud/prowler/" html_logo_url = "https://github.com/prowler-cloud/prowler/"
square_logo_img = "https://prowler.com/wp-content/uploads/logo-html.png" square_logo_img = "https://prowler.com/wp-content/uploads/logo-html.png"
@@ -84,6 +110,34 @@ encoding_format_utf_8 = "utf-8"
available_output_formats = ["csv", "json-asff", "json-ocsf", "html"] available_output_formats = ["csv", "json-asff", "json-ocsf", "html"]
def set_output_timestamp(
new_timestamp: datetime,
) -> Tuple[datetime, datetime, str, str]:
"""
Override the global output timestamps so generated artifacts reflect a specific scan.
Returns the previous values so callers can restore them afterwards.
"""
global timestamp, timestamp_utc, output_file_timestamp, timestamp_iso
previous_values = (
timestamp.value,
timestamp_utc.value,
output_file_timestamp,
timestamp_iso,
)
timestamp.set(new_timestamp)
timestamp_utc.set(
new_timestamp.astimezone(timezone.utc)
if new_timestamp.tzinfo
else new_timestamp.replace(tzinfo=timezone.utc)
)
output_file_timestamp = timestamp.strftime("%Y%m%d%H%M%S")
timestamp_iso = timestamp.isoformat(sep=" ", timespec="seconds")
return previous_values
def get_default_mute_file_path(provider: str): def get_default_mute_file_path(provider: str):
""" """
get_default_mute_file_path returns the default mute file path for the provider get_default_mute_file_path returns the default mute file path for the provider