Add comprehensive security tests for OWASP A02, A05, A07, and A08 categories
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Lighthouse CI / Lighthouse Audit (push) Has been cancelled
Lighthouse CI / Axe Accessibility Audit (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled

- Implemented tests for Cryptographic Failures (A02) to ensure proper handling of sensitive data, secure algorithms, and key management.
- Added tests for Security Misconfiguration (A05) to validate production configurations, security headers, CORS settings, and feature management.
- Developed tests for Authentication Failures (A07) to enforce strong password policies, rate limiting, session management, and MFA support.
- Created tests for Software and Data Integrity Failures (A08) to verify artifact signatures, SBOM integrity, attestation chains, and feed updates.
This commit is contained in:
master
2025-12-16 16:40:19 +02:00
parent 415eff1207
commit 2170a58734
206 changed files with 30547 additions and 534 deletions

View File

@@ -0,0 +1,434 @@
openapi: 3.1.0
info:
title: StellaOps Evidence & Decision API
description: |
REST API for evidence retrieval and decision recording.
Sprint: SPRINT_3602_0001_0001
version: 1.0.0
license:
name: AGPL-3.0-or-later
url: https://www.gnu.org/licenses/agpl-3.0.html
servers:
- url: /v1
description: API v1
security:
- bearerAuth: []
paths:
/alerts:
get:
operationId: listAlerts
summary: List alerts with filtering and pagination
tags:
- Alerts
parameters:
- name: band
in: query
schema:
type: string
enum: [critical, high, medium, low, info]
- name: severity
in: query
schema:
type: string
- name: status
in: query
schema:
type: string
enum: [open, acknowledged, resolved, suppressed]
- name: artifactId
in: query
schema:
type: string
- name: vulnId
in: query
schema:
type: string
- name: componentPurl
in: query
schema:
type: string
- name: limit
in: query
schema:
type: integer
default: 50
maximum: 500
- name: offset
in: query
schema:
type: integer
default: 0
responses:
'200':
description: Alert list
content:
application/json:
schema:
$ref: '#/components/schemas/AlertListResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/alerts/{alertId}:
get:
operationId: getAlert
summary: Get alert details
tags:
- Alerts
parameters:
- $ref: '#/components/parameters/alertId'
responses:
'200':
description: Alert details
content:
application/json:
schema:
$ref: '#/components/schemas/AlertSummary'
'404':
$ref: '#/components/responses/NotFound'
/alerts/{alertId}/evidence:
get:
operationId: getAlertEvidence
summary: Get evidence bundle for an alert
tags:
- Evidence
parameters:
- $ref: '#/components/parameters/alertId'
responses:
'200':
description: Evidence payload
content:
application/json:
schema:
$ref: '#/components/schemas/EvidencePayloadResponse'
'404':
$ref: '#/components/responses/NotFound'
/alerts/{alertId}/decisions:
post:
operationId: recordDecision
summary: Record a decision for an alert
tags:
- Decisions
parameters:
- $ref: '#/components/parameters/alertId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DecisionRequest'
responses:
'201':
description: Decision recorded
content:
application/json:
schema:
$ref: '#/components/schemas/DecisionResponse'
'404':
$ref: '#/components/responses/NotFound'
'400':
$ref: '#/components/responses/BadRequest'
/alerts/{alertId}/audit:
get:
operationId: getAlertAudit
summary: Get audit timeline for an alert
tags:
- Audit
parameters:
- $ref: '#/components/parameters/alertId'
responses:
'200':
description: Audit timeline
content:
application/json:
schema:
$ref: '#/components/schemas/AuditTimelineResponse'
'404':
$ref: '#/components/responses/NotFound'
/alerts/{alertId}/bundle:
get:
operationId: downloadAlertBundle
summary: Download evidence bundle as tar.gz
tags:
- Bundles
parameters:
- $ref: '#/components/parameters/alertId'
responses:
'200':
description: Evidence bundle file
content:
application/gzip:
schema:
type: string
format: binary
'404':
$ref: '#/components/responses/NotFound'
/alerts/{alertId}/bundle/verify:
post:
operationId: verifyAlertBundle
summary: Verify evidence bundle integrity
tags:
- Bundles
parameters:
- $ref: '#/components/parameters/alertId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/BundleVerificationRequest'
responses:
'200':
description: Verification result
content:
application/json:
schema:
$ref: '#/components/schemas/BundleVerificationResponse'
'404':
$ref: '#/components/responses/NotFound'
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
parameters:
alertId:
name: alertId
in: path
required: true
schema:
type: string
description: Alert identifier
responses:
BadRequest:
description: Bad request
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
Unauthorized:
description: Unauthorized
NotFound:
description: Resource not found
schemas:
AlertListResponse:
type: object
required:
- items
- total_count
properties:
items:
type: array
items:
$ref: '#/components/schemas/AlertSummary'
total_count:
type: integer
next_page_token:
type: string
AlertSummary:
type: object
required:
- alert_id
- artifact_id
- vuln_id
- severity
- band
- status
- created_at
properties:
alert_id:
type: string
artifact_id:
type: string
vuln_id:
type: string
component_purl:
type: string
severity:
type: string
band:
type: string
enum: [critical, high, medium, low, info]
status:
type: string
enum: [open, acknowledged, resolved, suppressed]
score:
type: number
format: double
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
decision_count:
type: integer
EvidencePayloadResponse:
type: object
required:
- alert_id
properties:
alert_id:
type: string
reachability:
$ref: '#/components/schemas/EvidenceSection'
callstack:
$ref: '#/components/schemas/EvidenceSection'
vex:
$ref: '#/components/schemas/EvidenceSection'
EvidenceSection:
type: object
properties:
data:
type: object
hash:
type: string
source:
type: string
DecisionRequest:
type: object
required:
- decision
- rationale
properties:
decision:
type: string
enum: [accept_risk, mitigate, suppress, escalate]
rationale:
type: string
minLength: 10
maxLength: 2000
justification_code:
type: string
metadata:
type: object
DecisionResponse:
type: object
required:
- decision_id
- alert_id
- decision
- recorded_at
properties:
decision_id:
type: string
alert_id:
type: string
decision:
type: string
rationale:
type: string
recorded_at:
type: string
format: date-time
recorded_by:
type: string
replay_token:
type: string
AuditTimelineResponse:
type: object
required:
- alert_id
- events
- total_count
properties:
alert_id:
type: string
events:
type: array
items:
$ref: '#/components/schemas/AuditEvent'
total_count:
type: integer
AuditEvent:
type: object
required:
- event_id
- event_type
- timestamp
properties:
event_id:
type: string
event_type:
type: string
timestamp:
type: string
format: date-time
actor:
type: string
details:
type: object
replay_token:
type: string
BundleVerificationRequest:
type: object
required:
- bundle_hash
properties:
bundle_hash:
type: string
description: SHA-256 hash of the bundle
signature:
type: string
description: Optional DSSE signature
BundleVerificationResponse:
type: object
required:
- alert_id
- is_valid
- verified_at
properties:
alert_id:
type: string
is_valid:
type: boolean
verified_at:
type: string
format: date-time
signature_valid:
type: boolean
hash_valid:
type: boolean
chain_valid:
type: boolean
errors:
type: array
items:
type: string
ProblemDetails:
type: object
properties:
type:
type: string
title:
type: string
status:
type: integer
detail:
type: string
instance:
type: string

View File

@@ -0,0 +1,325 @@
# Smart-Diff API Types
> Sprint: SPRINT_3500_0002_0001
> Module: Scanner, Policy, Attestor
This document describes the Smart-Diff types exposed through APIs.
## Smart-Diff Predicate
The Smart-Diff predicate is a DSSE-signed attestation describing differential analysis between two scans.
### Predicate Type URI
```
stellaops.dev/predicates/smart-diff@v1
```
### OpenAPI Schema Fragment
```yaml
SmartDiffPredicate:
type: object
required:
- schemaVersion
- baseImage
- targetImage
- diff
- reachabilityGate
- scanner
properties:
schemaVersion:
type: string
pattern: "^[0-9]+\\.[0-9]+\\.[0-9]+$"
example: "1.0.0"
description: Schema version (semver)
baseImage:
$ref: '#/components/schemas/ImageReference'
targetImage:
$ref: '#/components/schemas/ImageReference'
diff:
$ref: '#/components/schemas/DiffPayload'
reachabilityGate:
$ref: '#/components/schemas/ReachabilityGate'
scanner:
$ref: '#/components/schemas/ScannerInfo'
context:
$ref: '#/components/schemas/RuntimeContext'
suppressedCount:
type: integer
minimum: 0
description: Number of findings suppressed by pre-filters
materialChanges:
type: array
items:
$ref: '#/components/schemas/MaterialChange'
ImageReference:
type: object
required:
- digest
properties:
digest:
type: string
pattern: "^sha256:[a-f0-9]{64}$"
example: "sha256:abc123..."
repository:
type: string
example: "ghcr.io/org/image"
tag:
type: string
example: "v1.2.3"
DiffPayload:
type: object
required:
- added
- removed
- modified
properties:
added:
type: array
items:
$ref: '#/components/schemas/DiffEntry'
description: New vulnerabilities in target
removed:
type: array
items:
$ref: '#/components/schemas/DiffEntry'
description: Vulnerabilities fixed in target
modified:
type: array
items:
$ref: '#/components/schemas/DiffEntry'
description: Changed vulnerability status
DiffEntry:
type: object
required:
- vulnId
- componentPurl
properties:
vulnId:
type: string
example: "CVE-2024-1234"
componentPurl:
type: string
example: "pkg:npm/lodash@4.17.21"
severity:
type: string
enum: [CRITICAL, HIGH, MEDIUM, LOW, UNKNOWN]
changeType:
type: string
enum: [added, removed, severity_changed, status_changed]
ReachabilityGate:
type: object
required:
- class
- isSinkReachable
- isEntryReachable
properties:
class:
type: integer
minimum: 0
maximum: 7
description: |
3-bit reachability class:
- Bit 0: Entry point reachable
- Bit 1: Sink reachable
- Bit 2: Direct path exists
isSinkReachable:
type: boolean
description: Whether a sensitive sink is reachable
isEntryReachable:
type: boolean
description: Whether an entry point is reachable
sinkCategory:
type: string
enum: [file, network, crypto, command, sql, ldap, xpath, ssrf, log, deserialization, reflection]
description: Category of the matched sink
ScannerInfo:
type: object
required:
- name
- version
properties:
name:
type: string
example: "stellaops-scanner"
version:
type: string
example: "1.5.0"
commit:
type: string
example: "abc123"
RuntimeContext:
type: object
additionalProperties: true
description: Optional runtime context for the scan
example:
env: "production"
namespace: "default"
cluster: "us-east-1"
MaterialChange:
type: object
properties:
type:
type: string
enum: [file, package, config]
path:
type: string
hash:
type: string
changeKind:
type: string
enum: [added, removed, modified]
```
## Reachability Gate Classes
| Class | Entry | Sink | Direct | Description |
|-------|-------|------|--------|-------------|
| 0 | ❌ | ❌ | ❌ | Not reachable |
| 1 | ✅ | ❌ | ❌ | Entry point only |
| 2 | ❌ | ✅ | ❌ | Sink only |
| 3 | ✅ | ✅ | ❌ | Both, no direct path |
| 4 | ❌ | ❌ | ✅ | Direct path, no endpoints |
| 5 | ✅ | ❌ | ✅ | Entry + direct |
| 6 | ❌ | ✅ | ✅ | Sink + direct |
| 7 | ✅ | ✅ | ✅ | Full reachability confirmed |
## Sink Categories
| Category | Description | Examples |
|----------|-------------|----------|
| `file` | File system operations | `File.Open`, `fopen` |
| `network` | Network I/O | `HttpClient`, `socket` |
| `crypto` | Cryptographic operations | `SHA256`, `AES` |
| `command` | Command execution | `Process.Start`, `exec` |
| `sql` | SQL queries | `SqlCommand`, query builders |
| `ldap` | LDAP operations | `DirectoryEntry` |
| `xpath` | XPath queries | `XPathNavigator` |
| `ssrf` | Server-side request forgery | HTTP clients with user input |
| `log` | Logging operations | `ILogger`, `Console.Write` |
| `deserialization` | Deserialization | `JsonSerializer`, `BinaryFormatter` |
| `reflection` | Reflection operations | `Type.GetType`, `Assembly.Load` |
## Suppression Rules
### OpenAPI Schema Fragment
```yaml
SuppressionRule:
type: object
required:
- id
- type
properties:
id:
type: string
description: Unique rule identifier
type:
type: string
enum:
- cve_pattern
- purl_pattern
- severity_below
- patch_churn
- sink_category
- reachability_class
pattern:
type: string
description: Regex pattern (for pattern rules)
threshold:
type: string
description: Threshold value (for severity/class rules)
enabled:
type: boolean
default: true
reason:
type: string
description: Human-readable reason for suppression
expires:
type: string
format: date-time
description: Optional expiration timestamp
SuppressionResult:
type: object
properties:
suppressed:
type: boolean
matchedRuleId:
type: string
reason:
type: string
```
## Usage Examples
### Creating a Smart-Diff Predicate
```csharp
var predicate = new SmartDiffPredicate
{
SchemaVersion = "1.0.0",
BaseImage = new ImageReference
{
Digest = "sha256:abc123...",
Repository = "ghcr.io/org/image",
Tag = "v1.0.0"
},
TargetImage = new ImageReference
{
Digest = "sha256:def456...",
Repository = "ghcr.io/org/image",
Tag = "v1.1.0"
},
Diff = new DiffPayload
{
Added = [new DiffEntry { VulnId = "CVE-2024-1234", ... }],
Removed = [],
Modified = []
},
ReachabilityGate = new ReachabilityGate
{
Class = 7,
IsSinkReachable = true,
IsEntryReachable = true,
SinkCategory = SinkCategory.Network
},
Scanner = new ScannerInfo
{
Name = "stellaops-scanner",
Version = "1.5.0"
},
SuppressedCount = 5
};
```
### Evaluating Suppression Rules
```csharp
var evaluator = services.GetRequiredService<ISuppressionRuleEvaluator>();
var result = await evaluator.EvaluateAsync(finding, rules);
if (result.Suppressed)
{
logger.LogInformation(
"Finding {VulnId} suppressed by rule {RuleId}: {Reason}",
finding.VulnId,
result.MatchedRuleId,
result.Reason);
}
```
## Related Documentation
- [Smart-Diff Technical Reference](../product-advisories/14-Dec-2025%20-%20Smart-Diff%20Technical%20Reference.md)
- [Scanner Architecture](../modules/scanner/architecture.md)
- [Policy Architecture](../modules/policy/architecture.md)