save checkpoint: save features
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"StellaOps.Attestation/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"StellaOps.Attestor.Envelope": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Attestation.dll": {}
|
||||
}
|
||||
},
|
||||
"Blake3/1.1.0": {
|
||||
"runtime": {
|
||||
"lib/net7.0/Blake3.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.1.0.0"
|
||||
}
|
||||
},
|
||||
"runtimeTargets": {
|
||||
"runtimes/linux-arm/native/libblake3_dotnet.so": {
|
||||
"rid": "linux-arm",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/linux-arm64/native/libblake3_dotnet.so": {
|
||||
"rid": "linux-arm64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/linux-x64/native/libblake3_dotnet.so": {
|
||||
"rid": "linux-x64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/osx-arm64/native/libblake3_dotnet.dylib": {
|
||||
"rid": "osx-arm64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/osx-x64/native/libblake3_dotnet.dylib": {
|
||||
"rid": "osx-x64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/win-arm64/native/blake3_dotnet.dll": {
|
||||
"rid": "win-arm64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/win-x64/native/blake3_dotnet.dll": {
|
||||
"rid": "win-x64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/win-x86/native/blake3_dotnet.dll": {
|
||||
"rid": "win-x86",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BouncyCastle.Cryptography/2.6.2": {
|
||||
"runtime": {
|
||||
"lib/net6.0/BouncyCastle.Cryptography.dll": {
|
||||
"assemblyVersion": "2.0.0.0",
|
||||
"fileVersion": "2.6.2.46322"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Logging.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Options/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Primitives": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Options.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives/10.0.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Primitives.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/8.15.0": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Abstractions.dll": {
|
||||
"assemblyVersion": "8.15.0.0",
|
||||
"fileVersion": "8.15.0.61118"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/8.15.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Abstractions": "8.15.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Logging.dll": {
|
||||
"assemblyVersion": "8.15.0.0",
|
||||
"fileVersion": "8.15.0.61118"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/8.15.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.IdentityModel.Logging": "8.15.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Tokens.dll": {
|
||||
"assemblyVersion": "8.15.0.0",
|
||||
"fileVersion": "8.15.0.61118"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Attestor.Envelope/1.0.0": {
|
||||
"dependencies": {
|
||||
"BouncyCastle.Cryptography": "2.6.2",
|
||||
"StellaOps.Cryptography": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Attestor.Envelope.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Cryptography/1.0.0": {
|
||||
"dependencies": {
|
||||
"Blake3": "1.1.0",
|
||||
"BouncyCastle.Cryptography": "2.6.2",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1",
|
||||
"Microsoft.IdentityModel.Tokens": "8.15.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Cryptography.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"StellaOps.Attestation/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Blake3/1.1.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-/gWRFsXYeIFof8YAoFJwzv2fYjSTCo+6vvTSL6pyXw2ZLXQdRvEyXhO43jyDfEFBCTxMxWpoHbIcIEIF6a3QdQ==",
|
||||
"path": "blake3/1.1.0",
|
||||
"hashPath": "blake3.1.1.0.nupkg.sha512"
|
||||
},
|
||||
"BouncyCastle.Cryptography/2.6.2": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w==",
|
||||
"path": "bouncycastle.cryptography/2.6.2",
|
||||
"hashPath": "bouncycastle.cryptography.2.6.2.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==",
|
||||
"path": "microsoft.extensions.dependencyinjection.abstractions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==",
|
||||
"path": "microsoft.extensions.logging.abstractions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.logging.abstractions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Options/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==",
|
||||
"path": "microsoft.extensions.options/10.0.1",
|
||||
"hashPath": "microsoft.extensions.options.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Primitives/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==",
|
||||
"path": "microsoft.extensions.primitives/10.0.1",
|
||||
"hashPath": "microsoft.extensions.primitives.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/8.15.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-e/DApa1GfxUqHSBHcpiQg8yaghKAvFVBQFcWh25jNoRobDZbduTUACY8bZ54eeGWXvimGmEDdF0zkS5Dq16XPQ==",
|
||||
"path": "microsoft.identitymodel.abstractions/8.15.0",
|
||||
"hashPath": "microsoft.identitymodel.abstractions.8.15.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/8.15.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-1gJLjhy0LV2RQMJ9NGzi5Tnb2l+c37o8D8Lrk2mrvmb6OQHZ7XJstd/XxvncXgBpad4x9CGXdipbZzJJCXKyAg==",
|
||||
"path": "microsoft.identitymodel.logging/8.15.0",
|
||||
"hashPath": "microsoft.identitymodel.logging.8.15.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/8.15.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-zUE9ysJXBtXlHHRtcRK3Sp8NzdCI1z/BRDTXJQ2TvBoI0ENRtnufYIep0O5TSCJRJGDwwuLTUx+l/bEYZUxpCA==",
|
||||
"path": "microsoft.identitymodel.tokens/8.15.0",
|
||||
"hashPath": "microsoft.identitymodel.tokens.8.15.0.nupkg.sha512"
|
||||
},
|
||||
"StellaOps.Attestor.Envelope/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Cryptography/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"StellaOps.Attestor.Envelope/1.0.0": {
|
||||
"dependencies": {
|
||||
"BouncyCastle.Cryptography": "2.6.2",
|
||||
"StellaOps.Cryptography": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Attestor.Envelope.dll": {}
|
||||
}
|
||||
},
|
||||
"Blake3/1.1.0": {
|
||||
"runtime": {
|
||||
"lib/net7.0/Blake3.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.1.0.0"
|
||||
}
|
||||
},
|
||||
"runtimeTargets": {
|
||||
"runtimes/linux-arm/native/libblake3_dotnet.so": {
|
||||
"rid": "linux-arm",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/linux-arm64/native/libblake3_dotnet.so": {
|
||||
"rid": "linux-arm64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/linux-x64/native/libblake3_dotnet.so": {
|
||||
"rid": "linux-x64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/osx-arm64/native/libblake3_dotnet.dylib": {
|
||||
"rid": "osx-arm64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/osx-x64/native/libblake3_dotnet.dylib": {
|
||||
"rid": "osx-x64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/win-arm64/native/blake3_dotnet.dll": {
|
||||
"rid": "win-arm64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/win-x64/native/blake3_dotnet.dll": {
|
||||
"rid": "win-x64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/win-x86/native/blake3_dotnet.dll": {
|
||||
"rid": "win-x86",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BouncyCastle.Cryptography/2.6.2": {
|
||||
"runtime": {
|
||||
"lib/net6.0/BouncyCastle.Cryptography.dll": {
|
||||
"assemblyVersion": "2.0.0.0",
|
||||
"fileVersion": "2.6.2.46322"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Logging.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Options/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Primitives": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Options.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives/10.0.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Primitives.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/8.15.0": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Abstractions.dll": {
|
||||
"assemblyVersion": "8.15.0.0",
|
||||
"fileVersion": "8.15.0.61118"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/8.15.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Abstractions": "8.15.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Logging.dll": {
|
||||
"assemblyVersion": "8.15.0.0",
|
||||
"fileVersion": "8.15.0.61118"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/8.15.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.IdentityModel.Logging": "8.15.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Tokens.dll": {
|
||||
"assemblyVersion": "8.15.0.0",
|
||||
"fileVersion": "8.15.0.61118"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Cryptography/1.0.0": {
|
||||
"dependencies": {
|
||||
"Blake3": "1.1.0",
|
||||
"BouncyCastle.Cryptography": "2.6.2",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1",
|
||||
"Microsoft.IdentityModel.Tokens": "8.15.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Cryptography.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"StellaOps.Attestor.Envelope/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Blake3/1.1.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-/gWRFsXYeIFof8YAoFJwzv2fYjSTCo+6vvTSL6pyXw2ZLXQdRvEyXhO43jyDfEFBCTxMxWpoHbIcIEIF6a3QdQ==",
|
||||
"path": "blake3/1.1.0",
|
||||
"hashPath": "blake3.1.1.0.nupkg.sha512"
|
||||
},
|
||||
"BouncyCastle.Cryptography/2.6.2": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w==",
|
||||
"path": "bouncycastle.cryptography/2.6.2",
|
||||
"hashPath": "bouncycastle.cryptography.2.6.2.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==",
|
||||
"path": "microsoft.extensions.dependencyinjection.abstractions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==",
|
||||
"path": "microsoft.extensions.logging.abstractions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.logging.abstractions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Options/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==",
|
||||
"path": "microsoft.extensions.options/10.0.1",
|
||||
"hashPath": "microsoft.extensions.options.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Primitives/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==",
|
||||
"path": "microsoft.extensions.primitives/10.0.1",
|
||||
"hashPath": "microsoft.extensions.primitives.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/8.15.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-e/DApa1GfxUqHSBHcpiQg8yaghKAvFVBQFcWh25jNoRobDZbduTUACY8bZ54eeGWXvimGmEDdF0zkS5Dq16XPQ==",
|
||||
"path": "microsoft.identitymodel.abstractions/8.15.0",
|
||||
"hashPath": "microsoft.identitymodel.abstractions.8.15.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/8.15.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-1gJLjhy0LV2RQMJ9NGzi5Tnb2l+c37o8D8Lrk2mrvmb6OQHZ7XJstd/XxvncXgBpad4x9CGXdipbZzJJCXKyAg==",
|
||||
"path": "microsoft.identitymodel.logging/8.15.0",
|
||||
"hashPath": "microsoft.identitymodel.logging.8.15.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/8.15.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-zUE9ysJXBtXlHHRtcRK3Sp8NzdCI1z/BRDTXJQ2TvBoI0ENRtnufYIep0O5TSCJRJGDwwuLTUx+l/bEYZUxpCA==",
|
||||
"path": "microsoft.identitymodel.tokens/8.15.0",
|
||||
"hashPath": "microsoft.identitymodel.tokens.8.15.0.nupkg.sha512"
|
||||
},
|
||||
"StellaOps.Cryptography/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,4 +93,20 @@ public sealed partial class ChangeTraceAttestationService
|
||||
_ => ExploitabilityImpact.Unchanged
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsHysteresisSuppressed(PackageDelta delta, double threshold)
|
||||
{
|
||||
if (threshold <= 0 || delta.TrustDelta is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var isOscillationCandidate = delta.ChangeType is PackageChangeType.Modified or PackageChangeType.Rebuilt;
|
||||
if (!isOscillationCandidate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Math.Abs(delta.TrustDelta.Score) < threshold;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,11 @@ public sealed partial class ChangeTraceAttestationService
|
||||
ChangeTraceModel trace,
|
||||
ChangeTraceAttestationOptions options)
|
||||
{
|
||||
var deltas = trace.Deltas
|
||||
var filteredDeltas = trace.Deltas
|
||||
.Where(delta => !IsHysteresisSuppressed(delta, options.HysteresisThreshold))
|
||||
.ToImmutableArray();
|
||||
|
||||
var deltas = filteredDeltas
|
||||
.Take(options.MaxDeltas)
|
||||
.Select(d => new ChangeTraceDeltaEntry
|
||||
{
|
||||
@@ -40,14 +44,14 @@ public sealed partial class ChangeTraceAttestationService
|
||||
})
|
||||
.ToImmutableArray();
|
||||
|
||||
var proofSteps = trace.Deltas
|
||||
var proofSteps = filteredDeltas
|
||||
.Where(d => d.TrustDelta is not null)
|
||||
.SelectMany(d => d.TrustDelta!.ProofSteps)
|
||||
.Distinct()
|
||||
.Take(options.MaxProofSteps)
|
||||
.ToImmutableArray();
|
||||
|
||||
var aggregateReachability = AggregateReachabilityImpact(trace.Deltas);
|
||||
var aggregateReachability = AggregateReachabilityImpact(filteredDeltas);
|
||||
var aggregateExploitability = DetermineExploitabilityFromScore(trace.Summary.RiskDelta);
|
||||
|
||||
return new ChangeTracePredicate
|
||||
|
||||
@@ -55,4 +55,10 @@ public sealed record ChangeTraceAttestationOptions
|
||||
/// Maximum number of deltas to include in the predicate.
|
||||
/// </summary>
|
||||
public int MaxDeltas { get; init; } = 1000;
|
||||
|
||||
/// <summary>
|
||||
/// Suppress minor modified/rebuilt deltas whose absolute trust score is below this threshold.
|
||||
/// This dampens noisy flip-flop changes between near-identical snapshots.
|
||||
/// </summary>
|
||||
public double HysteresisThreshold { get; init; } = 0.05;
|
||||
}
|
||||
|
||||
@@ -4,12 +4,20 @@ namespace StellaOps.Attestor.ProofChain.Generators;
|
||||
|
||||
public sealed partial class BackportProofGenerator
|
||||
{
|
||||
private enum ProofStrength
|
||||
{
|
||||
Heuristic = 40,
|
||||
StaticAnalysis = 60,
|
||||
BinaryProof = 80,
|
||||
Authoritative = 100
|
||||
}
|
||||
|
||||
private static double ComputeAggregateConfidence(IReadOnlyList<ProofEvidence> evidences)
|
||||
{
|
||||
// Confidence aggregation strategy:
|
||||
// 1. Start with highest individual confidence
|
||||
// 2. Add bonus for multiple independent sources
|
||||
// 3. Cap at 0.98 (never 100% certain)
|
||||
// 1. Start from strongest proof tier (Authoritative > Binary > Static > Heuristic)
|
||||
// 2. Add small diminishing bonus for independent corroboration
|
||||
// 3. Cap at 0.98 (never claim perfect certainty)
|
||||
|
||||
var baseConfidence = evidences.Count switch
|
||||
{
|
||||
@@ -18,8 +26,7 @@ public sealed partial class BackportProofGenerator
|
||||
_ => evidences.Max(e => DetermineEvidenceConfidence(e.Type))
|
||||
};
|
||||
|
||||
// Bonus for multiple sources (diminishing returns)
|
||||
var multiSourceBonus = evidences.Count switch
|
||||
var corroborationBonus = evidences.Count switch
|
||||
{
|
||||
<= 1 => 0.0,
|
||||
2 => 0.05,
|
||||
@@ -27,22 +34,23 @@ public sealed partial class BackportProofGenerator
|
||||
_ => 0.10
|
||||
};
|
||||
|
||||
return Math.Min(baseConfidence + multiSourceBonus, 0.98);
|
||||
return Math.Min(baseConfidence + corroborationBonus, 0.98);
|
||||
}
|
||||
|
||||
private static double DetermineEvidenceConfidence(EvidenceType type)
|
||||
{
|
||||
return type switch
|
||||
=> (double)ResolveStrength(type) / 100.0;
|
||||
|
||||
private static ProofStrength ResolveStrength(EvidenceType type)
|
||||
=> type switch
|
||||
{
|
||||
EvidenceType.DistroAdvisory => 0.98,
|
||||
EvidenceType.ChangelogMention => 0.80,
|
||||
EvidenceType.PatchHeader => 0.85,
|
||||
EvidenceType.BinaryFingerprint => 0.70,
|
||||
EvidenceType.VersionComparison => 0.95,
|
||||
EvidenceType.BuildCatalog => 0.90,
|
||||
_ => 0.50
|
||||
EvidenceType.DistroAdvisory => ProofStrength.Authoritative,
|
||||
EvidenceType.VersionComparison => ProofStrength.BinaryProof,
|
||||
EvidenceType.BuildCatalog => ProofStrength.BinaryProof,
|
||||
EvidenceType.PatchHeader => ProofStrength.StaticAnalysis,
|
||||
EvidenceType.ChangelogMention => ProofStrength.StaticAnalysis,
|
||||
EvidenceType.BinaryFingerprint => ProofStrength.Heuristic,
|
||||
_ => ProofStrength.Heuristic
|
||||
};
|
||||
}
|
||||
|
||||
private static string DetermineMethod(IReadOnlyList<ProofEvidence> evidences)
|
||||
{
|
||||
|
||||
@@ -11,6 +11,18 @@ public sealed partial class InMemoryProofGraphService
|
||||
string targetId,
|
||||
ProofGraphEdgeType edgeType,
|
||||
CancellationToken ct = default)
|
||||
=> AddEdgeAsync(sourceId, targetId, edgeType, provenance: null, ct);
|
||||
|
||||
/// <summary>
|
||||
/// Add a provenance-aware edge. Duplicate edges are merged by semantic key
|
||||
/// and provenance values are merged deterministically.
|
||||
/// </summary>
|
||||
public Task<ProofGraphEdge> AddEdgeAsync(
|
||||
string sourceId,
|
||||
string targetId,
|
||||
ProofGraphEdgeType edgeType,
|
||||
IEnumerable<string>? provenance,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(sourceId);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(targetId);
|
||||
@@ -26,6 +38,7 @@ public sealed partial class InMemoryProofGraphService
|
||||
}
|
||||
|
||||
var edgeId = $"{sourceId}->{edgeType}->{targetId}";
|
||||
var normalizedProvenance = NormalizeProvenance(provenance);
|
||||
|
||||
var edge = new ProofGraphEdge
|
||||
{
|
||||
@@ -33,7 +46,8 @@ public sealed partial class InMemoryProofGraphService
|
||||
SourceId = sourceId,
|
||||
TargetId = targetId,
|
||||
Type = edgeType,
|
||||
CreatedAt = _timeProvider.GetUtcNow()
|
||||
CreatedAt = _timeProvider.GetUtcNow(),
|
||||
Provenance = normalizedProvenance
|
||||
};
|
||||
|
||||
if (_edges.TryAdd(edgeId, edge))
|
||||
@@ -51,8 +65,38 @@ public sealed partial class InMemoryProofGraphService
|
||||
else
|
||||
{
|
||||
edge = _edges[edgeId];
|
||||
if (normalizedProvenance.Count != 0)
|
||||
{
|
||||
var merged = edge.Provenance
|
||||
.Concat(normalizedProvenance)
|
||||
.Where(value => !string.IsNullOrWhiteSpace(value))
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.OrderBy(value => value, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
|
||||
if (merged.Length != edge.Provenance.Count || !merged.SequenceEqual(edge.Provenance, StringComparer.Ordinal))
|
||||
{
|
||||
edge = edge with { Provenance = merged };
|
||||
_edges[edgeId] = edge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult(edge);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> NormalizeProvenance(IEnumerable<string>? provenance)
|
||||
{
|
||||
if (provenance is null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
return provenance
|
||||
.Where(value => !string.IsNullOrWhiteSpace(value))
|
||||
.Select(value => value.Trim())
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.OrderBy(value => value, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,4 +31,10 @@ public sealed record ProofGraphEdge
|
||||
/// When this edge was created.
|
||||
/// </summary>
|
||||
public required DateTimeOffset CreatedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Provenance sources associated with this edge.
|
||||
/// When duplicate edges are ingested, provenance is merged deterministically.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> Provenance { get; init; } = [];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
|
||||
namespace StellaOps.Attestor.ProofChain.Predicates;
|
||||
|
||||
public sealed partial record DeltaVerdictPredicate
|
||||
{
|
||||
public const string ChangeTypeNew = "New";
|
||||
public const string ChangeTypeResolved = "Resolved";
|
||||
public const string ChangeTypeConfidenceUp = "ConfidenceUp";
|
||||
public const string ChangeTypeConfidenceDown = "ConfidenceDown";
|
||||
public const string ChangeTypePolicyImpact = "PolicyImpact";
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy where every change has a normalized change type.
|
||||
/// </summary>
|
||||
public DeltaVerdictPredicate CategorizeChanges()
|
||||
=> this with
|
||||
{
|
||||
Changes = Changes.Select(CategorizeChange).ToImmutableArray()
|
||||
};
|
||||
|
||||
internal static DeltaVerdictChange CategorizeChange(DeltaVerdictChange change)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(change);
|
||||
|
||||
var explicitType = NormalizeExplicitType(change.ChangeType);
|
||||
if (explicitType is not null)
|
||||
{
|
||||
return change with { ChangeType = explicitType };
|
||||
}
|
||||
|
||||
var inferred = InferChangeType(change);
|
||||
return change with { ChangeType = inferred };
|
||||
}
|
||||
|
||||
private static string InferChangeType(DeltaVerdictChange change)
|
||||
{
|
||||
if (IsPolicyImpact(change))
|
||||
{
|
||||
return ChangeTypePolicyImpact;
|
||||
}
|
||||
|
||||
if (IsNew(change))
|
||||
{
|
||||
return ChangeTypeNew;
|
||||
}
|
||||
|
||||
if (IsResolved(change))
|
||||
{
|
||||
return ChangeTypeResolved;
|
||||
}
|
||||
|
||||
if (TryParseScoreDelta(change.PreviousValue, change.CurrentValue, out var delta))
|
||||
{
|
||||
if (delta > 0)
|
||||
{
|
||||
return ChangeTypeConfidenceUp;
|
||||
}
|
||||
|
||||
if (delta < 0)
|
||||
{
|
||||
return ChangeTypeConfidenceDown;
|
||||
}
|
||||
}
|
||||
|
||||
var direction = change.Direction ?? string.Empty;
|
||||
if (direction.Contains("increase", StringComparison.OrdinalIgnoreCase)
|
||||
|| direction.Contains("up", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ChangeTypeConfidenceUp;
|
||||
}
|
||||
|
||||
if (direction.Contains("decrease", StringComparison.OrdinalIgnoreCase)
|
||||
|| direction.Contains("down", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ChangeTypeConfidenceDown;
|
||||
}
|
||||
|
||||
return ChangeTypePolicyImpact;
|
||||
}
|
||||
|
||||
private static bool IsPolicyImpact(DeltaVerdictChange change)
|
||||
=> ContainsAny(change.Rule, "policy", "rule")
|
||||
|| ContainsAny(change.Reason, "policy", "gate", "rule");
|
||||
|
||||
private static bool IsNew(DeltaVerdictChange change)
|
||||
=> ContainsAny(change.Direction, "new", "added", "introduced")
|
||||
|| string.IsNullOrWhiteSpace(change.PreviousValue) && !string.IsNullOrWhiteSpace(change.CurrentValue);
|
||||
|
||||
private static bool IsResolved(DeltaVerdictChange change)
|
||||
=> ContainsAny(change.Direction, "resolved", "removed", "eliminated")
|
||||
|| !string.IsNullOrWhiteSpace(change.PreviousValue) && string.IsNullOrWhiteSpace(change.CurrentValue);
|
||||
|
||||
private static bool ContainsAny(string? value, params string[] needles)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return needles.Any(needle => value.Contains(needle, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static bool TryParseScoreDelta(string? before, string? after, out double delta)
|
||||
{
|
||||
delta = 0;
|
||||
if (!double.TryParse(before, NumberStyles.Float, CultureInfo.InvariantCulture, out var beforeValue)
|
||||
|| !double.TryParse(after, NumberStyles.Float, CultureInfo.InvariantCulture, out var afterValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
delta = afterValue - beforeValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string? NormalizeExplicitType(string? rawType)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(rawType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return rawType.Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"new" => ChangeTypeNew,
|
||||
"resolved" => ChangeTypeResolved,
|
||||
"confidenceup" => ChangeTypeConfidenceUp,
|
||||
"confidencedown" => ChangeTypeConfidenceDown,
|
||||
"policyimpact" => ChangeTypePolicyImpact,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
# Attestor ProofChain Task Board
|
||||
# Attestor ProofChain Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| QA-ATTESTOR-VERIFY-006 | DONE | `ai-remediation-plan-attestation` verified with run-001 Tier 0/1/2 evidence; fixed remediation threshold fixture and completed retest (`17/17`). |
|
||||
| QA-ATTESTOR-VERIFY-007 | DONE | `asn-1-native-rfc-3161-timestamp-token-parsing` run-001 completed with terminal `not_implemented`; dossier moved to `docs/features/unimplemented/attestor/`. |
|
||||
| QA-ATTESTOR-VERIFY-008 | DONE | `attestable-exception-objects-with-expiries-and-audit-trails` run-001 reached terminal `not_implemented`; dossier moved to `docs/features/unimplemented/attestor/`. |
|
||||
| QA-ATTESTOR-VERIFY-009 | DONE | `attestable-reachability-slices` run-001 verified with targeted reachability witness DSSE behavior tests (`5/5`) and moved to `docs/features/checked/attestor/`. |
|
||||
| QA-ATTESTOR-VERIFY-001 | DONE | `adaptive-noise-gating-for-vulnerability-graphs` verified with run-002 evidence and moved to checked. |
|
||||
| QA-ATTESTOR-VERIFY-002 | DONE | `ai-assisted-explanation-and-classification` verified with run-001 evidence and moved to checked. |
|
||||
| QA-ATTESTOR-VERIFY-003 | DONE | `ai-authority-classification-engine` revalidated with run-002 (`11/11`) and docs/state synchronized. |
|
||||
@@ -13,3 +17,6 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
|
||||
| AUDIT-0062-T | DONE | Revalidated 2026-01-06. |
|
||||
| AUDIT-0062-A | TODO | Reopened after revalidation 2026-01-06. |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,558 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"StellaOps.Attestor.ProofChain/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"StellaOps.Attestor.Envelope": "1.0.0",
|
||||
"StellaOps.Canonical.Json": "1.0.0",
|
||||
"StellaOps.Concelier.SourceIntel": "1.0.0",
|
||||
"StellaOps.Feedser.BinaryAnalysis": "1.0.0",
|
||||
"StellaOps.Feedser.Core": "1.0.0",
|
||||
"StellaOps.Scanner.ChangeTrace": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Attestor.ProofChain.dll": {}
|
||||
}
|
||||
},
|
||||
"Blake3/1.1.0": {
|
||||
"runtime": {
|
||||
"lib/net7.0/Blake3.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.1.0.0"
|
||||
}
|
||||
},
|
||||
"runtimeTargets": {
|
||||
"runtimes/linux-arm/native/libblake3_dotnet.so": {
|
||||
"rid": "linux-arm",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/linux-arm64/native/libblake3_dotnet.so": {
|
||||
"rid": "linux-arm64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/linux-x64/native/libblake3_dotnet.so": {
|
||||
"rid": "linux-x64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/osx-arm64/native/libblake3_dotnet.dylib": {
|
||||
"rid": "osx-arm64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/osx-x64/native/libblake3_dotnet.dylib": {
|
||||
"rid": "osx-x64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/win-arm64/native/blake3_dotnet.dll": {
|
||||
"rid": "win-arm64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/win-x64/native/blake3_dotnet.dll": {
|
||||
"rid": "win-x64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/win-x86/native/blake3_dotnet.dll": {
|
||||
"rid": "win-x86",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BouncyCastle.Cryptography/2.6.2": {
|
||||
"runtime": {
|
||||
"lib/net6.0/BouncyCastle.Cryptography.dll": {
|
||||
"assemblyVersion": "2.0.0.0",
|
||||
"fileVersion": "2.6.2.46322"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Caching.Abstractions/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Caching.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Caching.Memory/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Caching.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1",
|
||||
"Microsoft.Extensions.Primitives": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Caching.Memory.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Primitives": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Configuration.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Abstractions/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Configuration.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Binder/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration": "10.0.1",
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Configuration.Binder.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.DependencyInjection.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Diagnostics/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration": "10.0.1",
|
||||
"Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Diagnostics.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Diagnostics.Abstractions/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Diagnostics.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Http/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Diagnostics": "10.0.1",
|
||||
"Microsoft.Extensions.Logging": "10.0.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Http.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "10.0.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Logging.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Logging.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Options/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Primitives": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Options.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Options.ConfigurationExtensions/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Configuration.Binder": "10.0.1",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1",
|
||||
"Microsoft.Extensions.Primitives": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives/10.0.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Primitives.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/8.15.0": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Abstractions.dll": {
|
||||
"assemblyVersion": "8.15.0.0",
|
||||
"fileVersion": "8.15.0.61118"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/8.15.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Abstractions": "8.15.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Logging.dll": {
|
||||
"assemblyVersion": "8.15.0.0",
|
||||
"fileVersion": "8.15.0.61118"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/8.15.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.IdentityModel.Logging": "8.15.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Tokens.dll": {
|
||||
"assemblyVersion": "8.15.0.0",
|
||||
"fileVersion": "8.15.0.61118"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Attestor.Envelope/1.0.0": {
|
||||
"dependencies": {
|
||||
"BouncyCastle.Cryptography": "2.6.2",
|
||||
"StellaOps.Cryptography": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Attestor.Envelope.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Canonical.Json/1.0.0": {
|
||||
"runtime": {
|
||||
"StellaOps.Canonical.Json.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Concelier.SourceIntel/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Caching.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Caching.Memory": "10.0.1",
|
||||
"Microsoft.Extensions.Http": "10.0.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.SourceIntel.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Cryptography/1.0.0": {
|
||||
"dependencies": {
|
||||
"Blake3": "1.1.0",
|
||||
"BouncyCastle.Cryptography": "2.6.2",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1",
|
||||
"Microsoft.IdentityModel.Tokens": "8.15.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Cryptography.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Feedser.BinaryAnalysis/1.0.0": {
|
||||
"runtime": {
|
||||
"StellaOps.Feedser.BinaryAnalysis.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Feedser.Core/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Feedser.Core.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Scanner.ChangeTrace/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1",
|
||||
"StellaOps.Canonical.Json": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Scanner.ChangeTrace.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"StellaOps.Attestor.ProofChain/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Blake3/1.1.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-/gWRFsXYeIFof8YAoFJwzv2fYjSTCo+6vvTSL6pyXw2ZLXQdRvEyXhO43jyDfEFBCTxMxWpoHbIcIEIF6a3QdQ==",
|
||||
"path": "blake3/1.1.0",
|
||||
"hashPath": "blake3.1.1.0.nupkg.sha512"
|
||||
},
|
||||
"BouncyCastle.Cryptography/2.6.2": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w==",
|
||||
"path": "bouncycastle.cryptography/2.6.2",
|
||||
"hashPath": "bouncycastle.cryptography.2.6.2.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Caching.Abstractions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Vb1vVAQDxHpXVdL9fpOX2BzeV7bbhzG4pAcIKRauRl0/VfkE8mq0f+fYC+gWICh3dlzTZInJ/cTeBS2MgU/XvQ==",
|
||||
"path": "microsoft.extensions.caching.abstractions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.caching.abstractions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Caching.Memory/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-NxqSP0Ky4dZ5ybszdZCqs1X2C70s+dXflqhYBUh/vhcQVTIooNCXIYnLVbafoAFGZMs51d9+rHxveXs0ZC3SQQ==",
|
||||
"path": "microsoft.extensions.caching.memory/10.0.1",
|
||||
"hashPath": "microsoft.extensions.caching.memory.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Configuration/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==",
|
||||
"path": "microsoft.extensions.configuration/10.0.1",
|
||||
"hashPath": "microsoft.extensions.configuration.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Abstractions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==",
|
||||
"path": "microsoft.extensions.configuration.abstractions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.configuration.abstractions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Binder/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==",
|
||||
"path": "microsoft.extensions.configuration.binder/10.0.1",
|
||||
"hashPath": "microsoft.extensions.configuration.binder.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==",
|
||||
"path": "microsoft.extensions.dependencyinjection/10.0.1",
|
||||
"hashPath": "microsoft.extensions.dependencyinjection.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==",
|
||||
"path": "microsoft.extensions.dependencyinjection.abstractions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Diagnostics/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-YaocqxscJLxLit0F5yq2XyB+9C7rSRfeTL7MJIl7XwaOoUO3i0EqfO2kmtjiRduYWw7yjcSINEApYZbzjau2gQ==",
|
||||
"path": "microsoft.extensions.diagnostics/10.0.1",
|
||||
"hashPath": "microsoft.extensions.diagnostics.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Diagnostics.Abstractions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-QMoMrkNpnQym5mpfdxfxpRDuqLpsOuztguFvzH9p+Ex+do+uLFoi7UkAsBO4e9/tNR3eMFraFf2fOAi2cp3jjA==",
|
||||
"path": "microsoft.extensions.diagnostics.abstractions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.diagnostics.abstractions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Http/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ZXJup9ReE1Ot3M8jqcw1b/lnc8USxyYS3cyLsssU39u04TES9JNGviWUGIvP3K7mMU3TF7kQl2aS0SmVwegflw==",
|
||||
"path": "microsoft.extensions.http/10.0.1",
|
||||
"hashPath": "microsoft.extensions.http.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Logging/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==",
|
||||
"path": "microsoft.extensions.logging/10.0.1",
|
||||
"hashPath": "microsoft.extensions.logging.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==",
|
||||
"path": "microsoft.extensions.logging.abstractions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.logging.abstractions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Options/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==",
|
||||
"path": "microsoft.extensions.options/10.0.1",
|
||||
"hashPath": "microsoft.extensions.options.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Options.ConfigurationExtensions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==",
|
||||
"path": "microsoft.extensions.options.configurationextensions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.options.configurationextensions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Primitives/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==",
|
||||
"path": "microsoft.extensions.primitives/10.0.1",
|
||||
"hashPath": "microsoft.extensions.primitives.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/8.15.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-e/DApa1GfxUqHSBHcpiQg8yaghKAvFVBQFcWh25jNoRobDZbduTUACY8bZ54eeGWXvimGmEDdF0zkS5Dq16XPQ==",
|
||||
"path": "microsoft.identitymodel.abstractions/8.15.0",
|
||||
"hashPath": "microsoft.identitymodel.abstractions.8.15.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/8.15.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-1gJLjhy0LV2RQMJ9NGzi5Tnb2l+c37o8D8Lrk2mrvmb6OQHZ7XJstd/XxvncXgBpad4x9CGXdipbZzJJCXKyAg==",
|
||||
"path": "microsoft.identitymodel.logging/8.15.0",
|
||||
"hashPath": "microsoft.identitymodel.logging.8.15.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/8.15.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-zUE9ysJXBtXlHHRtcRK3Sp8NzdCI1z/BRDTXJQ2TvBoI0ENRtnufYIep0O5TSCJRJGDwwuLTUx+l/bEYZUxpCA==",
|
||||
"path": "microsoft.identitymodel.tokens/8.15.0",
|
||||
"hashPath": "microsoft.identitymodel.tokens.8.15.0.nupkg.sha512"
|
||||
},
|
||||
"StellaOps.Attestor.Envelope/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Canonical.Json/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Concelier.SourceIntel/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Cryptography/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Feedser.BinaryAnalysis/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Feedser.Core/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Scanner.ChangeTrace/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,646 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"StellaOps.Attestor.StandardPredicates/1.0.0": {
|
||||
"dependencies": {
|
||||
"JsonSchema.Net": "8.0.4",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1",
|
||||
"StellaOps.Attestor.Envelope": "1.0.0",
|
||||
"StellaOps.Attestor.ProofChain": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Attestor.StandardPredicates.dll": {}
|
||||
}
|
||||
},
|
||||
"Blake3/1.1.0": {
|
||||
"runtime": {
|
||||
"lib/net7.0/Blake3.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.1.0.0"
|
||||
}
|
||||
},
|
||||
"runtimeTargets": {
|
||||
"runtimes/linux-arm/native/libblake3_dotnet.so": {
|
||||
"rid": "linux-arm",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/linux-arm64/native/libblake3_dotnet.so": {
|
||||
"rid": "linux-arm64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/linux-x64/native/libblake3_dotnet.so": {
|
||||
"rid": "linux-x64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/osx-arm64/native/libblake3_dotnet.dylib": {
|
||||
"rid": "osx-arm64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/osx-x64/native/libblake3_dotnet.dylib": {
|
||||
"rid": "osx-x64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/win-arm64/native/blake3_dotnet.dll": {
|
||||
"rid": "win-arm64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/win-x64/native/blake3_dotnet.dll": {
|
||||
"rid": "win-x64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
},
|
||||
"runtimes/win-x86/native/blake3_dotnet.dll": {
|
||||
"rid": "win-x86",
|
||||
"assetType": "native",
|
||||
"fileVersion": "0.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BouncyCastle.Cryptography/2.6.2": {
|
||||
"runtime": {
|
||||
"lib/net6.0/BouncyCastle.Cryptography.dll": {
|
||||
"assemblyVersion": "2.0.0.0",
|
||||
"fileVersion": "2.6.2.46322"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Humanizer.Core/3.0.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Humanizer.dll": {
|
||||
"assemblyVersion": "3.0.0.0",
|
||||
"fileVersion": "3.0.1.28244"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Json.More.Net/2.2.0": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Json.More.dll": {
|
||||
"assemblyVersion": "2.0.0.0",
|
||||
"fileVersion": "2.2.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"JsonPointer.Net/6.0.1": {
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "3.0.1",
|
||||
"Json.More.Net": "2.2.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/JsonPointer.Net.dll": {
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"JsonSchema.Net/8.0.4": {
|
||||
"dependencies": {
|
||||
"JsonPointer.Net": "6.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net9.0/JsonSchema.Net.dll": {
|
||||
"assemblyVersion": "8.0.4.0",
|
||||
"fileVersion": "8.0.4.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Caching.Abstractions/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Caching.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Caching.Memory/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Caching.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1",
|
||||
"Microsoft.Extensions.Primitives": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Caching.Memory.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Primitives": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Configuration.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Abstractions/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Configuration.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Binder/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration": "10.0.1",
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Configuration.Binder.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.DependencyInjection.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Diagnostics/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration": "10.0.1",
|
||||
"Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Diagnostics.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Diagnostics.Abstractions/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Diagnostics.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Http/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Diagnostics": "10.0.1",
|
||||
"Microsoft.Extensions.Logging": "10.0.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Http.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "10.0.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Logging.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Logging.Abstractions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Options/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Primitives": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Options.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Options.ConfigurationExtensions/10.0.1": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Configuration.Binder": "10.0.1",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1",
|
||||
"Microsoft.Extensions.Primitives": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives/10.0.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.Extensions.Primitives.dll": {
|
||||
"assemblyVersion": "10.0.0.0",
|
||||
"fileVersion": "10.0.125.57005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/8.15.0": {
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Abstractions.dll": {
|
||||
"assemblyVersion": "8.15.0.0",
|
||||
"fileVersion": "8.15.0.61118"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/8.15.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Abstractions": "8.15.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Logging.dll": {
|
||||
"assemblyVersion": "8.15.0.0",
|
||||
"fileVersion": "8.15.0.61118"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/8.15.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.IdentityModel.Logging": "8.15.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/Microsoft.IdentityModel.Tokens.dll": {
|
||||
"assemblyVersion": "8.15.0.0",
|
||||
"fileVersion": "8.15.0.61118"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Attestor.Envelope/1.0.0": {
|
||||
"dependencies": {
|
||||
"BouncyCastle.Cryptography": "2.6.2",
|
||||
"StellaOps.Cryptography": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Attestor.Envelope.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Attestor.ProofChain/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"StellaOps.Attestor.Envelope": "1.0.0",
|
||||
"StellaOps.Canonical.Json": "1.0.0",
|
||||
"StellaOps.Concelier.SourceIntel": "1.0.0",
|
||||
"StellaOps.Feedser.BinaryAnalysis": "1.0.0",
|
||||
"StellaOps.Feedser.Core": "1.0.0",
|
||||
"StellaOps.Scanner.ChangeTrace": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Attestor.ProofChain.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Canonical.Json/1.0.0": {
|
||||
"runtime": {
|
||||
"StellaOps.Canonical.Json.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Concelier.SourceIntel/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Caching.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Caching.Memory": "10.0.1",
|
||||
"Microsoft.Extensions.Http": "10.0.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Concelier.SourceIntel.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Cryptography/1.0.0": {
|
||||
"dependencies": {
|
||||
"Blake3": "1.1.0",
|
||||
"BouncyCastle.Cryptography": "2.6.2",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1",
|
||||
"Microsoft.IdentityModel.Tokens": "8.15.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Cryptography.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Feedser.BinaryAnalysis/1.0.0": {
|
||||
"runtime": {
|
||||
"StellaOps.Feedser.BinaryAnalysis.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Feedser.Core/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Feedser.Core.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Scanner.ChangeTrace/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "10.0.1",
|
||||
"Microsoft.Extensions.Options": "10.0.1",
|
||||
"StellaOps.Canonical.Json": "1.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"StellaOps.Scanner.ChangeTrace.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"StellaOps.Attestor.StandardPredicates/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Blake3/1.1.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-/gWRFsXYeIFof8YAoFJwzv2fYjSTCo+6vvTSL6pyXw2ZLXQdRvEyXhO43jyDfEFBCTxMxWpoHbIcIEIF6a3QdQ==",
|
||||
"path": "blake3/1.1.0",
|
||||
"hashPath": "blake3.1.1.0.nupkg.sha512"
|
||||
},
|
||||
"BouncyCastle.Cryptography/2.6.2": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-7oWOcvnntmMKNzDLsdxAYqApt+AjpRpP2CShjMfIa3umZ42UQMvH0tl1qAliYPNYO6vTdcGMqnRrCPmsfzTI1w==",
|
||||
"path": "bouncycastle.cryptography/2.6.2",
|
||||
"hashPath": "bouncycastle.cryptography.2.6.2.nupkg.sha512"
|
||||
},
|
||||
"Humanizer.Core/3.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-scB3+KcxNmEjZK5V8rKCW2gIiL8m8KH91w14FuuExyhi9xTyAJ+jr+DDxGdy12mHmioe2uvjxTfMgM7WmSUFlw==",
|
||||
"path": "humanizer.core/3.0.1",
|
||||
"hashPath": "humanizer.core.3.0.1.nupkg.sha512"
|
||||
},
|
||||
"Json.More.Net/2.2.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-fiyEZJNgiCzDa7/N9bZ+CnM5qnawyJ54+CkHNZ2svwxWBWNNFzydJ7RlroqMdOjokGBiLcKIxdajNvOklzlYqQ==",
|
||||
"path": "json.more.net/2.2.0",
|
||||
"hashPath": "json.more.net.2.2.0.nupkg.sha512"
|
||||
},
|
||||
"JsonPointer.Net/6.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-EbSJkd/1y9r0WBIptjUctA6BzjgKh1aNU/g6QGgdMZVZ8wc0S/ysQhfiQerP9/VFTeENFHFdCQaoOk9fZqwnFQ==",
|
||||
"path": "jsonpointer.net/6.0.1",
|
||||
"hashPath": "jsonpointer.net.6.0.1.nupkg.sha512"
|
||||
},
|
||||
"JsonSchema.Net/8.0.4": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-NSJ1iu+7Dg6fxaev90bWLvHi5mCqrGMgJuRzXr9mIaRt54Niox3D5g3uKH7ic7vcfkyhDU9opkAPGvek+cG53g==",
|
||||
"path": "jsonschema.net/8.0.4",
|
||||
"hashPath": "jsonschema.net.8.0.4.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Caching.Abstractions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Vb1vVAQDxHpXVdL9fpOX2BzeV7bbhzG4pAcIKRauRl0/VfkE8mq0f+fYC+gWICh3dlzTZInJ/cTeBS2MgU/XvQ==",
|
||||
"path": "microsoft.extensions.caching.abstractions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.caching.abstractions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Caching.Memory/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-NxqSP0Ky4dZ5ybszdZCqs1X2C70s+dXflqhYBUh/vhcQVTIooNCXIYnLVbafoAFGZMs51d9+rHxveXs0ZC3SQQ==",
|
||||
"path": "microsoft.extensions.caching.memory/10.0.1",
|
||||
"hashPath": "microsoft.extensions.caching.memory.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Configuration/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==",
|
||||
"path": "microsoft.extensions.configuration/10.0.1",
|
||||
"hashPath": "microsoft.extensions.configuration.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Abstractions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==",
|
||||
"path": "microsoft.extensions.configuration.abstractions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.configuration.abstractions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Binder/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==",
|
||||
"path": "microsoft.extensions.configuration.binder/10.0.1",
|
||||
"hashPath": "microsoft.extensions.configuration.binder.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==",
|
||||
"path": "microsoft.extensions.dependencyinjection/10.0.1",
|
||||
"hashPath": "microsoft.extensions.dependencyinjection.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==",
|
||||
"path": "microsoft.extensions.dependencyinjection.abstractions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Diagnostics/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-YaocqxscJLxLit0F5yq2XyB+9C7rSRfeTL7MJIl7XwaOoUO3i0EqfO2kmtjiRduYWw7yjcSINEApYZbzjau2gQ==",
|
||||
"path": "microsoft.extensions.diagnostics/10.0.1",
|
||||
"hashPath": "microsoft.extensions.diagnostics.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Diagnostics.Abstractions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-QMoMrkNpnQym5mpfdxfxpRDuqLpsOuztguFvzH9p+Ex+do+uLFoi7UkAsBO4e9/tNR3eMFraFf2fOAi2cp3jjA==",
|
||||
"path": "microsoft.extensions.diagnostics.abstractions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.diagnostics.abstractions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Http/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ZXJup9ReE1Ot3M8jqcw1b/lnc8USxyYS3cyLsssU39u04TES9JNGviWUGIvP3K7mMU3TF7kQl2aS0SmVwegflw==",
|
||||
"path": "microsoft.extensions.http/10.0.1",
|
||||
"hashPath": "microsoft.extensions.http.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Logging/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==",
|
||||
"path": "microsoft.extensions.logging/10.0.1",
|
||||
"hashPath": "microsoft.extensions.logging.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==",
|
||||
"path": "microsoft.extensions.logging.abstractions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.logging.abstractions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Options/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==",
|
||||
"path": "microsoft.extensions.options/10.0.1",
|
||||
"hashPath": "microsoft.extensions.options.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Options.ConfigurationExtensions/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==",
|
||||
"path": "microsoft.extensions.options.configurationextensions/10.0.1",
|
||||
"hashPath": "microsoft.extensions.options.configurationextensions.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Primitives/10.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==",
|
||||
"path": "microsoft.extensions.primitives/10.0.1",
|
||||
"hashPath": "microsoft.extensions.primitives.10.0.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/8.15.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-e/DApa1GfxUqHSBHcpiQg8yaghKAvFVBQFcWh25jNoRobDZbduTUACY8bZ54eeGWXvimGmEDdF0zkS5Dq16XPQ==",
|
||||
"path": "microsoft.identitymodel.abstractions/8.15.0",
|
||||
"hashPath": "microsoft.identitymodel.abstractions.8.15.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/8.15.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-1gJLjhy0LV2RQMJ9NGzi5Tnb2l+c37o8D8Lrk2mrvmb6OQHZ7XJstd/XxvncXgBpad4x9CGXdipbZzJJCXKyAg==",
|
||||
"path": "microsoft.identitymodel.logging/8.15.0",
|
||||
"hashPath": "microsoft.identitymodel.logging.8.15.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/8.15.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-zUE9ysJXBtXlHHRtcRK3Sp8NzdCI1z/BRDTXJQ2TvBoI0ENRtnufYIep0O5TSCJRJGDwwuLTUx+l/bEYZUxpCA==",
|
||||
"path": "microsoft.identitymodel.tokens/8.15.0",
|
||||
"hashPath": "microsoft.identitymodel.tokens.8.15.0.nupkg.sha512"
|
||||
},
|
||||
"StellaOps.Attestor.Envelope/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Attestor.ProofChain/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Canonical.Json/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Concelier.SourceIntel/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Cryptography/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Feedser.BinaryAnalysis/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Feedser.Core/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"StellaOps.Scanner.ChangeTrace/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,418 @@
|
||||
using StellaOps.Attestor.ProofChain.Predicates.AI;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Attestor.ProofChain.Tests.AI;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for AIAuthorityClassifier.
|
||||
/// Sprint: SPRINT_20251226_018_AI_attestations
|
||||
/// Task: AIATTEST-22
|
||||
/// </summary>
|
||||
public sealed class AIAuthorityClassifierTests
|
||||
{
|
||||
private static readonly AIModelIdentifier TestModelId = new()
|
||||
{
|
||||
Provider = "anthropic",
|
||||
Model = "claude-3-opus",
|
||||
Version = "20240229"
|
||||
};
|
||||
|
||||
private static readonly AIDecodingParameters TestDecodingParams = new()
|
||||
{
|
||||
Temperature = 0.0,
|
||||
Seed = 12345
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void ClassifyExplanation_HighCitationRate_ReturnsEvidenceBacked()
|
||||
{
|
||||
// Arrange
|
||||
var classifier = new AIAuthorityClassifier();
|
||||
var predicate = CreateExplanationPredicate(citationRate: 0.85, confidenceScore: 0.8, verifiedRate: 0.95);
|
||||
|
||||
// Act
|
||||
var result = classifier.ClassifyExplanation(predicate);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(AIArtifactAuthority.EvidenceBacked, result.Authority);
|
||||
Assert.True(result.QualityScore > 0.7);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClassifyExplanation_LowCitationRate_ReturnsSuggestion()
|
||||
{
|
||||
// Arrange
|
||||
var classifier = new AIAuthorityClassifier();
|
||||
var predicate = CreateExplanationPredicate(citationRate: 0.5, confidenceScore: 0.6, verifiedRate: 0.7);
|
||||
|
||||
// Act
|
||||
var result = classifier.ClassifyExplanation(predicate);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(AIArtifactAuthority.Suggestion, result.Authority);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClassifyExplanation_VeryHighQuality_ReturnsAuthorityThreshold()
|
||||
{
|
||||
// Arrange
|
||||
var thresholds = new AIAuthorityThresholds { AuthorityThresholdScore = 0.9 };
|
||||
var classifier = new AIAuthorityClassifier(thresholds);
|
||||
var predicate = CreateExplanationPredicate(
|
||||
citationRate: 0.98,
|
||||
confidenceScore: 0.95,
|
||||
verifiedRate: 1.0,
|
||||
contentLength: 700);
|
||||
|
||||
// Act
|
||||
var result = classifier.ClassifyExplanation(predicate);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(AIArtifactAuthority.AuthorityThreshold, result.Authority);
|
||||
Assert.True(result.CanAutoProcess);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClassifyRemediationPlan_WithResolvableEvidence_ReturnsEvidenceBacked()
|
||||
{
|
||||
// Arrange
|
||||
Func<string, bool> resolver = _ => true; // All evidence is resolvable
|
||||
var classifier = new AIAuthorityClassifier(evidenceResolver: resolver);
|
||||
var predicate = CreateRemediationPredicate(evidenceCount: 5, prReady: true);
|
||||
|
||||
// Act
|
||||
var result = classifier.ClassifyRemediationPlan(predicate);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(AIArtifactAuthority.EvidenceBacked, result.Authority);
|
||||
Assert.Equal(5, result.ResolvableEvidenceCount);
|
||||
Assert.Equal(0, result.UnresolvableEvidenceCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClassifyRemediationPlan_WithUnresolvableEvidence_ReturnsSuggestion()
|
||||
{
|
||||
// Arrange
|
||||
Func<string, bool> resolver = evidenceRef => evidenceRef.Contains("valid"); // Only some evidence is resolvable
|
||||
var classifier = new AIAuthorityClassifier(evidenceResolver: resolver);
|
||||
var predicate = CreateRemediationPredicate(evidenceCount: 5, prReady: false);
|
||||
|
||||
// Act
|
||||
var result = classifier.ClassifyRemediationPlan(predicate);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(AIArtifactAuthority.Suggestion, result.Authority);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClassifyVexDraft_AutoApprovable_CanAutoProcess()
|
||||
{
|
||||
// Arrange
|
||||
var classifier = new AIAuthorityClassifier();
|
||||
var predicate = CreateVexDraftPredicate(
|
||||
avgConfidence: 0.95,
|
||||
evidenceCount: 3,
|
||||
hasConflicts: false);
|
||||
|
||||
// Act
|
||||
var result = classifier.ClassifyVexDraft(predicate);
|
||||
|
||||
// Assert
|
||||
// Note: CanAutoProcess depends on AutoApprovable in the predicate
|
||||
Assert.True(result.QualityScore > 0.5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClassifyVexDraft_UnresolvableEvidence_DropsToSuggestion()
|
||||
{
|
||||
// Arrange
|
||||
Func<string, bool> resolver = evidenceRef => evidenceRef.EndsWith("0", StringComparison.Ordinal);
|
||||
var classifier = new AIAuthorityClassifier(evidenceResolver: resolver);
|
||||
var predicate = CreateVexDraftPredicate(
|
||||
avgConfidence: 0.95,
|
||||
evidenceCount: 3,
|
||||
hasConflicts: false);
|
||||
|
||||
// Act
|
||||
var result = classifier.ClassifyVexDraft(predicate);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(AIArtifactAuthority.Suggestion, result.Authority);
|
||||
Assert.Equal(1, result.ResolvableEvidenceCount);
|
||||
Assert.Equal(2, result.UnresolvableEvidenceCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClassifyPolicyDraft_AllTestsPassed_HighQuality()
|
||||
{
|
||||
// Arrange
|
||||
var classifier = new AIAuthorityClassifier();
|
||||
var predicate = CreatePolicyDraftPredicate(
|
||||
avgConfidence: 0.9,
|
||||
passedTestCount: 5,
|
||||
totalTestCount: 5,
|
||||
validationPassed: true);
|
||||
|
||||
// Act
|
||||
var result = classifier.ClassifyPolicyDraft(predicate);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.QualityScore > 0.7);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClassifyPolicyDraft_QualityAtAuthorityThreshold_ReturnsAuthorityThreshold()
|
||||
{
|
||||
// Arrange
|
||||
var classifier = new AIAuthorityClassifier();
|
||||
var predicate = CreatePolicyDraftPredicate(
|
||||
avgConfidence: 1.0,
|
||||
passedTestCount: 5,
|
||||
totalTestCount: 5,
|
||||
validationPassed: true);
|
||||
|
||||
// Act
|
||||
var result = classifier.ClassifyPolicyDraft(predicate);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(AIArtifactAuthority.AuthorityThreshold, result.Authority);
|
||||
Assert.True(result.CanAutoProcess);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClassifyPolicyDraft_FailedTests_LowerQuality()
|
||||
{
|
||||
// Arrange
|
||||
var classifier = new AIAuthorityClassifier();
|
||||
var predicate = CreatePolicyDraftPredicate(
|
||||
avgConfidence: 0.9,
|
||||
passedTestCount: 2,
|
||||
totalTestCount: 5,
|
||||
validationPassed: false);
|
||||
|
||||
// Act
|
||||
var result = classifier.ClassifyPolicyDraft(predicate);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.QualityScore < 0.7);
|
||||
Assert.False(result.CanAutoProcess);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CustomThresholds_AreRespected()
|
||||
{
|
||||
// Arrange
|
||||
var thresholds = new AIAuthorityThresholds
|
||||
{
|
||||
MinCitationRate = 0.5,
|
||||
MinConfidenceScore = 0.5,
|
||||
MinVerifiedCitationRate = 0.5
|
||||
};
|
||||
var classifier = new AIAuthorityClassifier(thresholds);
|
||||
var predicate = CreateExplanationPredicate(citationRate: 0.6, confidenceScore: 0.6, verifiedRate: 0.6);
|
||||
|
||||
// Act
|
||||
var result = classifier.ClassifyExplanation(predicate);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(AIArtifactAuthority.EvidenceBacked, result.Authority);
|
||||
}
|
||||
|
||||
private static AIExplanationPredicate CreateExplanationPredicate(
|
||||
double citationRate,
|
||||
double confidenceScore,
|
||||
double verifiedRate,
|
||||
int contentLength = 220)
|
||||
{
|
||||
var totalCitations = 10;
|
||||
var verifiedCitations = (int)(totalCitations * verifiedRate);
|
||||
|
||||
var citations = new List<AIExplanationCitation>();
|
||||
for (int i = 0; i < totalCitations; i++)
|
||||
{
|
||||
citations.Add(new AIExplanationCitation
|
||||
{
|
||||
ClaimIndex = i,
|
||||
ClaimText = $"Claim {i}",
|
||||
EvidenceId = $"sha256:evidence{i}",
|
||||
EvidenceType = "sbom",
|
||||
Verified = i < verifiedCitations
|
||||
});
|
||||
}
|
||||
|
||||
return new AIExplanationPredicate
|
||||
{
|
||||
ArtifactId = "sha256:test123",
|
||||
ModelId = TestModelId,
|
||||
PromptTemplateVersion = "explanation@v1",
|
||||
DecodingParams = TestDecodingParams,
|
||||
InputHashes = ["sha256:input1"],
|
||||
Authority = AIArtifactAuthority.Suggestion,
|
||||
GeneratedAt = "2025-12-26T00:00:00Z",
|
||||
OutputHash = "sha256:output1",
|
||||
ExplanationType = AIExplanationType.Exploitability,
|
||||
Content = new string('x', contentLength),
|
||||
Citations = citations,
|
||||
ConfidenceScore = confidenceScore,
|
||||
CitationRate = citationRate,
|
||||
Subject = "CVE-2025-1234"
|
||||
};
|
||||
}
|
||||
|
||||
private static AIRemediationPlanPredicate CreateRemediationPredicate(int evidenceCount, bool prReady)
|
||||
{
|
||||
var evidenceRefs = new List<string>();
|
||||
for (int i = 0; i < evidenceCount; i++)
|
||||
{
|
||||
evidenceRefs.Add($"sha256:evidence{i}");
|
||||
}
|
||||
|
||||
return new AIRemediationPlanPredicate
|
||||
{
|
||||
ArtifactId = "sha256:test123",
|
||||
ModelId = TestModelId,
|
||||
PromptTemplateVersion = "remediation@v1",
|
||||
DecodingParams = TestDecodingParams,
|
||||
InputHashes = ["sha256:input1"],
|
||||
Authority = AIArtifactAuthority.Suggestion,
|
||||
GeneratedAt = "2025-12-26T00:00:00Z",
|
||||
OutputHash = "sha256:output1",
|
||||
VulnerabilityId = "CVE-2025-1234",
|
||||
AffectedComponent = "pkg:npm/example@1.0.0",
|
||||
Steps =
|
||||
[
|
||||
new RemediationStep
|
||||
{
|
||||
Order = 1,
|
||||
ActionType = RemediationActionType.PackageUpgrade,
|
||||
Description = "Upgrade package",
|
||||
Target = "pkg:npm/example@1.0.0",
|
||||
ProposedValue = "1.0.1",
|
||||
RiskReduction = 0.8,
|
||||
CanAutomate = true
|
||||
}
|
||||
],
|
||||
ExpectedDelta = 0.7,
|
||||
RiskAssessment = new RemediationRiskAssessment
|
||||
{
|
||||
RiskBefore = 0.9,
|
||||
RiskAfter = 0.2,
|
||||
BreakingChanges = []
|
||||
},
|
||||
VerificationStatus = RemediationVerificationStatus.Verified,
|
||||
PrReady = prReady,
|
||||
EvidenceRefs = evidenceRefs
|
||||
};
|
||||
}
|
||||
|
||||
private static AIVexDraftPredicate CreateVexDraftPredicate(
|
||||
double avgConfidence,
|
||||
int evidenceCount,
|
||||
bool hasConflicts)
|
||||
{
|
||||
var evidenceRefs = new List<string>();
|
||||
for (int i = 0; i < evidenceCount; i++)
|
||||
{
|
||||
evidenceRefs.Add($"sha256:evidence{i}");
|
||||
}
|
||||
|
||||
return new AIVexDraftPredicate
|
||||
{
|
||||
ArtifactId = "sha256:test123",
|
||||
ModelId = TestModelId,
|
||||
PromptTemplateVersion = "vexdraft@v1",
|
||||
DecodingParams = TestDecodingParams,
|
||||
InputHashes = ["sha256:input1"],
|
||||
Authority = AIArtifactAuthority.Suggestion,
|
||||
GeneratedAt = "2025-12-26T00:00:00Z",
|
||||
OutputHash = "sha256:output1",
|
||||
VexStatements =
|
||||
[
|
||||
new AIVexStatementDraft
|
||||
{
|
||||
VulnerabilityId = "CVE-2025-1234",
|
||||
ProductId = "pkg:npm/example@1.0.0",
|
||||
Status = "not_affected",
|
||||
Justification = "vulnerable_code_not_in_execute_path",
|
||||
Confidence = avgConfidence,
|
||||
SupportingEvidence = evidenceRefs
|
||||
}
|
||||
],
|
||||
Justifications =
|
||||
[
|
||||
new AIVexJustification
|
||||
{
|
||||
StatementIndex = 0,
|
||||
Reasoning = "Code path analysis shows function is never called",
|
||||
EvidencePoints = ["Reachability analysis", "Call graph"],
|
||||
ConflictsWithExisting = hasConflicts
|
||||
}
|
||||
],
|
||||
EvidenceRefs = evidenceRefs,
|
||||
TargetFormat = "openvex",
|
||||
AutoApprovable = !hasConflicts && avgConfidence > 0.9,
|
||||
Scope = "image",
|
||||
ScopeId = "sha256:image123"
|
||||
};
|
||||
}
|
||||
|
||||
private static AIPolicyDraftPredicate CreatePolicyDraftPredicate(
|
||||
double avgConfidence,
|
||||
int passedTestCount,
|
||||
int totalTestCount,
|
||||
bool validationPassed)
|
||||
{
|
||||
var testCases = new List<PolicyRuleTestCase>();
|
||||
for (int i = 0; i < totalTestCount; i++)
|
||||
{
|
||||
testCases.Add(new PolicyRuleTestCase
|
||||
{
|
||||
TestId = $"test-{i}",
|
||||
RuleId = "rule-1",
|
||||
Description = $"Test case {i}",
|
||||
Input = "{}",
|
||||
ExpectedOutcome = "pass",
|
||||
Passed = i < passedTestCount
|
||||
});
|
||||
}
|
||||
|
||||
return new AIPolicyDraftPredicate
|
||||
{
|
||||
ArtifactId = "sha256:test123",
|
||||
ModelId = TestModelId,
|
||||
PromptTemplateVersion = "policydraft@v1",
|
||||
DecodingParams = TestDecodingParams,
|
||||
InputHashes = ["sha256:input1"],
|
||||
Authority = AIArtifactAuthority.Suggestion,
|
||||
GeneratedAt = "2025-12-26T00:00:00Z",
|
||||
OutputHash = "sha256:output1",
|
||||
NaturalLanguageInput = "Block critical CVEs in production",
|
||||
Rules =
|
||||
[
|
||||
new AIPolicyRuleDraft
|
||||
{
|
||||
RuleId = "rule-1",
|
||||
RuleType = PolicyRuleType.Gate,
|
||||
Name = "Block Critical CVEs",
|
||||
Description = "Block deployments with critical vulnerabilities",
|
||||
Condition = "severity == 'critical' && environment == 'prod'",
|
||||
Action = "block",
|
||||
Priority = 100,
|
||||
OriginalInput = "Block critical CVEs in production",
|
||||
Confidence = avgConfidence
|
||||
}
|
||||
],
|
||||
TestCases = testCases,
|
||||
ValidationResult = new PolicyValidationResult
|
||||
{
|
||||
SyntaxValid = true,
|
||||
SemanticsValid = validationPassed,
|
||||
OverallPassed = validationPassed
|
||||
},
|
||||
TargetPolicyPack = "default",
|
||||
TargetVersion = "1.0.0",
|
||||
DetectedIntents = ["gate", "severity-filter", "environment-scope"],
|
||||
DeployReady = validationPassed
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.Attestor.ProofChain.Identifiers;
|
||||
using StellaOps.Attestor.ProofChain.Predicates.AI;
|
||||
using StellaOps.Attestor.ProofChain.Verification;
|
||||
using StellaOps.TestKit;
|
||||
|
||||
namespace StellaOps.Attestor.ProofChain.Tests.AI;
|
||||
|
||||
public sealed class AIExplanationAndVerificationBehaviorTests
|
||||
{
|
||||
private static readonly AIModelIdentifier ModelId = new()
|
||||
{
|
||||
Provider = "anthropic",
|
||||
Model = "claude-3-opus",
|
||||
Version = "20240229"
|
||||
};
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void ClassifyExplanation_HighCitationAndVerifiedRate_ReturnsEvidenceBacked()
|
||||
{
|
||||
var classifier = new AIAuthorityClassifier();
|
||||
var predicate = CreateExplanationPredicate(citationRate: 0.85, confidenceScore: 0.80, verifiedCount: 9, totalCitations: 10, contentLength: 220);
|
||||
|
||||
var result = classifier.ClassifyExplanation(predicate);
|
||||
|
||||
Assert.Equal(AIArtifactAuthority.EvidenceBacked, result.Authority);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void ClassifyExplanation_LowCitationRate_ReturnsSuggestion()
|
||||
{
|
||||
var classifier = new AIAuthorityClassifier();
|
||||
var predicate = CreateExplanationPredicate(citationRate: 0.40, confidenceScore: 0.90, verifiedCount: 10, totalCitations: 10, contentLength: 220);
|
||||
|
||||
var result = classifier.ClassifyExplanation(predicate);
|
||||
|
||||
Assert.Equal(AIArtifactAuthority.Suggestion, result.Authority);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void ClassifyExplanation_HighQualityScore_ReturnsAuthorityThreshold()
|
||||
{
|
||||
var classifier = new AIAuthorityClassifier();
|
||||
var predicate = CreateExplanationPredicate(citationRate: 1.0, confidenceScore: 1.0, verifiedCount: 10, totalCitations: 10, contentLength: 700);
|
||||
|
||||
var result = classifier.ClassifyExplanation(predicate);
|
||||
|
||||
Assert.Equal(AIArtifactAuthority.AuthorityThreshold, result.Authority);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void ClassifyExplanation_UnverifiedCitationsBelowThreshold_ReturnsSuggestion()
|
||||
{
|
||||
var classifier = new AIAuthorityClassifier();
|
||||
var predicate = CreateExplanationPredicate(citationRate: 0.90, confidenceScore: 0.95, verifiedCount: 5, totalCitations: 10, contentLength: 220);
|
||||
|
||||
var result = classifier.ClassifyExplanation(predicate);
|
||||
|
||||
Assert.Equal(AIArtifactAuthority.Suggestion, result.Authority);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIModelIdentifier_ToString_UsesCanonicalProviderModelVersionFormat()
|
||||
{
|
||||
var model = new AIModelIdentifier
|
||||
{
|
||||
Provider = "openai",
|
||||
Model = "gpt-4.1",
|
||||
Version = "2026-01-01"
|
||||
};
|
||||
|
||||
Assert.Equal("openai:gpt-4.1:2026-01-01", model.ToString());
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task ExecuteAsync_WithValidAIExplanationStatement_PassesAndStoresResults()
|
||||
{
|
||||
var bundleId = new ProofBundleId(new string('a', 64));
|
||||
var predicate = CreateExplanationPredicate(citationRate: 0.90, confidenceScore: 0.90, verifiedCount: 10, totalCitations: 10, contentLength: 300);
|
||||
var statement = new ProofStatement
|
||||
{
|
||||
StatementId = "stmt-1",
|
||||
PredicateType = "ai-explanation.stella/v1",
|
||||
Predicate = predicate
|
||||
};
|
||||
|
||||
var store = new StubProofBundleStore(new ProofBundle
|
||||
{
|
||||
Statements = [statement],
|
||||
Envelopes = []
|
||||
});
|
||||
var step = new AIArtifactVerificationStep(store, NullLogger<AIArtifactVerificationStep>.Instance);
|
||||
var context = new VerificationContext
|
||||
{
|
||||
ProofBundleId = bundleId,
|
||||
VerifyRekor = false
|
||||
};
|
||||
|
||||
var result = await step.ExecuteAsync(context);
|
||||
var artifacts = context.GetData<List<AIArtifactVerificationResult>>("aiArtifactResults");
|
||||
|
||||
Assert.True(result.Passed);
|
||||
Assert.NotNull(artifacts);
|
||||
Assert.Single(artifacts!);
|
||||
Assert.True(artifacts[0].IsValid);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task ExecuteAsync_WithMalformedAIPredicate_FailsVerification()
|
||||
{
|
||||
var bundleId = new ProofBundleId(new string('b', 64));
|
||||
var statement = new ProofStatement
|
||||
{
|
||||
StatementId = "stmt-2",
|
||||
PredicateType = "ai-explanation.stella/v1",
|
||||
Predicate = new
|
||||
{
|
||||
// Missing required explanation predicate fields on purpose.
|
||||
foo = "bar"
|
||||
}
|
||||
};
|
||||
|
||||
var store = new StubProofBundleStore(new ProofBundle
|
||||
{
|
||||
Statements = [statement],
|
||||
Envelopes = []
|
||||
});
|
||||
var step = new AIArtifactVerificationStep(store, NullLogger<AIArtifactVerificationStep>.Instance);
|
||||
var context = new VerificationContext
|
||||
{
|
||||
ProofBundleId = bundleId,
|
||||
VerifyRekor = false
|
||||
};
|
||||
|
||||
var result = await step.ExecuteAsync(context);
|
||||
|
||||
Assert.False(result.Passed);
|
||||
Assert.Contains("failed", result.Details ?? string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static AIExplanationPredicate CreateExplanationPredicate(
|
||||
double citationRate,
|
||||
double confidenceScore,
|
||||
int verifiedCount,
|
||||
int totalCitations,
|
||||
int contentLength)
|
||||
{
|
||||
var citations = new List<AIExplanationCitation>(capacity: totalCitations);
|
||||
for (var i = 0; i < totalCitations; i++)
|
||||
{
|
||||
citations.Add(new AIExplanationCitation
|
||||
{
|
||||
ClaimIndex = i,
|
||||
ClaimText = $"Claim {i}",
|
||||
EvidenceId = $"sha256:{new string('c', 64)}",
|
||||
EvidenceType = "sbom",
|
||||
Verified = i < verifiedCount
|
||||
});
|
||||
}
|
||||
|
||||
return new AIExplanationPredicate
|
||||
{
|
||||
ArtifactId = $"sha256:{new string('d', 64)}",
|
||||
ModelId = ModelId,
|
||||
PromptTemplateVersion = "explanation@v1",
|
||||
DecodingParams = new AIDecodingParameters
|
||||
{
|
||||
Temperature = 0.0,
|
||||
Seed = 12345
|
||||
},
|
||||
InputHashes = [$"sha256:{new string('e', 64)}"],
|
||||
Authority = AIArtifactAuthority.Suggestion,
|
||||
GeneratedAt = "2026-02-11T00:00:00Z",
|
||||
OutputHash = $"sha256:{new string('f', 64)}",
|
||||
ExplanationType = AIExplanationType.Exploitability,
|
||||
Content = new string('x', contentLength),
|
||||
Citations = citations,
|
||||
ConfidenceScore = confidenceScore,
|
||||
CitationRate = citationRate,
|
||||
Subject = "CVE-2026-0001"
|
||||
};
|
||||
}
|
||||
|
||||
private sealed class StubProofBundleStore : IProofBundleStore
|
||||
{
|
||||
private readonly ProofBundle bundle;
|
||||
|
||||
public StubProofBundleStore(ProofBundle bundle)
|
||||
{
|
||||
this.bundle = bundle;
|
||||
}
|
||||
|
||||
public Task<ProofBundle?> GetBundleAsync(ProofBundleId bundleId, CancellationToken ct = default)
|
||||
{
|
||||
return Task.FromResult<ProofBundle?>(bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
using System.Text.Json;
|
||||
using StellaOps.Attestor.ProofChain.MediaTypes;
|
||||
using StellaOps.Attestor.ProofChain.Predicates.AI;
|
||||
using StellaOps.Attestor.ProofChain.Replay;
|
||||
using StellaOps.Attestor.ProofChain.Statements;
|
||||
using StellaOps.Attestor.ProofChain.Statements.AI;
|
||||
using StellaOps.TestKit;
|
||||
|
||||
namespace StellaOps.Attestor.ProofChain.Tests.AI;
|
||||
|
||||
public sealed class AIExplanationAttestationTypesBehaviorTests
|
||||
{
|
||||
[Trait("Intent", "Regulatory")]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIExplanationPredicate_SerializesWithExpectedContractFields()
|
||||
{
|
||||
var predicate = CreatePredicate(AIExplanationType.PolicyDecision);
|
||||
|
||||
var json = JsonSerializer.Serialize(predicate);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
Assert.Equal("PolicyDecision", root.GetProperty("explanationType").GetString());
|
||||
Assert.Equal(predicate.Content, root.GetProperty("content").GetString());
|
||||
Assert.Equal(predicate.ConfidenceScore, root.GetProperty("confidenceScore").GetDouble(), 6);
|
||||
Assert.Equal(predicate.CitationRate, root.GetProperty("citationRate").GetDouble(), 6);
|
||||
Assert.Equal(predicate.Subject, root.GetProperty("subject").GetString());
|
||||
Assert.Equal(predicate.ContextScope, root.GetProperty("contextScope").GetString());
|
||||
Assert.Equal(predicate.ArtifactId, root.GetProperty("artifactId").GetString());
|
||||
Assert.Equal(predicate.PromptTemplateVersion, root.GetProperty("promptTemplateVersion").GetString());
|
||||
Assert.Equal(predicate.GeneratedAt, root.GetProperty("generatedAt").GetString());
|
||||
Assert.Equal(predicate.OutputHash, root.GetProperty("outputHash").GetString());
|
||||
|
||||
var modelId = root.GetProperty("modelId");
|
||||
Assert.Equal("openai", modelId.GetProperty("provider").GetString());
|
||||
Assert.Equal("gpt-4.1", modelId.GetProperty("model").GetString());
|
||||
Assert.Equal("2026-02-01", modelId.GetProperty("version").GetString());
|
||||
|
||||
var decodingParams = root.GetProperty("decodingParams");
|
||||
Assert.Equal(0.0, decodingParams.GetProperty("temperature").GetDouble(), 6);
|
||||
Assert.Equal(0.95, decodingParams.GetProperty("topP").GetDouble(), 6);
|
||||
Assert.Equal(16, decodingParams.GetProperty("topK").GetInt32());
|
||||
Assert.Equal(1024, decodingParams.GetProperty("maxTokens").GetInt32());
|
||||
Assert.Equal(12345L, decodingParams.GetProperty("seed").GetInt64());
|
||||
|
||||
var citations = root.GetProperty("citations");
|
||||
Assert.Equal(5, citations.GetArrayLength());
|
||||
Assert.Contains(
|
||||
citations.EnumerateArray().Select(c => c.GetProperty("verified").GetBoolean()),
|
||||
verified => verified == false);
|
||||
}
|
||||
|
||||
[Trait("Intent", "Safety")]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIExplanationPredicate_RoundTripSerialization_PreservesFields()
|
||||
{
|
||||
var original = CreatePredicate(AIExplanationType.EvidenceChain);
|
||||
|
||||
var json = JsonSerializer.Serialize(original);
|
||||
var roundTripped = JsonSerializer.Deserialize<AIExplanationPredicate>(json);
|
||||
|
||||
Assert.NotNull(roundTripped);
|
||||
Assert.Equal(original.ExplanationType, roundTripped!.ExplanationType);
|
||||
Assert.Equal(original.Content, roundTripped.Content);
|
||||
Assert.Equal(original.ConfidenceScore, roundTripped.ConfidenceScore, 6);
|
||||
Assert.Equal(original.CitationRate, roundTripped.CitationRate, 6);
|
||||
Assert.Equal(original.Subject, roundTripped.Subject);
|
||||
Assert.Equal(original.ContextScope, roundTripped.ContextScope);
|
||||
Assert.Equal(original.ArtifactId, roundTripped.ArtifactId);
|
||||
Assert.Equal(original.ModelId, roundTripped.ModelId);
|
||||
Assert.Equal(original.DecodingParams, roundTripped.DecodingParams);
|
||||
Assert.Equal(original.InputHashes, roundTripped.InputHashes);
|
||||
Assert.Equal(original.Citations, roundTripped.Citations);
|
||||
}
|
||||
|
||||
[Trait("Intent", "Operational")]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIExplanationType_EnumValues_SerializeAndDeserializeAsStableStrings()
|
||||
{
|
||||
var expectedNames = Enum.GetNames<AIExplanationType>();
|
||||
|
||||
foreach (var explanationType in Enum.GetValues<AIExplanationType>())
|
||||
{
|
||||
var json = JsonSerializer.Serialize(explanationType);
|
||||
var serialized = JsonSerializer.Deserialize<string>(json);
|
||||
var deserialized = JsonSerializer.Deserialize<AIExplanationType>(json);
|
||||
|
||||
Assert.Contains(serialized, expectedNames);
|
||||
Assert.Equal(explanationType, deserialized);
|
||||
}
|
||||
}
|
||||
|
||||
[Trait("Intent", "Safety")]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIExplanationCitation_MixedVerification_ProducesDeterministicCitationRate()
|
||||
{
|
||||
var citations = CreateCitations(total: 5, verified: 3);
|
||||
var citationRate = citations.Count(c => c.Verified) / (double)citations.Count;
|
||||
var predicate = CreatePredicate(AIExplanationType.Exploitability, citations, citationRate);
|
||||
|
||||
Assert.Equal(0.6, citationRate, 6);
|
||||
Assert.Equal(citationRate, predicate.CitationRate, 6);
|
||||
}
|
||||
|
||||
[Trait("Intent", "Regulatory")]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIExplanationStatement_SerializesAsInTotoStatementWithExplanationPredicateType()
|
||||
{
|
||||
var statement = new AIExplanationStatement
|
||||
{
|
||||
Subject =
|
||||
[
|
||||
new Subject
|
||||
{
|
||||
Name = "pkg:oci/stellaops/image@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
Digest = new Dictionary<string, string>
|
||||
{
|
||||
["sha256"] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
}
|
||||
}
|
||||
],
|
||||
Predicate = CreatePredicate(AIExplanationType.RiskFactors)
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(statement);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
Assert.Equal("https://in-toto.io/Statement/v1", root.GetProperty("_type").GetString());
|
||||
Assert.Equal("ai-explanation.stella/v1", root.GetProperty("predicateType").GetString());
|
||||
Assert.Equal("RiskFactors", root.GetProperty("predicate").GetProperty("explanationType").GetString());
|
||||
Assert.Equal(1, root.GetProperty("subject").GetArrayLength());
|
||||
}
|
||||
|
||||
[Trait("Intent", "Operational")]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIArtifactReplayManifest_FromExplanationPredicate_PreservesModelAndDecodingInputs()
|
||||
{
|
||||
var predicate = CreatePredicate(AIExplanationType.PlainLanguageSummary);
|
||||
var manifest = CreateReplayManifest(predicate);
|
||||
|
||||
Assert.Equal(predicate.ArtifactId, manifest.ArtifactId);
|
||||
Assert.Equal(predicate.ModelId, manifest.ModelId);
|
||||
Assert.Equal(predicate.DecodingParams, manifest.DecodingParams);
|
||||
Assert.Equal(predicate.GeneratedAt, manifest.GeneratedAt);
|
||||
Assert.Equal(predicate.OutputHash, manifest.ExpectedOutputHash);
|
||||
Assert.True(manifest.Replayable);
|
||||
Assert.Equal(2, manifest.Inputs.Count);
|
||||
}
|
||||
|
||||
[Trait("Intent", "Safety")]
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIArtifactMediaTypes_ExplanationPredicateType_MappingIsBidirectional()
|
||||
{
|
||||
var mediaType = AIArtifactMediaTypes.GetMediaTypeForPredicateType("ai-explanation.stella/v1");
|
||||
var predicateType = AIArtifactMediaTypes.GetPredicateTypeForMediaType(AIArtifactMediaTypes.AIExplanation);
|
||||
|
||||
Assert.Equal(AIArtifactMediaTypes.AIExplanation, mediaType);
|
||||
Assert.Equal("ai-explanation.stella/v1", predicateType);
|
||||
Assert.True(AIArtifactMediaTypes.IsAIArtifactMediaType(AIArtifactMediaTypes.AIReplayManifest));
|
||||
}
|
||||
|
||||
private static AIExplanationPredicate CreatePredicate(
|
||||
AIExplanationType explanationType,
|
||||
IReadOnlyList<AIExplanationCitation>? citations = null,
|
||||
double? citationRate = null)
|
||||
{
|
||||
var items = citations ?? CreateCitations(total: 5, verified: 4);
|
||||
var rate = citationRate ?? (items.Count(c => c.Verified) / (double)items.Count);
|
||||
|
||||
return new AIExplanationPredicate
|
||||
{
|
||||
ArtifactId = "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
ModelId = new AIModelIdentifier
|
||||
{
|
||||
Provider = "openai",
|
||||
Model = "gpt-4.1",
|
||||
Version = "2026-02-01",
|
||||
WeightsDigest = "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
|
||||
},
|
||||
PromptTemplateVersion = "explain@v3",
|
||||
DecodingParams = new AIDecodingParameters
|
||||
{
|
||||
Temperature = 0.0,
|
||||
TopP = 0.95,
|
||||
TopK = 16,
|
||||
MaxTokens = 1024,
|
||||
Seed = 12345
|
||||
},
|
||||
InputHashes =
|
||||
[
|
||||
"sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
|
||||
"sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
||||
],
|
||||
Authority = AIArtifactAuthority.EvidenceBacked,
|
||||
GeneratedAt = "2026-02-11T12:00:00Z",
|
||||
OutputHash = "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
ExplanationType = explanationType,
|
||||
Content = "Evidence-backed explanation content.",
|
||||
Citations = items,
|
||||
ConfidenceScore = 0.92,
|
||||
CitationRate = rate,
|
||||
Subject = "CVE-2026-12345",
|
||||
ContextScope = "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
};
|
||||
}
|
||||
|
||||
private static IReadOnlyList<AIExplanationCitation> CreateCitations(int total, int verified)
|
||||
{
|
||||
var citations = new List<AIExplanationCitation>(capacity: total);
|
||||
|
||||
for (var i = 0; i < total; i++)
|
||||
{
|
||||
citations.Add(new AIExplanationCitation
|
||||
{
|
||||
ClaimIndex = i,
|
||||
ClaimText = $"Claim {i}",
|
||||
EvidenceId = $"sha256:{new string((char)('a' + i), 64)}",
|
||||
EvidenceType = i % 2 == 0 ? "sbom" : "vex",
|
||||
Verified = i < verified
|
||||
});
|
||||
}
|
||||
|
||||
return citations;
|
||||
}
|
||||
|
||||
private static AIArtifactReplayManifest CreateReplayManifest(AIExplanationPredicate predicate)
|
||||
{
|
||||
return new AIArtifactReplayManifest
|
||||
{
|
||||
ManifestId = "replay:ai-expl-0001",
|
||||
ArtifactId = predicate.ArtifactId,
|
||||
ArtifactType = "explanation",
|
||||
ModelId = predicate.ModelId,
|
||||
DecodingParams = predicate.DecodingParams,
|
||||
PromptTemplate = new ReplayPromptTemplate
|
||||
{
|
||||
Name = "explain",
|
||||
Version = predicate.PromptTemplateVersion,
|
||||
Hash = "sha256:1212121212121212121212121212121212121212121212121212121212121212",
|
||||
Location = "oci://stellaops/prompts/explain@v3"
|
||||
},
|
||||
Inputs =
|
||||
[
|
||||
new ReplayInputArtifact
|
||||
{
|
||||
Hash = "sha256:0202020202020202020202020202020202020202020202020202020202020202",
|
||||
Type = "sbom",
|
||||
MediaType = "application/spdx+json",
|
||||
Size = 4096,
|
||||
Location = "cas://sbom/0101",
|
||||
Order = 0
|
||||
},
|
||||
new ReplayInputArtifact
|
||||
{
|
||||
Hash = "sha256:0404040404040404040404040404040404040404040404040404040404040404",
|
||||
Type = "reachability",
|
||||
MediaType = "application/json",
|
||||
Size = 2048,
|
||||
Location = "cas://reach/0303",
|
||||
Order = 1
|
||||
}
|
||||
],
|
||||
ExpectedOutputHash = predicate.OutputHash,
|
||||
GeneratedAt = predicate.GeneratedAt,
|
||||
Replayable = true
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
using System.Text.Json;
|
||||
using StellaOps.Attestor.ProofChain.Predicates.AI;
|
||||
using StellaOps.Attestor.ProofChain.Replay;
|
||||
using StellaOps.Attestor.ProofChain.Statements;
|
||||
using StellaOps.Attestor.ProofChain.Statements.AI;
|
||||
using StellaOps.TestKit;
|
||||
|
||||
namespace StellaOps.Attestor.ProofChain.Tests.AI;
|
||||
|
||||
public sealed class AIExplanationAttestationTypesTests
|
||||
{
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIExplanationPredicate_Serialization_UsesExpectedContractFieldNames()
|
||||
{
|
||||
var predicate = CreateExplanationPredicate();
|
||||
|
||||
var json = JsonSerializer.Serialize(predicate);
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var root = document.RootElement;
|
||||
|
||||
Assert.True(root.TryGetProperty("artifactId", out _));
|
||||
Assert.True(root.TryGetProperty("modelId", out var modelId));
|
||||
Assert.True(modelId.TryGetProperty("provider", out _));
|
||||
Assert.True(modelId.TryGetProperty("model", out _));
|
||||
Assert.True(modelId.TryGetProperty("version", out _));
|
||||
Assert.True(root.TryGetProperty("decodingParams", out var decoding));
|
||||
Assert.True(decoding.TryGetProperty("temperature", out _));
|
||||
Assert.True(decoding.TryGetProperty("seed", out _));
|
||||
Assert.True(root.TryGetProperty("explanationType", out var explanationType));
|
||||
Assert.Equal("Exploitability", explanationType.GetString());
|
||||
Assert.True(root.TryGetProperty("content", out _));
|
||||
Assert.True(root.TryGetProperty("citations", out var citations));
|
||||
Assert.Equal(JsonValueKind.Array, citations.ValueKind);
|
||||
Assert.True(root.TryGetProperty("confidenceScore", out _));
|
||||
Assert.True(root.TryGetProperty("citationRate", out _));
|
||||
Assert.True(root.TryGetProperty("subject", out _));
|
||||
Assert.True(root.TryGetProperty("contextScope", out _));
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIExplanationPredicate_Serialization_RoundTrips()
|
||||
{
|
||||
var original = CreateExplanationPredicate();
|
||||
|
||||
var json = JsonSerializer.Serialize(original);
|
||||
var deserialized = JsonSerializer.Deserialize<AIExplanationPredicate>(json);
|
||||
|
||||
Assert.NotNull(deserialized);
|
||||
Assert.Equal(original.ArtifactId, deserialized!.ArtifactId);
|
||||
Assert.Equal(original.ModelId.Provider, deserialized.ModelId.Provider);
|
||||
Assert.Equal(original.ModelId.Model, deserialized.ModelId.Model);
|
||||
Assert.Equal(original.ModelId.Version, deserialized.ModelId.Version);
|
||||
Assert.Equal(original.DecodingParams.Temperature, deserialized.DecodingParams.Temperature);
|
||||
Assert.Equal(original.DecodingParams.Seed, deserialized.DecodingParams.Seed);
|
||||
Assert.Equal(original.ExplanationType, deserialized.ExplanationType);
|
||||
Assert.Equal(original.Content, deserialized.Content);
|
||||
Assert.Equal(original.Citations.Count, deserialized.Citations.Count);
|
||||
Assert.Equal(original.Citations[0].EvidenceId, deserialized.Citations[0].EvidenceId);
|
||||
Assert.Equal(original.ConfidenceScore, deserialized.ConfidenceScore);
|
||||
Assert.Equal(original.CitationRate, deserialized.CitationRate);
|
||||
Assert.Equal(original.Subject, deserialized.Subject);
|
||||
Assert.Equal(original.ContextScope, deserialized.ContextScope);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIArtifactReplayManifest_Serialization_CapturesModelAndDecodingParameters()
|
||||
{
|
||||
var predicate = CreateExplanationPredicate();
|
||||
var manifest = new AIArtifactReplayManifest
|
||||
{
|
||||
ManifestId = "manifest-ai-explanation-001",
|
||||
ArtifactId = predicate.ArtifactId,
|
||||
ArtifactType = "explanation",
|
||||
ModelId = predicate.ModelId,
|
||||
DecodingParams = predicate.DecodingParams,
|
||||
PromptTemplate = new ReplayPromptTemplate
|
||||
{
|
||||
Name = "explanation",
|
||||
Version = "v1",
|
||||
Hash = "sha256:1111111111111111111111111111111111111111111111111111111111111111",
|
||||
Location = "oci://stellaops/prompt/explanation:v1"
|
||||
},
|
||||
Inputs =
|
||||
[
|
||||
new ReplayInputArtifact
|
||||
{
|
||||
Hash = "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
Type = "sbom",
|
||||
MediaType = "application/spdx+json",
|
||||
Size = 2048,
|
||||
Location = "blob://sbom/1",
|
||||
Order = 0
|
||||
}
|
||||
],
|
||||
ExpectedOutputHash = predicate.OutputHash,
|
||||
GeneratedAt = predicate.GeneratedAt,
|
||||
Replayable = true
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(manifest);
|
||||
var parsed = JsonSerializer.Deserialize<AIArtifactReplayManifest>(json);
|
||||
|
||||
Assert.NotNull(parsed);
|
||||
Assert.Equal(predicate.ModelId, parsed!.ModelId);
|
||||
Assert.Equal(predicate.DecodingParams, parsed.DecodingParams);
|
||||
Assert.Equal("explanation", parsed.ArtifactType);
|
||||
Assert.True(parsed.Replayable);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIExplanationCitation_MixedVerifiedState_SupportsCitationRateAccounting()
|
||||
{
|
||||
var citations = new List<AIExplanationCitation>
|
||||
{
|
||||
new()
|
||||
{
|
||||
ClaimIndex = 0,
|
||||
ClaimText = "Claim 0",
|
||||
EvidenceId = "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
EvidenceType = "sbom",
|
||||
Verified = true
|
||||
},
|
||||
new()
|
||||
{
|
||||
ClaimIndex = 1,
|
||||
ClaimText = "Claim 1",
|
||||
EvidenceId = "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
EvidenceType = "vex",
|
||||
Verified = false
|
||||
},
|
||||
new()
|
||||
{
|
||||
ClaimIndex = 2,
|
||||
ClaimText = "Claim 2",
|
||||
EvidenceId = "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
|
||||
EvidenceType = "reachability",
|
||||
Verified = true
|
||||
}
|
||||
};
|
||||
|
||||
var verifiedCount = citations.Count(c => c.Verified);
|
||||
var citationRate = (double)verifiedCount / citations.Count;
|
||||
|
||||
Assert.Equal(2, verifiedCount);
|
||||
Assert.Equal(2d / 3d, citationRate, 4);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIExplanationType_Serialization_UsesStringValues_ForAllMembers()
|
||||
{
|
||||
foreach (var explanationType in Enum.GetValues<AIExplanationType>())
|
||||
{
|
||||
var json = JsonSerializer.Serialize(new { explanationType });
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var serializedValue = document.RootElement.GetProperty("explanationType").GetString();
|
||||
Assert.Equal(explanationType.ToString(), serializedValue);
|
||||
}
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIExplanationStatement_UsesExpectedPredicateType_AndInTotoStatementShape()
|
||||
{
|
||||
var statement = new AIExplanationStatement
|
||||
{
|
||||
Subject =
|
||||
[
|
||||
new Subject
|
||||
{
|
||||
Name = "pkg:oci/stellaops/app@sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
|
||||
Digest = new Dictionary<string, string>
|
||||
{
|
||||
["sha256"] = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
|
||||
}
|
||||
}
|
||||
],
|
||||
Predicate = CreateExplanationPredicate()
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(statement);
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var root = document.RootElement;
|
||||
|
||||
Assert.Equal("ai-explanation.stella/v1", statement.PredicateType);
|
||||
Assert.Equal("https://in-toto.io/Statement/v1", root.GetProperty("_type").GetString());
|
||||
Assert.Equal("ai-explanation.stella/v1", root.GetProperty("predicateType").GetString());
|
||||
Assert.True(root.TryGetProperty("predicate", out var predicate));
|
||||
Assert.Equal("Exploitability", predicate.GetProperty("explanationType").GetString());
|
||||
}
|
||||
|
||||
private static AIExplanationPredicate CreateExplanationPredicate()
|
||||
{
|
||||
return new AIExplanationPredicate
|
||||
{
|
||||
ArtifactId = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
ModelId = new AIModelIdentifier
|
||||
{
|
||||
Provider = "openai",
|
||||
Model = "gpt-4.1",
|
||||
Version = "2026-01-01"
|
||||
},
|
||||
PromptTemplateVersion = "explanation@v1",
|
||||
DecodingParams = new AIDecodingParameters
|
||||
{
|
||||
Temperature = 0.0,
|
||||
TopP = 0.95,
|
||||
MaxTokens = 1024,
|
||||
Seed = 424242
|
||||
},
|
||||
InputHashes =
|
||||
[
|
||||
"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
],
|
||||
Authority = AIArtifactAuthority.EvidenceBacked,
|
||||
GeneratedAt = "2026-02-11T12:00:00Z",
|
||||
OutputHash = "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
|
||||
ExplanationType = AIExplanationType.Exploitability,
|
||||
Content = "Evidence-backed exploitability explanation.",
|
||||
Citations =
|
||||
[
|
||||
new AIExplanationCitation
|
||||
{
|
||||
ClaimIndex = 0,
|
||||
ClaimText = "Reachability confirms vulnerable path.",
|
||||
EvidenceId = "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
|
||||
EvidenceType = "reachability",
|
||||
Verified = true
|
||||
}
|
||||
],
|
||||
ConfidenceScore = 0.92,
|
||||
CitationRate = 1.0,
|
||||
Subject = "CVE-2026-12345",
|
||||
ContextScope = "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
using System.Text.Json;
|
||||
using StellaOps.Attestor.ProofChain.Predicates.AI;
|
||||
using StellaOps.Attestor.ProofChain.Statements;
|
||||
using StellaOps.Attestor.ProofChain.Statements.AI;
|
||||
using StellaOps.TestKit;
|
||||
|
||||
namespace StellaOps.Attestor.ProofChain.Tests.AI;
|
||||
|
||||
public sealed class AIRemediationPlanAttestationBehaviorTests
|
||||
{
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIRemediationPlanPredicate_Serialization_UsesExpectedContractFields()
|
||||
{
|
||||
var predicate = CreateRemediationPredicate();
|
||||
|
||||
var json = JsonSerializer.Serialize(predicate);
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var root = document.RootElement;
|
||||
|
||||
Assert.True(root.TryGetProperty("vulnerabilityId", out _));
|
||||
Assert.True(root.TryGetProperty("affectedComponent", out _));
|
||||
Assert.True(root.TryGetProperty("steps", out var steps));
|
||||
Assert.Equal(JsonValueKind.Array, steps.ValueKind);
|
||||
Assert.Equal(3, steps.GetArrayLength());
|
||||
Assert.Equal("PackageUpgrade", steps[0].GetProperty("actionType").GetString());
|
||||
Assert.Equal("ConfigurationChange", steps[1].GetProperty("actionType").GetString());
|
||||
Assert.Equal("CompensatingControl", steps[2].GetProperty("actionType").GetString());
|
||||
Assert.True(root.TryGetProperty("riskAssessment", out var riskAssessment));
|
||||
Assert.True(riskAssessment.TryGetProperty("riskBefore", out _));
|
||||
Assert.True(riskAssessment.TryGetProperty("riskAfter", out _));
|
||||
Assert.True(root.TryGetProperty("verificationStatus", out var verificationStatus));
|
||||
Assert.Equal("Verified", verificationStatus.GetString());
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIRemediationPlanPredicate_Serialization_RoundTripsRiskAndVerificationStatus()
|
||||
{
|
||||
var original = CreateRemediationPredicate();
|
||||
|
||||
var json = JsonSerializer.Serialize(original);
|
||||
var deserialized = JsonSerializer.Deserialize<AIRemediationPlanPredicate>(json);
|
||||
|
||||
Assert.NotNull(deserialized);
|
||||
Assert.Equal(0.9, deserialized!.RiskAssessment.RiskBefore);
|
||||
Assert.Equal(0.2, deserialized.RiskAssessment.RiskAfter);
|
||||
Assert.Equal(RemediationVerificationStatus.Verified, deserialized.VerificationStatus);
|
||||
Assert.Equal(3, deserialized.Steps.Count);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void ClassifyRemediationPlan_HighResolvableEvidence_ReturnsEvidenceBacked()
|
||||
{
|
||||
Func<string, bool> resolver = _ => true;
|
||||
var classifier = new AIAuthorityClassifier(evidenceResolver: resolver);
|
||||
var predicate = CreateRemediationPredicate() with
|
||||
{
|
||||
RiskAssessment = new RemediationRiskAssessment
|
||||
{
|
||||
RiskBefore = 0.92,
|
||||
RiskAfter = 0.18,
|
||||
BreakingChanges = ["Config format changed"],
|
||||
RequiredTestCoverage = ["integration:release-validation", "e2e:smoke"]
|
||||
}
|
||||
};
|
||||
|
||||
var result = classifier.ClassifyRemediationPlan(predicate);
|
||||
|
||||
Assert.Equal(AIArtifactAuthority.EvidenceBacked, result.Authority);
|
||||
Assert.Equal(predicate.EvidenceRefs.Count, result.ResolvableEvidenceCount);
|
||||
Assert.Equal(0, result.UnresolvableEvidenceCount);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void ClassifyRemediationPlan_LowEvidenceBacking_ReturnsSuggestion()
|
||||
{
|
||||
Func<string, bool> resolver = evidenceRef => evidenceRef.EndsWith("0", StringComparison.Ordinal);
|
||||
var classifier = new AIAuthorityClassifier(evidenceResolver: resolver);
|
||||
var predicate = CreateRemediationPredicate();
|
||||
|
||||
var result = classifier.ClassifyRemediationPlan(predicate);
|
||||
|
||||
Assert.Equal(AIArtifactAuthority.Suggestion, result.Authority);
|
||||
Assert.Equal(1, result.ResolvableEvidenceCount);
|
||||
Assert.Equal(3, result.UnresolvableEvidenceCount);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void AIRemediationPlanStatement_UsesInTotoShapeAndPredicateType()
|
||||
{
|
||||
var statement = new AIRemediationPlanStatement
|
||||
{
|
||||
Subject =
|
||||
[
|
||||
new Subject
|
||||
{
|
||||
Name = "pkg:oci/stellaops/app@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
Digest = new Dictionary<string, string>
|
||||
{
|
||||
["sha256"] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
}
|
||||
}
|
||||
],
|
||||
Predicate = CreateRemediationPredicate()
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(statement);
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var root = document.RootElement;
|
||||
|
||||
Assert.Equal("https://in-toto.io/Statement/v1", root.GetProperty("_type").GetString());
|
||||
Assert.Equal("ai-remediation.stella/v1", root.GetProperty("predicateType").GetString());
|
||||
Assert.True(root.TryGetProperty("predicate", out var predicate));
|
||||
Assert.Equal("CVE-2026-1001", predicate.GetProperty("vulnerabilityId").GetString());
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void RemediationStepStatus_AndVerificationStatus_PersistThroughSerialization()
|
||||
{
|
||||
var predicate = CreateRemediationPredicate() with
|
||||
{
|
||||
Steps =
|
||||
[
|
||||
CreateStep(1, RemediationActionType.PackageUpgrade, RemediationStepStatus.Pending),
|
||||
CreateStep(2, RemediationActionType.ConfigurationChange, RemediationStepStatus.InProgress),
|
||||
CreateStep(3, RemediationActionType.CompensatingControl, RemediationStepStatus.Complete)
|
||||
],
|
||||
VerificationStatus = RemediationVerificationStatus.Applied
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(predicate);
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var steps = document.RootElement.GetProperty("steps");
|
||||
|
||||
Assert.Equal("Pending", steps[0].GetProperty("status").GetString());
|
||||
Assert.Equal("InProgress", steps[1].GetProperty("status").GetString());
|
||||
Assert.Equal("Complete", steps[2].GetProperty("status").GetString());
|
||||
Assert.Equal("Applied", document.RootElement.GetProperty("verificationStatus").GetString());
|
||||
}
|
||||
|
||||
private static AIRemediationPlanPredicate CreateRemediationPredicate()
|
||||
{
|
||||
return new AIRemediationPlanPredicate
|
||||
{
|
||||
ArtifactId = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
ModelId = new AIModelIdentifier
|
||||
{
|
||||
Provider = "openai",
|
||||
Model = "gpt-4.1",
|
||||
Version = "2026-01-01"
|
||||
},
|
||||
PromptTemplateVersion = "remediation@v1",
|
||||
DecodingParams = new AIDecodingParameters
|
||||
{
|
||||
Temperature = 0.0,
|
||||
TopP = 0.9,
|
||||
MaxTokens = 2048,
|
||||
Seed = 7001
|
||||
},
|
||||
InputHashes =
|
||||
[
|
||||
"sha256:1111111111111111111111111111111111111111111111111111111111111111",
|
||||
"sha256:2222222222222222222222222222222222222222222222222222222222222222"
|
||||
],
|
||||
Authority = AIArtifactAuthority.Suggestion,
|
||||
GeneratedAt = "2026-02-11T12:30:00Z",
|
||||
OutputHash = "sha256:3333333333333333333333333333333333333333333333333333333333333333",
|
||||
VulnerabilityId = "CVE-2026-1001",
|
||||
AffectedComponent = "pkg:nuget/Example.Component@1.2.3",
|
||||
Steps =
|
||||
[
|
||||
CreateStep(1, RemediationActionType.PackageUpgrade, RemediationStepStatus.Pending),
|
||||
CreateStep(2, RemediationActionType.ConfigurationChange, RemediationStepStatus.InProgress),
|
||||
CreateStep(3, RemediationActionType.CompensatingControl, RemediationStepStatus.Complete)
|
||||
],
|
||||
ExpectedDelta = 0.68,
|
||||
RiskAssessment = new RemediationRiskAssessment
|
||||
{
|
||||
RiskBefore = 0.9,
|
||||
RiskAfter = 0.2,
|
||||
BreakingChanges = ["Config format changed"],
|
||||
RequiredTestCoverage = ["integration:release-validation", "e2e:smoke"]
|
||||
},
|
||||
VerificationStatus = RemediationVerificationStatus.Verified,
|
||||
PrReady = true,
|
||||
FixBranchCommit = "9d7f1ab",
|
||||
EvidenceRefs =
|
||||
[
|
||||
"sha256:evidence0",
|
||||
"sha256:evidence1",
|
||||
"sha256:evidence2",
|
||||
"sha256:evidence3"
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
private static RemediationStep CreateStep(
|
||||
int order,
|
||||
RemediationActionType actionType,
|
||||
RemediationStepStatus status)
|
||||
{
|
||||
return new RemediationStep
|
||||
{
|
||||
Order = order,
|
||||
ActionType = actionType,
|
||||
Description = $"Step {order}",
|
||||
Target = $"target-{order}",
|
||||
CurrentValue = "old",
|
||||
ProposedValue = "new",
|
||||
RiskReduction = 0.2 * order,
|
||||
CanAutomate = true,
|
||||
AutomationScript = $"stella remediate --step {order}",
|
||||
Status = status,
|
||||
EvidenceRefs = [$"sha256:step{order}"]
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,13 @@ namespace StellaOps.Attestor.ProofChain.Tests;
|
||||
|
||||
public sealed class BackportProofGeneratorTests
|
||||
{
|
||||
private static readonly DateTimeOffset FixedTime = new(2026, 01, 02, 12, 45, 30, TimeSpan.Zero);
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Unknown_UsesTimeProviderForTimestamps()
|
||||
{
|
||||
var fixedTime = new DateTimeOffset(2026, 01, 02, 12, 45, 30, TimeSpan.Zero);
|
||||
var timeProvider = new FixedTimeProvider(fixedTime);
|
||||
var timeProvider = new FixedTimeProvider(FixedTime);
|
||||
|
||||
var proof = BackportProofGenerator.Unknown(
|
||||
"CVE-2026-0001",
|
||||
@@ -21,10 +22,83 @@ public sealed class BackportProofGeneratorTests
|
||||
Array.Empty<ProofEvidence>(),
|
||||
timeProvider);
|
||||
|
||||
proof.CreatedAt.Should().Be(fixedTime);
|
||||
proof.CreatedAt.Should().Be(FixedTime);
|
||||
proof.SnapshotId.Should().Be("20260102-124530-UTC");
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void CombineEvidence_UsesStrengthHierarchyForConfidence()
|
||||
{
|
||||
var timeProvider = new FixedTimeProvider(FixedTime);
|
||||
|
||||
var authoritative = BackportProofGenerator.CombineEvidence(
|
||||
"CVE-2026-1000",
|
||||
"pkg:npm/sample@1.0.0",
|
||||
[CreateEvidence(EvidenceType.DistroAdvisory, "authority-feed")],
|
||||
timeProvider);
|
||||
|
||||
var binary = BackportProofGenerator.CombineEvidence(
|
||||
"CVE-2026-1000",
|
||||
"pkg:npm/sample@1.0.0",
|
||||
[CreateEvidence(EvidenceType.BuildCatalog, "catalog-feed")],
|
||||
timeProvider);
|
||||
|
||||
var staticAnalysis = BackportProofGenerator.CombineEvidence(
|
||||
"CVE-2026-1000",
|
||||
"pkg:npm/sample@1.0.0",
|
||||
[CreateEvidence(EvidenceType.PatchHeader, "patch-feed")],
|
||||
timeProvider);
|
||||
|
||||
var heuristic = BackportProofGenerator.CombineEvidence(
|
||||
"CVE-2026-1000",
|
||||
"pkg:npm/sample@1.0.0",
|
||||
[CreateEvidence(EvidenceType.BinaryFingerprint, "binary-feed")],
|
||||
timeProvider);
|
||||
|
||||
authoritative.Confidence.Should().BeGreaterThan(binary.Confidence);
|
||||
binary.Confidence.Should().BeGreaterThan(staticAnalysis.Confidence);
|
||||
staticAnalysis.Confidence.Should().BeGreaterThan(heuristic.Confidence);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void CombineEvidence_MultipleIndependentSources_IncreaseConfidenceWithinCap()
|
||||
{
|
||||
var timeProvider = new FixedTimeProvider(FixedTime);
|
||||
var single = BackportProofGenerator.CombineEvidence(
|
||||
"CVE-2026-1001",
|
||||
"pkg:npm/sample@1.0.0",
|
||||
[CreateEvidence(EvidenceType.BinaryFingerprint, "binary-feed")],
|
||||
timeProvider);
|
||||
|
||||
var corroborated = BackportProofGenerator.CombineEvidence(
|
||||
"CVE-2026-1001",
|
||||
"pkg:npm/sample@1.0.0",
|
||||
[
|
||||
CreateEvidence(EvidenceType.BinaryFingerprint, "binary-feed"),
|
||||
CreateEvidence(EvidenceType.PatchHeader, "patch-feed"),
|
||||
CreateEvidence(EvidenceType.DistroAdvisory, "authority-feed")
|
||||
],
|
||||
timeProvider);
|
||||
|
||||
corroborated.Confidence.Should().BeGreaterThan(single.Confidence);
|
||||
corroborated.Confidence.Should().BeLessThanOrEqualTo(0.98);
|
||||
}
|
||||
|
||||
private static ProofEvidence CreateEvidence(EvidenceType type, string source)
|
||||
{
|
||||
return new ProofEvidence
|
||||
{
|
||||
EvidenceId = $"evidence:{source}:{type}",
|
||||
Type = type,
|
||||
Source = source,
|
||||
Timestamp = FixedTime,
|
||||
Data = System.Text.Json.JsonDocument.Parse("{\"source\":\"fixture\"}").RootElement.Clone(),
|
||||
DataHash = "sha256:fixture"
|
||||
};
|
||||
}
|
||||
|
||||
private sealed class FixedTimeProvider : TimeProvider
|
||||
{
|
||||
private readonly DateTimeOffset _utcNow;
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
using System.Collections.Immutable;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Attestor.ProofChain.ChangeTrace;
|
||||
using StellaOps.Attestor.ProofChain.Signing;
|
||||
using StellaOps.Attestor.ProofChain.Statements;
|
||||
using StellaOps.Scanner.ChangeTrace.Models;
|
||||
using StellaOps.TestKit;
|
||||
using ChangeTraceModel = StellaOps.Scanner.ChangeTrace.Models.ChangeTrace;
|
||||
|
||||
namespace StellaOps.Attestor.ProofChain.Tests.ChangeTrace;
|
||||
|
||||
public sealed class ChangeTraceAttestationServiceTests
|
||||
{
|
||||
private static readonly DateTimeOffset FixedTime = new(2026, 02, 11, 12, 0, 0, TimeSpan.Zero);
|
||||
|
||||
[Trait("Category", TestCategories.Integration)]
|
||||
[Fact]
|
||||
public async Task GenerateAttestationAsync_SuppressesMinorFlipFlops_WithHysteresisThreshold()
|
||||
{
|
||||
var signer = new CapturingProofChainSigner();
|
||||
var service = new ChangeTraceAttestationService(signer, new FixedTimeProvider(FixedTime));
|
||||
var trace = CreateTrace(
|
||||
CreateDelta("pkg:npm/minor@1.0.0", PackageChangeType.Modified, score: 0.02),
|
||||
CreateDelta("pkg:npm/major@2.0.0", PackageChangeType.Added, score: 0.70));
|
||||
|
||||
await service.GenerateAttestationAsync(
|
||||
trace,
|
||||
new ChangeTraceAttestationOptions { HysteresisThreshold = 0.05 });
|
||||
|
||||
var statement = signer.LastSignedStatement.Should().BeOfType<ChangeTraceStatement>().Subject;
|
||||
statement.Predicate.Deltas.Should().HaveCount(1);
|
||||
statement.Predicate.Deltas[0].Purl.Should().Be("pkg:npm/major@2.0.0");
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Integration)]
|
||||
[Fact]
|
||||
public async Task GenerateAttestationAsync_DisablesHysteresis_WhenThresholdIsZero()
|
||||
{
|
||||
var signer = new CapturingProofChainSigner();
|
||||
var service = new ChangeTraceAttestationService(signer, new FixedTimeProvider(FixedTime));
|
||||
var trace = CreateTrace(
|
||||
CreateDelta("pkg:npm/minor@1.0.0", PackageChangeType.Modified, score: 0.02),
|
||||
CreateDelta("pkg:npm/major@2.0.0", PackageChangeType.Added, score: 0.70));
|
||||
|
||||
await service.GenerateAttestationAsync(
|
||||
trace,
|
||||
new ChangeTraceAttestationOptions { HysteresisThreshold = 0.0 });
|
||||
|
||||
var statement = signer.LastSignedStatement.Should().BeOfType<ChangeTraceStatement>().Subject;
|
||||
statement.Predicate.Deltas.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
private static ChangeTraceModel CreateTrace(params PackageDelta[] deltas)
|
||||
{
|
||||
return new ChangeTraceModel
|
||||
{
|
||||
Subject = new ChangeTraceSubject
|
||||
{
|
||||
Type = "oci.image",
|
||||
Digest = "sha256:subject",
|
||||
Purl = "pkg:oci/example@1.0.0",
|
||||
Name = "example"
|
||||
},
|
||||
Basis = new ChangeTraceBasis
|
||||
{
|
||||
ScanId = "scan-123",
|
||||
FromScanId = "scan-122",
|
||||
ToScanId = "scan-123",
|
||||
Policies = ["policy-a"],
|
||||
DiffMethod = ["pkg"],
|
||||
EngineVersion = "1.0.0",
|
||||
AnalyzedAt = FixedTime
|
||||
},
|
||||
Deltas = deltas.ToImmutableArray(),
|
||||
Summary = new ChangeTraceSummary
|
||||
{
|
||||
ChangedPackages = deltas.Length,
|
||||
ChangedSymbols = 1,
|
||||
ChangedBytes = 128,
|
||||
RiskDelta = 0.6,
|
||||
Verdict = ChangeTraceVerdict.RiskUp,
|
||||
BeforeRiskScore = 0.2,
|
||||
AfterRiskScore = 0.8
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static PackageDelta CreateDelta(string purl, PackageChangeType changeType, double score)
|
||||
{
|
||||
return new PackageDelta
|
||||
{
|
||||
Purl = purl,
|
||||
Name = purl,
|
||||
FromVersion = "1.0.0",
|
||||
ToVersion = "1.0.1",
|
||||
ChangeType = changeType,
|
||||
Explain = PackageChangeExplanation.SecurityPatch,
|
||||
Evidence = new PackageDeltaEvidence
|
||||
{
|
||||
SymbolsChanged = 1,
|
||||
BytesChanged = 64,
|
||||
Confidence = 0.8
|
||||
},
|
||||
TrustDelta = new TrustDelta
|
||||
{
|
||||
ReachabilityImpact = ReachabilityImpact.Increased,
|
||||
ExploitabilityImpact = ExploitabilityImpact.Up,
|
||||
Score = score,
|
||||
BeforeScore = 0.3,
|
||||
AfterScore = 0.3 + score,
|
||||
ProofSteps = ["step-1"]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private sealed class CapturingProofChainSigner : IProofChainSigner
|
||||
{
|
||||
public InTotoStatement? LastSignedStatement { get; private set; }
|
||||
|
||||
public Task<DsseEnvelope> SignStatementAsync<T>(
|
||||
T statement,
|
||||
SigningKeyProfile keyProfile,
|
||||
CancellationToken ct = default) where T : InTotoStatement
|
||||
{
|
||||
LastSignedStatement = statement;
|
||||
|
||||
return Task.FromResult(new DsseEnvelope
|
||||
{
|
||||
PayloadType = "application/vnd.in-toto+json",
|
||||
Payload = Convert.ToBase64String([]),
|
||||
Signatures =
|
||||
[
|
||||
new DsseSignature
|
||||
{
|
||||
KeyId = "test-key",
|
||||
Sig = Convert.ToBase64String([1, 2, 3])
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
public Task<SignatureVerificationResult> VerifyEnvelopeAsync(
|
||||
DsseEnvelope envelope,
|
||||
IReadOnlyList<string> allowedKeyIds,
|
||||
CancellationToken ct = default)
|
||||
=> Task.FromResult(new SignatureVerificationResult
|
||||
{
|
||||
IsValid = true,
|
||||
KeyId = "test-key"
|
||||
});
|
||||
}
|
||||
|
||||
private sealed class FixedTimeProvider(DateTimeOffset utcNow) : TimeProvider
|
||||
{
|
||||
public override DateTimeOffset GetUtcNow() => utcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using StellaOps.Attestor.ProofChain.Graph;
|
||||
using StellaOps.TestKit;
|
||||
|
||||
namespace StellaOps.Attestor.ProofChain.Tests.Graph;
|
||||
|
||||
public sealed class InMemoryProofGraphServiceBehaviorTests
|
||||
{
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task AddEdgeAsync_DuplicateEdge_IsDeduplicated()
|
||||
{
|
||||
var sut = new InMemoryProofGraphService();
|
||||
var artifact = await sut.AddNodeAsync(ProofGraphNodeType.Artifact, "sha256:artifact");
|
||||
var sbom = await sut.AddNodeAsync(ProofGraphNodeType.SbomDocument, "sha256:sbom");
|
||||
|
||||
var edge1 = await sut.AddEdgeAsync(artifact.Id, sbom.Id, ProofGraphEdgeType.DescribedBy);
|
||||
var edge2 = await sut.AddEdgeAsync(artifact.Id, sbom.Id, ProofGraphEdgeType.DescribedBy);
|
||||
|
||||
Assert.Equal(edge1.Id, edge2.Id);
|
||||
Assert.Equal(2, sut.NodeCount);
|
||||
Assert.Equal(1, sut.EdgeCount);
|
||||
Assert.Empty(edge1.Provenance);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task AddEdgeAsync_DuplicateEdge_MergesProvenanceSets()
|
||||
{
|
||||
var sut = new InMemoryProofGraphService();
|
||||
var artifact = await sut.AddNodeAsync(ProofGraphNodeType.Artifact, "sha256:artifact-with-prov");
|
||||
var sbom = await sut.AddNodeAsync(ProofGraphNodeType.SbomDocument, "sha256:sbom-with-prov");
|
||||
|
||||
await sut.AddEdgeAsync(
|
||||
artifact.Id,
|
||||
sbom.Id,
|
||||
ProofGraphEdgeType.DescribedBy,
|
||||
provenance: ["scanner-a", "scanner-b"]);
|
||||
|
||||
var merged = await sut.AddEdgeAsync(
|
||||
artifact.Id,
|
||||
sbom.Id,
|
||||
ProofGraphEdgeType.DescribedBy,
|
||||
provenance: ["scanner-b", "scanner-c"]);
|
||||
|
||||
Assert.Equal(1, sut.EdgeCount);
|
||||
Assert.Equal(["scanner-a", "scanner-b", "scanner-c"], merged.Provenance);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetArtifactSubgraphAsync_DepthLimit_ExcludesDeeperNodes()
|
||||
{
|
||||
var sut = new InMemoryProofGraphService();
|
||||
var root = await sut.AddNodeAsync(ProofGraphNodeType.Artifact, "sha256:root");
|
||||
var level1 = await sut.AddNodeAsync(ProofGraphNodeType.SbomDocument, "sha256:level1");
|
||||
var level2 = await sut.AddNodeAsync(ProofGraphNodeType.InTotoStatement, "sha256:level2");
|
||||
|
||||
await sut.AddEdgeAsync(root.Id, level1.Id, ProofGraphEdgeType.DescribedBy);
|
||||
await sut.AddEdgeAsync(level1.Id, level2.Id, ProofGraphEdgeType.AttestedBy);
|
||||
|
||||
var subgraph = await sut.GetArtifactSubgraphAsync(root.Id, maxDepth: 1);
|
||||
|
||||
Assert.Equal(root.Id, subgraph.RootNodeId);
|
||||
Assert.Contains(subgraph.Nodes, n => n.Id == root.Id);
|
||||
Assert.Contains(subgraph.Nodes, n => n.Id == level1.Id);
|
||||
Assert.DoesNotContain(subgraph.Nodes, n => n.Id == level2.Id);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task GetArtifactSubgraphAsync_ReturnsOnlyReachableNodesFromRoot()
|
||||
{
|
||||
var sut = new InMemoryProofGraphService();
|
||||
var root = await sut.AddNodeAsync(ProofGraphNodeType.Artifact, "sha256:root2");
|
||||
var related = await sut.AddNodeAsync(ProofGraphNodeType.SbomDocument, "sha256:related");
|
||||
var unrelated = await sut.AddNodeAsync(ProofGraphNodeType.Artifact, "sha256:unrelated");
|
||||
|
||||
await sut.AddEdgeAsync(root.Id, related.Id, ProofGraphEdgeType.DescribedBy);
|
||||
|
||||
var subgraph = await sut.GetArtifactSubgraphAsync(root.Id, maxDepth: 3);
|
||||
|
||||
Assert.Contains(subgraph.Nodes, n => n.Id == root.Id);
|
||||
Assert.Contains(subgraph.Nodes, n => n.Id == related.Id);
|
||||
Assert.DoesNotContain(subgraph.Nodes, n => n.Id == unrelated.Id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System.Collections.Immutable;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Attestor.ProofChain.Predicates;
|
||||
using StellaOps.TestKit;
|
||||
|
||||
namespace StellaOps.Attestor.ProofChain.Tests.Statements;
|
||||
|
||||
public sealed class DeltaVerdictPredicateCategorizationTests
|
||||
{
|
||||
private static readonly DateTimeOffset FixedTime = new(2026, 02, 11, 12, 0, 0, TimeSpan.Zero);
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void CategorizeChanges_AssignsExpectedCategories()
|
||||
{
|
||||
var predicate = CreatePredicate(
|
||||
CreateChange("new finding", direction: "added", before: null, after: "0.5"),
|
||||
CreateChange("resolved finding", direction: "resolved", before: "0.5", after: null),
|
||||
CreateChange("confidence up", direction: "changed", before: "0.4", after: "0.7"),
|
||||
CreateChange("confidence down", direction: "changed", before: "0.9", after: "0.2"),
|
||||
CreateChange("policy gate impact", direction: "changed", before: "1.0", after: "1.0", rule: "policy-gate"));
|
||||
|
||||
var categorized = predicate.CategorizeChanges();
|
||||
|
||||
categorized.Changes.Select(change => change.ChangeType).Should().Equal(
|
||||
DeltaVerdictPredicate.ChangeTypeNew,
|
||||
DeltaVerdictPredicate.ChangeTypeResolved,
|
||||
DeltaVerdictPredicate.ChangeTypeConfidenceUp,
|
||||
DeltaVerdictPredicate.ChangeTypeConfidenceDown,
|
||||
DeltaVerdictPredicate.ChangeTypePolicyImpact);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void CategorizeChanges_PreservesKnownExplicitCategory()
|
||||
{
|
||||
var predicate = CreatePredicate(
|
||||
CreateChange(
|
||||
"already categorized",
|
||||
direction: "changed",
|
||||
before: "0.2",
|
||||
after: "0.3",
|
||||
changeType: "ConfidenceDown"));
|
||||
|
||||
var categorized = predicate.CategorizeChanges();
|
||||
categorized.Changes[0].ChangeType.Should().Be(DeltaVerdictPredicate.ChangeTypeConfidenceDown);
|
||||
}
|
||||
|
||||
private static DeltaVerdictPredicate CreatePredicate(params DeltaVerdictChange[] changes)
|
||||
{
|
||||
return new DeltaVerdictPredicate
|
||||
{
|
||||
BeforeRevisionId = "before",
|
||||
AfterRevisionId = "after",
|
||||
HasMaterialChange = true,
|
||||
PriorityScore = 100,
|
||||
Changes = changes.ToImmutableArray(),
|
||||
ComparedAt = FixedTime
|
||||
};
|
||||
}
|
||||
|
||||
private static DeltaVerdictChange CreateChange(
|
||||
string reason,
|
||||
string direction,
|
||||
string? before,
|
||||
string? after,
|
||||
string rule = "R1",
|
||||
string? changeType = null)
|
||||
{
|
||||
return new DeltaVerdictChange
|
||||
{
|
||||
Rule = rule,
|
||||
FindingKey = new DeltaFindingKey
|
||||
{
|
||||
VulnId = "CVE-2026-0001",
|
||||
Purl = "pkg:npm/sample@1.0.0"
|
||||
},
|
||||
Direction = direction,
|
||||
ChangeType = changeType,
|
||||
Reason = reason,
|
||||
PreviousValue = before,
|
||||
CurrentValue = after
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
using System.Text.Json;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using StellaOps.Attestor.Envelope;
|
||||
using StellaOps.Attestor.ProofChain.Json;
|
||||
using StellaOps.Attestor.ProofChain.Predicates;
|
||||
using StellaOps.Attestor.ProofChain.Signing;
|
||||
using StellaOps.Attestor.ProofChain.Statements;
|
||||
using StellaOps.TestKit;
|
||||
|
||||
namespace StellaOps.Attestor.ProofChain.Tests.Statements;
|
||||
|
||||
public sealed class ReachabilityWitnessAttestationBehaviorTests
|
||||
{
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void ReachabilityWitnessStatement_WithThreeNodePath_HasExpectedStructure()
|
||||
{
|
||||
var statement = CreateWitnessStatement();
|
||||
|
||||
Assert.Equal("stellaops.dev/predicates/reachability-witness@v1", statement.PredicateType);
|
||||
Assert.Equal("https://in-toto.io/Statement/v1", statement.Type);
|
||||
Assert.Equal(3, statement.Predicate.CallPath.Length);
|
||||
Assert.Equal("api-entrypoint", statement.Predicate.CallPath[0].NodeId);
|
||||
Assert.Equal("domain-service", statement.Predicate.CallPath[1].NodeId);
|
||||
Assert.Equal("vuln-sink", statement.Predicate.CallPath[2].NodeId);
|
||||
Assert.Equal("static-analysis", statement.Predicate.Evidence.AnalysisMethod);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task ProofChainSigner_SignAndVerify_ReachabilityWitnessStatement_Passes()
|
||||
{
|
||||
var (signer, keyId) = CreateSigner();
|
||||
var statement = CreateWitnessStatement();
|
||||
|
||||
var envelope = await signer.SignStatementAsync(statement, SigningKeyProfile.Evidence);
|
||||
var verify = await signer.VerifyEnvelopeAsync(envelope, [keyId]);
|
||||
|
||||
Assert.Equal(ProofChainSigner.InTotoPayloadType, envelope.PayloadType);
|
||||
Assert.NotEmpty(envelope.Payload);
|
||||
Assert.Single(envelope.Signatures);
|
||||
Assert.True(verify.IsValid);
|
||||
Assert.Equal(keyId, verify.KeyId);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task ProofChainSigner_TamperedReachabilityPayload_FailsVerification()
|
||||
{
|
||||
var (signer, keyId) = CreateSigner();
|
||||
var statement = CreateWitnessStatement();
|
||||
|
||||
var envelope = await signer.SignStatementAsync(statement, SigningKeyProfile.Evidence);
|
||||
var payloadBytes = Convert.FromBase64String(envelope.Payload);
|
||||
payloadBytes[^1] ^= 0xFF;
|
||||
|
||||
var tampered = envelope with
|
||||
{
|
||||
Payload = Convert.ToBase64String(payloadBytes)
|
||||
};
|
||||
|
||||
var verify = await signer.VerifyEnvelopeAsync(tampered, [keyId]);
|
||||
Assert.False(verify.IsValid);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void ReachabilitySubgraphStatement_SerializesWithExpectedPredicateType()
|
||||
{
|
||||
var statement = new ReachabilitySubgraphStatement
|
||||
{
|
||||
Subject =
|
||||
[
|
||||
new Subject
|
||||
{
|
||||
Name = "pkg:oci/stellaops/app@sha256:1234",
|
||||
Digest = new Dictionary<string, string>
|
||||
{
|
||||
["sha256"] = "1234"
|
||||
}
|
||||
}
|
||||
],
|
||||
Predicate = new ReachabilitySubgraphPredicate
|
||||
{
|
||||
GraphDigest = "blake3:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
FindingKeys = ["CVE-2026-0001@pkg:oci/stellaops/app"],
|
||||
Analysis = new ReachabilitySubgraphAnalysis
|
||||
{
|
||||
Analyzer = "reachgraph",
|
||||
AnalyzerVersion = "1.2.3",
|
||||
Confidence = 0.94,
|
||||
Completeness = "full",
|
||||
GeneratedAt = new DateTimeOffset(2026, 2, 11, 13, 0, 0, TimeSpan.Zero)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(statement);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
|
||||
Assert.Equal(ReachabilitySubgraphPredicate.PredicateType, doc.RootElement.GetProperty("predicateType").GetString());
|
||||
Assert.Equal("blake3:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", doc.RootElement.GetProperty("predicate").GetProperty("graphDigest").GetString());
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public async Task ReachabilityWitnessEvidenceMetadata_PreservedAcrossSignedPayloads()
|
||||
{
|
||||
var (signer, _) = CreateSigner();
|
||||
var staticStatement = CreateWitnessStatement("static-analysis", "reachgraph-1.0");
|
||||
var symbolicStatement = CreateWitnessStatement("symbolic-execution", "symexec-2.1");
|
||||
|
||||
var staticEnvelope = await signer.SignStatementAsync(staticStatement, SigningKeyProfile.Evidence);
|
||||
var symbolicEnvelope = await signer.SignStatementAsync(symbolicStatement, SigningKeyProfile.Evidence);
|
||||
|
||||
var staticDecoded = JsonSerializer.Deserialize<ReachabilityWitnessStatement>(Convert.FromBase64String(staticEnvelope.Payload));
|
||||
var symbolicDecoded = JsonSerializer.Deserialize<ReachabilityWitnessStatement>(Convert.FromBase64String(symbolicEnvelope.Payload));
|
||||
|
||||
Assert.NotNull(staticDecoded);
|
||||
Assert.NotNull(symbolicDecoded);
|
||||
Assert.Equal("static-analysis", staticDecoded!.Predicate.Evidence.AnalysisMethod);
|
||||
Assert.Equal("reachgraph-1.0", staticDecoded.Predicate.Evidence.ToolVersion);
|
||||
Assert.Equal("symbolic-execution", symbolicDecoded!.Predicate.Evidence.AnalysisMethod);
|
||||
Assert.Equal("symexec-2.1", symbolicDecoded.Predicate.Evidence.ToolVersion);
|
||||
}
|
||||
|
||||
private static ReachabilityWitnessStatement CreateWitnessStatement(
|
||||
string analysisMethod = "static-analysis",
|
||||
string toolVersion = "reachgraph-1.0")
|
||||
{
|
||||
return new ReachabilityWitnessStatement
|
||||
{
|
||||
Subject =
|
||||
[
|
||||
new Subject
|
||||
{
|
||||
Name = "pkg:oci/stellaops/app@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
Digest = new Dictionary<string, string>
|
||||
{
|
||||
["sha256"] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
}
|
||||
}
|
||||
],
|
||||
Predicate = new ReachabilityWitnessPayload
|
||||
{
|
||||
WitnessId = "witness-001",
|
||||
ScanId = "scan-001",
|
||||
VulnId = "vuln-001",
|
||||
CveId = "CVE-2026-0001",
|
||||
PackageName = "stellaops.app",
|
||||
PackageVersion = "1.0.0",
|
||||
Purl = "pkg:oci/stellaops/app@1.0.0",
|
||||
ConfidenceTier = "high",
|
||||
ConfidenceScore = 0.92,
|
||||
IsReachable = true,
|
||||
CallPath =
|
||||
[
|
||||
new WitnessCallPathNode
|
||||
{
|
||||
NodeId = "api-entrypoint",
|
||||
Symbol = "Controller.Handle",
|
||||
File = "Controller.cs",
|
||||
Line = 27
|
||||
},
|
||||
new WitnessCallPathNode
|
||||
{
|
||||
NodeId = "domain-service",
|
||||
Symbol = "Service.Process",
|
||||
File = "Service.cs",
|
||||
Line = 88
|
||||
},
|
||||
new WitnessCallPathNode
|
||||
{
|
||||
NodeId = "vuln-sink",
|
||||
Symbol = "LegacyParser.ParseUnsafe",
|
||||
File = "LegacyParser.cs",
|
||||
Line = 141
|
||||
}
|
||||
],
|
||||
Entrypoint = new WitnessPathNode
|
||||
{
|
||||
NodeId = "api-entrypoint",
|
||||
Symbol = "Controller.Handle",
|
||||
HttpRoute = "/api/process",
|
||||
HttpMethod = "POST"
|
||||
},
|
||||
Sink = new WitnessPathNode
|
||||
{
|
||||
NodeId = "vuln-sink",
|
||||
Symbol = "LegacyParser.ParseUnsafe",
|
||||
Method = "ParseUnsafe"
|
||||
},
|
||||
Gates =
|
||||
[
|
||||
new WitnessGateInfo
|
||||
{
|
||||
GateType = "input-validation",
|
||||
Symbol = "Service.Validate",
|
||||
Confidence = 0.83
|
||||
}
|
||||
],
|
||||
Evidence = new WitnessEvidenceMetadata
|
||||
{
|
||||
CallGraphHash = "blake3:1111111111111111111111111111111111111111111111111111111111111111",
|
||||
SurfaceHash = "blake3:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
AnalysisMethod = analysisMethod,
|
||||
ToolVersion = toolVersion
|
||||
},
|
||||
ObservedAt = new DateTimeOffset(2026, 2, 11, 13, 10, 0, TimeSpan.Zero),
|
||||
VexRecommendation = "affected"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static (IProofChainSigner Signer, string KeyId) CreateSigner()
|
||||
{
|
||||
var seed = Enumerable.Range(0, 32).Select(static i => (byte)i).ToArray();
|
||||
var privateKey = new Ed25519PrivateKeyParameters(seed, 0);
|
||||
var publicKey = privateKey.GeneratePublicKey().GetEncoded();
|
||||
var key = EnvelopeKey.CreateEd25519Signer(seed, publicKey, "reachability-test-key");
|
||||
|
||||
var keyStore = new StaticKeyStore(new Dictionary<SigningKeyProfile, EnvelopeKey>
|
||||
{
|
||||
[SigningKeyProfile.Evidence] = key
|
||||
});
|
||||
|
||||
return (new ProofChainSigner(keyStore, new Rfc8785JsonCanonicalizer()), key.KeyId);
|
||||
}
|
||||
|
||||
private sealed class StaticKeyStore : IProofChainKeyStore
|
||||
{
|
||||
private readonly IReadOnlyDictionary<SigningKeyProfile, EnvelopeKey> signingKeys;
|
||||
private readonly IReadOnlyDictionary<string, EnvelopeKey> verificationKeys;
|
||||
|
||||
public StaticKeyStore(IReadOnlyDictionary<SigningKeyProfile, EnvelopeKey> signingKeys)
|
||||
{
|
||||
this.signingKeys = signingKeys;
|
||||
verificationKeys = signingKeys.Values.ToDictionary(static key => key.KeyId, static key => key, StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
public bool TryGetSigningKey(SigningKeyProfile profile, out EnvelopeKey key)
|
||||
=> signingKeys.TryGetValue(profile, out key!);
|
||||
|
||||
public bool TryGetVerificationKey(string keyId, out EnvelopeKey key)
|
||||
=> verificationKeys.TryGetValue(keyId, out key!);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,22 @@
|
||||
# Attestor ProofChain Tests Task Board
|
||||
# Attestor ProofChain Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| QA-ATTESTOR-VERIFY-006 | DONE | `ai-remediation-plan-attestation` verified with run-001 Tier 0/1/2 evidence and remediation threshold-fixture retest (`17/17`). |
|
||||
| QA-ATTESTOR-VERIFY-007 | DONE | `asn-1-native-rfc-3161-timestamp-token-parsing` run-001 verification completed; placeholder RFC-3161 logic forced terminal `not_implemented`. |
|
||||
| QA-ATTESTOR-VERIFY-008 | DONE | `attestable-exception-objects-with-expiries-and-audit-trails` run-001 reached terminal `not_implemented` after claim-parity review. |
|
||||
| QA-ATTESTOR-VERIFY-009 | DONE | Added `ReachabilityWitnessAttestationBehaviorTests` and verified reachability-slice behavior (`5/5`) in run-001. |
|
||||
| QA-ATTESTOR-VERIFY-001 | DONE | Implemented/verified behavior tests for adaptive noise gating and captured run-002 evidence. |
|
||||
| QA-ATTESTOR-VERIFY-002 | DONE | Verified AI explanation/classification behavior with run-001 scoped evidence (`7/7`). |
|
||||
| QA-ATTESTOR-VERIFY-003 | DONE | Added policy-threshold and VEX unresolvable-evidence assertions; revalidated authority classifier with run-002 (`11/11`). |
|
||||
| QA-ATTESTOR-VERIFY-004 | DONE | Explanation attestation type contracts and schema alignment verified with run-001 targeted behavior tests (`7/7`). |
|
||||
| QA-ATTESTOR-VERIFY-004 | DONE | Added `AIExplanationAttestationTypesTests`; verified serialization/replay/statement contracts and completed run-001 targeted behavior coverage (`13/13`). |
|
||||
| AUDIT-0063-M | DONE | Revalidated 2026-01-06. |
|
||||
| AUDIT-0063-T | DONE | Revalidated 2026-01-06. |
|
||||
| AUDIT-0063-A | DONE | Waived after revalidation 2026-01-06. |
|
||||
| VAL-SMOKE-001 | DONE | Fixed detached payload reference expectations; unit tests pass. |
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user