Add PHP Analyzer Plugin and Composer Lock Data Handling
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:
StellaOps Bot
2025-11-22 14:02:49 +02:00
parent a7f3c7869a
commit b6b9ffc050
158 changed files with 16272 additions and 809 deletions

View File

@@ -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",

View File

@@ -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);