Skip to content

fix(devices): flag stale device agents as non-compliant + CSV export#2612

Merged
Marfuen merged 18 commits intomainfrom
mariano/stale-device-compliance
Apr 20, 2026
Merged

fix(devices): flag stale device agents as non-compliant + CSV export#2612
Marfuen merged 18 commits intomainfrom
mariano/stale-device-compliance

Conversation

@Marfuen
Copy link
Copy Markdown
Contributor

@Marfuen Marfuen commented Apr 20, 2026

Summary

  • Fixes "outdated CompAI device agents stop syncing yet still display as fully compliant" — a security blind spot where 34–51 day old check-ins were rendered green.
  • Adds a 7-day staleness threshold. Devices past it are derived as a third state (stale) in the API and the UI, and a daily trigger.dev cron flips isCompliant = false in the DB so direct queries stay honest.
  • Device list gains a client-side CSV export (RFC 4180 with CRLF, UTF-8 BOM, sanitized filename) so IT can pull a worklist of non-syncing devices for agent-update campaigns.

What changed

  • packages/utils/src/devices.ts — pure helpers: STALE_DEVICE_THRESHOLD_DAYS, daysSinceCheckIn, isDeviceStale, getDeviceComplianceStatus.
  • apps/app/src/app/api/people/agent-devices/route.ts + NestJS DevicesService.mapAgentDeviceToDto — both derive complianceStatus on read.
  • apps/app/src/trigger/tasks/device/flag-stale-devices.ts — daily cron at 0 6 * * * UTC.
  • DeviceAgentDevicesList.tsx — three-state compliant badge (Yes / No / Stale (Nd)); stale rows render check badges as neutral em-dashes; new Export CSV button.
  • DeviceComplianceChart.tsx — now counts stale devices toward Non-Compliant (matches the table).

Test plan

  • Navigate to /<orgId>/people → Devices tab.
  • Confirm fresh compliant devices still show green "Yes".
  • Confirm non-compliant fresh devices still show red "No".
  • For a device with lastCheckIn older than 7 days (tweak via Prisma Studio if needed), confirm the row shows a muted "Stale (Nd)" badge and neutral em-dash check badges.
  • Click "Export CSV" — file downloads as devices-<orgId>-<YYYY-MM-DD>.csv, opens cleanly in Excel, non-ASCII device names render correctly.
  • Confirm the compliance pie chart treats stale devices as Non-Compliant (no disagreement with the table).
  • Verify the flag-stale-devices trigger.dev schedule appears in the Trigger.dev dashboard and runs successfully on its first invocation.

Notes

  • No Prisma migration. Threshold is a hardcoded 7 days — promote to an org setting later if requested.
  • The hyphenated 'non-compliant' vocabulary on /v1/devices.status is preserved for external API consumers; internal code uses underscore form.
  • DeviceComplianceChart stays binary (stale counts toward Non-Compliant) rather than adding a third slice.

🤖 Generated with Claude Code


Summary by cubic

Flags device agents that haven’t checked in for 7+ days as a new “stale” state across API and UI, and adds a hardened CSV export for the devices list to support update campaigns. Stale devices are treated as non‑compliant in charts and the external API now returns 'stale'.

  • New Features

    • Three-state compliance in @trycompai/utils (compliant, non_compliant, stale) with helpers; /api/people/agent-devices returns complianceStatus and daysSinceLastCheckIn; v1 devices DTO exposes 'compliant' | 'non-compliant' | 'stale'.
    • Devices list shows a “Stale (Nd)” badge with em‑dash check badges; export CSV button using RFC 4180 (CRLF) with UTF‑8 BOM and sanitized filename devices-<org|slug>-<YYYY-MM-DD>.csv; escapes quotes/newlines and neutralizes CSV injection by prefixing leading = + - @ (tab/CR) with '.
    • Device compliance chart stays binary and counts stale as Non‑Compliant.
  • Bug Fixes

    • Stale/never‑checked‑in devices no longer appear compliant; a daily @trigger.dev/sdk task flips them to isCompliant = false.
    • daysSinceCheckIn guards invalid lastCheckIn strings (treated as stale).
    • External API keeps hyphenated 'non-compliant' while internal code uses underscores.

Written for commit dba59ff. Summary will update on new commits.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app Ready Ready Preview, Comment Apr 20, 2026 7:59pm
comp-framework-editor Ready Ready Preview, Comment Apr 20, 2026 7:59pm
portal Ready Ready Preview, Comment Apr 20, 2026 7:59pm

Request Review

@Marfuen
Copy link
Copy Markdown
Contributor Author

Marfuen commented Apr 20, 2026

@cubic-dev-ai review this

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented Apr 20, 2026

@cubic-dev-ai review this

@Marfuen I have started the AI code review. It will take a few minutes to complete.

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.

3 issues found across 17 files

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="apps/app/src/app/(app)/[orgId]/people/devices/lib/devices-csv.ts">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/people/devices/lib/devices-csv.ts:19">
P2: CSV injection (formula injection) risk: cells starting with `=`, `+`, `-`, or `@` are not sanitized, so a malicious device name like `=CMD(...)` could execute when the file is opened in a spreadsheet. Prefix dangerous leading characters with a single quote or tab to neutralize them.</violation>
</file>

<file name="apps/app/src/trigger/tasks/device/flag-stale-devices.ts">

<violation number="1" location="apps/app/src/trigger/tasks/device/flag-stale-devices.ts:10">
P2: `maxDuration` in trigger.dev is specified in seconds, not milliseconds. `1000 * 60 * 5` = 300,000 seconds (~3.5 days), not 5 minutes. The correct value is `60 * 5` (300 seconds). Other tasks in this repo use the same pattern, so this likely doesn't cause real issues (trigger.dev probably caps at the plan limit), but it's worth noting for correctness.</violation>
</file>

<file name="packages/utils/src/devices.ts">

<violation number="1" location="packages/utils/src/devices.ts:54">
P1: An invalid date string (empty or malformed) causes `daysSinceCheckIn` to return `NaN`, which makes `isDeviceStale` return `false` because `NaN >= 7` is `false` in JavaScript. This silently treats devices with corrupt `lastCheckIn` values as non-stale, undermining the security intent of this feature. Add a `NaN` guard to treat unparseable dates as stale.</violation>
</file>

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

Comment thread packages/utils/src/devices.ts
Comment thread apps/app/src/app/(app)/[orgId]/people/devices/lib/devices-csv.ts
Comment thread apps/app/src/trigger/tasks/device/flag-stale-devices.ts Outdated
Marfuen added 3 commits April 20, 2026 15:33
- packages/utils: guard daysSinceCheckIn against NaN so corrupt
  lastCheckIn strings are treated as stale (P1).
- devices-csv: neutralize CSV formula injection (leading =, +, -, @,
  tab, CR) by prefixing with apostrophe per OWASP (P2).
- flag-stale-devices: maxDuration is in seconds, not ms (P2).
@vercel vercel Bot temporarily deployed to Preview – app April 20, 2026 19:35 Inactive
@vercel vercel Bot temporarily deployed to Preview – portal April 20, 2026 19:35 Inactive
@Marfuen
Copy link
Copy Markdown
Contributor Author

Marfuen commented Apr 20, 2026

@cubic-dev-ai review this

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented Apr 20, 2026

@cubic-dev-ai review this

@Marfuen I have started the AI code review. It will take a few minutes to complete.

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.

No issues found across 17 files

Requires human review: Modifies core compliance logic, introduces a background cron task for bulk DB updates, and changes API/UI across multiple layers, requiring human domain review.

@Marfuen Marfuen merged commit 0d59e8f into main Apr 20, 2026
11 checks passed
@Marfuen Marfuen deleted the mariano/stale-device-compliance branch April 20, 2026 20:17
claudfuen pushed a commit that referenced this pull request Apr 20, 2026
# [3.27.0](v3.26.1...v3.27.0) (2026-04-20)

### Bug Fixes

* **devices:** flag stale device agents as non-compliant + CSV export ([#2612](#2612)) ([0d59e8f](0d59e8f))
* **integration-platform:** preserve VCS url fragments in python matcher ([f820738](f820738))
* **integration-platform:** use toml-aware comment stripping for pyproject.toml ([2cf8979](2cf8979))
* **integrations-catalog:** add global request pacing to prevent 429s ([0dfb793](0dfb793))

### Features

* add compliance timeline to overview (feature flagged) ([26c04d8](26c04d8))
* **integration-platform:** expand validation library detection for sanitized inputs check ([964cb1b](964cb1b))
* **integrations-catalog:** add public catalog ([ea297de](ea297de))

### Reverts

* Revert "chore(app): remove the duplicated prisma/ setup" ([#2621](#2621)) ([a1889a1](a1889a1))
@claudfuen
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.27.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants