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:
@@ -68,6 +68,11 @@ internal sealed class AdmissionResponseBuilder
|
||||
metadata["cache"] = "hit";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(decision.SurfacePointer))
|
||||
{
|
||||
metadata["surfacePointer"] = decision.SurfacePointer!;
|
||||
}
|
||||
|
||||
var resolved = decision.ResolvedDigest ?? decision.OriginalImage;
|
||||
|
||||
images.Add(new AdmissionImageVerdict
|
||||
|
||||
@@ -2,10 +2,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Scanner.Surface.Env;
|
||||
using StellaOps.Zastava.Core.Contracts;
|
||||
using StellaOps.Zastava.Core.Diagnostics;
|
||||
using StellaOps.Zastava.Webhook.Backend;
|
||||
using StellaOps.Zastava.Webhook.Configuration;
|
||||
using StellaOps.Zastava.Webhook.Surface;
|
||||
|
||||
namespace StellaOps.Zastava.Webhook.Admission;
|
||||
|
||||
@@ -22,6 +24,8 @@ internal sealed class RuntimeAdmissionPolicyService : IRuntimeAdmissionPolicySer
|
||||
private readonly IOptionsMonitor<ZastavaWebhookOptions> options;
|
||||
private readonly IZastavaRuntimeMetrics runtimeMetrics;
|
||||
private readonly TimeProvider timeProvider;
|
||||
private readonly IWebhookSurfaceFsClient surfaceFsClient;
|
||||
private readonly SurfaceEnvironmentSettings surfaceSettings;
|
||||
private readonly ILogger<RuntimeAdmissionPolicyService> logger;
|
||||
|
||||
public RuntimeAdmissionPolicyService(
|
||||
@@ -31,6 +35,8 @@ internal sealed class RuntimeAdmissionPolicyService : IRuntimeAdmissionPolicySer
|
||||
IOptionsMonitor<ZastavaWebhookOptions> options,
|
||||
IZastavaRuntimeMetrics runtimeMetrics,
|
||||
TimeProvider timeProvider,
|
||||
IWebhookSurfaceFsClient surfaceFsClient,
|
||||
SurfaceEnvironmentSettings surfaceSettings,
|
||||
ILogger<RuntimeAdmissionPolicyService> logger)
|
||||
{
|
||||
this.policyClient = policyClient ?? throw new ArgumentNullException(nameof(policyClient));
|
||||
@@ -39,6 +45,8 @@ internal sealed class RuntimeAdmissionPolicyService : IRuntimeAdmissionPolicySer
|
||||
this.options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
this.runtimeMetrics = runtimeMetrics ?? throw new ArgumentNullException(nameof(runtimeMetrics));
|
||||
this.timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||
this.surfaceFsClient = surfaceFsClient ?? throw new ArgumentNullException(nameof(surfaceFsClient));
|
||||
this.surfaceSettings = surfaceSettings ?? throw new ArgumentNullException(nameof(surfaceSettings));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
@@ -67,9 +75,25 @@ internal sealed class RuntimeAdmissionPolicyService : IRuntimeAdmissionPolicySer
|
||||
var combinedResults = new Dictionary<string, RuntimePolicyImageResult>(StringComparer.Ordinal);
|
||||
var backendMisses = new List<string>();
|
||||
var fromCache = new HashSet<string>(StringComparer.Ordinal);
|
||||
var missingSurface = new HashSet<string>(StringComparer.Ordinal);
|
||||
var surfacePointers = new Dictionary<string, string?>(StringComparer.Ordinal);
|
||||
var enforceSurface = surfaceSettings.FeatureFlags.Count == 0
|
||||
|| surfaceSettings.FeatureFlags.Contains("admission", StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var digest in resolved)
|
||||
{
|
||||
if (enforceSurface)
|
||||
{
|
||||
var (found, pointer) = await surfaceFsClient.TryGetManifestAsync(digest.Digest, cancellationToken).ConfigureAwait(false);
|
||||
if (!found)
|
||||
{
|
||||
missingSurface.Add(digest.Digest);
|
||||
continue;
|
||||
}
|
||||
|
||||
surfacePointers[digest.Digest] = pointer;
|
||||
}
|
||||
|
||||
if (cache.TryGet(digest.Digest, out var cached))
|
||||
{
|
||||
combinedResults[digest.Digest] = cached;
|
||||
@@ -134,15 +158,16 @@ internal sealed class RuntimeAdmissionPolicyService : IRuntimeAdmissionPolicySer
|
||||
ResolvedDigest = resolution.ResolvedDigest,
|
||||
Verdict = allowed ? PolicyVerdict.Warn : PolicyVerdict.Error,
|
||||
Allowed = allowed,
|
||||
Policy = null,
|
||||
Reasons = reasons,
|
||||
FromCache = false,
|
||||
ResolutionFailed = false
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
decisions.Add(CreateResolutionFailureDecision(resolution));
|
||||
Policy = null,
|
||||
Reasons = reasons,
|
||||
FromCache = false,
|
||||
ResolutionFailed = false,
|
||||
SurfacePointer = surfacePointers.GetValueOrDefault(resolution.ResolvedDigest)
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
decisions.Add(CreateResolutionFailureDecision(resolution));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +191,25 @@ internal sealed class RuntimeAdmissionPolicyService : IRuntimeAdmissionPolicySer
|
||||
continue;
|
||||
}
|
||||
|
||||
if (missingSurface.Contains(resolution.ResolvedDigest))
|
||||
{
|
||||
var manifestDecision = new RuntimeAdmissionDecision
|
||||
{
|
||||
OriginalImage = resolution.Original,
|
||||
ResolvedDigest = resolution.ResolvedDigest,
|
||||
Verdict = PolicyVerdict.Error,
|
||||
Allowed = false,
|
||||
Policy = null,
|
||||
Reasons = new[] { "surface.manifest.missing" },
|
||||
FromCache = false,
|
||||
ResolutionFailed = false,
|
||||
SurfacePointer = null
|
||||
};
|
||||
RecordDecisionMetrics(false, false, false, RuntimeEventKind.ContainerStart);
|
||||
decisions.Add(manifestDecision);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!combinedResults.TryGetValue(resolution.ResolvedDigest, out var policyResult))
|
||||
{
|
||||
var synthetic = new RuntimeAdmissionDecision
|
||||
@@ -177,7 +221,8 @@ internal sealed class RuntimeAdmissionPolicyService : IRuntimeAdmissionPolicySer
|
||||
Policy = null,
|
||||
Reasons = new[] { "zastava.policy.result.missing" },
|
||||
FromCache = false,
|
||||
ResolutionFailed = false
|
||||
ResolutionFailed = false,
|
||||
SurfacePointer = surfacePointers.GetValueOrDefault(resolution.ResolvedDigest)
|
||||
};
|
||||
RecordDecisionMetrics(false, false, false, RuntimeEventKind.ContainerStart);
|
||||
decisions.Add(synthetic);
|
||||
@@ -197,7 +242,8 @@ internal sealed class RuntimeAdmissionPolicyService : IRuntimeAdmissionPolicySer
|
||||
Policy = policyResult,
|
||||
Reasons = reasons,
|
||||
FromCache = cached,
|
||||
ResolutionFailed = false
|
||||
ResolutionFailed = false,
|
||||
SurfacePointer = surfacePointers.GetValueOrDefault(resolution.ResolvedDigest)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -290,6 +336,7 @@ internal sealed record RuntimeAdmissionDecision
|
||||
public IReadOnlyList<string> Reasons { get; init; } = Array.Empty<string>();
|
||||
public bool FromCache { get; init; }
|
||||
public bool ResolutionFailed { get; init; }
|
||||
public string? SurfacePointer { get; init; }
|
||||
}
|
||||
|
||||
internal sealed record RuntimeAdmissionEvaluation
|
||||
|
||||
Reference in New Issue
Block a user