Problem 1: Atlas sync was querying ALL host_ids from ivanti_findings
regardless of BU, writing 'no plan' entries for ACCESS-OPS hosts that
Atlas doesn't cover. Now the sync respects the user's active teams scope
(passed via query param) and falls back to IVANTI_MANAGED_BUS when no
scope is provided.
Problem 2: Atlas /metrics and /status endpoints returned unscoped data
from the full cache, so changing scope didn't update the Atlas Coverage
donut or badge counts. Both endpoints now accept a teams query param and
JOIN against ivanti_findings to scope results by BU.
Frontend changes:
- fetchAtlasStatus and fetchAtlasMetrics now pass teams param
- Atlas sync button passes active teams to the sync endpoint
- Scope change (adminScope) triggers Atlas data refresh
Also purged 6,461 polluted cache entries for non-managed BU hosts.
- Rewrite .gitlab-ci.yml with proper stages, blocking tests, staging
environment on dev box, and SSH-based production deploy to 71.85.90.6
- Add POST /api/health endpoint for pipeline verification
- Add POST /atlas/hosts/:hostId/refresh-cache for Atlas cache staleness
- AtlasSlideOutPanel: auto-resolve qualys_id from Atlas vulnerabilities,
prefer qualys_id over active_host_findings_id, retry on failure
- Add FeedbackModal component with bug report button in header and
feature request in UserMenu, creates GitLab issues via /api/feedback
- Fix all frontend test failures (ESM transforms, TextDecoder polyfill,
fast-check resolution, App.test.js boilerplate replacement)
- Fix root package.json test script to run jest
- Add deploy/ directory with staging systemd service and setup script
- All 16 route files now import pool from ../db directly
- Removed db parameter from all factory functions
- All callbacks replaced with async/await pool.query()
- All ? placeholders converted to $1, $2... numbered params
- datetime('now') → NOW(), INSERT OR IGNORE → ON CONFLICT DO NOTHING
- LIKE → ILIKE for case-insensitive searches
- Error detection: err.code === '23505' for unique violations
- server.js no longer passes pool/db/requireAuth to route factories
- Only ivantiFindings.js still receives pool (pending task 8 rewrite)
- Add bu_teams column to users table (migration + fresh schema)
- Create shared KNOWN_TEAMS constant and validateTeams helper
- Expose user teams in auth middleware, login, and /me responses
- Add bu_teams CRUD to user management routes with audit logging
- Make Ivanti FINDINGS_FILTERS configurable via IVANTI_BU_FILTER env var
- Add query-time team filtering to GET /findings and /findings/counts
- Update AuthContext with teams helpers and admin scope toggle
- Create AdminScopeToggle component (My Teams / All BUs)
- Scope ReportingPage findings fetch by user teams
- Scope CompliancePage team selector by user teams
- Scope ExportsPage findings exports by user teams
- Add BU teams multi-select to UserManagement create/edit forms
- Display team badges in user list table
Integrate Atlas InfoSec API to manage compliance action plans directly from
the ReportingPage. Users can view, create, and update action plans for host
findings without switching to the Atlas web tool.
Backend:
- Add atlasApi.js helper with Basic Auth, TLS skip, GET/PUT/PATCH/POST
- Add atlas_action_plans_cache migration for SQLite cache table
- Add atlas.js router with sync, status, and proxy CRUD endpoints
- Mount Atlas router at /api/atlas in server.js
- Extract hostId from Ivanti host findings during sync
Frontend:
- Add AtlasBadge component (amber=needs plan, green=has plan)
- Add AtlasSlideOutPanel with plan list, create form, edit capability
- Separate active plans from inactive history in collapsible section
- Custom dark-themed plan type dropdown
- Optimistic local state shows pending plans immediately after creation
- Atlas sync button on ReportingPage toolbar
- Prepopulate finding ID in create form from clicked row
Environment:
- Add ATLAS_API_URL, ATLAS_API_USER, ATLAS_API_PASS, ATLAS_SKIP_TLS to .env.example