Skip to content

Add OWASP Slack invitation link expiration check#4417

Merged
arkid15r merged 5 commits intomainfrom
ark/invitataion-link-expiration-monitoring
Mar 28, 2026
Merged

Add OWASP Slack invitation link expiration check#4417
arkid15r merged 5 commits intomainfrom
ark/invitataion-link-expiration-monitoring

Conversation

@arkid15r
Copy link
Copy Markdown
Collaborator

Proposed change

Add OWASP Slack invitation link basic monitorin/alerting via NestBot.

Checklist

  • Required: I followed the contributing workflow
  • Required: I verified that my code works as intended and resolves the issue as described
  • Required: I ran make check-test locally: all warnings addressed, tests passed
  • I used AI for code, documentation, tests, or communication related to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1bdd7fc9-75c6-43f9-af96-c734d2e8f70c

📥 Commits

Reviewing files that changed from the base of the PR and between 0427d0d and 8af0723.

📒 Files selected for processing (3)
  • backend/apps/github/utils.py
  • backend/tests/apps/slack/commands/slack_check_invite_link_test.py
  • backend/tests/apps/slack/models/workspace_test.py
✅ Files skipped from review due to trivial changes (1)
  • backend/tests/apps/slack/commands/slack_check_invite_link_test.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/apps/github/utils.py

Summary by CodeRabbit

  • New Features

    • Slack invite link monitoring with configurable member-count alerts and automatic baseline handling
    • Automatic synchronization of public invite-link metadata from version control
  • Tests

    • Comprehensive tests covering baseline setup, alerting behavior, error paths, and message composition
  • Chores

    • Scheduled weekday automation for invite-link checks and a convenience make target to run the check
    • Minor utility/package documentation updates and spelling dictionary entry added

Walkthrough

Adds Slack invite-link monitoring: new GitHub utility to find the invite commit, a Django management command to check member thresholds and post Slack alerts, model fields and migrations to track invite metadata and alerting state, Makefile/cron entries to run the check, utility exports, and tests.

Changes

Cohort / File(s) Summary
GitHub Utilities
backend/apps/github/utils.py
Add get_latest_invite_link_commit() to scan repository commits for a file and message substring; returns commit datetime and SHA. Introduces timezone handling, GithubException imports, and default repo constants.
Slack Management Command
backend/apps/slack/management/commands/slack_check_invite_link.py
New Django management command that syncs invite-link metadata from GitHub, initializes or evaluates invite baselines and thresholds, posts Slack alerts (with optional CC), and records last-alert timestamps; includes error handling for Slack/GitHub failures.
Models & Migrations
backend/apps/slack/models/workspace.py, backend/apps/slack/migrations/0020_workspace_invite_link_fields.py, backend/apps/slack/migrations/0021_alter_workspace_invite_link_alert_channel_id_and_more.py
Add Workspace fields for invite-link tracking and alerting (channel ID, user IDs, member offset, created_at, commit_sha, last_alert_sent_at, member_count) and invite_link_alert_threshold property; migrations add and then adjust these fields.
Makefile & Cron
backend/apps/slack/Makefile, cron/production
Add slack-check-invite-link Makefile target and a weekday cron entry to run slack-sync-data then slack-check-invite-link, with logs redirected.
Package & Utils
backend/apps/slack/__init__.py, backend/apps/slack/utils/__init__.py, backend/apps/slack/utils/format.py
Add module docstring, create apps.slack.utils package re-exporting format helpers, and update format module docstring.
Tests
backend/tests/apps/slack/commands/slack_check_invite_link_test.py, backend/tests/apps/slack/models/workspace_test.py
Add comprehensive tests for the management command (baseline init, threshold evaluation, Slack posting including CC, and error paths) and unit tests for invite_link_alert_threshold.
Other
cspell/custom-dict.txt
Add custom dictionary entry ZYXW.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

backend

Suggested reviewers

  • kasya
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a Slack invitation link expiration check feature with monitoring and alerting capabilities.
Description check ✅ Passed The description is related to the changeset, mentioning the addition of Slack invitation link monitoring/alerting via NestBot, which aligns with the implemented changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ark/invitataion-link-expiration-monitoring

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 11 files

Confidence score: 4/5

  • This PR looks safe to merge with minimal risk: the only flagged item is a moderate, localized null-handling gap rather than a broad behavioral regression.
  • In backend/apps/slack/management/commands/slack_check_invite_link.py, calling .strip() on a NULL invite_link_commit_sha can raise an AttributeError, which could cause the management command to fail for records missing that field.
  • Severity/confidence (4/10, 7/10) suggests a real but limited impact area, so this is a small robustness fix rather than a merge-blocking defect.
  • Pay close attention to backend/apps/slack/management/commands/slack_check_invite_link.py - guard invite_link_commit_sha before .strip() to prevent runtime errors on NULL values.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/apps/slack/management/commands/slack_check_invite_link.py">

<violation number="1" location="backend/apps/slack/management/commands/slack_check_invite_link.py:61">
P2: Handle a missing invite_link_commit_sha before calling .strip() to avoid an AttributeError when the field is NULL.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
cron/production (1)

6-6: Consider a lock (flock) for this new cron chain.

Line 6 can overlap with the next run if a job stalls, which may duplicate alerts/log churn. Wrapping the chain with a lockfile would harden this path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cron/production` at line 6, Wrap the existing cron command chain that runs
"make slack-sync-data" and "make slack-check-invite-link" in a flock-based lock
to prevent overlapping runs: acquire a named lockfile (e.g.
slack-sync-data.lock) and run the cd && make ... && make ... sequence under that
lock (use non-blocking or a reasonable timeout as desired) while preserving the
existing log redirections so stalled jobs cannot start a second concurrent run.
backend/tests/apps/slack/commands/slack_check_invite_link_test.py (2)

384-386: Consider adding a test case for mixed valid and blank IDs.

The tests cover empty/blank-only lists, but a case with mixed valid and blank IDs (e.g., ["U123", "", "U456"]) would verify filtering works correctly when some IDs are valid.

🧪 Suggested additional test
def test_filters_blank_ids_from_valid_list(self):
    cmd = SlackCheckInviteLinkCommand()
    result = cmd.invite_link_alert_cc_line(["U123", "", "U456"])
    assert result == "\n\ncc: <@U123> <@U456>"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/tests/apps/slack/commands/slack_check_invite_link_test.py` around
lines 384 - 386, Add a unit test that verifies invite_link_alert_cc_line filters
out blank IDs when presented with a mixed list; specifically call
SlackCheckInviteLinkCommand().invite_link_alert_cc_line with a list like
["U123", "", "U456"] and assert the output contains only the valid mentions
(e.g., "\n\ncc: <@U123> <@U456>"), ensuring blank strings are ignored.

52-56: Add comments explaining intentionally empty stub methods.

SonarCloud flagged these empty methods. Since they're intentional no-op stubs for the in-memory test double, add brief comments to document this.

📝 Proposed fix
     def save(self, update_fields=None):
-        pass
+        pass  # No-op: in-memory stub, no DB persistence needed.

     def refresh_from_db(self):
-        pass
+        pass  # No-op: state is already in-memory.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/tests/apps/slack/commands/slack_check_invite_link_test.py` around
lines 52 - 56, The empty methods save and refresh_from_db in the test double are
intentional no-op stubs and should be documented to silence SonarCloud and help
future readers; update the save(self, update_fields=None) and
refresh_from_db(self) definitions to include brief inline comments explaining
they are intentionally left blank because this is an in-memory test double and
persistence/refresh are not required in tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/apps/github/utils.py`:
- Around line 174-176: Replace use of django.utils.timezone.utc with the stdlib
UTC object: in the block where timezone.is_naive(dt) and timezone.make_aware(dt,
timezone.utc) are used (around the dt handling in utils.py), change the second
argument to datetime.timezone.utc and ensure datetime is imported at the module
top if not already; keep the existing calls to timezone.is_naive and
timezone.make_aware unchanged aside from swapping timezone.utc to
datetime.timezone.utc to restore Django 6 compatibility.

---

Nitpick comments:
In `@backend/tests/apps/slack/commands/slack_check_invite_link_test.py`:
- Around line 384-386: Add a unit test that verifies invite_link_alert_cc_line
filters out blank IDs when presented with a mixed list; specifically call
SlackCheckInviteLinkCommand().invite_link_alert_cc_line with a list like
["U123", "", "U456"] and assert the output contains only the valid mentions
(e.g., "\n\ncc: <@U123> <@U456>"), ensuring blank strings are ignored.
- Around line 52-56: The empty methods save and refresh_from_db in the test
double are intentional no-op stubs and should be documented to silence
SonarCloud and help future readers; update the save(self, update_fields=None)
and refresh_from_db(self) definitions to include brief inline comments
explaining they are intentionally left blank because this is an in-memory test
double and persistence/refresh are not required in tests.

In `@cron/production`:
- Line 6: Wrap the existing cron command chain that runs "make slack-sync-data"
and "make slack-check-invite-link" in a flock-based lock to prevent overlapping
runs: acquire a named lockfile (e.g. slack-sync-data.lock) and run the cd &&
make ... && make ... sequence under that lock (use non-blocking or a reasonable
timeout as desired) while preserving the existing log redirections so stalled
jobs cannot start a second concurrent run.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f84ce200-b858-4c64-86a9-376136b60152

📥 Commits

Reviewing files that changed from the base of the PR and between 76d6657 and 7678a24.

📒 Files selected for processing (11)
  • backend/apps/github/utils.py
  • backend/apps/slack/Makefile
  • backend/apps/slack/__init__.py
  • backend/apps/slack/management/commands/slack_check_invite_link.py
  • backend/apps/slack/migrations/0020_workspace_invite_link_fields.py
  • backend/apps/slack/models/workspace.py
  • backend/apps/slack/utils/__init__.py
  • backend/apps/slack/utils/format.py
  • backend/tests/apps/slack/commands/slack_check_invite_link_test.py
  • cron/production
  • cspell/custom-dict.txt

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 28, 2026

Codecov Report

❌ Patch coverage is 86.89655% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 99.17%. Comparing base (2fba720) to head (8af0723).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
backend/apps/github/utils.py 24.00% 19 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4417      +/-   ##
==========================================
- Coverage   99.28%   99.17%   -0.11%     
==========================================
  Files         521      522       +1     
  Lines       16507    16648     +141     
  Branches     2262     2279      +17     
==========================================
+ Hits        16389    16511     +122     
- Misses         68       87      +19     
  Partials       50       50              
Flag Coverage Δ
backend 99.64% <86.89%> (-0.16%) ⬇️
frontend 97.84% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...ack/management/commands/slack_check_invite_link.py 100.00% <100.00%> (ø)
backend/apps/slack/models/workspace.py 100.00% <100.00%> (ø)
backend/apps/slack/utils/format.py 100.00% <ø> (ø)
backend/apps/github/utils.py 69.35% <24.00%> (-30.65%) ⬇️

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 2fba720...8af0723. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@arkid15r arkid15r changed the title Ark/invitataion link expiration monitoring Add OWASP Slack invitation link expiration check Mar 28, 2026
cubic-dev-ai[bot]
cubic-dev-ai bot previously approved these changes Mar 28, 2026
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 4 files (changes from recent commits).

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
backend/apps/slack/models/workspace.py (1)

43-101: Deduplicate the 350 offset default to a single constant.

Line 47 and Line 99 repeat the same default value. Centralizing it avoids drift between field default and runtime fallback.

Proposed refactor
 class Workspace(TimestampedModel):
     """Slack Workspace model."""
+    INVITE_LINK_ALERT_MEMBER_OFFSET_DEFAULT = 350
@@
     invite_link_alert_member_offset = models.PositiveSmallIntegerField(
@@
-        default=350,
+        default=INVITE_LINK_ALERT_MEMBER_OFFSET_DEFAULT,
@@
     def invite_link_alert_threshold(self) -> int | None:
@@
         offset = (
             self.invite_link_alert_member_offset
             if self.invite_link_alert_member_offset is not None
-            else 350
+            else self.INVITE_LINK_ALERT_MEMBER_OFFSET_DEFAULT
         )
         return self.invite_link_member_count + offset
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/apps/slack/models/workspace.py` around lines 43 - 101, Create a
single module-level constant (e.g. INVITE_LINK_ALERT_OFFSET_DEFAULT = 350) and
use it everywhere the literal 350 appears so the field default and runtime
fallback stay in sync; update the PositiveSmallIntegerField default for
invite_link_alert_member_offset and replace the hardcoded 350 in the
invite_link_alert_threshold property fallback with that constant (ensure the
constant name is exported/placed above the Workspace model so
invite_link_alert_member_offset and method invite_link_alert_threshold reference
it).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/tests/apps/slack/commands/slack_check_invite_link_test.py`:
- Around line 57-61: The test-double methods save and refresh_from_db currently
have empty bodies which trips static analysis; update the methods in the test
double to include explicit no-op docstrings or comments (e.g., """No-op in
tests: intentionally does not persist/refresh state.""") so intent is clear to
linters and readers, leaving the pass statement in place; reference the save and
refresh_from_db method definitions in the test class and add the
docstrings/comments inside those methods.

---

Nitpick comments:
In `@backend/apps/slack/models/workspace.py`:
- Around line 43-101: Create a single module-level constant (e.g.
INVITE_LINK_ALERT_OFFSET_DEFAULT = 350) and use it everywhere the literal 350
appears so the field default and runtime fallback stay in sync; update the
PositiveSmallIntegerField default for invite_link_alert_member_offset and
replace the hardcoded 350 in the invite_link_alert_threshold property fallback
with that constant (ensure the constant name is exported/placed above the
Workspace model so invite_link_alert_member_offset and method
invite_link_alert_threshold reference it).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 486a8d14-5971-49d1-85fd-6b7d78904f4e

📥 Commits

Reviewing files that changed from the base of the PR and between 7678a24 and 0427d0d.

📒 Files selected for processing (4)
  • backend/apps/slack/management/commands/slack_check_invite_link.py
  • backend/apps/slack/migrations/0021_alter_workspace_invite_link_alert_channel_id_and_more.py
  • backend/apps/slack/models/workspace.py
  • backend/tests/apps/slack/commands/slack_check_invite_link_test.py
✅ Files skipped from review due to trivial changes (1)
  • backend/apps/slack/management/commands/slack_check_invite_link.py

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 3 files (changes from recent commits).

@arkid15r arkid15r marked this pull request as ready for review March 28, 2026 20:49
@arkid15r arkid15r requested a review from kasya as a code owner March 28, 2026 20:49
Copy link
Copy Markdown
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍🏼

@arkid15r arkid15r added this pull request to the merge queue Mar 28, 2026
Merged via the queue into main with commit 9808551 Mar 28, 2026
41 checks passed
@arkid15r arkid15r deleted the ark/invitataion-link-expiration-monitoring branch March 28, 2026 22:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants