Files
cve-dashboard/.kiro/specs/ticket-linking/requirements.md

8.4 KiB

Requirements Document

Introduction

The CVE Dashboard currently tracks three entity types — Archer tickets (risk acceptance exceptions), Jira tickets (work items), and CVEs (vulnerabilities) — but relationships between them are limited to a single primary CVE foreign key on each ticket. This feature introduces a generic ticket_links table that enables bidirectional, many-to-many associations between any combination of these entities. Users will be able to view, create, and remove links from ticket detail views in the UI.

Glossary

  • Link_Service: The backend service responsible for creating, querying, and deleting ticket links.
  • Link_UI: The frontend component that displays linked items and provides controls for adding/removing links.
  • Ticket_Links_Table: The PostgreSQL table storing associations between entities.
  • Entity: One of the three linkable types in the system — an Archer ticket, a Jira ticket, or a CVE.
  • Source: The entity on the left side of a link record.
  • Target: The entity on the right side of a link record.
  • Entity_Type: A classification string identifying the kind of entity: archer, jira, or cve.
  • Entity_ID: The unique identifier for an entity within its type (e.g., EXC-6056, STEAM-1234, CVE-2025-0905).
  • Relationship: A label describing the nature of a link (e.g., related, spawned_by, blocks).

Requirements

User Story: As a database administrator, I want a dedicated table for storing entity associations, so that any entity can be linked to any other entity without schema changes.

Acceptance Criteria

  1. THE Ticket_Links_Table SHALL store a source entity (source_type, source_id) and a target entity (target_type, target_id) in each row.
  2. THE Ticket_Links_Table SHALL enforce a UNIQUE constraint on the combination of (source_type, source_id, target_type, target_id) to prevent duplicate links.
  3. THE Ticket_Links_Table SHALL restrict source_type and target_type values to archer, jira, or cve.
  4. THE Ticket_Links_Table SHALL store a relationship label defaulting to related.
  5. THE Ticket_Links_Table SHALL record the user who created the link via a created_by foreign key to the users table.
  6. THE Ticket_Links_Table SHALL record the creation timestamp defaulting to the current time.
  7. THE Ticket_Links_Table SHALL include indexes on (source_type, source_id) and (target_type, target_id) to support efficient bidirectional queries.

User Story: As a security analyst, I want to create a link between two entities, so that I can document relationships between Archer exceptions, Jira work items, and CVEs.

Acceptance Criteria

  1. WHEN a valid source entity, target entity, and relationship are provided, THE Link_Service SHALL insert a new row into the Ticket_Links_Table and return the created link record.
  2. WHEN the source entity and target entity are identical (same type and same ID), THE Link_Service SHALL reject the request with a validation error.
  3. WHEN a link between the same source and target already exists, THE Link_Service SHALL reject the request with a conflict error indicating the duplicate.
  4. WHEN the source_type or target_type is not one of archer, jira, or cve, THE Link_Service SHALL reject the request with a validation error.
  5. WHEN the entity ID format does not match the expected pattern for its type, THE Link_Service SHALL reject the request with a validation error.
  6. THE Link_Service SHALL require the user to be authenticated before creating a link.
  7. THE Link_Service SHALL log an audit entry when a link is successfully created.

User Story: As a security analyst, I want to see all items linked to a given entity, so that I can understand the full context of a ticket or vulnerability.

Acceptance Criteria

  1. WHEN an entity type and entity ID are provided, THE Link_Service SHALL return all links where the entity appears as either source or target (bidirectional query).
  2. THE Link_Service SHALL return each linked entity's type, ID, relationship label, creator, and creation timestamp.
  3. WHEN no links exist for the given entity, THE Link_Service SHALL return an empty list.
  4. THE Link_Service SHALL require the user to be authenticated before querying links.

User Story: As a security analyst, I want to remove a link between two entities, so that I can correct mistakes or remove outdated associations.

Acceptance Criteria

  1. WHEN a valid link ID is provided, THE Link_Service SHALL delete the corresponding row from the Ticket_Links_Table.
  2. WHEN the provided link ID does not exist, THE Link_Service SHALL return a not-found error.
  3. THE Link_Service SHALL require the user to be authenticated before deleting a link.
  4. THE Link_Service SHALL log an audit entry when a link is successfully deleted.

Requirement 5: Display Linked Items in the UI

User Story: As a security analyst, I want to see a "Linked Items" section on ticket detail views, so that I can quickly navigate between related entities.

Acceptance Criteria

  1. WHILE viewing an Archer ticket detail page, THE Link_UI SHALL display a "Linked Items" section listing all entities linked to that Archer ticket.
  2. WHILE viewing a Jira ticket detail page, THE Link_UI SHALL display a "Linked Items" section listing all entities linked to that Jira ticket.
  3. WHEN no links exist for the displayed entity, THE Link_UI SHALL show an empty state message indicating no linked items.
  4. THE Link_UI SHALL display each linked item with its entity type, entity ID, and relationship label.
  5. THE Link_UI SHALL render each linked item's entity ID as a navigable link to that entity's detail view.

User Story: As a security analyst, I want an "Add Link" button on ticket detail views, so that I can create associations without leaving the page.

Acceptance Criteria

  1. WHEN the user clicks the "Add Link" button, THE Link_UI SHALL display a form allowing the user to enter a target entity key and select a relationship type.
  2. WHEN the user submits the form with a valid entity key, THE Link_UI SHALL call the Link_Service to create the link and refresh the Linked Items section.
  3. WHEN the Link_Service returns a validation or conflict error, THE Link_UI SHALL display the error message to the user without clearing the form.
  4. THE Link_UI SHALL auto-detect the entity type from the entered key format (EXC-XXXX for Archer, PROJECT-XXXX for Jira, CVE-YYYY-NNNNN for CVE).

User Story: As a security analyst, I want to remove a link directly from the Linked Items section, so that I can manage associations without navigating away.

Acceptance Criteria

  1. THE Link_UI SHALL display a remove control on each linked item in the Linked Items section.
  2. WHEN the user activates the remove control, THE Link_UI SHALL prompt for confirmation before proceeding.
  3. WHEN the user confirms removal, THE Link_UI SHALL call the Link_Service to delete the link and refresh the Linked Items section.
  4. WHEN the Link_Service returns an error during removal, THE Link_UI SHALL display the error message to the user.

User Story: As a security analyst, I want links to be visible from both sides of the association, so that navigating from either entity shows the connection.

Acceptance Criteria

  1. WHEN a link is created from Entity A to Entity B, THE Link_Service SHALL return that link when queried from Entity A.
  2. WHEN a link is created from Entity A to Entity B, THE Link_Service SHALL return that link when queried from Entity B.
  3. THE Link_Service SHALL treat (source=A, target=B) and (source=B, target=A) as the same logical link — creating one SHALL prevent creating the other.

User Story: As a security analyst, I want the linking feature to be purely additive, so that existing tickets without links continue to function normally.

Acceptance Criteria

  1. THE Link_Service SHALL not require any entity to have links — entities with zero links remain fully functional.
  2. THE Link_Service SHALL not modify the existing cve_id and vendor foreign key columns on archer_tickets or jira_tickets.
  3. WHEN an entity has no links, THE Link_UI SHALL display the empty state without errors or warnings.