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:
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user