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

@@ -1,5 +1,4 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace StellaOps.Provenance.Attestation;
@@ -9,13 +8,49 @@ public sealed record PromotionPredicate(
string VexDigest,
string PromotionId,
string? RekorEntry = null,
IReadOnlyDictionary<string,string>? Metadata = null);
IReadOnlyDictionary<string, string>? Metadata = null);
public sealed record PromotionAttestation(
PromotionPredicate Predicate,
byte[] Payload,
SignResult Signature);
public static class PromotionAttestationBuilder
{
public const string PredicateType = "stella.ops/promotion@v1";
public const string ContentType = "application/vnd.stella.promotion+json";
public static byte[] CreateCanonicalJson(PromotionPredicate predicate)
{
if (predicate is null) throw new ArgumentNullException(nameof(predicate));
return CanonicalJson.SerializeToUtf8Bytes(predicate);
}
public static async Task<PromotionAttestation> BuildAsync(
PromotionPredicate predicate,
ISigner signer,
IReadOnlyDictionary<string, string>? claims = null,
CancellationToken cancellationToken = default)
{
if (predicate is null) throw new ArgumentNullException(nameof(predicate));
if (signer is null) throw new ArgumentNullException(nameof(signer));
var payload = CreateCanonicalJson(predicate);
// ensure predicate type claim is always present
var mergedClaims = claims is null
? new Dictionary<string, string>(StringComparer.Ordinal)
: new Dictionary<string, string>(claims, StringComparer.Ordinal);
mergedClaims["predicateType"] = PredicateType;
var request = new SignRequest(
Payload: payload,
ContentType: ContentType,
Claims: mergedClaims,
RequiredClaims: new[] { "predicateType" });
var signature = await signer.SignAsync(request, cancellationToken).ConfigureAwait(false);
return new PromotionAttestation(predicate, payload, signature);
}
}