Skip to content

[DRAFT] FIX Surface AttackResultEntry.timestamp on hydrated AttackResult#1653

Open
thirteeneight wants to merge 2 commits intomicrosoft:mainfrom
thirteeneight:fix/attack-result-surface-timestamp
Open

[DRAFT] FIX Surface AttackResultEntry.timestamp on hydrated AttackResult#1653
thirteeneight wants to merge 2 commits intomicrosoft:mainfrom
thirteeneight:fix/attack-result-surface-timestamp

Conversation

@thirteeneight
Copy link
Copy Markdown

Description

Fixes #1651.

AttackResultEntry persists a non-nullable timestamp column, but get_attack_result() dropped it when rebuilding the domain AttackResult. The backend mapper's attack_result_to_summary then fell back to datetime.now(timezone.utc), so the UI showed today's date on every row no matter when it was actually persisted.

Three small, additive changes:

  • Add an optional timestamp: Optional[datetime] = None field to the AttackResult dataclass.
  • Pass timestamp=_ensure_utc(self.timestamp) in AttackResultEntry.get_attack_result().
  • In attack_result_to_summary, prefer ar.timestamp over datetime.now() when metadata["created_at"] is absent. The metadata override path is preserved unchanged.

Backwards compatible: AttackResultEntries.timestamp has been non-nullable since the entry was introduced, so every existing row has a value to hydrate from. Callers constructing an AttackResult without the new field get None, which the mapper treats the same as missing metadata.

Filed as draft pending maintainer feedback on #1651.

Tests and Documentation

New unit tests:

  • tests/unit/models/test_attack_result.py::TestAttackResultTimestamp — default is None, aware datetime is preserved, round-trip through AttackResultEntry, naive SQLite timestamps are normalized to UTC on hydration.
  • tests/unit/backend/test_mappers.py::TestAttackResultToSummary — three new cases covering ar.timestamp preferred when metadata is absent, metadata still wins when both are present, fallback to datetime.now() when both are absent.

Verification:

  • pytest tests/unit/ — 1339 passed, 0 regressions.
  • mypy --strict on the three modified source files — clean.
  • ruff format and ruff check — clean.
  • No JupyText / docs changes: this is a bug fix and the only public addition is one optional dataclass field.

AttackResultEntry stores a non-nullable timestamp column, but
get_attack_result() dropped it when rebuilding the AttackResult. The
backend mapper then fell back to datetime.now() in
attack_result_to_summary, so the UI showed today's date on every row
no matter when it was actually persisted.

Adds an optional timestamp field to AttackResult, passes it through
_ensure_utc() in get_attack_result(), and has attack_result_to_summary
prefer ar.timestamp over datetime.now() when metadata["created_at"] is
absent. The metadata override path is unchanged, and callers that do
not set the new field get None (same behavior as before).

Fixes microsoft#1651
@thirteeneight
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@thirteeneight thirteeneight marked this pull request as ready for review April 25, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AttackResultEntry.timestamp is dropped on hydration — backend UI shows datetime.now() instead of real row time

1 participant