lookup_user_jira_account_id() only ever searched by Aegis login email,
ignoring the jira_email override field that exists precisely for users
whose corporate Atlassian email differs from their Aegis login email
(the rest of jira_service.py already documents and uses this priority
order). Found via jesus-huertas, whose real Jira account uses a
different email than his Aegis account.
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.
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.
Jira Cloud's REST API rejects a bare string for select-type custom
fields with 'Specify a valid id or name for <field>' — confirmed live
against the real Jira instance. This silently broke Severity, Data
Sensitivity, Attack success, Attack detected, and Attack contained on
every ticket create/update, masked until now by the non-fatal
exception handling around each call.
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.
atlassian-python-api takes limit, not maxResults. The TypeError was
silently swallowed at DEBUG level, so every non-admin user's Jira
account-id lookup failed even with a valid email and working admin
Jira connection.
Previously only red_executing (-> "In Progress") ever changed the Jira
issue's status; every other Aegis transition (red_review, blue_evaluating,
blue_review, in_review, validated, rejected, disputed) only posted a
comment, leaving the Jira board stuck on whatever status it started with.
Expands push_test_event() to a full TestState -> Jira-status table
covering the whole lifecycle (To-Do -> In Progress -> RT Test Review ->
Queued Blue Team -> In Progress -> Blue Team Test Review -> Validation ->
Done/Rejected/Dispute), matching the Purple Team Jira workflow. Adds two
new lifecycle hooks that the generic dispatch can't handle correctly:
- push_bt_work_started(): blue_evaluating covers both "queued, unclaimed"
and "actively being worked" — needs an explicit push when the blue tech
clicks Start Evaluation, or it never leaves "Queued Blue Team".
- disputed state push: a conflicting lead vote previously produced zero
Jira signal at all.
Also fills two real gaps: reopen_red_review and reopen_blue_review never
synced anything to Jira before. Their target status differs on purpose —
reopen_red_review pushes "In Progress" (Aegis resumes the same operator
immediately, no re-claim), while reopen_blue_review pushes "Queued Blue
Team" (Aegis clears blue_work_started_at, forcing a fresh "Start
Evaluation" click) — verified against the actual field-reset behavior in
test_entity.py rather than assumed symmetry between the two teams.
Maps Test.containment_result (contained/partially_contained/not_contained)
to Yes/Partial/No, mirroring the existing Attack Success/Attack Detected
field push in push_bt_submitted. Field ID was the last missing piece
blocking this from Block 4.