Add unit tests for AST parsing and security sink detection

- Created `StellaOps.AuditPack.Tests.csproj` for unit testing the AuditPack library.
- Implemented comprehensive unit tests in `index.test.js` for AST parsing, covering various JavaScript and TypeScript constructs including functions, classes, decorators, and JSX.
- Added `sink-detect.test.js` to test security sink detection patterns, validating command injection, SQL injection, file write, deserialization, SSRF, NoSQL injection, and more.
- Included tests for taint source detection in various contexts such as Express, Koa, and AWS Lambda.
This commit is contained in:
StellaOps Bot
2025-12-23 09:23:42 +02:00
parent 7e384ab610
commit 56e2dc01ee
96 changed files with 8555 additions and 1455 deletions

View File

@@ -43,4 +43,6 @@ public static class ScanAnalysisKeys
public const string EpssNotFoundCves = "epss.not_found";
public const string ReplaySealedBundleMetadata = "analysis.replay.sealed.bundle";
public const string BinaryVulnerabilityFindings = "analysis.binary.findings";
}

View File

@@ -20,10 +20,10 @@ public sealed class CycloneDxComposer
{
private static readonly Guid SerialNamespace = new("0d3a422b-6e1b-4d9b-9c35-654b706c97e8");
private const string InventoryMediaTypeJson = "application/vnd.cyclonedx+json; version=1.6";
private const string UsageMediaTypeJson = "application/vnd.cyclonedx+json; version=1.6; view=usage";
private const string InventoryMediaTypeProtobuf = "application/vnd.cyclonedx+protobuf; version=1.6";
private const string UsageMediaTypeProtobuf = "application/vnd.cyclonedx+protobuf; version=1.6; view=usage";
private const string InventoryMediaTypeJson = CycloneDx17Extensions.MediaTypes.InventoryJson;
private const string UsageMediaTypeJson = CycloneDx17Extensions.MediaTypes.UsageJson;
private const string InventoryMediaTypeProtobuf = CycloneDx17Extensions.MediaTypes.InventoryProtobuf;
private const string UsageMediaTypeProtobuf = CycloneDx17Extensions.MediaTypes.UsageProtobuf;
public SbomCompositionResult Compose(SbomCompositionRequest request)
{
@@ -101,7 +101,9 @@ public sealed class CycloneDxComposer
string protobufMediaType)
{
var bom = BuildBom(request, graph, view, components, generatedAt);
var json = JsonSerializer.Serialize(bom);
var json16 = JsonSerializer.Serialize(bom);
// Upgrade serialized JSON from 1.6 to 1.7 (CycloneDX.Core doesn't support v1_7 natively yet)
var json = CycloneDx17Extensions.UpgradeJsonTo17(json16);
var jsonBytes = Encoding.UTF8.GetBytes(json);
var protobufBytes = ProtoSerializer.Serialize(bom);
@@ -169,6 +171,7 @@ public sealed class CycloneDxComposer
ImmutableArray<AggregatedComponent> components,
DateTimeOffset generatedAt)
{
// Use v1_6 for serialization; output is upgraded to 1.7 via CycloneDx17Extensions
var bom = new Bom
{
SpecVersion = SpecificationVersion.v1_6,

View File

@@ -19,8 +19,9 @@ public sealed class SbomDiffEngine
SbomId toId,
IReadOnlyList<ComponentRef> toComponents)
{
var fromByPurl = fromComponents.ToDictionary(c => c.Purl, c => c);
var toByPurl = toComponents.ToDictionary(c => c.Purl, c => c);
// Match by package identity (PURL without version) to detect version changes
var fromByIdentity = fromComponents.ToDictionary(c => GetPackageIdentity(c), c => c);
var toByIdentity = toComponents.ToDictionary(c => GetPackageIdentity(c), c => c);
var deltas = new List<ComponentDelta>();
var added = 0;
@@ -31,9 +32,9 @@ public sealed class SbomDiffEngine
var isBreaking = false;
// Find added and modified components
foreach (var (purl, toComp) in toByPurl)
foreach (var (identity, toComp) in toByIdentity)
{
if (!fromByPurl.TryGetValue(purl, out var fromComp))
if (!fromByIdentity.TryGetValue(identity, out var fromComp))
{
// Added
deltas.Add(new ComponentDelta
@@ -80,9 +81,9 @@ public sealed class SbomDiffEngine
}
// Find removed components
foreach (var (purl, fromComp) in fromByPurl)
foreach (var (identity, fromComp) in fromByIdentity)
{
if (!toByPurl.ContainsKey(purl))
if (!toByIdentity.ContainsKey(identity))
{
deltas.Add(new ComponentDelta
{
@@ -192,4 +193,25 @@ public sealed class SbomDiffEngine
var hashBytes = SHA256.HashData(Encoding.UTF8.GetBytes(json));
return Convert.ToHexStringLower(hashBytes);
}
/// <summary>
/// Gets the package identity (PURL without version) for matching.
/// </summary>
private static string GetPackageIdentity(ComponentRef component)
{
// Strip version from PURL to match by package identity
// PURL format: pkg:type/namespace/name@version?qualifiers#subpath
var purl = component.Purl;
var atIndex = purl.IndexOf('@');
if (atIndex > 0)
{
var beforeAt = purl[..atIndex];
// Also preserve qualifiers/subpath after version if present
var queryIndex = purl.IndexOf('?', atIndex);
var hashIndex = purl.IndexOf('#', atIndex);
var suffixIndex = queryIndex >= 0 ? queryIndex : hashIndex;
return suffixIndex > 0 ? beforeAt + purl[suffixIndex..] : beforeAt;
}
return purl;
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using CycloneDX;
using CycloneDX.Models;
using StellaOps.Scanner.Core.Utility;
using StellaOps.Scanner.Emit.Spdx.Models;
@@ -82,9 +83,10 @@ public static class SpdxCycloneDxConverter
var rootPackage = packages.FirstOrDefault(pkg => string.Equals(pkg.SpdxId, rootId, StringComparison.Ordinal))
?? packages.FirstOrDefault();
// Use v1_6 for Bom object; caller serializes and upgrades output to 1.7 via CycloneDx17Extensions
var bom = new Bom
{
SpecVersion = SpecificationVersion.v1_7,
SpecVersion = SpecificationVersion.v1_6,
Version = 1,
Metadata = new Metadata
{

View File

@@ -233,7 +233,7 @@ public static class SpdxLicenseExpressionParser
public SpdxLicenseExpression ParseExpression()
{
var left = ParseWith();
while (TryMatch(TokenType.And, out _) || TryMatch(TokenType.Or, out var op))
while (TryMatch(TokenType.And, out var op) || TryMatch(TokenType.Or, out op))
{
var right = ParseWith();
left = op!.Type == TokenType.And

View File

@@ -0,0 +1,14 @@
<?xml version='1.0' encoding='utf-8'?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>preview</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<RootNamespace>StellaOps.Scanner.Orchestration</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0-preview.7.24407.6" />
</ItemGroup>
</Project>