- Campaign issue type changed from Task to Epic (required to nest under
Initiative OFS-20795 in classic Jira)
- Added customfield_10011 (Epic Name) — required when creating Epics
- Removed JIRA_ISSUE_TYPE_SUBTASK; all tests are now Task regardless of
whether they are inside a campaign or standalone
- Standalone tests use the configured standalone parent (OFS-20798, an
Epic) so Task→Task parent is never attempted
- Campaign tests use the campaign Epic key passed via parent_ticket_override
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
OFS-20798 is a Task (child of OFS-20795 Epic), so tests nested
under it must be Sub-tasks, not Tasks — Task cannot parent Task.
Logic:
- parent_ticket_override (campaign) → Sub-task (unchanged)
- standalone_parent configured and differs from general parent → Sub-task
- only general parent (Epic) → Task
This fixes 'Please select valid parent issue' for standalone tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. Jira status → In Progress on Start Execution
- push_test_event calls set_issue_status("In Progress") when
new_state == "red_executing" (non-fatal, separate try/except)
2. Jira assignee set on Start Execution
- assign_issue() called with actor.jira_account_id when operator
clicks Start (non-fatal)
3. Standalone tests parent ticket (OFS-20798)
- New jira.parent_ticket_standalone config key
- get_jira_parent_ticket_standalone() falls back to parent_ticket
- auto_create_test_issue uses standalone parent for non-campaign tests
- Exposed in /system/jira-config GET+PATCH and SettingsPage UI
4. Tests table: Created + Updated columns
- Add Created column (created_at), fix Updated to show updated_at
- Both use UTC-aware date parsing (append Z if no tz suffix)
- updated_at added to Test TypeScript interface
5. Sortable columns in tests table
- All 7 columns sortable: Name, Technique, State, Current Team,
Platform, Created, Updated
- Click to sort asc, click again to reverse; ChevronUp/Down indicator
- Default sort: Created desc (newest first)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause: Jira rejects Task-under-Task nesting ("Please select valid
parent issue"). Campaign tickets and test tickets were both created as
Task, so nesting test under campaign failed for all 62 APT32 tests.
Fix:
- JIRA_ISSUE_TYPE_CAMPAIGN: "Epic" -> "Task" (was unused, now used)
- JIRA_ISSUE_TYPE_SUBTASK: "Sub-task" (new config key)
- auto_create_campaign_issue: uses JIRA_ISSUE_TYPE_CAMPAIGN (Task)
- auto_create_test_issue: uses Sub-task when parent_ticket_override is
set (campaign context), Task otherwise (standalone)
Hierarchy: OFS-9107 -> Campaign (Task) -> Test (Sub-task)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Jira URL fix:
- JiraLinkPanel now fetches the configured Jira base URL via getJiraConfig()
instead of hardcoding https://jira.atlassian.com; falls back to the old
value if config is not yet loaded
Description fix:
- _build_test_description: renamed 'h3. Procedure' -> 'h3. Proof of Concept'
so the procedure/tool block maps to the correct Jira field label
Tempo debug:
- New POST /system/tempo-test endpoint: checks TEMPO_ENABLED, token,
user jira_account_id, and makes a real API call; always returns HTTP 200
with status field (Cloudflare-safe)
- docker-compose.prod.yml: added TEMPO_ENABLED, TEMPO_API_TOKEN,
TEMPO_DEFAULT_WORK_TYPE env vars (default off, ready to enable)
- SettingsPage: added 'Test Tempo Connection' button in Jira admin tab
with clear feedback showing what's missing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The OFS Jira project does not have the default Jira priority scheme
(Highest/High/Medium/Low/Lowest), causing a 'priority selected is invalid'
error on every ticket creation. Removing the priority field lets Jira use
the project default.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Jira tickets now follow the correct hierarchy:
OFS-9107 (system parent)
├── Standalone test ticket (unchanged — was already working)
└── Campaign ticket (NEW — created on campaign creation)
├── Test 1 ticket (NEW — created per test)
└── Test 2 ticket (NEW — created per test)
Changes:
- jira_service: add auto_create_campaign_issue() — creates campaign
ticket as child of OFS-9107; stores JiraLink(entity_type=campaign)
- jira_service: add get_campaign_jira_key() / get_test_jira_key()
helpers to look up existing Jira links by entity
- jira_service: auto_create_test_issue() gains parent_ticket_override
param — when set, uses it as parent instead of OFS-9107
- campaigns router/create_campaign: triggers auto_create_campaign_issue
after commit
- campaigns router/from-threat-actor: triggers campaign ticket then
iterates campaign_tests and creates each test ticket under it
- campaigns router/add_test_to_campaign: if campaign has a Jira ticket
and the test has none yet, creates test ticket under campaign ticket
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- jira-test now returns {status: "ok"|"error", message: ...} with
HTTP 200 so Cloudflare never intercepts the response
- jira_service strips trailing slash from URL before creating Jira
client (avoids double-slash in REST paths)
- Frontend reads data.status field instead of HTTP status code
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Users can now set a separate Atlassian email for Jira authentication
in Settings → Profile → Jira Integration. Falls back to the Aegis
account email when not set, so existing setups are unaffected.
- Migration b043: adds jira_email column to users table
- User model/schema: expose jira_email read/write
- jira_service: _effective_jira_email() uses jira_email ?? email
- Frontend: replaces read-only email display with editable input
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- backend: add parent_ticket field to JiraConfigOut/JiraConfigUpdate/_JIRA_KEYS
- backend: add get_jira_parent_ticket() helper in jira_service; use it in auto_create_test_issue() to set issue parent
- frontend/api: add jira_token_set to UserMeOut, jira_api_token to UserPreferencesUpdate, and full JiraConfigOut/Update types with getJiraConfig/updateJiraConfig/testJiraConnection functions
- frontend: expand ProfileSection with Jira API token password field (show/hide), token status badge, and account-id field
- frontend: add JiraConfigSection component (admin): enabled toggle, URL, project key, parent ticket, save + test connection
- frontend: add Jira tab (admin-only) with Link2 icon in SettingsPage sidebar
- Add jira_api_token field to User model + migration b042
- Per-user Jira client: user's corporate email + personal Atlassian token
- Admin-configurable Jira URL/project via system_configs (GET/PATCH /system/jira-config + POST /system/jira-test)
- Auto-create Jira ticket when a test is created (non-fatal)
- Push lifecycle comments on every state transition: draft→red_executing→blue_evaluating→in_review→validated/rejected→draft
- Rich ticket descriptions with technique, MITRE ID, priority from severity, labels
- UserOut.jira_token_set (bool) instead of exposing raw token
- PATCH /users/me/preferences now accepts jira_api_token
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Full Jira/Tempo pipeline: link Aegis entities to Jira issues, auto-sync
status hourly, log time internally with integrity hashing, and optionally
push worklogs to Tempo.
- 1.1 JiraLink model + Worklog model: Alembic migration b020 with indexes,
enums (jiralinkentitytype, jirasyncdirection), and integrity_hash column
- 1.2 Jira service: atlassian-python-api wrapper with lazy singleton client,
search/create/sync operations, feature-flagged via JIRA_ENABLED
- 1.3 Jira router: CRUD endpoints for /jira/links, /jira/search,
/jira/create-issue with audit logging and entity-to-issue auto-creation
- 1.4 Tempo service: worklog push via tempo-api-python-client, auto-log from
test completions when TEMPO_ENABLED, graceful fallback on failure
- 1.5 Worklog service + router: immutable internal time records with SHA-256
integrity hash, CRUD at /worklogs, /worklogs/{id}/verify endpoint
- 1.6 Frontend: JiraLinkPanel component (search, link, sync, unlink) and
WorklogTimeline component (timeline view, manual log form) integrated into
TestDetailPage sidebar, CampaignDetailPage grid, TechniqueDetailPage
- 1.7 Jira sync job: APScheduler hourly job syncs all links from Jira,
registered in background scheduler alongside existing jobs