consolidation of some of the modules, localization fixes, product advisories work, qa work

This commit is contained in:
master
2026-03-05 03:54:22 +02:00
parent 7bafcc3eef
commit 8e1cb9448d
3878 changed files with 72600 additions and 46861 deletions

View File

@@ -15,7 +15,7 @@
<ItemGroup>
<ProjectReference Include="../StellaOps.Policy/StellaOps.Policy.csproj" />
<ProjectReference Include="../../../RiskEngine/StellaOps.RiskEngine/StellaOps.RiskEngine.Core/StellaOps.RiskEngine.Core.csproj" />
<ProjectReference Include="../../../Findings/StellaOps.RiskEngine.Core/StellaOps.RiskEngine.Core.csproj" />
<ProjectReference Include="../../../BinaryIndex/__Libraries/StellaOps.BinaryIndex.GoldenSet/StellaOps.BinaryIndex.GoldenSet.csproj" />
</ItemGroup>
</Project>

View File

@@ -4,12 +4,21 @@
"title": "StellaOps Score Policy v1",
"description": "Defines deterministic vulnerability scoring weights, buckets, and overrides",
"type": "object",
"required": ["policyVersion", "weightsBps"],
"required": ["policyVersion", "policyId", "weightsBps"],
"properties": {
"policyVersion": {
"const": "score.v1",
"description": "Policy schema version"
},
"policyId": {
"type": "string",
"minLength": 1,
"description": "Deterministic score policy identifier"
},
"scoringProfile": {
"type": "string",
"description": "Scoring profile selector"
},
"weightsBps": {
"type": "object",
"description": "Weight distribution in basis points (must sum to 10000)",

View File

@@ -14,6 +14,7 @@ public sealed class ScorePolicyLoader
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.IgnoreUnmatchedProperties()
.Build();
private static readonly ScorePolicyValidator Validator = new();
/// <summary>
/// Loads a score policy from a YAML file.
@@ -56,6 +57,21 @@ public sealed class ScorePolicyLoader
throw new ScorePolicyLoadException(
$"Unsupported policy version '{policy.PolicyVersion}' in {source}. Expected 'score.v1'");
if (string.IsNullOrWhiteSpace(policy.PolicyId))
{
throw new ScorePolicyLoadException($"Missing required field 'policyId' in {source}");
}
var validation = Validator.Validate(policy);
if (!validation.IsValid)
{
var details = validation.Errors.Count > 0
? string.Join("; ", validation.Errors)
: "schema validation returned no detailed errors";
throw new ScorePolicyLoadException(
$"Schema validation failed in {source}: {details}");
}
// Validate weight sum
if (!policy.ValidateWeights())
{

View File

@@ -6,6 +6,7 @@ namespace StellaOps.Policy.Scoring;
public sealed record ScorePolicy
{
public required string PolicyVersion { get; init; }
public required string PolicyId { get; init; }
/// <summary>
/// Scoring profile to use. Defaults to "advanced".
@@ -35,6 +36,7 @@ public sealed record ScorePolicy
public static ScorePolicy Default => new()
{
PolicyVersion = "score.v1",
PolicyId = "score-policy.default.v1",
ScoringProfile = "advanced",
WeightsBps = WeightsBps.Default,
Reachability = ReachabilityPolicyConfig.Default,

View File

@@ -0,0 +1,28 @@
using System.Reflection;
using System.Text;
namespace StellaOps.Policy.Scoring;
public static class ScorePolicySchemaResource
{
private const string SchemaResourceName = "StellaOps.Policy.Schemas.score-policy.v1.schema.json";
public static Stream OpenSchemaStream()
{
var assembly = Assembly.GetExecutingAssembly();
var stream = assembly.GetManifestResourceStream(SchemaResourceName);
if (stream is null)
{
throw new InvalidOperationException($"Unable to locate embedded schema resource '{SchemaResourceName}'.");
}
return stream;
}
public static string ReadSchemaJson()
{
using var stream = OpenSchemaStream();
using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
return reader.ReadToEnd();
}
}

View File

@@ -7,6 +7,7 @@
using Json.Schema;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace StellaOps.Policy.Scoring;
@@ -22,7 +23,7 @@ public sealed class ScorePolicyValidator
/// </summary>
public ScorePolicyValidator()
{
_schema = JsonSchema.FromText(ScorePolicySchemaJson);
_schema = JsonSchema.FromText(ScorePolicySchemaResource.ReadSchemaJson());
}
/// <summary>
@@ -113,179 +114,9 @@ public sealed class ScorePolicyValidator
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
WriteIndented = false
};
/// <summary>
/// Embedded JSON Schema for score.v1 policies.
/// </summary>
private const string ScorePolicySchemaJson = """
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://stellaops.dev/schemas/score-policy.v1.json",
"title": "Score Policy",
"type": "object",
"required": ["policyVersion", "policyId", "weightsBps"],
"properties": {
"policyVersion": {
"type": "string",
"const": "score.v1"
},
"policyId": {
"type": "string",
"minLength": 1
},
"policyName": {
"type": "string"
},
"description": {
"type": "string"
},
"weightsBps": {
"$ref": "#/$defs/WeightsBps"
},
"reachabilityConfig": {
"$ref": "#/$defs/ReachabilityConfig"
},
"evidenceConfig": {
"$ref": "#/$defs/EvidenceConfig"
},
"provenanceConfig": {
"$ref": "#/$defs/ProvenanceConfig"
},
"overrides": {
"type": "array",
"items": {
"$ref": "#/$defs/ScoreOverride"
}
}
},
"$defs": {
"WeightsBps": {
"type": "object",
"required": ["baseSeverity", "reachability", "evidence", "provenance"],
"properties": {
"baseSeverity": {
"type": "integer",
"minimum": 0,
"maximum": 10000
},
"reachability": {
"type": "integer",
"minimum": 0,
"maximum": 10000
},
"evidence": {
"type": "integer",
"minimum": 0,
"maximum": 10000
},
"provenance": {
"type": "integer",
"minimum": 0,
"maximum": 10000
}
}
},
"ReachabilityConfig": {
"type": "object",
"properties": {
"reachableMultiplier": {
"type": "number",
"minimum": 0,
"maximum": 2
},
"unreachableMultiplier": {
"type": "number",
"minimum": 0,
"maximum": 2
},
"unknownMultiplier": {
"type": "number",
"minimum": 0,
"maximum": 2
}
}
},
"EvidenceConfig": {
"type": "object",
"properties": {
"kevWeight": {
"type": "number",
"minimum": 0
},
"epssThreshold": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"epssWeight": {
"type": "number",
"minimum": 0
}
}
},
"ProvenanceConfig": {
"type": "object",
"properties": {
"signedBonus": {
"type": "number"
},
"rekorVerifiedBonus": {
"type": "number"
},
"unsignedPenalty": {
"type": "number"
}
}
},
"ScoreOverride": {
"type": "object",
"required": ["id", "match"],
"properties": {
"id": {
"type": "string"
},
"match": {
"type": "object",
"properties": {
"cvePattern": {
"type": "string"
},
"purlPattern": {
"type": "string"
},
"severityEquals": {
"type": "string"
}
}
},
"action": {
"type": "object",
"properties": {
"setScore": {
"type": "number"
},
"addScore": {
"type": "number"
},
"multiplyScore": {
"type": "number"
}
}
},
"reason": {
"type": "string"
},
"expires": {
"type": "string",
"format": "date-time"
}
}
}
}
}
""";
}
/// <summary>

View File

@@ -19,6 +19,7 @@
<EmbeddedResource Include="Schemas\policy-schema@1.json" />
<EmbeddedResource Include="Schemas\policy-scoring-default.json" />
<EmbeddedResource Include="Schemas\policy-scoring-schema@1.json" />
<EmbeddedResource Include="Schemas\score-policy.v1.schema.json" />
<EmbeddedResource Include="Schemas\signals-schema@1.json" />
<EmbeddedResource Include="Schemas\spl-schema@1.json" />
<EmbeddedResource Include="Schemas\spl-sample@1.json" />