357 lines
11 KiB
YAML
357 lines
11 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: Findings Workflow Semantic Contract
|
|
version: 0.1.0
|
|
description: |
|
|
Semantic contract for tenant-context findings workflow operations and audit review.
|
|
These endpoints describe the canonical server-side behavior required by the feature,
|
|
even if concrete implementation remains behind Filament and Livewire action handlers.
|
|
Human-driven and system-driven lifecycle mutations share the same server-side
|
|
transition gateway and audit contract.
|
|
servers:
|
|
- url: http://localhost
|
|
paths:
|
|
/admin/t/{tenant}/findings/{finding}:
|
|
get:
|
|
summary: View a tenant-scoped finding
|
|
operationId: viewFinding
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/FindingId'
|
|
responses:
|
|
'200':
|
|
description: Finding returned within the entitled tenant scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Finding'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
/admin/t/{tenant}/findings/{finding}/workflow/triage:
|
|
post:
|
|
summary: Triage a finding
|
|
operationId: triageFinding
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/FindingId'
|
|
responses:
|
|
'200':
|
|
$ref: '#/components/responses/FindingMutationSucceeded'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
'409':
|
|
$ref: '#/components/responses/InvalidTransition'
|
|
/admin/t/{tenant}/findings/{finding}/workflow/start-progress:
|
|
post:
|
|
summary: Move a finding to in progress
|
|
operationId: startFindingProgress
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/FindingId'
|
|
responses:
|
|
'200':
|
|
$ref: '#/components/responses/FindingMutationSucceeded'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
'409':
|
|
$ref: '#/components/responses/InvalidTransition'
|
|
/admin/t/{tenant}/findings/{finding}/workflow/assign:
|
|
post:
|
|
summary: Assign finding ownership or assignee
|
|
operationId: assignFinding
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/FindingId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
assignee_user_id:
|
|
type:
|
|
- integer
|
|
- 'null'
|
|
owner_user_id:
|
|
type:
|
|
- integer
|
|
- 'null'
|
|
responses:
|
|
'200':
|
|
$ref: '#/components/responses/FindingMutationSucceeded'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
'409':
|
|
$ref: '#/components/responses/InvalidTransition'
|
|
'422':
|
|
$ref: '#/components/responses/ValidationError'
|
|
/admin/t/{tenant}/findings/{finding}/workflow/resolve:
|
|
post:
|
|
summary: Resolve a finding
|
|
operationId: resolveFinding
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/FindingId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [reason]
|
|
properties:
|
|
reason:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
$ref: '#/components/responses/FindingMutationSucceeded'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
'409':
|
|
$ref: '#/components/responses/InvalidTransition'
|
|
'422':
|
|
$ref: '#/components/responses/ValidationError'
|
|
/admin/t/{tenant}/findings/{finding}/workflow/close:
|
|
post:
|
|
summary: Close a finding
|
|
operationId: closeFinding
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/FindingId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [reason]
|
|
properties:
|
|
reason:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
$ref: '#/components/responses/FindingMutationSucceeded'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
'409':
|
|
$ref: '#/components/responses/InvalidTransition'
|
|
'422':
|
|
$ref: '#/components/responses/ValidationError'
|
|
/admin/t/{tenant}/findings/{finding}/workflow/risk-accept:
|
|
post:
|
|
summary: Mark a finding as risk accepted
|
|
operationId: riskAcceptFinding
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/FindingId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [reason]
|
|
properties:
|
|
reason:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
$ref: '#/components/responses/FindingMutationSucceeded'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
'409':
|
|
$ref: '#/components/responses/InvalidTransition'
|
|
'422':
|
|
$ref: '#/components/responses/ValidationError'
|
|
/admin/t/{tenant}/findings/{finding}/workflow/reopen:
|
|
post:
|
|
summary: Reopen a terminal finding
|
|
operationId: reopenFinding
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- $ref: '#/components/parameters/FindingId'
|
|
responses:
|
|
'200':
|
|
$ref: '#/components/responses/FindingMutationSucceeded'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
'409':
|
|
$ref: '#/components/responses/InvalidTransition'
|
|
/admin/t/{tenant}/findings/workflow/bulk:
|
|
post:
|
|
summary: Execute a bulk workflow mutation over multiple findings
|
|
operationId: bulkMutateFindings
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [operation, finding_ids]
|
|
properties:
|
|
operation:
|
|
type: string
|
|
enum: [triage, assign, resolve, close, risk_accept, reopen]
|
|
finding_ids:
|
|
type: array
|
|
items:
|
|
type: integer
|
|
payload:
|
|
type: object
|
|
additionalProperties: true
|
|
responses:
|
|
'200':
|
|
description: Bulk mutation completed with per-record audit history
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [processed, succeeded, failed]
|
|
properties:
|
|
processed:
|
|
type: integer
|
|
succeeded:
|
|
type: integer
|
|
failed:
|
|
type: integer
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
'409':
|
|
$ref: '#/components/responses/InvalidTransition'
|
|
/admin/audit-log:
|
|
get:
|
|
summary: Review finding-related audit history on the canonical audit surface
|
|
operationId: listFindingAuditEvents
|
|
parameters:
|
|
- name: resource_type
|
|
in: query
|
|
schema:
|
|
type: string
|
|
enum: [finding]
|
|
- name: tenant_id
|
|
in: query
|
|
schema:
|
|
type: integer
|
|
responses:
|
|
'200':
|
|
description: Audit events visible within current workspace and tenant entitlement
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/FindingAuditEvent'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
components:
|
|
parameters:
|
|
TenantId:
|
|
name: tenant
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
FindingId:
|
|
name: finding
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
responses:
|
|
FindingMutationSucceeded:
|
|
description: Workflow mutation completed and durable audit history was written
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [finding, audit_event]
|
|
properties:
|
|
finding:
|
|
$ref: '#/components/schemas/Finding'
|
|
audit_event:
|
|
$ref: '#/components/schemas/FindingAuditEvent'
|
|
Forbidden:
|
|
description: Viewer is in scope but lacks required capability
|
|
NotFound:
|
|
description: Viewer is not entitled to the requested workspace or tenant scope, or the record is outside that scope, including workspace-mismatched findings in canonical tenant context
|
|
InvalidTransition:
|
|
description: Requested lifecycle mutation is not valid from the current state
|
|
ValidationError:
|
|
description: Request payload is syntactically valid but missing required business fields
|
|
schemas:
|
|
Finding:
|
|
type: object
|
|
required: [id, tenant_id, workspace_id, status, severity]
|
|
properties:
|
|
id:
|
|
type: integer
|
|
tenant_id:
|
|
type: integer
|
|
workspace_id:
|
|
type: integer
|
|
status:
|
|
type: string
|
|
enum: [new, acknowledged, triaged, in_progress, reopened, resolved, closed, risk_accepted]
|
|
severity:
|
|
type: string
|
|
enum: [low, medium, high, critical]
|
|
due_at:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
format: date-time
|
|
resolved_reason:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
closed_reason:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
FindingAuditEvent:
|
|
type: object
|
|
required: [action, resource_type, resource_id, tenant_id, workspace_id, metadata]
|
|
properties:
|
|
action:
|
|
type: string
|
|
example: finding.resolved
|
|
resource_type:
|
|
type: string
|
|
enum: [finding]
|
|
resource_id:
|
|
type: string
|
|
tenant_id:
|
|
type:
|
|
- integer
|
|
- 'null'
|
|
workspace_id:
|
|
type: integer
|
|
metadata:
|
|
$ref: './finding-audit-event.schema.yaml#/components/schemas/FindingAuditMetadata'
|
|
actor_type:
|
|
type: string
|
|
enum: [human, system, scheduled, integration, platform]
|
|
description: Distinguishes system-origin workflow mutations from human-initiated actions on the canonical audit surface. |