Sync .kiro/ from master — v2.2.0 release batch
New specs: archer-template-library, ccp-metrics-view-restructure, compliance-list-stale-after-sidebar-edit, compliance-metric-estimated-resolution-date, compliance-remediation-display-fix, flexible-jira-ticket-creation, forecast-burndown-chart, granite-loader-export, ivanti-queue-clear-completed-fix, multi-item-jira-ticket, queue-collapsible-sections, vendor-issue-type-dropdown New steering: archer-template-gen.md Updated: migration-registration-check hook, remediation-plan-history spec, gitlab-workflow, tech, versioning steering files
This commit is contained in:
1
.kiro/specs/archer-template-library/.config.kiro
Normal file
1
.kiro/specs/archer-template-library/.config.kiro
Normal file
@@ -0,0 +1 @@
|
||||
{"specId": "69cc3734-7a9a-4d50-be5e-6e47a2806651", "workflowType": "requirements-first", "specType": "feature"}
|
||||
518
.kiro/specs/archer-template-library/design.md
Normal file
518
.kiro/specs/archer-template-library/design.md
Normal file
@@ -0,0 +1,518 @@
|
||||
# Design Document: Archer Template Library
|
||||
|
||||
## Overview
|
||||
|
||||
The Archer Template Library adds a template management system to the STEAM Security Dashboard, allowing editors to store, browse, and reuse pre-filled content for Archer Risk Acceptance forms. Templates are organized by a Vendor > Platform > Model hierarchy and contain the static and semi-static content sections that map directly to the external Archer eGRC application's form fields.
|
||||
|
||||
The feature integrates into the existing Ivanti Todo Queue page where Archer workflow items are worked. When an editor selects an Archer queue item, they can pick a template, view its sections, and copy content to their clipboard for pasting into the external Archer application.
|
||||
|
||||
### Design Decisions
|
||||
|
||||
1. **Separate route module** (`routes/archerTemplates.js`) following the project's factory pattern — keeps template CRUD isolated from existing `archerTickets.js` which handles EXC number tracking.
|
||||
2. **PostgreSQL table** with a composite unique index on `(LOWER(TRIM(vendor)), LOWER(TRIM(platform)), LOWER(TRIM(model)))` to enforce case-insensitive uniqueness at the database level.
|
||||
3. **Single page component** (`ArcherTemplatePage.js`) for the Template Manager, plus a **reusable TemplateSelector component** embedded in the existing `IvantiTodoQueuePage.js` for the queue workflow integration.
|
||||
4. **No separate search endpoint** — the list endpoint handles search/filter via query parameters, keeping the API surface small.
|
||||
5. **Clipboard API** (`navigator.clipboard.writeText`) for copy operations — the dashboard already runs on HTTPS in production.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph Frontend
|
||||
TM[ArcherTemplatePage.js<br/>Template Manager]
|
||||
TS[TemplateSelector.js<br/>Queue Integration]
|
||||
IQ[IvantiTodoQueuePage.js]
|
||||
end
|
||||
|
||||
subgraph Backend
|
||||
R[routes/archerTemplates.js<br/>Express Router]
|
||||
A[helpers/auditLog.js]
|
||||
DB[(PostgreSQL<br/>archer_templates)]
|
||||
end
|
||||
|
||||
TM -->|fetch /api/archer-templates| R
|
||||
TS -->|fetch /api/archer-templates| R
|
||||
IQ -->|embeds| TS
|
||||
R -->|pool.query| DB
|
||||
R -->|logAudit| A
|
||||
A -->|fire-and-forget INSERT| DB
|
||||
```
|
||||
|
||||
### Request Flow
|
||||
|
||||
1. Frontend component calls `/api/archer-templates/*` with session cookie
|
||||
2. `requireAuth()` middleware validates session, attaches `req.user`
|
||||
3. Write operations additionally pass through `requireGroup('Admin', 'Standard_User')`
|
||||
4. Route handler validates input, executes PostgreSQL query via `pool`
|
||||
5. On success of write operations, `logAudit()` is called fire-and-forget
|
||||
6. Response returned to frontend
|
||||
|
||||
---
|
||||
|
||||
## Components and Interfaces
|
||||
|
||||
### Backend: `routes/archerTemplates.js`
|
||||
|
||||
Factory function signature:
|
||||
|
||||
```javascript
|
||||
function createArcherTemplatesRouter() {
|
||||
// Returns Express Router
|
||||
// Imports pool from '../db', auth from '../middleware/auth', logAudit from '../helpers/auditLog'
|
||||
}
|
||||
```
|
||||
|
||||
#### API Endpoints
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| `GET` | `/api/archer-templates` | requireAuth | List/search/filter templates |
|
||||
| `GET` | `/api/archer-templates/:id` | requireAuth | Get single template by ID |
|
||||
| `POST` | `/api/archer-templates` | requireAuth + requireGroup | Create template |
|
||||
| `PUT` | `/api/archer-templates/:id` | requireAuth + requireGroup | Update template |
|
||||
| `DELETE` | `/api/archer-templates/:id` | requireAuth + requireGroup | Delete template |
|
||||
| `POST` | `/api/archer-templates/:id/clone` | requireAuth + requireGroup | Clone template |
|
||||
| `GET` | `/api/archer-templates/hierarchy/vendors` | requireAuth | Distinct vendors list |
|
||||
| `GET` | `/api/archer-templates/hierarchy/platforms` | requireAuth | Distinct platforms for vendor |
|
||||
| `GET` | `/api/archer-templates/hierarchy/models` | requireAuth | Distinct models for vendor+platform |
|
||||
|
||||
#### `GET /api/archer-templates`
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `search` | string | Substring match across vendor, platform, model (case-insensitive). Ignored if empty/whitespace-only. |
|
||||
| `vendor` | string | Exact match filter on vendor (case-insensitive) |
|
||||
| `platform` | string | Exact match filter on platform (case-insensitive) |
|
||||
| `model` | string | Exact match filter on model (case-insensitive) |
|
||||
|
||||
**Response:** `200 OK`
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"vendor": "Harmonic",
|
||||
"platform": "vCMTS",
|
||||
"model": "3.29.1",
|
||||
"environment_overview": "...",
|
||||
"segmentation": "...",
|
||||
"mitigating_controls": "...",
|
||||
"additional_info": "...",
|
||||
"charter_network_banner": "...",
|
||||
"data_classification": "...",
|
||||
"charter_network": "...",
|
||||
"additional_access_list": "...",
|
||||
"created_by": 3,
|
||||
"created_at": "2025-01-15T10:30:00Z",
|
||||
"updated_at": "2025-01-15T10:30:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Filtering logic:**
|
||||
- `search` applies as `ILIKE '%value%'` across vendor OR platform OR model
|
||||
- Field-specific filters apply as `LOWER(TRIM(field)) = LOWER(TRIM(value))`
|
||||
- When both search and filters are present, they combine with AND logic
|
||||
- Results always sorted by `vendor ASC, platform ASC, model ASC`
|
||||
|
||||
#### `POST /api/archer-templates`
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"vendor": "Harmonic",
|
||||
"platform": "vCMTS",
|
||||
"model": "3.29.1",
|
||||
"environment_overview": "content...",
|
||||
"segmentation": "content...",
|
||||
"mitigating_controls": "",
|
||||
"additional_info": "",
|
||||
"charter_network_banner": "",
|
||||
"data_classification": "",
|
||||
"charter_network": "",
|
||||
"additional_access_list": ""
|
||||
}
|
||||
```
|
||||
|
||||
**Validation:**
|
||||
- `vendor`, `platform`, `model`: required, 1-100 chars after trim, non-empty after trim
|
||||
- Section fields: optional, max 10,000 chars each, default to empty string
|
||||
- Uniqueness: `LOWER(TRIM(vendor)) + LOWER(TRIM(platform)) + LOWER(TRIM(model))` must be unique
|
||||
|
||||
**Response:** `201 Created`
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"vendor": "Harmonic",
|
||||
"platform": "vCMTS",
|
||||
"model": "3.29.1",
|
||||
"environment_overview": "content...",
|
||||
"...": "...",
|
||||
"created_by": 3,
|
||||
"created_at": "2025-01-15T10:30:00Z",
|
||||
"updated_at": "2025-01-15T10:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses:**
|
||||
- `400` — missing or invalid fields (includes field names in error message)
|
||||
- `409` — duplicate vendor/platform/model combination
|
||||
|
||||
#### `PUT /api/archer-templates/:id`
|
||||
|
||||
**Request Body:** Partial — only provided fields are updated.
|
||||
|
||||
```json
|
||||
{
|
||||
"vendor": "Harmonic",
|
||||
"mitigating_controls": "updated content..."
|
||||
}
|
||||
```
|
||||
|
||||
**Validation:** Same rules as create for any provided field. If vendor/platform/model change, uniqueness is re-checked against other templates (excluding self).
|
||||
|
||||
**Response:** `200 OK` — returns full updated template object.
|
||||
|
||||
**Error Responses:**
|
||||
- `400` — invalid field values
|
||||
- `404` — template ID not found
|
||||
- `409` — new vendor/platform/model combination conflicts with another template
|
||||
|
||||
#### `DELETE /api/archer-templates/:id`
|
||||
|
||||
**Response:** `200 OK`
|
||||
```json
|
||||
{ "message": "Template deleted successfully" }
|
||||
```
|
||||
|
||||
**Error Responses:**
|
||||
- `404` — template ID not found
|
||||
|
||||
#### `POST /api/archer-templates/:id/clone`
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"vendor": "Harmonic",
|
||||
"platform": "vCMTS",
|
||||
"model": "3.30.0"
|
||||
}
|
||||
```
|
||||
|
||||
**Validation:**
|
||||
- All three hierarchy fields required
|
||||
- At least one must differ from source (enforced by uniqueness constraint)
|
||||
- Same length/format validation as create
|
||||
|
||||
**Response:** `201 Created` — returns full new template object with copied section content.
|
||||
|
||||
**Error Responses:**
|
||||
- `400` — missing/invalid hierarchy fields
|
||||
- `404` — source template not found
|
||||
- `409` — target combination already exists
|
||||
|
||||
#### `GET /api/archer-templates/hierarchy/vendors`
|
||||
|
||||
**Response:** `200 OK`
|
||||
```json
|
||||
["Adtran", "Harmonic", "Vecima"]
|
||||
```
|
||||
|
||||
#### `GET /api/archer-templates/hierarchy/platforms?vendor=Harmonic`
|
||||
|
||||
**Query Parameters:** `vendor` (required)
|
||||
|
||||
**Response:** `200 OK`
|
||||
```json
|
||||
["RPD", "vCMTS"]
|
||||
```
|
||||
|
||||
**Error:** `400` if `vendor` param missing.
|
||||
|
||||
#### `GET /api/archer-templates/hierarchy/models?vendor=Harmonic&platform=vCMTS`
|
||||
|
||||
**Query Parameters:** `vendor` (required), `platform` (required)
|
||||
|
||||
**Response:** `200 OK`
|
||||
```json
|
||||
["3.29.1", "3.30.0"]
|
||||
```
|
||||
|
||||
**Error:** `400` if either param missing.
|
||||
|
||||
---
|
||||
|
||||
### Frontend: `ArcherTemplatePage.js`
|
||||
|
||||
Full-page Template Manager component at `frontend/src/components/pages/ArcherTemplatePage.js`.
|
||||
|
||||
**Component Structure:**
|
||||
|
||||
```
|
||||
ArcherTemplatePage
|
||||
├── Header (title + create button)
|
||||
├── TemplateList (grouped by vendor > platform)
|
||||
│ ├── VendorGroup (collapsible)
|
||||
│ │ ├── PlatformSubgroup
|
||||
│ │ │ └── TemplateRow (model, edit/clone/delete buttons)
|
||||
│ │ └── ...
|
||||
│ └── ...
|
||||
├── TemplateFormModal (create/edit/clone)
|
||||
│ ├── Hierarchy fields (vendor, platform, model)
|
||||
│ └── Section textareas (8 sections with labels)
|
||||
└── DeleteConfirmModal
|
||||
```
|
||||
|
||||
**State Management:** Local `useState` — no global state needed. Template list fetched on mount and after mutations.
|
||||
|
||||
**Role-based rendering:** Uses `useAuth().canWrite()` to conditionally show action buttons. Viewers see the list but no create/edit/delete/clone controls.
|
||||
|
||||
---
|
||||
|
||||
### Frontend: `TemplateSelector.js`
|
||||
|
||||
Reusable component embedded in `IvantiTodoQueuePage.js` when viewing an Archer workflow item.
|
||||
|
||||
**Component Structure:**
|
||||
|
||||
```
|
||||
TemplateSelector
|
||||
├── SearchableDropdown
|
||||
│ ├── Search input
|
||||
│ └── Filtered template list
|
||||
├── SectionPanel (shown after selection)
|
||||
│ ├── StaticSections (Environment Overview, Segmentation, Mitigating Controls)
|
||||
│ ├── SemiStaticSections (Additional Info, Charter Network Banner, etc.)
|
||||
│ └── CopyAllButton
|
||||
└── SectionBlock (repeated for each section)
|
||||
├── Label
|
||||
├── Content (or "No content stored" placeholder)
|
||||
└── CopyButton (with confirmation state)
|
||||
```
|
||||
|
||||
**Props:**
|
||||
```javascript
|
||||
// No props needed — fetches templates from API independently
|
||||
// Appears as an expandable panel within the Archer queue item view
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Models
|
||||
|
||||
### Database Table: `archer_templates`
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS archer_templates (
|
||||
id SERIAL PRIMARY KEY,
|
||||
vendor VARCHAR(100) NOT NULL,
|
||||
platform VARCHAR(100) NOT NULL,
|
||||
model VARCHAR(100) NOT NULL,
|
||||
environment_overview TEXT NOT NULL DEFAULT '',
|
||||
segmentation TEXT NOT NULL DEFAULT '',
|
||||
mitigating_controls TEXT NOT NULL DEFAULT '',
|
||||
additional_info TEXT NOT NULL DEFAULT '',
|
||||
charter_network_banner TEXT NOT NULL DEFAULT '',
|
||||
data_classification TEXT NOT NULL DEFAULT '',
|
||||
charter_network TEXT NOT NULL DEFAULT '',
|
||||
additional_access_list TEXT NOT NULL DEFAULT '',
|
||||
created_by INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Case-insensitive uniqueness on trimmed vendor/platform/model
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_archer_templates_unique_combo
|
||||
ON archer_templates (LOWER(TRIM(vendor)), LOWER(TRIM(platform)), LOWER(TRIM(model)));
|
||||
|
||||
-- Index for list ordering and search
|
||||
CREATE INDEX IF NOT EXISTS idx_archer_templates_vendor ON archer_templates(vendor);
|
||||
CREATE INDEX IF NOT EXISTS idx_archer_templates_platform ON archer_templates(platform);
|
||||
```
|
||||
|
||||
### Migration File: `backend/migrations/add_archer_templates_table.js`
|
||||
|
||||
Follows the project's migration pattern — idempotent, uses `IF NOT EXISTS`.
|
||||
|
||||
### Section Field Mapping
|
||||
|
||||
| DB Column | Archer Form Label | Type |
|
||||
|-----------|------------------|------|
|
||||
| `environment_overview` | Environment Overview | Static |
|
||||
| `segmentation` | Segmentation | Static |
|
||||
| `mitigating_controls` | Mitigating Controls | Static |
|
||||
| `additional_info` | Additional Info/Background | Semi-Static |
|
||||
| `charter_network_banner` | Charter Network Banner | Semi-Static |
|
||||
| `data_classification` | Data Classification | Semi-Static |
|
||||
| `charter_network` | Charter Network | Semi-Static |
|
||||
| `additional_access_list` | Additional Access List | Semi-Static |
|
||||
|
||||
---
|
||||
|
||||
## Correctness Properties
|
||||
|
||||
*A property is a characteristic or behavior that should hold true across all valid executions of a system — essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
|
||||
|
||||
### Property 1: Template data round-trip preservation
|
||||
|
||||
*For any* valid template with vendor (1-100 chars), platform (1-100 chars), model (1-100 chars), and section content (0-10,000 chars each), creating the template via the API and then retrieving it by ID SHALL return the exact same field values (after trimming vendor/platform/model whitespace).
|
||||
|
||||
**Validates: Requirements 1.1, 1.2, 1.5, 2.1**
|
||||
|
||||
### Property 2: Uniqueness enforcement across all write operations
|
||||
|
||||
*For any* two templates where `LOWER(TRIM(vendor))`, `LOWER(TRIM(platform))`, and `LOWER(TRIM(model))` are identical, the system SHALL reject the second operation (create, update, or clone) with a 409 status code, regardless of case or leading/trailing whitespace differences.
|
||||
|
||||
**Validates: Requirements 1.3, 2.2, 2.8, 3.2, 3.5**
|
||||
|
||||
### Property 3: Input validation rejects invalid hierarchy fields
|
||||
|
||||
*For any* template create or update request where vendor, platform, or model is empty after trimming, consists only of whitespace, or exceeds 100 characters, the API SHALL reject the request with a 400 status code and an error message identifying which field(s) failed validation.
|
||||
|
||||
**Validates: Requirements 1.6, 2.3**
|
||||
|
||||
### Property 4: List and search results are sorted
|
||||
|
||||
*For any* set of templates in the database and any combination of search/filter parameters, the API response SHALL return results ordered by vendor ascending, then platform ascending, then model ascending (lexicographic, case-insensitive).
|
||||
|
||||
**Validates: Requirements 2.4, 6.4**
|
||||
|
||||
### Property 5: Search and filter semantics with AND logic
|
||||
|
||||
*For any* search query (substring match on vendor/platform/model) combined with any field-specific exact-match filters (vendor, platform, model), the API SHALL return only templates that satisfy ALL conditions simultaneously: the search substring appears in at least one hierarchy field AND each provided filter exactly matches its respective field (case-insensitive).
|
||||
|
||||
**Validates: Requirements 2.5, 2.6, 6.1, 6.2, 6.3**
|
||||
|
||||
### Property 6: Partial update preserves unspecified fields
|
||||
|
||||
*For any* existing template and any subset of updatable fields provided in a PUT request, the API SHALL modify only the specified fields and leave all other fields unchanged, while setting `updated_at` to the current timestamp.
|
||||
|
||||
**Validates: Requirements 2.7**
|
||||
|
||||
### Property 7: Delete removes template permanently
|
||||
|
||||
*For any* existing template, after a successful DELETE request, a subsequent GET request for that template's ID SHALL return 404.
|
||||
|
||||
**Validates: Requirements 2.9**
|
||||
|
||||
### Property 8: Write operations require editor or admin role
|
||||
|
||||
*For any* user with viewer role (or no authenticated session), all write operations (POST create, PUT update, DELETE, POST clone) SHALL be rejected with 401 (unauthenticated) or 403 (insufficient permissions).
|
||||
|
||||
**Validates: Requirements 2.10, 4.13**
|
||||
|
||||
### Property 9: Non-existent template ID returns 404
|
||||
|
||||
*For any* template ID that does not exist in the database, GET, PUT, DELETE, and clone requests targeting that ID SHALL return a 404 status code.
|
||||
|
||||
**Validates: Requirements 2.12, 3.4**
|
||||
|
||||
### Property 10: Clone preserves all section content
|
||||
|
||||
*For any* existing template, cloning it with new vendor/platform/model values SHALL produce a new template whose 8 section content fields are byte-for-byte identical to the source template, with a different ID, new `created_at` timestamp, and `created_by` set to the requesting user.
|
||||
|
||||
**Validates: Requirements 3.1, 3.3**
|
||||
|
||||
### Property 11: Hierarchy endpoints return distinct sorted values
|
||||
|
||||
*For any* set of templates, the vendors endpoint SHALL return a deduplicated, alphabetically sorted array of vendor names; the platforms endpoint (given a vendor) SHALL return only distinct platforms from templates matching that vendor; and the models endpoint (given vendor+platform) SHALL return only distinct models from matching templates — all sorted alphabetically ascending.
|
||||
|
||||
**Validates: Requirements 7.1, 7.2, 7.3**
|
||||
|
||||
### Property 12: Copy All concatenation format
|
||||
|
||||
*For any* template with a mix of populated and empty sections, the "Copy All" operation SHALL produce a string that concatenates only non-empty sections, each preceded by its human-readable section header, in the order: static sections first (Environment Overview, Segmentation, Mitigating Controls) then semi-static sections (Additional Info/Background, Charter Network Banner, Data Classification, Charter Network, Additional Access List).
|
||||
|
||||
**Validates: Requirements 5.9**
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Backend Error Strategy
|
||||
|
||||
| Scenario | HTTP Status | Error Response Format |
|
||||
|----------|-------------|----------------------|
|
||||
| Missing required field | 400 | `{ "error": "Vendor is required" }` or `{ "error": "Missing fields: vendor, model" }` |
|
||||
| Field too long | 400 | `{ "error": "Vendor must be 100 characters or fewer" }` |
|
||||
| Section too long | 400 | `{ "error": "environment_overview must be 10,000 characters or fewer" }` |
|
||||
| Duplicate combination | 409 | `{ "error": "A template with this vendor/platform/model combination already exists" }` |
|
||||
| Template not found | 404 | `{ "error": "Template not found" }` |
|
||||
| Not authenticated | 401 | `{ "error": "Authentication required" }` |
|
||||
| Insufficient permissions | 403 | `{ "error": "Insufficient permissions", "required": [...], "current": "..." }` |
|
||||
| Database error | 500 | `{ "error": "Internal server error" }` |
|
||||
|
||||
### Frontend Error Handling
|
||||
|
||||
- **Network errors:** Caught in try/catch around `fetch()`, displayed as a banner message within the component.
|
||||
- **Validation errors (400):** Displayed inline next to the relevant form field or as a general form error.
|
||||
- **Conflict errors (409):** Displayed as a warning banner at the top of the form indicating the duplicate.
|
||||
- **Auth errors (401/403):** Handled by AuthContext — 401 triggers redirect to login, 403 shows permission denied message.
|
||||
- **Clipboard failures:** If `navigator.clipboard.writeText` rejects (e.g., permissions denied), display a fallback message suggesting manual copy.
|
||||
|
||||
### Audit Log Resilience
|
||||
|
||||
Per requirement 8.5, `logAudit()` is called fire-and-forget. If the audit insert fails, the error is logged to `console.error` but the main operation's response is unaffected. This matches the existing pattern in `helpers/auditLog.js`.
|
||||
|
||||
---
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Property-Based Tests (fast-check)
|
||||
|
||||
The project uses `fast-check` for property-based testing (visible in existing `__tests__/*.property.test.js` files). Each correctness property above maps to one property-based test with a minimum of 100 iterations.
|
||||
|
||||
**Test file:** `backend/__tests__/archer-template-library.property.test.js`
|
||||
|
||||
**Properties to implement:**
|
||||
|
||||
| Property | Test Description |
|
||||
|----------|-----------------|
|
||||
| 1 | Template data round-trip — create + GET preserves all fields |
|
||||
| 2 | Uniqueness enforcement — duplicate combinations rejected across create/update/clone |
|
||||
| 3 | Input validation — invalid hierarchy fields rejected with 400 |
|
||||
| 4 | Sorted results — list/search always returns sorted by vendor/platform/model |
|
||||
| 5 | Search + filter AND logic — combined criteria narrow results correctly |
|
||||
| 6 | Partial update semantics — unspecified fields preserved |
|
||||
| 7 | Delete permanence — deleted templates return 404 |
|
||||
| 8 | Access control — viewer role cannot perform write operations |
|
||||
| 9 | Non-existent ID — 404 for all operations on missing templates |
|
||||
| 10 | Clone content preservation — cloned sections match source |
|
||||
| 11 | Hierarchy distinct values — deduplicated and sorted |
|
||||
| 12 | Copy All format — concatenation includes only non-empty sections in order |
|
||||
|
||||
**Tag format:** Each test tagged with `// Feature: archer-template-library, Property N: <property text>`
|
||||
|
||||
**Configuration:** 100 iterations minimum per property.
|
||||
|
||||
### Unit / Example-Based Tests
|
||||
|
||||
**Test file:** `backend/__tests__/archer-template-library.test.js`
|
||||
|
||||
- Template creation with all sections populated
|
||||
- Template creation with no sections (defaults to empty strings)
|
||||
- Timestamp metadata correctness (created_at, updated_at, created_by)
|
||||
- Clone metadata (new created_at, new created_by)
|
||||
- Hierarchy endpoint without required params returns 400
|
||||
- Empty search results return 200 with empty array
|
||||
- Whitespace-only search param is ignored
|
||||
- Authentication required for read endpoints (401 without session)
|
||||
|
||||
### Integration Tests
|
||||
|
||||
**Test file:** `backend/__tests__/archer-template-library.integration.test.js`
|
||||
|
||||
- Audit log entries created for create/update/delete/clone
|
||||
- Audit log failure does not block template operation
|
||||
- Failed operations do not produce audit entries
|
||||
- Full workflow: create → list → update → clone → delete
|
||||
|
||||
### Frontend Tests
|
||||
|
||||
- TemplateSelector copy-to-clipboard behavior
|
||||
- TemplateFormModal validation prevents submission with empty required fields
|
||||
- Viewer role sees no action buttons
|
||||
- Template list grouped by vendor/platform renders correctly
|
||||
143
.kiro/specs/archer-template-library/requirements.md
Normal file
143
.kiro/specs/archer-template-library/requirements.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Requirements Document
|
||||
|
||||
## Introduction
|
||||
|
||||
The Archer Template Library adds a template management system to the Ivanti Queue's Archer Risk Acceptance workflow. Templates store the static and semi-static content sections of Archer Risk Acceptance forms, keyed by Vendor > Platform > Model hierarchy. When working an Archer queue item, users select a template and copy pre-filled section content into the external Archer application (https://egrcprod.corp.chartercom.com), eliminating repetitive manual entry of identical content across submissions for the same vendor/platform/model combination.
|
||||
|
||||
## Glossary
|
||||
|
||||
- **Dashboard**: The STEAM Security Dashboard application
|
||||
- **Template_Library**: The collection of stored Archer Risk Acceptance templates, organized by Vendor > Platform > Model hierarchy
|
||||
- **Template**: A single template record containing static and semi-static section content for a specific Vendor/Platform/Model combination
|
||||
- **Template_Manager**: The UI section where users create, edit, clone, and delete templates
|
||||
- **Template_Selector**: The UI component within the Archer queue workflow that allows users to pick a template and copy section content
|
||||
- **Template_API**: The backend REST endpoints for CRUD operations on templates
|
||||
- **Section**: A named content block within a template corresponding to an Archer form section (e.g., Environment Overview, Segmentation, Mitigating Controls)
|
||||
- **Static_Section**: A template section whose content is identical across all submissions for a given Vendor/Platform/Model (Environment Overview, Segmentation, Mitigating Controls)
|
||||
- **Semi_Static_Section**: A template section with default content that may need per-request editing (Additional Info/Background, Charter Network Banner, Data Classification, Charter Network, Additional Access List)
|
||||
- **Vendor**: The equipment manufacturer (e.g., Harmonic, Vecima, Adtran)
|
||||
- **Platform**: The product category under a vendor (e.g., vCMTS, RPD, OLT)
|
||||
- **Model**: The specific hardware or software version under a platform (e.g., 3.29.1, ERM3-2-2, 9504N)
|
||||
- **Archer_Form**: The external Archer Risk Acceptance form in the eGRC platform with sections for General Information, Issues, Justification, Remediation Plans, Related Items, Mitigating Controls, and Attachments
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement 1: Template Data Model
|
||||
|
||||
**User Story:** As an editor, I want templates to store content organized by Vendor/Platform/Model and form section, so that I can maintain reusable content for each device type I submit Archer exceptions for.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Template_API SHALL store each Template with a vendor field (1 to 100 characters), a platform field (1 to 100 characters), and a model field (1 to 100 characters)
|
||||
2. THE Template_API SHALL store each Template with named Section content blocks for: environment_overview, segmentation, mitigating_controls, additional_info, charter_network_banner, data_classification, charter_network, and additional_access_list, each with a maximum length of 10,000 characters
|
||||
3. THE Template_API SHALL enforce uniqueness on the combination of vendor, platform, and model after trimming leading and trailing whitespace (case-insensitive)
|
||||
4. THE Template_API SHALL store a created_at timestamp, updated_at timestamp, and created_by user reference for each Template
|
||||
5. WHEN a Template is created without content for any section, THE Template_API SHALL store empty strings for those sections
|
||||
6. IF a Template creation or update request provides a vendor, platform, or model value that is empty or exceeds 100 characters, THEN THE Template_API SHALL reject the request with a 400 status code and an error message indicating which field failed validation
|
||||
|
||||
### Requirement 2: Template CRUD API
|
||||
|
||||
**User Story:** As an editor, I want to create, read, update, and delete templates through the API, so that I can manage my template library programmatically and through the UI.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN a valid Template creation request is received with vendor, platform, model, and at least one section content field, THE Template_API SHALL create the Template and return the created record including its generated ID with a 201 status code
|
||||
2. WHEN a Template creation request contains a vendor/platform/model combination that already exists (case-insensitive after trimming), THE Template_API SHALL return a 409 status code with an error message identifying the conflicting combination
|
||||
3. WHEN a Template creation request is missing vendor, platform, or model, THE Template_API SHALL return a 400 status code with an error message identifying the missing fields
|
||||
4. WHEN a GET request is made to the templates list endpoint, THE Template_API SHALL return all templates ordered by vendor, platform, and model ascending
|
||||
5. WHEN a GET request includes a vendor query parameter, THE Template_API SHALL filter results to templates matching that vendor (case-insensitive)
|
||||
6. WHEN a GET request includes a platform query parameter, THE Template_API SHALL filter results to templates matching that platform (case-insensitive)
|
||||
7. WHEN a valid Template update request is received, THE Template_API SHALL update only the specified fields and set the updated_at timestamp to the current time
|
||||
8. WHEN a Template update request changes vendor, platform, or model to a combination that already exists in another Template, THE Template_API SHALL return a 409 status code
|
||||
9. WHEN a Template delete request is received for an existing Template, THE Template_API SHALL remove the Template record and return a 200 status code
|
||||
10. THE Template_API SHALL require an authenticated session with editor or admin role for all write operations (create, update, delete, clone)
|
||||
11. THE Template_API SHALL require an authenticated session for all read operations
|
||||
12. IF a Template ID referenced in a GET, PUT, or DELETE request does not exist, THEN THE Template_API SHALL return a 404 status code with an error message indicating the Template was not found
|
||||
|
||||
### Requirement 3: Template Cloning
|
||||
|
||||
**User Story:** As an editor, I want to clone an existing template to create a new one for a similar device, so that I can reuse shared content between models on the same platform without retyping it.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN a clone request is received for an existing Template, THE Template_API SHALL create a new Template that copies all section content from the source Template and return the newly created Template including its generated ID
|
||||
2. WHEN cloning a Template, THE Template_API SHALL require the caller to supply vendor, platform, and model values where at least one value differs from the source Template, such that the resulting combination is unique across all Templates in the system
|
||||
3. WHEN cloning a Template, THE Template_API SHALL set created_at to the current time and created_by to the requesting user
|
||||
4. IF the source Template ID does not exist, THEN THE Template_API SHALL return a 404 status code with an error message indicating the source Template was not found
|
||||
5. IF the supplied vendor, platform, and model combination already exists in another Template, THEN THE Template_API SHALL reject the clone request with a 409 status code and an error message indicating the combination is not unique
|
||||
|
||||
### Requirement 4: Template Manager UI
|
||||
|
||||
**User Story:** As an editor, I want a dedicated Template Manager section in the dashboard, so that I can browse, create, edit, and delete templates with a clear view of what is available.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Template_Manager SHALL display a list of all templates grouped by vendor, then by platform
|
||||
2. THE Template_Manager SHALL display the vendor, platform, and model for each template in the list
|
||||
3. WHEN the user clicks a create button, THE Template_Manager SHALL display a form with fields for vendor (max 100 characters), platform (max 100 characters), model (max 100 characters), and all eight section content areas defined in the Template data model
|
||||
4. WHEN the user clicks an edit button on a template, THE Template_Manager SHALL display the template content pre-populated in an editable form with the same fields as the create form
|
||||
5. WHEN the user clicks a delete button on a template, THE Template_Manager SHALL display a confirmation dialog identifying the template by vendor, platform, and model before deleting
|
||||
6. WHEN the user confirms the delete action in the confirmation dialog, THE Template_Manager SHALL delete the template via the API and remove it from the displayed list
|
||||
7. IF the user cancels the delete confirmation dialog, THEN THE Template_Manager SHALL dismiss the dialog and leave the template unchanged
|
||||
8. THE Template_Manager SHALL display section content fields as multi-line text areas with labels matching the Archer form section names
|
||||
9. WHEN the user submits the create or edit form with vendor, platform, or model fields empty, THE Template_Manager SHALL prevent submission and indicate which required fields are missing
|
||||
10. WHEN the user submits the create or edit form with a duplicate vendor/platform/model combination, THE Template_Manager SHALL display the conflict error returned by the API
|
||||
11. WHEN a create, edit, or delete operation succeeds, THE Template_Manager SHALL refresh the template list to reflect the change
|
||||
12. THE Template_Manager SHALL provide a clone button for each template that opens the create form pre-filled with the source template section content and empty vendor/platform/model fields
|
||||
13. WHILE the user has viewer role only, THE Template_Manager SHALL hide create, edit, delete, and clone buttons
|
||||
|
||||
### Requirement 5: Template Selection in Archer Queue Workflow
|
||||
|
||||
**User Story:** As an editor, I want to select a template when working an Archer queue item, so that I can quickly access pre-filled content to paste into the Archer application.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN the user is viewing an Archer workflow queue item, THE Template_Selector SHALL display a searchable dropdown that lists all available templates by their vendor/platform/model label
|
||||
2. WHEN the user types in the template picker search field, THE Template_Selector SHALL filter the template list to show only templates whose vendor, platform, or model contains the typed text as a case-insensitive substring
|
||||
3. WHEN the user selects a template from the dropdown, THE Template_Selector SHALL display all populated sections of the selected template in a panel, with Static_Sections (Environment Overview, Segmentation, Mitigating Controls) displayed first, followed by Semi_Static_Sections
|
||||
4. THE Template_Selector SHALL display each section with a human-readable label matching the Archer form section name (e.g., "Environment Overview" not "environment_overview")
|
||||
5. THE Template_Selector SHALL display a copy-to-clipboard button adjacent to each section content block
|
||||
6. WHEN the user clicks a copy button, THE Template_Selector SHALL copy that section's full text content to the system clipboard using the Clipboard API
|
||||
7. WHEN a section is copied successfully, THE Template_Selector SHALL display a brief visual confirmation (checkmark icon or "Copied!" text) for 2 seconds before reverting to the copy button state
|
||||
8. WHEN a template has an empty section, THE Template_Selector SHALL display that section with a muted italic placeholder text "No content stored" and disable the copy button for that section
|
||||
9. THE Template_Selector SHALL provide a "Copy All" button that concatenates all non-empty sections with section headers and copies the combined text to the clipboard
|
||||
|
||||
### Requirement 6: Template Search and Filtering API
|
||||
|
||||
**User Story:** As an editor, I want to search templates by vendor, platform, or model, so that I can quickly find the right template when working a queue item.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN a search query parameter of at least 1 non-whitespace character is provided, THE Template_API SHALL return templates where the vendor, platform, or model contains the search string as a substring (case-insensitive partial match)
|
||||
2. WHEN vendor, platform, or model filter parameters are provided, THE Template_API SHALL return only templates whose corresponding field exactly matches the filter value (case-insensitive exact match), supporting any combination of these filters simultaneously
|
||||
3. WHEN both a search query parameter and field-specific filter parameters are provided, THE Template_API SHALL return only templates that satisfy both the search substring match AND all field-specific exact match filters (AND logic)
|
||||
4. THE Template_API SHALL return search and filter results ordered by vendor, platform, and model ascending
|
||||
5. WHEN no templates match the search or filter criteria, THE Template_API SHALL return an empty array with a 200 status code
|
||||
6. IF a search query parameter is provided but contains only whitespace or is empty, THEN THE Template_API SHALL ignore the search parameter and return results as if no search was specified
|
||||
|
||||
### Requirement 7: Distinct Values API for Hierarchy Navigation
|
||||
|
||||
**User Story:** As an editor, I want to browse the template hierarchy by vendor and platform, so that I can navigate to templates without knowing the exact model identifier.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN a request is made to the vendors endpoint, THE Template_API SHALL return an array of distinct vendor names from all templates sorted alphabetically ascending
|
||||
2. WHEN a request is made to the platforms endpoint with a vendor parameter, THE Template_API SHALL return an array of distinct platform names for templates matching that vendor (case-insensitive) sorted alphabetically ascending
|
||||
3. WHEN a request is made to the models endpoint with vendor and platform parameters, THE Template_API SHALL return an array of distinct model names for templates matching that vendor and platform combination (case-insensitive) sorted alphabetically ascending
|
||||
4. IF the platforms endpoint is called without a vendor parameter, THEN THE Template_API SHALL return a 400 status code with an error message indicating vendor is required
|
||||
5. IF the models endpoint is called without both vendor and platform parameters, THEN THE Template_API SHALL return a 400 status code with an error message indicating which parameters are missing
|
||||
6. WHEN no templates match the provided parameters, THE Template_API SHALL return an empty array with a 200 status code
|
||||
7. THE Template_API SHALL require an authenticated session for all hierarchy endpoints
|
||||
|
||||
### Requirement 8: Audit Logging for Template Operations
|
||||
|
||||
**User Story:** As an admin, I want all template create, update, delete, and clone operations logged, so that I can track who modified template content and when.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. WHEN a Template is created successfully, THE Template_API SHALL log an audit entry with action "template_created", the template ID, the requesting user's ID and username, and the requester's IP address
|
||||
2. WHEN a Template is updated successfully, THE Template_API SHALL log an audit entry with action "template_updated", the template ID, the requesting user's ID and username, the requester's IP address, and the list of field names that changed
|
||||
3. WHEN a Template is deleted successfully, THE Template_API SHALL log an audit entry with action "template_deleted", the template ID, the requesting user's ID and username, the requester's IP address, and the vendor/platform/model of the deleted template
|
||||
4. WHEN a Template is cloned successfully, THE Template_API SHALL log an audit entry with action "template_cloned", both the source and new template IDs, the requesting user's ID and username, and the requester's IP address
|
||||
5. IF an audit log entry fails to persist, THEN THE Template_API SHALL log the failure to the application error log and SHALL NOT block or fail the original template operation
|
||||
6. IF a template create, update, delete, or clone operation fails, THEN THE Template_API SHALL NOT log an audit entry for that operation
|
||||
242
.kiro/specs/archer-template-library/tasks.md
Normal file
242
.kiro/specs/archer-template-library/tasks.md
Normal file
@@ -0,0 +1,242 @@
|
||||
# Implementation Plan: Archer Template Library
|
||||
|
||||
## Overview
|
||||
|
||||
This plan implements the Archer Template Library feature — a template management system for storing and reusing Archer Risk Acceptance form content organized by Vendor > Platform > Model hierarchy. The implementation progresses from database schema, through backend CRUD/search API, to frontend Template Manager page and TemplateSelector integration in the Ivanti Todo Queue workflow.
|
||||
|
||||
The backend uses Node.js/Express 5 with PostgreSQL (pool from `backend/db.js`), following the existing factory-pattern route module convention. The frontend uses React 19 (plain JavaScript) with the project's dark theme aesthetic. Property-based tests use `fast-check` in `backend/__tests__/`.
|
||||
|
||||
## Tasks
|
||||
|
||||
- [x] 1. Database migration and schema setup
|
||||
- [x] 1.1 Create the `archer_templates` table migration
|
||||
- Create `backend/migrations/add_archer_templates_table.js` using the idempotent migration pattern (IF NOT EXISTS)
|
||||
- Define the `archer_templates` table with columns: `id SERIAL PRIMARY KEY`, `vendor VARCHAR(100) NOT NULL`, `platform VARCHAR(100) NOT NULL`, `model VARCHAR(100) NOT NULL`, 8 TEXT section columns (all `NOT NULL DEFAULT ''`), `created_by INTEGER REFERENCES users(id) ON DELETE SET NULL`, `created_at TIMESTAMPTZ DEFAULT NOW()`, `updated_at TIMESTAMPTZ DEFAULT NOW()`
|
||||
- Create unique index `idx_archer_templates_unique_combo` on `(LOWER(TRIM(vendor)), LOWER(TRIM(platform)), LOWER(TRIM(model)))`
|
||||
- Create index `idx_archer_templates_vendor` on `(vendor)` and `idx_archer_templates_platform` on `(platform)` for list query performance
|
||||
- Register the migration in `backend/migrations/run-all.js` by appending `'add_archer_templates_table.js'` to the `POSTGRES_MIGRATIONS` array
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5_
|
||||
|
||||
- [x] 2. Backend route module — core CRUD
|
||||
- [x] 2.1 Create `backend/routes/archerTemplates.js` with factory function and create endpoint
|
||||
- Create the file following the factory pattern: `function createArcherTemplatesRouter()` returning an Express Router
|
||||
- Import `pool` from `'../db'`, `{ requireAuth, requireGroup }` from `'../middleware/auth'`, and `logAudit` from `'../helpers/auditLog'`
|
||||
- Implement `POST /` — validate vendor/platform/model (required, 1-100 chars after trim, non-empty after trim), validate section fields (max 10,000 chars each, default to empty string), INSERT with RETURNING *, handle unique constraint violation (23505) as 409, call `logAudit` fire-and-forget on success with action `template_created`
|
||||
- Implement `GET /` — query all templates with optional search (ILIKE across vendor/platform/model), optional exact-match filters on vendor/platform/model (case-insensitive via LOWER/TRIM), combine with AND logic, ORDER BY vendor, platform, model ASC
|
||||
- Implement `GET /:id` — fetch single template by ID, return 404 if not found
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.10, 2.11, 2.12, 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 8.1_
|
||||
|
||||
- [x] 2.2 Implement update, delete, and clone endpoints
|
||||
- Implement `PUT /:id` — validate provided fields (same rules as create), build dynamic UPDATE SET clause for only provided fields, always set `updated_at = NOW()`, check uniqueness if vendor/platform/model changed (excluding self), return 404/409/400 as appropriate, call `logAudit` with action `template_updated` and changed field names
|
||||
- Implement `DELETE /:id` — verify template exists (404 if not), DELETE, call `logAudit` with action `template_deleted` and vendor/platform/model of the deleted template, return 200
|
||||
- Implement `POST /:id/clone` — verify source exists (404 if not), validate new vendor/platform/model (required, same rules as create), INSERT copying all 8 section fields from source with new hierarchy values and `created_by = req.user.id`, handle uniqueness violation as 409, call `logAudit` with action `template_cloned` and both source/new IDs
|
||||
- _Requirements: 2.7, 2.8, 2.9, 2.10, 2.12, 3.1, 3.2, 3.3, 3.4, 3.5, 8.2, 8.3, 8.4, 8.5, 8.6_
|
||||
|
||||
- [x] 2.3 Implement hierarchy endpoints (vendors, platforms, models)
|
||||
- Implement `GET /hierarchy/vendors` — SELECT DISTINCT vendor ORDER BY vendor ASC
|
||||
- Implement `GET /hierarchy/platforms` — require `vendor` query param (400 if missing), SELECT DISTINCT platform WHERE LOWER(TRIM(vendor)) matches, ORDER BY platform ASC
|
||||
- Implement `GET /hierarchy/models` — require `vendor` and `platform` query params (400 if either missing), SELECT DISTINCT model WHERE both match, ORDER BY model ASC
|
||||
- All hierarchy endpoints require `requireAuth()` middleware
|
||||
- _Requirements: 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7_
|
||||
|
||||
- [x] 2.4 Wire the router into `backend/server.js`
|
||||
- Add `const createArcherTemplatesRouter = require('./routes/archerTemplates');` to the imports section
|
||||
- Mount with `app.use('/api/archer-templates', createArcherTemplatesRouter());` after the existing archer-tickets route
|
||||
- _Requirements: 2.10, 2.11_
|
||||
|
||||
- [x] 3. Checkpoint - Backend API verification
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
- [ ] 4. Backend property-based tests
|
||||
- [ ]* 4.1 Write property test for template data round-trip preservation
|
||||
- File: `backend/__tests__/archer-template-library.property.test.js`
|
||||
- **Property 1: Template data round-trip preservation**
|
||||
- Generate valid templates with random vendor (1-100 chars), platform (1-100 chars), model (1-100 chars), and section content (0-10,000 chars each); create via POST then GET by ID; assert all fields match (vendor/platform/model trimmed)
|
||||
- Minimum 100 iterations
|
||||
- **Validates: Requirements 1.1, 1.2, 1.5, 2.1**
|
||||
|
||||
- [ ]* 4.2 Write property test for uniqueness enforcement
|
||||
- File: `backend/__tests__/archer-template-library.property.test.js`
|
||||
- **Property 2: Uniqueness enforcement across all write operations**
|
||||
- Generate template pairs with matching LOWER(TRIM(vendor/platform/model)) but varying case/whitespace; assert second create returns 409, update to matching combo returns 409, clone to matching combo returns 409
|
||||
- Minimum 100 iterations
|
||||
- **Validates: Requirements 1.3, 2.2, 2.8, 3.2, 3.5**
|
||||
|
||||
- [ ]* 4.3 Write property test for input validation
|
||||
- File: `backend/__tests__/archer-template-library.property.test.js`
|
||||
- **Property 3: Input validation rejects invalid hierarchy fields**
|
||||
- Generate invalid inputs: empty strings, whitespace-only, strings > 100 chars for vendor/platform/model; assert POST and PUT return 400 with error identifying which field(s) failed
|
||||
- Minimum 100 iterations
|
||||
- **Validates: Requirements 1.6, 2.3**
|
||||
|
||||
- [ ]* 4.4 Write property test for sorted results
|
||||
- File: `backend/__tests__/archer-template-library.property.test.js`
|
||||
- **Property 4: List and search results are sorted**
|
||||
- Generate multiple templates with random hierarchy values, insert all, then GET with various search/filter combos; assert response is sorted by vendor ASC, platform ASC, model ASC (case-insensitive)
|
||||
- Minimum 100 iterations
|
||||
- **Validates: Requirements 2.4, 6.4**
|
||||
|
||||
- [ ]* 4.5 Write property test for search and filter AND logic
|
||||
- File: `backend/__tests__/archer-template-library.property.test.js`
|
||||
- **Property 5: Search and filter semantics with AND logic**
|
||||
- Generate template sets with overlapping vendor/platform/model values; apply combined search substring + field exact-match filters; assert every result satisfies ALL conditions and no matching template is missing
|
||||
- Minimum 100 iterations
|
||||
- **Validates: Requirements 2.5, 2.6, 6.1, 6.2, 6.3**
|
||||
|
||||
- [ ]* 4.6 Write property test for partial update preservation
|
||||
- File: `backend/__tests__/archer-template-library.property.test.js`
|
||||
- **Property 6: Partial update preserves unspecified fields**
|
||||
- Generate a template, then PUT with a random subset of fields; assert unmodified fields remain unchanged and `updated_at` is updated
|
||||
- Minimum 100 iterations
|
||||
- **Validates: Requirements 2.7**
|
||||
|
||||
- [ ]* 4.7 Write property test for delete permanence
|
||||
- File: `backend/__tests__/archer-template-library.property.test.js`
|
||||
- **Property 7: Delete removes template permanently**
|
||||
- Generate a template, DELETE it, then GET by ID; assert 404 response
|
||||
- Minimum 100 iterations
|
||||
- **Validates: Requirements 2.9**
|
||||
|
||||
- [ ]* 4.8 Write property test for access control
|
||||
- File: `backend/__tests__/archer-template-library.property.test.js`
|
||||
- **Property 8: Write operations require editor or admin role**
|
||||
- Generate write operation requests (POST/PUT/DELETE/clone) with viewer-role or unauthenticated sessions; assert all are rejected with 401 or 403
|
||||
- Minimum 100 iterations
|
||||
- **Validates: Requirements 2.10, 4.13**
|
||||
|
||||
- [ ]* 4.9 Write property test for non-existent ID handling
|
||||
- File: `backend/__tests__/archer-template-library.property.test.js`
|
||||
- **Property 9: Non-existent template ID returns 404**
|
||||
- Generate random IDs that don't exist; assert GET, PUT, DELETE, and clone all return 404
|
||||
- Minimum 100 iterations
|
||||
- **Validates: Requirements 2.12, 3.4**
|
||||
|
||||
- [ ]* 4.10 Write property test for clone content preservation
|
||||
- File: `backend/__tests__/archer-template-library.property.test.js`
|
||||
- **Property 10: Clone preserves all section content**
|
||||
- Generate a template with random section content, clone with new hierarchy values; assert all 8 section fields are byte-for-byte identical, ID differs, `created_at` is new, `created_by` matches requesting user
|
||||
- Minimum 100 iterations
|
||||
- **Validates: Requirements 3.1, 3.3**
|
||||
|
||||
- [ ]* 4.11 Write property test for hierarchy distinct values
|
||||
- File: `backend/__tests__/archer-template-library.property.test.js`
|
||||
- **Property 11: Hierarchy endpoints return distinct sorted values**
|
||||
- Generate templates with overlapping vendors/platforms/models; assert vendors endpoint returns deduplicated sorted array, platforms for a vendor returns only that vendor's platforms sorted, models for vendor+platform returns only matching models sorted
|
||||
- Minimum 100 iterations
|
||||
- **Validates: Requirements 7.1, 7.2, 7.3**
|
||||
|
||||
- [ ]* 4.12 Write property test for Copy All concatenation format
|
||||
- File: `backend/__tests__/archer-template-library.property.test.js`
|
||||
- **Property 12: Copy All concatenation format**
|
||||
- Generate templates with random mix of populated and empty sections; construct the expected "Copy All" output (only non-empty sections, with headers, static first then semi-static); assert the concatenation matches expected format and order
|
||||
- Minimum 100 iterations
|
||||
- **Validates: Requirements 5.9**
|
||||
|
||||
- [x] 5. Checkpoint - Backend tests pass
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
- [x] 6. Frontend Template Manager page
|
||||
- [x] 6.1 Create `frontend/src/components/pages/ArcherTemplatePage.js` with list view
|
||||
- Create the page component following the project's dark-theme inline-styles pattern
|
||||
- Implement template list fetch on mount from `GET /api/archer-templates`
|
||||
- Display templates grouped by vendor, then platform, with collapsible vendor groups
|
||||
- Show vendor, platform, and model for each template row
|
||||
- Conditionally render create/edit/clone/delete buttons using `useAuth()` role check (hide for viewer role)
|
||||
- _Requirements: 4.1, 4.2, 4.13_
|
||||
|
||||
- [x] 6.2 Implement TemplateFormModal (create/edit/clone)
|
||||
- Create a modal component (inline in ArcherTemplatePage.js or as a separate subcomponent)
|
||||
- Include fields for vendor (max 100 chars), platform (max 100 chars), model (max 100 chars), and all 8 section content textareas with human-readable labels
|
||||
- For create mode: all fields empty; for edit mode: pre-populate from template; for clone mode: pre-populate sections from source, leave hierarchy fields empty
|
||||
- Client-side validation: prevent submission with empty vendor/platform/model, show which required fields are missing
|
||||
- On submit: POST for create/clone, PUT for edit; handle 409 conflict errors by displaying the conflict message from API
|
||||
- On success: close modal and refresh template list
|
||||
- _Requirements: 4.3, 4.4, 4.8, 4.9, 4.10, 4.11, 4.12_
|
||||
|
||||
- [x] 6.3 Implement DeleteConfirmModal and delete flow
|
||||
- Create a confirmation dialog that identifies the template by vendor/platform/model
|
||||
- On confirm: call DELETE API, remove template from displayed list, close dialog
|
||||
- On cancel: dismiss dialog, leave template unchanged
|
||||
- _Requirements: 4.5, 4.6, 4.7_
|
||||
|
||||
- [x] 6.4 Wire ArcherTemplatePage into navigation
|
||||
- Add a navigation entry in `NavDrawer.js` for the Template Manager page
|
||||
- Add the page routing in `App.js` (following the existing page-switching pattern)
|
||||
- _Requirements: 4.1_
|
||||
|
||||
- [x] 7. Frontend TemplateSelector component
|
||||
- [x] 7.1 Create `frontend/src/components/TemplateSelector.js` with searchable dropdown
|
||||
- Create the reusable component that fetches templates from `GET /api/archer-templates`
|
||||
- Implement search input that filters the template list (case-insensitive substring match on vendor/platform/model — client-side filter for responsiveness)
|
||||
- Display templates in dropdown as `Vendor / Platform / Model` label
|
||||
- _Requirements: 5.1, 5.2_
|
||||
|
||||
- [x] 7.2 Implement section display panel and copy functionality
|
||||
- When a template is selected, display all populated sections in a panel
|
||||
- Order: static sections first (Environment Overview, Segmentation, Mitigating Controls), then semi-static sections (Additional Info/Background, Charter Network Banner, Data Classification, Charter Network, Additional Access List)
|
||||
- Display each section with human-readable label (not DB column name)
|
||||
- Show copy-to-clipboard button adjacent to each section; disable for empty sections (show muted "No content stored" placeholder)
|
||||
- On copy: use `navigator.clipboard.writeText()`, show checkmark/"Copied!" for 2 seconds then revert
|
||||
- Implement "Copy All" button: concatenate all non-empty sections with section headers, copy combined text
|
||||
- _Requirements: 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9_
|
||||
|
||||
- [x] 7.3 Integrate TemplateSelector into IvantiTodoQueuePage
|
||||
- Embed the TemplateSelector component in `IvantiTodoQueuePage.js` when viewing an Archer workflow queue item
|
||||
- Display as an expandable panel within the Archer queue item view
|
||||
- _Requirements: 5.1_
|
||||
|
||||
- [x] 8. Checkpoint - Frontend build and integration verification
|
||||
- Run `cd frontend && npm run build` to verify no ESLint or build errors
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
- [ ] 9. Backend unit and integration tests
|
||||
- [ ]* 9.1 Write unit tests for template CRUD operations
|
||||
- File: `backend/__tests__/archer-template-library.test.js`
|
||||
- Test template creation with all sections populated returns 201 with full record
|
||||
- Test template creation with no sections defaults to empty strings
|
||||
- Test timestamp metadata correctness (created_at, updated_at, created_by set properly)
|
||||
- Test clone metadata (new created_at, new created_by, different ID)
|
||||
- Test hierarchy endpoint without required params returns 400
|
||||
- Test empty search results return 200 with empty array
|
||||
- Test whitespace-only search param is ignored (returns all templates)
|
||||
- Test authentication required for read endpoints (401 without session)
|
||||
- _Requirements: 1.1, 1.2, 1.4, 1.5, 2.1, 2.3, 2.4, 2.11, 2.12, 3.1, 3.3, 6.5, 6.6, 7.4, 7.5, 7.6_
|
||||
|
||||
- [ ]* 9.2 Write integration tests for audit logging and full workflow
|
||||
- File: `backend/__tests__/archer-template-library.integration.test.js`
|
||||
- Test audit log entries created for create/update/delete/clone operations
|
||||
- Test audit log failure does not block template operation (mock logAudit to throw)
|
||||
- Test failed operations (400, 409, 404) do not produce audit entries
|
||||
- Test full workflow: create → list → update → clone → delete
|
||||
- _Requirements: 8.1, 8.2, 8.3, 8.4, 8.5, 8.6_
|
||||
|
||||
- [x] 10. Final checkpoint - Full test suite passes
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
## Notes
|
||||
|
||||
- Tasks marked with `*` are optional test sub-tasks and can be skipped for a faster MVP.
|
||||
- Each task references specific requirements for traceability.
|
||||
- Property tests use `fast-check` with a minimum of 100 iterations and are tagged per the design's Testing Strategy.
|
||||
- The migration must be registered in `run-all.js` to be picked up by CI/CD deploys.
|
||||
- The route module follows the existing `createArcherTicketsRouter()` pattern in `routes/archerTickets.js`.
|
||||
- Frontend styling follows the dark-theme tactical intelligence aesthetic defined in `DESIGN_SYSTEM.md`.
|
||||
- Checkpoints (tasks 3, 5, 8, 10) ensure incremental validation at each layer.
|
||||
|
||||
## Task Dependency Graph
|
||||
|
||||
```json
|
||||
{
|
||||
"waves": [
|
||||
{ "id": 0, "tasks": ["1.1"] },
|
||||
{ "id": 1, "tasks": ["2.1"] },
|
||||
{ "id": 2, "tasks": ["2.2", "2.3"] },
|
||||
{ "id": 3, "tasks": ["2.4"] },
|
||||
{ "id": 4, "tasks": ["4.1", "4.2", "4.3", "4.4", "4.5", "4.6", "4.7", "4.8", "4.9", "4.10", "4.11", "4.12"] },
|
||||
{ "id": 5, "tasks": ["6.1", "7.1"] },
|
||||
{ "id": 6, "tasks": ["6.2", "6.3", "7.2"] },
|
||||
{ "id": 7, "tasks": ["6.4", "7.3"] },
|
||||
{ "id": 8, "tasks": ["9.1", "9.2"] }
|
||||
]
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user