models/DependencyModel.php::addDependency() (~lines 148-178) — the "already exists" check only matches the exact (ticket_id, depends_on_id, dependency_type) tuple. A user can add "A blocks B" from ticket A's page, then separately add "B blocked_by A" from ticket B's page — wouldCreateCycle() normalizes both to the same precedence edge A→B and correctly finds no cycle, so the insert is allowed, but this creates two DB rows describing one real relationship.
Impact: Ticket B's page shows the relationship twice — once under "Dependencies" (the row it owns) and once under "Dependents" (the row A owns) — and removing one leaves the other still implying the same constraint.
Fix: Before inserting, also check for the reverse row (ticket_id=dependsOnId AND depends_on_id=ticketId AND dependency_type=<inverse>) for the blocks/blocked_by pair (and any other inverse pairs, e.g. relates_to which may be its own inverse).
**Severity:** Medium
`models/DependencyModel.php::addDependency()` (~lines 148-178) — the "already exists" check only matches the exact `(ticket_id, depends_on_id, dependency_type)` tuple. A user can add "A blocks B" from ticket A's page, then separately add "B blocked_by A" from ticket B's page — `wouldCreateCycle()` normalizes both to the same precedence edge A→B and correctly finds no *cycle*, so the insert is allowed, but this creates two DB rows describing one real relationship.
**Impact:** Ticket B's page shows the relationship twice — once under "Dependencies" (the row it owns) and once under "Dependents" (the row A owns) — and removing one leaves the other still implying the same constraint.
**Fix:** Before inserting, also check for the reverse row (`ticket_id=dependsOnId AND depends_on_id=ticketId AND dependency_type=<inverse>`) for the `blocks`/`blocked_by` pair (and any other inverse pairs, e.g. `relates_to` which may be its own inverse).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Severity: Medium
models/DependencyModel.php::addDependency()(~lines 148-178) — the "already exists" check only matches the exact(ticket_id, depends_on_id, dependency_type)tuple. A user can add "A blocks B" from ticket A's page, then separately add "B blocked_by A" from ticket B's page —wouldCreateCycle()normalizes both to the same precedence edge A→B and correctly finds no cycle, so the insert is allowed, but this creates two DB rows describing one real relationship.Impact: Ticket B's page shows the relationship twice — once under "Dependencies" (the row it owns) and once under "Dependents" (the row A owns) — and removing one leaves the other still implying the same constraint.
Fix: Before inserting, also check for the reverse row (
ticket_id=dependsOnId AND depends_on_id=ticketId AND dependency_type=<inverse>) for theblocks/blocked_bypair (and any other inverse pairs, e.g.relates_towhich may be its own inverse).