Add PHP Analyzer Plugin and Composer Lock Data Handling
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Implemented the PhpAnalyzerPlugin to analyze PHP projects. - Created ComposerLockData class to represent data from composer.lock files. - Developed ComposerLockReader to load and parse composer.lock files asynchronously. - Introduced ComposerPackage class to encapsulate package details. - Added PhpPackage class to represent PHP packages with metadata and evidence. - Implemented PhpPackageCollector to gather packages from ComposerLockData. - Created PhpLanguageAnalyzer to perform analysis and emit results. - Added capability signals for known PHP frameworks and CMS. - Developed unit tests for the PHP language analyzer and its components. - Included sample composer.lock and expected output for testing. - Updated project files for the new PHP analyzer library and tests.
This commit is contained in:
@@ -189,6 +189,7 @@ internal static class AdvisoryLinksetNormalization
|
||||
var reason = key switch
|
||||
{
|
||||
"severity" => "severity-mismatch",
|
||||
var k when k.StartsWith("cvss", StringComparison.OrdinalIgnoreCase) => "cvss-mismatch",
|
||||
"ranges" => "affected-range-divergence",
|
||||
"references" => "reference-clash",
|
||||
"aliases" => "alias-inconsistency",
|
||||
|
||||
@@ -4,6 +4,8 @@ using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using StellaOps.Concelier.Models;
|
||||
|
||||
#pragma warning disable CS8620 // nullability mismatches guarded by explicit filtering
|
||||
|
||||
namespace StellaOps.Concelier.Core.Linksets;
|
||||
|
||||
internal static class LinksetCorrelation
|
||||
@@ -109,19 +111,15 @@ internal static class LinksetCorrelation
|
||||
List<HashSet<string>> packageKeysPerInput = inputs
|
||||
.Select(i => i.Purls
|
||||
.Select(ExtractPackageKey)
|
||||
.Where(k => !string.IsNullOrEmpty(k))
|
||||
.Where(k => !string.IsNullOrWhiteSpace(k))
|
||||
.ToHashSet(StringComparer.Ordinal))
|
||||
.ToList();
|
||||
|
||||
var sharedPackages = packageKeysPerInput
|
||||
.Skip(1)
|
||||
.Aggregate(
|
||||
new HashSet<string>(packageKeysPerInput.First()!, StringComparer.Ordinal),
|
||||
(acc, next) =>
|
||||
{
|
||||
acc.IntersectWith(next!);
|
||||
return acc;
|
||||
});
|
||||
var sharedPackages = new HashSet<string>(packageKeysPerInput.FirstOrDefault() ?? new HashSet<string>(), StringComparer.Ordinal);
|
||||
foreach (var next in packageKeysPerInput.Skip(1))
|
||||
{
|
||||
sharedPackages.IntersectWith(next);
|
||||
}
|
||||
|
||||
if (sharedPackages.Count > 0)
|
||||
{
|
||||
@@ -140,12 +138,17 @@ internal static class LinksetCorrelation
|
||||
|
||||
private static IEnumerable<AdvisoryLinksetConflict> CollectRangeConflicts(
|
||||
IReadOnlyCollection<Input> inputs,
|
||||
HashSet<string> sharedPackages)
|
||||
HashSet<string?> sharedPackages)
|
||||
{
|
||||
var conflicts = new List<AdvisoryLinksetConflict>();
|
||||
|
||||
foreach (var package in sharedPackages)
|
||||
{
|
||||
if (package is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var values = inputs
|
||||
.SelectMany(i => i.Purls
|
||||
.Where(p => ExtractPackageKey(p) == package)
|
||||
@@ -169,6 +172,8 @@ internal static class LinksetCorrelation
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
#pragma warning restore CS8620
|
||||
|
||||
private static bool HasExactPurlOverlap(IReadOnlyCollection<Input> inputs)
|
||||
{
|
||||
var first = inputs.First().Purls.ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
@@ -28,4 +28,20 @@ public sealed class AdvisoryLinksetNormalizationConfidenceTests
|
||||
Assert.Equal("severity-mismatch", conflict.Reason);
|
||||
Assert.Contains("severity:mismatch", conflict.Values!);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromRawLinksetWithConfidence_EmitsCvssMismatchConflict()
|
||||
{
|
||||
var linkset = new RawLinkset
|
||||
{
|
||||
PackageUrls = ImmutableArray.Create("pkg:maven/com.acme/foo@2.0.0"),
|
||||
Notes = ImmutableDictionary.CreateRange(new[] { new KeyValuePair<string, string>("cvss_v3", "7.5/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H") })
|
||||
};
|
||||
|
||||
var (_, _, conflicts) = AdvisoryLinksetNormalization.FromRawLinksetWithConfidence(linkset);
|
||||
|
||||
var conflict = Assert.Single(conflicts);
|
||||
Assert.Equal("cvss-mismatch", conflict.Reason);
|
||||
Assert.Contains("cvss_v3:7.5/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", conflict.Values!);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ public sealed class AdvisoryObservationAggregationTests
|
||||
Assert.Contains(aggregate.Conflicts, c => c.Reason == "alias-inconsistency");
|
||||
Assert.Contains(aggregate.Conflicts, c => c.Reason == "affected-range-divergence");
|
||||
Assert.True(aggregate.Confidence is > 0.0 and < 1.0);
|
||||
Assert.All(aggregate.Conflicts, c => Assert.NotNull(c.SourceIds));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -33,8 +33,8 @@ public class AdvisoryObservationTransportWorkerTests
|
||||
"hash-1",
|
||||
DateTimeOffset.UtcNow,
|
||||
ReplayCursor: "cursor-1",
|
||||
supersedesId: null,
|
||||
traceId: "trace-1");
|
||||
SupersedesId: null,
|
||||
TraceId: "trace-1");
|
||||
|
||||
var outbox = new FakeOutbox(evt);
|
||||
var transport = new FakeTransport();
|
||||
|
||||
Reference in New Issue
Block a user