Auto-populate description field when creating Jira tickets from the Archer
page with ticket metadata (EXC number, CVE, vendor, status, Archer URL).
Previously the description was always empty, requiring manual entry.
Includes security audit fixes for SQL injection prevention and input
validation in compliance, VCL multi-vertical, and CCP metrics routes.
Updates security audit tracker documentation.
Normalize the date in groupByHostname() to handle PostgreSQL Date objects,
and add .slice(0,10) in the frontend render as a safety net. Prevents the
full ISO timestamp (2026-05-15T00:00:00.000Z) from displaying in the table.
Add ci.resolution_date and ci.remediation_plan to the GET /items endpoint
SELECT clause and update groupByHostname() to aggregate them as first-non-null
across each hostname's metric rows. The frontend already rendered these columns
but the list endpoint never fetched the data from the database.
Includes exploration and preservation property tests for groupByHostname().
Per-metric remediation plan scoping (GitLab issue #19):
- Add metric_id column to compliance_item_history table (migration)
- Extend PATCH /items/:hostname/metadata to accept metric_id/metric_ids
for targeting specific metrics instead of all active items
- Add MetricChipSelector UI in detail panel for choosing which metrics
to apply resolution_date and remediation_plan changes to
- Display per-metric labels (MetricChip or 'All metrics') on history entries
- Backward compatible: omitting metric_ids preserves hostname-level behavior
CI/CD pipeline improvements:
- Add migration idempotency integration test (runs against real Postgres)
- Add post-deploy smoke tests for compliance and VCL endpoints
- Bump lint --max-warnings from 10 to 25
- Configure varsIgnorePattern for _ prefix convention on unused vars
Closes#19
Deduplicate (hostname, metric_id) rows across verticals using DISTINCT ON in
GET /items, GET /items/:hostname, GET /vcl/stats (heavy-hitters + forecast),
GET /mttr, and persistUpload() snapshot block. Add defensive groupByHostname
Set and hostname_status CTE for snapshot classification.
Includes 38 property-based tests (11 exploration + 27 preservation) covering
all six affected sites.
Closes#13
Aggregate /trends, /top-recurring, /category-trend by report_date instead of
per-upload row. Add sibling-upload disclosure to /summary. Filter persistUpload
snapshot query by the upload's vertical to prevent cross-vertical contamination.
Fixes GitLab #12 (reported by nkapur — STEAM active findings chart showed 3
entries for 5/11 after uploading three vertical data sets for that date).
Includes 30 property-based tests covering bug condition and preservation.
New table compliance_item_history stores an append-only audit trail of
changes to resolution_date and remediation_plan. The current values remain
on compliance_items for fast VCL reporting queries (no double-counting).
Backend:
- Migration: creates compliance_item_history with indexes
- PATCH /items/:hostname/metadata: records old→new in history before updating,
accepts optional change_reason field (max 500 chars)
- GET /items/:hostname: returns history array (last 10 entries, newest first)
- POST /vcl/bulk-commit: records history for each changed field per hostname
Frontend:
- ComplianceDetailPanel: added change reason input below Save button
- Added Change History section showing field changes with timestamps,
usernames, old→new values, and reasons
- Re-fetches detail after save to show updated history immediately
Tests updated to match new transaction-based PATCH flow.
The /summary endpoint was fetching the most recent upload regardless of
vertical, which on dev was a PRDCT_VSO multi-vertical upload. Now it
looks for AEO uploads (vertical IS NULL) first, then falls back to the
NTS_AEO multi-vertical upload.
The /items endpoint now includes items from both vertical IS NULL and
vertical = 'NTS_AEO' so the AEO compliance page shows devices uploaded
through either flow.
- 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)
Bugs fixed:
- knowledgeBase.js: logAudit calls converted from positional args to object signature
- archerTickets.js: targetType/targetId renamed to entityType/entityId
- server.js: single CVE delete now has cascade/compliance check for Standard_User
Unprotected endpoints secured:
- ivantiTodoQueue.js: POST/PUT/DELETE now require Admin or Standard_User
- ivantiFindings.js: PUT note and POST sync now require Admin or Standard_User
- compliance.js: POST notes now requires Admin or Standard_User
- ivantiWorkflows.js: POST sync now requires Admin or Standard_User
- auth.js: cleanup-sessions now requires Admin via requireAuth + requireGroup
Additional fixes:
- ExportsPage.js: canExport() guard blocks Read_Only users
- knowledgeBase.js: Standard_User delete checks created_by ownership
- Migration: added INSERT/UPDATE triggers to enforce valid user_group values
Add 6 Recharts charts in a collapsible Historical Trends panel on the
Compliance page, covering all Tier-1 recommendations from the reporting
design doc.
Backend — 5 new API endpoints:
- GET /api/compliance/trends — active totals + per-team counts per upload
- GET /api/compliance/mttr — mean days to resolution per team
- GET /api/compliance/top-recurring — most persistent active findings by seen_count
- GET /api/compliance/category-trend — category breakdown per upload (future use)
- GET /api/archer-tickets/status-trend — ticket pipeline by creation date + status
Frontend — new ComplianceChartsPanel component:
- Active Findings Over Time (multi-line: total + per-team dashed)
- Change per Report Cycle (stacked bar: new/recurring + resolved)
- Team Compliance Health (multi-line per team)
- Mean Time to Resolution (horizontal bar per team)
- Most Persistent Findings (horizontal bar top-10 by seen_count)
- Archer Exception Pipeline (stacked bar by date + status)
All charts degrade gracefully to a no-data placeholder until uploads
accumulate. Panel is collapsible to stay out of the way when not needed.
Adds recharts dependency to frontend.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Modern Debian/Ubuntu enforces PEP 668 which blocks system-wide pip
installs. The backend now reads PYTHON_BIN from the environment
(defaulting to 'python3') so each server can point to a venv.
Updates README with venv setup instructions.
- Migration: compliance_uploads, compliance_items, compliance_notes tables
with indexes on (hostname, metric_id) identity key and team/status
- Python parser (parse_compliance_xlsx.py): reads NTS_AEO xlsx, extracts
non-compliant assets from all detail sheets, parses Summary sheet for
metric health data and overall scores, outputs JSON to stdout
- Route (/api/compliance): preview/commit upload flow with diff summary,
items endpoint grouped by hostname with seen_count tracking, metric
summary endpoint for health cards, notes endpoints keyed on
(hostname, metric_id) persisting across uploads
- server.js: register compliance router at /api/compliance
- .gitignore: exclude planning docs and xlsx source files