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,312 @@
// =============================================================================
// SmartDiffSchemaValidationTests.cs
// Sprint: SPRINT_3500_0002_0001
// Task: SDIFF-FND-016 - JSON Schema validation tests
// =============================================================================
using System.Text.Json;
using FluentAssertions;
using Json.Schema;
using Xunit;
namespace StellaOps.Scanner.SmartDiff.Tests;
/// <summary>
/// Tests to validate Smart-Diff predicates against JSON Schema.
/// </summary>
[Trait("Category", "Schema")]
[Trait("Sprint", "3500")]
public sealed class SmartDiffSchemaValidationTests
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false
};
[Fact(DisplayName = "Valid SmartDiffPredicate passes schema validation")]
public void ValidPredicate_PassesValidation()
{
// Arrange
var schema = GetSmartDiffSchema();
var predicate = CreateValidPredicate();
var json = JsonSerializer.Serialize(predicate, JsonOptions);
var jsonNode = JsonDocument.Parse(json).RootElement;
// Act
var result = schema.Evaluate(jsonNode);
// Assert
result.IsValid.Should().BeTrue("Valid predicate should pass schema validation");
}
[Fact(DisplayName = "Predicate missing required field fails validation")]
public void MissingRequiredField_FailsValidation()
{
// Arrange
var schema = GetSmartDiffSchema();
var json = """
{
"schemaVersion": "1.0.0",
"baseImage": { "digest": "sha256:abc123" }
}
""";
var jsonNode = JsonDocument.Parse(json).RootElement;
// Act
var result = schema.Evaluate(jsonNode);
// Assert
result.IsValid.Should().BeFalse("Missing required fields should fail validation");
}
[Fact(DisplayName = "Predicate with invalid schema version fails validation")]
public void InvalidSchemaVersion_FailsValidation()
{
// Arrange
var schema = GetSmartDiffSchema();
var json = """
{
"schemaVersion": "invalid",
"baseImage": { "digest": "sha256:abc123" },
"targetImage": { "digest": "sha256:def456" },
"diff": { "added": [], "removed": [], "modified": [] },
"reachabilityGate": { "class": 0, "isSinkReachable": false, "isEntryReachable": false },
"scanner": { "name": "test", "version": "1.0.0" }
}
""";
var jsonNode = JsonDocument.Parse(json).RootElement;
// Act
var result = schema.Evaluate(jsonNode);
// Assert
// Schema version must match semver pattern
result.IsValid.Should().BeFalse("Invalid schema version should fail validation");
}
[Fact(DisplayName = "ReachabilityGate class must be 0-7")]
public void ReachabilityGateClass_MustBe0To7()
{
// Arrange
var schema = GetSmartDiffSchema();
var json = """
{
"schemaVersion": "1.0.0",
"baseImage": { "digest": "sha256:abc123" },
"targetImage": { "digest": "sha256:def456" },
"diff": { "added": [], "removed": [], "modified": [] },
"reachabilityGate": { "class": 10, "isSinkReachable": false, "isEntryReachable": false },
"scanner": { "name": "test", "version": "1.0.0" }
}
""";
var jsonNode = JsonDocument.Parse(json).RootElement;
// Act
var result = schema.Evaluate(jsonNode);
// Assert
result.IsValid.Should().BeFalse("Reachability class > 7 should fail validation");
}
[Fact(DisplayName = "Valid reachability gate class 0 passes")]
public void ReachabilityGateClass0_Passes()
{
// Arrange
var schema = GetSmartDiffSchema();
var json = CreatePredicateJson(gateClass: 0);
var jsonNode = JsonDocument.Parse(json).RootElement;
// Act
var result = schema.Evaluate(jsonNode);
// Assert
result.IsValid.Should().BeTrue();
}
[Fact(DisplayName = "Valid reachability gate class 7 passes")]
public void ReachabilityGateClass7_Passes()
{
// Arrange
var schema = GetSmartDiffSchema();
var json = CreatePredicateJson(gateClass: 7);
var jsonNode = JsonDocument.Parse(json).RootElement;
// Act
var result = schema.Evaluate(jsonNode);
// Assert
result.IsValid.Should().BeTrue();
}
[Fact(DisplayName = "Suppressed count must be non-negative")]
public void SuppressedCount_MustBeNonNegative()
{
// Arrange
var schema = GetSmartDiffSchema();
var json = """
{
"schemaVersion": "1.0.0",
"baseImage": { "digest": "sha256:abc123" },
"targetImage": { "digest": "sha256:def456" },
"diff": { "added": [], "removed": [], "modified": [] },
"reachabilityGate": { "class": 0, "isSinkReachable": false, "isEntryReachable": false },
"scanner": { "name": "test", "version": "1.0.0" },
"suppressedCount": -1
}
""";
var jsonNode = JsonDocument.Parse(json).RootElement;
// Act
var result = schema.Evaluate(jsonNode);
// Assert
result.IsValid.Should().BeFalse("Negative suppressed count should fail");
}
[Fact(DisplayName = "Optional context field is valid when present")]
public void OptionalContext_ValidWhenPresent()
{
// Arrange
var schema = GetSmartDiffSchema();
var json = """
{
"schemaVersion": "1.0.0",
"baseImage": { "digest": "sha256:abc123" },
"targetImage": { "digest": "sha256:def456" },
"diff": { "added": [], "removed": [], "modified": [] },
"reachabilityGate": { "class": 0, "isSinkReachable": false, "isEntryReachable": false },
"scanner": { "name": "test", "version": "1.0.0" },
"context": { "env": "production", "namespace": "default" }
}
""";
var jsonNode = JsonDocument.Parse(json).RootElement;
// Act
var result = schema.Evaluate(jsonNode);
// Assert
result.IsValid.Should().BeTrue();
}
private static JsonSchema GetSmartDiffSchema()
{
// Define schema inline for testing
var schemaJson = """
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://stellaops.dev/schemas/smart-diff.v1.json",
"type": "object",
"required": ["schemaVersion", "baseImage", "targetImage", "diff", "reachabilityGate", "scanner"],
"properties": {
"schemaVersion": {
"type": "string",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
},
"baseImage": {
"type": "object",
"required": ["digest"],
"properties": {
"digest": { "type": "string" },
"repository": { "type": "string" },
"tag": { "type": "string" }
}
},
"targetImage": {
"type": "object",
"required": ["digest"],
"properties": {
"digest": { "type": "string" },
"repository": { "type": "string" },
"tag": { "type": "string" }
}
},
"diff": {
"type": "object",
"required": ["added", "removed", "modified"],
"properties": {
"added": { "type": "array" },
"removed": { "type": "array" },
"modified": { "type": "array" }
}
},
"reachabilityGate": {
"type": "object",
"required": ["class", "isSinkReachable", "isEntryReachable"],
"properties": {
"class": { "type": "integer", "minimum": 0, "maximum": 7 },
"isSinkReachable": { "type": "boolean" },
"isEntryReachable": { "type": "boolean" },
"sinkCategory": { "type": "string" }
}
},
"scanner": {
"type": "object",
"required": ["name", "version"],
"properties": {
"name": { "type": "string" },
"version": { "type": "string" }
}
},
"context": {
"type": "object",
"additionalProperties": true
},
"suppressedCount": {
"type": "integer",
"minimum": 0
},
"materialChanges": {
"type": "array",
"items": {
"type": "object"
}
}
}
}
""";
return JsonSchema.FromText(schemaJson);
}
private static object CreateValidPredicate()
{
return new
{
schemaVersion = "1.0.0",
baseImage = new { digest = "sha256:abc123" },
targetImage = new { digest = "sha256:def456" },
diff = new
{
added = Array.Empty<object>(),
removed = Array.Empty<object>(),
modified = Array.Empty<object>()
},
reachabilityGate = new
{
@class = 0,
isSinkReachable = false,
isEntryReachable = false
},
scanner = new
{
name = "stellaops-scanner",
version = "1.5.0"
}
};
}
private static string CreatePredicateJson(int gateClass)
{
return $$"""
{
"schemaVersion": "1.0.0",
"baseImage": { "digest": "sha256:abc123" },
"targetImage": { "digest": "sha256:def456" },
"diff": { "added": [], "removed": [], "modified": [] },
"reachabilityGate": { "class": {{gateClass}}, "isSinkReachable": false, "isEntryReachable": false },
"scanner": { "name": "test", "version": "1.0.0" }
}
""";
}
}