Leads get the same two-queue view operators already have: 'Available
to Review' (pending reviews currently assigned to a peer lead) and
'My Assigned Reviews' (assigned to me), replacing the old single
'My Reviews' toggle. Since the load-balanced auto-assignment always
picks a reviewer immediately, 'available' means peer-assigned reviews
a lead could pick up if the assignee can't get to them, not unclaimed
ones (there aren't any).
POST /tests/{id}/assign now also accepts red_reviewer_assignee /
blue_reviewer_assignee, validated against the matching lead role and
synced to Jira the same way operator assignment already is. The
AssigneeControl UI gained a 'reviewer' kind (leads-only picker) shown
on the test detail header while a test sits in red_review/blue_review
— this also fixes the reviewer assignment being invisible in Aegis
even though it was already being pushed to Jira correctly.
- Fix the red/blue draft form losing unsaved fields (e.g. execution_start_time) whenever an evidence upload refetches the test — the hydration effect now runs once per test, not on every refetch.
- Add color-scheme: dark so native date/time picker popups match the app theme instead of rendering white.
- Fix _STATE_TO_JIRA_STATUS: real Jira statuses are 'To Do' and 'Red Team test review', not 'To-Do' and 'RT Test Review' — confirmed live against the actual workflow transitions, which is why Submit to Blue Team never moved the ticket past 'In Progress'.
- Tempo worklog sync was silently blocked by TEMPO_ENABLED defaulting to False with no admin-facing toggle, even after configuring a Tempo token via Settings. Now bypassed once an admin or personal token is actually configured, mirroring Jira's DB-backed enabled flag.
- Hold now actually pauses the running phase timer (paused_at), and Resume accumulates the held duration into red/blue_paused_seconds — previously the timer kept counting through a hold.
Shrinks padding, icon size, and font sizes, and bumps the grid to 9
columns at the lg breakpoint so Draft through Disputed no longer wrap
to a second row.
Admin can no longer be picked as a red_tech/blue_tech assignee — it
administers the site, it doesn't operate tests. Applied to the
eligible-assignee list on both the picker UI and the assign endpoint's
validation, and dropped from the GET /users/operators pool.
push_assignee_update() now falls back to a fresh Jira account-id lookup
when the assignee hasn't logged in yet (so jira_account_id is still
empty), instead of silently no-op'ing — assignment now syncs to Jira
immediately regardless of whether the assignee has ever logged into
Aegis before.
The previous fix for the literal '\u2014' JSX text bug only replaced
the escape sequence with a real em dash but left the leading backslash
character behind, so it rendered as '\—' instead of '—'.
Admin administers the site — it no longer has any override on
validate-red/blue, review-red/blue, resolve-dispute, request-discussion,
reopen, hold, resume, assign-operators, or classification-update.
Introduces require_any_role_strict(), a role dependency without the
global admin bypass, so these specific endpoints truly exclude admin
instead of only removing it from the (redundant) role tuple.
Managers gain the ability to assign red_tech/blue_tech operators
(POST /tests/{id}/assign, GET /users/operators) alongside leads, since
that's coordination, not resolving a ticket.
Also enlarges and repositions the operator-assignment controls next
to the Start Execution button, and fixes a literal '\u2014' rendering
as text instead of an em dash in the test detail technique line.
GET /system/jira-config was admin-only, so non-admin users hit a 403
and the frontend silently fell back to a hardcoded jira.atlassian.com
base URL when building /browse/{key} links. Opened it to any
authenticated user (still no secrets returned).
Also drops the redundant 'security-test' Jira label, and adds a
page-size selector (10/25/50/100) and a 'no existing test yet' filter
to the Test Catalog.
Adds GET /users/operators (leads+admin) and wires a lead-only assign
control into the test detail header, calling the existing but
previously unreachable POST /tests/{id}/assign endpoint. Manual
assignment now also pushes the Jira ticket assignee immediately
instead of waiting for the operator to start execution.
Also adds test.platform as a kebab-case label on Jira ticket creation.
The auto-create-ticket call was sending customfield_11233 (a field that
doesn't exist in the project) instead of the real Data Sensitivity field
customfield_11814, which made Jira reject the WHOLE issue and silently
block ticket creation for every new test.
Also replaces Aegis's invented 4-tier data_classification scheme
(public_release/general_use/confidential/restricted) with the 6 values
Jira's Data Sensitivity field actually uses (public/general_use/
internal_use_only/trusted_people/customer_content/pii), since that is
the classification the organization actually applies. Includes a data
migration remapping existing rows and a defensive retry if Jira ever
rejects an unscreened custom field again.
Techniques (/techniques) and Coverage Matrix (/matrix) showed the same
underlying data (Technique.status_global) with completely different
visual styles — discrete Tailwind status colors + ID+name cells on
Techniques, vs. a continuous score-gradient hex color + ID-only cells
(name via tooltip) on the heatmap. Coverage Matrix also has 3 layers
(Threat Actor, Detection Rules, Campaign) with no Techniques equivalent,
so Techniques was the strict subset — merged into Coverage Matrix rather
than the other way around.
- HeatmapCell now uses the same discrete status-color palette as the old
TechniqueCell (cellColors.ts), shows the technique name inline, and
keeps zoom/virtualization/hover-tooltip untouched.
- HeatmapLegend restyled to match (swatch + label + description per
tier, review_required as an overlay badge) instead of the old
hex-gradient bar.
- MatrixPage gains a Matrix/List view toggle and a status filter
(coverage/threat-actor layers only) — both ported from the old
Techniques page — so nothing it offered is lost.
- /techniques now redirects to /matrix; removed TechniquesPage,
AttackMatrix, TechniqueCell (fully superseded). /techniques/:mitreId
(detail) and /techniques/review-queue are untouched.
Known pre-existing limitation carried over, not introduced or fixed
here: the heatmap only places a multi-tactic technique under its first
tactic column (_format_tactic takes tactic_str.split(",")[0]), while the
old Techniques matrix placed it under every tactic it belongs to.
The "200" badge on the Validated Tests page was a hardcoded query limit,
not the real total — silently hiding results beyond the 200th once there
were more validated tests than that. Now shows the true total via the new
/tests/count endpoint, and warns when the fetched page doesn't cover all
matches.
Adds filter controls beyond the existing name/technique text search:
technique (MITRE ID or name), platform, attack outcome, detection
outcome, and a validated-date range — all pushed down to the server via
the new GET /tests filters instead of only ever searching the current
page client-side.
- TestsPage gains a "My Reviews" toggle for red_lead/blue_lead, separate
from "My Tasks" — filters by reviewer_id at the red_review/blue_review
gate instead of the unrelated in_review validation stage
- Test Catalog cards now warn when a technique already has existing
tests, using the backend's new existing_test_count
- ExecutiveDashboardPage drops its duplicate client-side tactic-order
sort now that the backend returns canonical order; time-range filter
now notes which sections it actually scopes
- HeatmapLegend adds the missing review_required swatch (as an overlay
indicator, matching how it's actually rendered on cells) and hover
tooltips reusing StatusBadge's copy instead of being unexplained
static color chips
- Fix a real React key bug in ControlsTable: rows were grouped in a
keyless shorthand fragment, which can misreconcile when the table is
filtered/sorted; switched to a keyed Fragment
- Minor: CompliancePage summary-card grid no longer strands a lone card
on tablet widths
- Wrap 8 previously-raw description fields in MarkdownText so imported
markdown (links, bold, lists, MITRE citations) renders instead of
showing literal syntax: campaign cards, D3FEND countermeasure
descriptions, compliance framework/control descriptions, detection
rule descriptions, threat-actor and technique reference descriptions,
and the RT-import preview description
- Fix the "Validated" status tooltip claiming a "≥2 tests" threshold
when the real rule is ≥1 validated test with full detection; reworded
the adjacent "Partial" tooltip for the same clarity
- Generalize the technique review_required banner text away from
"MITRE ATT&CK sync" — review can also be triggered by Atomic/Caldera/
Elastic/Sigma/LOLBAS imports, intel scans, and stale-detection checks
- Executive Dashboard no longer auto-triggers POST /risk/compute or shows
the Refresh scores button for roles the backend rejects (viewer), which
was causing a 403 on load
- AuthContext now proactively renews the session cookie every 20 minutes
while the app is open, so an actively-used session doesn't rely solely
on the reactive on-401 refresh racing against its own token's expiry
Direct test removal is now gated to draft-only at the service layer
(active campaigns must go through a modification request that a
manager approves). The trash-icon button on the tests table still
showed for active campaigns and would always fail with a 400 —
restrict it to draft, matching the backend gate.
Adds the 4 backend-logged campaign actions missing from ACTION_LABELS
(delete_campaign, generate_campaign, schedule_campaign,
recurring_campaign_run), and wires campaign-timeline invalidation into
the remaining audit-logging mutations on the page (complete, schedule,
approve/reject modification request) so the timeline panel updates
live instead of only on next reload, consistent with submit/approve/
reject campaign.
Previously action/testId/justification only reset inside the success
handler, so cancelling and reopening the modal left stale form values.
Mirrors the reset-on-close pattern already used by AddTestToCampaignModal.
The backend endpoint no longer accepts a force query param (a draft
campaign can never have a future start_date to force past). No frontend
code currently calls this function, but keep it matching the real
backend contract in case a future admin UI wires it up.
- Single admin Jira account stored in system_configs (jira.admin_email, jira.admin_api_token)
- Admin Tempo token in system_configs (tempo.admin_token)
- get_admin_jira_client() + has_admin_jira_configured() replace per-user auth in all lifecycle hooks
- lookup_user_jira_account_id() auto-discovers each user's Atlassian accountId by email on login
- auto_log_test_worklog() now uses admin Tempo token and logs both red+blue team time
- Settings > Jira: admin credentials fields + test buttons moved to admin Jira tab
- Profile section simplified: jira_account_id shown read-only (auto-detected)
Tar Slip (CWE-22) — 3 import services:
threat_actor, lolbas, caldera: add path validation before extractall()
to prevent malicious zip members with ../ escaping the target directory.
(sigma, elastic, atomic already had this protection)
Path Traversal (CWE-23) — professional_reports.py:
Add _assert_safe_report_path() check on all 5 report endpoints to
verify the generated filepath stays within REPORT_OUTPUT_DIR.
Open Redirect (CWE-601) — sso.py:
Validate IdP redirect URL scheme (must be http/https) before
issuing RedirectResponse, blocking javascript: and data: redirects.
DOM XSS (CWE-79) — 4 frontend pages:
Create src/utils/url.ts with safeUrl() that rejects non-http/https
protocols; apply to actor.mitre_url, ref.url, intel.url.
Sanitize framework name to alphanumeric-only before DOM insertion.
Restrict evidence MIME types to an explicit safe allowlist (png/jpg/gif/webp).
Hardcoded credentials (CWE-798):
verify_gaps.py, create_wiki.py: replace literal passwords with
environment variable reads (AEGIS_ADMIN_PASSWORD, GITEA_PASSWORD).
npm ci installs exact versions from package-lock.json with no implicit
resolution, making builds fully reproducible and guaranteed to use the
audited safe dependency versions.
SystemPage now only shows operational content: MITRE Sync, Intel Scan,
ATT&CK Evaluations, Scheduled Jobs, and Template Management.
Settings gets two new admin-only tabs:
- "SSO / Azure AD": full SAML 2.0 wizard (5-step setup for Azure AD)
- "System": System Status + Version Info + Configuration Export/Import