Add SBOM, symbols, traces, and VEX files for CVE-2022-21661 SQLi case
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Created CycloneDX and SPDX SBOM files for both reachable and unreachable images. - Added symbols.json detailing function entry and sink points in the WordPress code. - Included runtime traces for function calls in both reachable and unreachable scenarios. - Developed OpenVEX files indicating vulnerability status and justification for both cases. - Updated README for evaluator harness to guide integration with scanner output.
This commit is contained in:
@@ -126,7 +126,7 @@ public sealed class EnsureAdvisoryCanonicalKeyBackfillMigration : IMongoMigratio
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return value.IsString ? value.AsString : value.ToString();
|
||||
return value.IsString ? value.AsString : value.ToString() ?? string.Empty;
|
||||
}
|
||||
|
||||
private static string? GetOptionalString(BsonDocument document, string name)
|
||||
@@ -150,7 +150,7 @@ public sealed class EnsureAdvisoryCanonicalKeyBackfillMigration : IMongoMigratio
|
||||
BsonInt32 i => i.AsInt32.ToString(CultureInfo.InvariantCulture),
|
||||
BsonInt64 l => l.AsInt64.ToString(CultureInfo.InvariantCulture),
|
||||
BsonDouble d => d.AsDouble.ToString(CultureInfo.InvariantCulture),
|
||||
_ => value.ToString()
|
||||
_ => value?.ToString() ?? string.Empty
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ public sealed class EnsureAdvisoryObservationsRawLinksetMigration : IMongoMigrat
|
||||
content,
|
||||
identifiers,
|
||||
linkset,
|
||||
supersedes.IsBsonNull ? null : supersedes.AsString);
|
||||
Supersedes: supersedes.IsBsonNull ? null : supersedes.AsString);
|
||||
}
|
||||
|
||||
private static RawSourceMetadata MapSource(BsonDocument source)
|
||||
|
||||
@@ -90,12 +90,27 @@ public sealed class MongoBootstrapper
|
||||
_logger.LogInformation("Mongo bootstrapper completed");
|
||||
}
|
||||
|
||||
private async Task<HashSet<string>> ListCollectionsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var cursor = await _database.ListCollectionNamesAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
var list = await cursor.ToListAsync(cancellationToken).ConfigureAwait(false);
|
||||
return new HashSet<string>(list, StringComparer.Ordinal);
|
||||
}
|
||||
private async Task<HashSet<string>> ListCollectionsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var cursor = await _database.ListCollectionNamesAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
var list = await cursor.ToListAsync(cancellationToken).ConfigureAwait(false);
|
||||
return new HashSet<string>(list, StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
private async Task<bool> CollectionIsViewAsync(string collectionName, CancellationToken cancellationToken)
|
||||
{
|
||||
var filter = Builders<BsonDocument>.Filter.Eq("name", collectionName);
|
||||
var options = new ListCollectionsOptions { Filter = filter };
|
||||
using var cursor = await _database.ListCollectionsAsync(options, cancellationToken).ConfigureAwait(false);
|
||||
var collections = await cursor.ToListAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (collections.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var typeValue = collections[0].GetValue("type", BsonString.Empty).AsString;
|
||||
return string.Equals(typeValue, "view", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private Task EnsureLocksIndexesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -129,9 +144,15 @@ public sealed class MongoBootstrapper
|
||||
return collection.Indexes.CreateManyAsync(indexes, cancellationToken);
|
||||
}
|
||||
|
||||
private Task EnsureAdvisoryIndexesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var collection = _database.GetCollection<BsonDocument>(MongoStorageDefaults.Collections.Advisory);
|
||||
private async Task EnsureAdvisoryIndexesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (await CollectionIsViewAsync(MongoStorageDefaults.Collections.Advisory, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
_logger.LogDebug("Skipping advisory index creation because {Collection} is a view", MongoStorageDefaults.Collections.Advisory);
|
||||
return;
|
||||
}
|
||||
|
||||
var collection = _database.GetCollection<BsonDocument>(MongoStorageDefaults.Collections.Advisory);
|
||||
var indexes = new List<CreateIndexModel<BsonDocument>>
|
||||
{
|
||||
new(
|
||||
@@ -159,7 +180,7 @@ public sealed class MongoBootstrapper
|
||||
new CreateIndexOptions { Name = "advisory_normalizedVersions_value", Sparse = true }));
|
||||
}
|
||||
|
||||
return collection.Indexes.CreateManyAsync(indexes, cancellationToken);
|
||||
await collection.Indexes.CreateManyAsync(indexes, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private Task EnsureDocumentsIndexesAsync(CancellationToken cancellationToken)
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Bson.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Concelier.Core.Raw;
|
||||
using StellaOps.Concelier.RawModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.IO;
|
||||
using MongoDB.Driver;
|
||||
using StellaOps.Concelier.Core.Raw;
|
||||
using StellaOps.Concelier.RawModels;
|
||||
using StellaOps.Ingestion.Telemetry;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Mongo.Raw;
|
||||
|
||||
@@ -34,76 +36,115 @@ internal sealed class MongoAdvisoryRawRepository : IAdvisoryRawRepository
|
||||
_collection = database.GetCollection<BsonDocument>(MongoStorageDefaults.Collections.AdvisoryRaw);
|
||||
}
|
||||
|
||||
public async Task<AdvisoryRawUpsertResult> UpsertAsync(AdvisoryRawDocument document, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
|
||||
var tenant = document.Tenant;
|
||||
var vendor = document.Source.Vendor;
|
||||
var upstreamId = document.Upstream.UpstreamId;
|
||||
var contentHash = document.Upstream.ContentHash;
|
||||
|
||||
var baseFilter = Builders<BsonDocument>.Filter.Eq("tenant", tenant) &
|
||||
Builders<BsonDocument>.Filter.Eq("source.vendor", vendor) &
|
||||
Builders<BsonDocument>.Filter.Eq("upstream.upstream_id", upstreamId);
|
||||
public async Task<AdvisoryRawUpsertResult> UpsertAsync(AdvisoryRawDocument document, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
|
||||
var tenant = document.Tenant;
|
||||
var vendor = document.Source.Vendor;
|
||||
var upstreamId = document.Upstream.UpstreamId;
|
||||
var contentHash = document.Upstream.ContentHash;
|
||||
var sourceUri = ResolveProvenanceUri(document);
|
||||
|
||||
var baseFilter = Builders<BsonDocument>.Filter.Eq("tenant", tenant) &
|
||||
Builders<BsonDocument>.Filter.Eq("source.vendor", vendor) &
|
||||
Builders<BsonDocument>.Filter.Eq("upstream.upstream_id", upstreamId);
|
||||
|
||||
var duplicateFilter = baseFilter &
|
||||
Builders<BsonDocument>.Filter.Eq("upstream.content_hash", contentHash);
|
||||
|
||||
var duplicate = await _collection
|
||||
.Find(duplicateFilter)
|
||||
.Limit(1)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (duplicate is not null)
|
||||
{
|
||||
var existing = MapToRecord(duplicate);
|
||||
return new AdvisoryRawUpsertResult(false, existing);
|
||||
}
|
||||
|
||||
var previous = await _collection
|
||||
.Find(baseFilter)
|
||||
.Sort(Builders<BsonDocument>.Sort.Descending("ingested_at").Descending("_id"))
|
||||
.Limit(1)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var supersedesId = previous?["_id"]?.AsString;
|
||||
var recordDocument = CreateBsonDocument(document, supersedesId);
|
||||
|
||||
try
|
||||
{
|
||||
await _collection.InsertOneAsync(recordDocument, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (MongoWriteException ex) when (ex.WriteError?.Category == ServerErrorCategory.DuplicateKey)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
ex,
|
||||
"Duplicate key detected while inserting advisory_raw document tenant={Tenant} vendor={Vendor} upstream={Upstream} hash={Hash}",
|
||||
tenant,
|
||||
vendor,
|
||||
upstreamId,
|
||||
contentHash);
|
||||
|
||||
var existingDoc = await _collection
|
||||
.Find(duplicateFilter)
|
||||
.Limit(1)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (existingDoc is not null)
|
||||
{
|
||||
var existing = MapToRecord(existingDoc);
|
||||
return new AdvisoryRawUpsertResult(false, existing);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
var inserted = MapToRecord(recordDocument);
|
||||
return new AdvisoryRawUpsertResult(true, inserted);
|
||||
}
|
||||
using var fetchActivity = IngestionTelemetry.StartFetchActivity(tenant, vendor, upstreamId, contentHash, sourceUri);
|
||||
var fetchWatch = Stopwatch.StartNew();
|
||||
|
||||
var duplicate = await _collection
|
||||
.Find(duplicateFilter)
|
||||
.Limit(1)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (duplicate is not null)
|
||||
{
|
||||
fetchWatch.Stop();
|
||||
fetchActivity?.SetTag("result", "duplicate");
|
||||
fetchActivity?.SetStatus(ActivityStatusCode.Ok);
|
||||
IngestionTelemetry.RecordLatency(tenant, vendor, IngestionTelemetry.PhaseFetch, fetchWatch.Elapsed);
|
||||
|
||||
var existing = MapToRecord(duplicate);
|
||||
return new AdvisoryRawUpsertResult(false, existing);
|
||||
}
|
||||
|
||||
var previous = await _collection
|
||||
.Find(baseFilter)
|
||||
.Sort(Builders<BsonDocument>.Sort.Descending("ingested_at").Descending("_id"))
|
||||
.Limit(1)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
fetchWatch.Stop();
|
||||
fetchActivity?.SetTag("result", previous is null ? "new" : "supersede");
|
||||
fetchActivity?.SetStatus(ActivityStatusCode.Ok);
|
||||
IngestionTelemetry.RecordLatency(tenant, vendor, IngestionTelemetry.PhaseFetch, fetchWatch.Elapsed);
|
||||
|
||||
var supersedesId = previous?["_id"]?.AsString;
|
||||
var recordDocument = CreateBsonDocument(document, supersedesId);
|
||||
|
||||
var writeWatch = Stopwatch.StartNew();
|
||||
using var writeActivity = IngestionTelemetry.StartWriteActivity(tenant, vendor, upstreamId, contentHash, MongoStorageDefaults.Collections.AdvisoryRaw);
|
||||
|
||||
try
|
||||
{
|
||||
await _collection.InsertOneAsync(recordDocument, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
writeActivity?.SetTag("result", IngestionTelemetry.ResultOk);
|
||||
writeActivity?.SetStatus(ActivityStatusCode.Ok);
|
||||
}
|
||||
catch (MongoWriteException ex) when (ex.WriteError?.Category == ServerErrorCategory.DuplicateKey)
|
||||
{
|
||||
writeActivity?.SetTag("result", IngestionTelemetry.ResultNoop);
|
||||
writeActivity?.SetStatus(ActivityStatusCode.Error, "duplicate_key");
|
||||
|
||||
_logger.LogWarning(
|
||||
ex,
|
||||
"Duplicate key detected while inserting advisory_raw document tenant={Tenant} vendor={Vendor} upstream={Upstream} hash={Hash}",
|
||||
tenant,
|
||||
vendor,
|
||||
upstreamId,
|
||||
contentHash);
|
||||
|
||||
var existingDoc = await _collection
|
||||
.Find(duplicateFilter)
|
||||
.Limit(1)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (existingDoc is not null)
|
||||
{
|
||||
var existing = MapToRecord(existingDoc);
|
||||
return new AdvisoryRawUpsertResult(false, existing);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
writeWatch.Stop();
|
||||
IngestionTelemetry.RecordLatency(tenant, vendor, IngestionTelemetry.PhaseWrite, writeWatch.Elapsed);
|
||||
}
|
||||
|
||||
var inserted = MapToRecord(recordDocument);
|
||||
return new AdvisoryRawUpsertResult(true, inserted);
|
||||
}
|
||||
|
||||
private static string? ResolveProvenanceUri(AdvisoryRawDocument document)
|
||||
{
|
||||
if (document.Upstream?.Provenance is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return document.Upstream.Provenance.TryGetValue("uri", out var uri) && !string.IsNullOrWhiteSpace(uri)
|
||||
? uri
|
||||
: null;
|
||||
}
|
||||
|
||||
public async Task<AdvisoryRawRecord?> FindByIdAsync(string tenant, string id, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0-rc.2.25502.107" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Core\StellaOps.Concelier.Core.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Models\StellaOps.Concelier.Models.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Core\StellaOps.Concelier.Core.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Models\StellaOps.Concelier.Models.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Ingestion.Telemetry\StellaOps.Ingestion.Telemetry.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user