Skip to content

Fix visibility change early return and dualreview cleanup

9ef30aa
Select commit
Loading
Failed to load commit list.
Sign in for the full log view
Closed

feat: Add Linear action item syncing for incidents #139

Fix visibility change early return and dualreview cleanup
9ef30aa
Select commit
Loading
Failed to load commit list.
GitHub Actions / warden: find-bugs completed Apr 30, 2026 in 2m 9s

4 issues

find-bugs: Found 4 issues (2 high, 1 medium, 1 low)

High

update_or_create can steal ActionItems from another incident - `src/firetower/incidents/services.py:274-285`

ActionItem.objects.update_or_create(linear_issue_id=issue["id"], defaults={"incident": incident, ...}) keys solely on linear_issue_id. If the same Linear issue is referenced as a related issue from a different incident's parent (or was previously associated with another incident), this sync will silently reassign the ActionItem to the current incident, mutating another incident's data. The subsequent delete step then removes ActionItems no longer matching by linear_issue_id, but the cross-incident hijack already occurred. This violates data isolation between incidents.

parse_incident_id uses re module without importing it - `src/firetower/incidents/views.py:67-69`

The new parse_incident_id function calls re.escape and re.match, but the import block shown does not include import re. If re is not imported elsewhere in the file, any call to parse_incident_id will raise NameError at runtime, breaking incident ID parsing for endpoints that depend on it.

Also found at:

  • src/firetower/integrations/services/linear.py:142-145

Medium

Token refresh has a TOCTOU race that can clobber concurrent tokens - `src/firetower/integrations/services/linear.py:73-78`

_request_new_token deletes all LinearOAuthToken rows and creates a new one inside transaction.atomic() without row-level locking. If two requests hit _get_access_token near expiry, both will call Linear's token endpoint and race to delete/insert, potentially invalidating a token another in-flight request just stored, and producing redundant token requests against Linear (rate-limit risk). Use select_for_update() or a database-level lock / cache lock so only one worker refreshes at a time.

Also found at:

  • src/firetower/integrations/services/linear.py:125-135

Low

ActionItemListView triggers Linear sync on every GET without throttling at view layer - `src/firetower/incidents/views.py:348-354`

The list() method calls sync_action_items_from_linear(incident) on every GET request. While the description claims throttling exists in the service layer, any unauthenticated-but-permitted user with access can repeatedly hit this endpoint to trigger external Linear API calls. If the service-level throttle is per-incident rather than per-user/IP, this could be abused for resource exhaustion or to amplify load on the Linear API. The broad except Exception swallows all errors silently, which can mask repeated failures.

Also found at:

  • src/firetower/incidents/views.py:380-396

Duration: 122.0s · Tokens: 521.2k in / 5.0k out · Cost: $2.75 (+merge: $0.00)

Annotations

Check failure on line 285 in src/firetower/incidents/services.py

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

update_or_create can steal ActionItems from another incident

`ActionItem.objects.update_or_create(linear_issue_id=issue["id"], defaults={"incident": incident, ...})` keys solely on `linear_issue_id`. If the same Linear issue is referenced as a related issue from a different incident's parent (or was previously associated with another incident), this sync will silently reassign the ActionItem to the current incident, mutating another incident's data. The subsequent delete step then removes ActionItems no longer matching by linear_issue_id, but the cross-incident hijack already occurred. This violates data isolation between incidents.

Check failure on line 69 in src/firetower/incidents/views.py

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

parse_incident_id uses re module without importing it

The new parse_incident_id function calls re.escape and re.match, but the import block shown does not include `import re`. If `re` is not imported elsewhere in the file, any call to parse_incident_id will raise NameError at runtime, breaking incident ID parsing for endpoints that depend on it.

Check failure on line 145 in src/firetower/integrations/services/linear.py

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

[EGV-DET] parse_incident_id uses re module without importing it (additional location)

The new parse_incident_id function calls re.escape and re.match, but the import block shown does not include `import re`. If `re` is not imported elsewhere in the file, any call to parse_incident_id will raise NameError at runtime, breaking incident ID parsing for endpoints that depend on it.

Check warning on line 78 in src/firetower/integrations/services/linear.py

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

Token refresh has a TOCTOU race that can clobber concurrent tokens

`_request_new_token` deletes all `LinearOAuthToken` rows and creates a new one inside `transaction.atomic()` without row-level locking. If two requests hit `_get_access_token` near expiry, both will call Linear's token endpoint and race to delete/insert, potentially invalidating a token another in-flight request just stored, and producing redundant token requests against Linear (rate-limit risk). Use `select_for_update()` or a database-level lock / cache lock so only one worker refreshes at a time.

Check warning on line 135 in src/firetower/integrations/services/linear.py

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

[U3P-SR3] Token refresh has a TOCTOU race that can clobber concurrent tokens (additional location)

`_request_new_token` deletes all `LinearOAuthToken` rows and creates a new one inside `transaction.atomic()` without row-level locking. If two requests hit `_get_access_token` near expiry, both will call Linear's token endpoint and race to delete/insert, potentially invalidating a token another in-flight request just stored, and producing redundant token requests against Linear (rate-limit risk). Use `select_for_update()` or a database-level lock / cache lock so only one worker refreshes at a time.