feat: add security sink detection patterns for JavaScript/TypeScript

- Introduced `sink-detect.js` with various security sink detection patterns categorized by type (e.g., command injection, SQL injection, file operations).
- Implemented functions to build a lookup map for fast sink detection and to match sink calls against known patterns.
- Added `package-lock.json` for dependency management.
This commit is contained in:
StellaOps Bot
2025-12-22 23:21:21 +02:00
parent 3ba7157b00
commit 5146204f1b
529 changed files with 73579 additions and 5985 deletions

View File

@@ -327,7 +327,7 @@ public sealed class AlpineConnector : IFeedConnector
}
}
private static string[] NormalizeList(string[] values)
private static string[] NormalizeList(string[]? values)
{
if (values is null || values.Length == 0)
{

View File

@@ -14,13 +14,25 @@ public sealed class AlpineOptions
/// <summary>
/// Releases to fetch (for example: v3.18, v3.19, v3.20, edge).
/// Defaults to v3.18, v3.19, v3.20, edge if not configured.
/// </summary>
public string[] Releases { get; set; } = new[] { "v3.18", "v3.19", "v3.20", "edge" };
public string[]? Releases { get; set; }
/// <summary>
/// Repository names to fetch (for example: main, community).
/// Defaults to main, community if not configured.
/// </summary>
public string[] Repositories { get; set; } = new[] { "main", "community" };
public string[]? Repositories { get; set; }
/// <summary>
/// Default Alpine releases if none are configured.
/// </summary>
public static readonly string[] DefaultReleases = ["v3.18", "v3.19", "v3.20", "edge"];
/// <summary>
/// Default Alpine repositories if none are configured.
/// </summary>
public static readonly string[] DefaultRepositories = ["main", "community"];
/// <summary>
/// Cap on release+repo documents fetched in a single run.
@@ -64,12 +76,16 @@ public sealed class AlpineOptions
throw new InvalidOperationException("RequestDelay must be between 0 and 10 seconds.");
}
if (Releases is null || Releases.Length == 0 || Releases.All(static value => string.IsNullOrWhiteSpace(value)))
// Apply defaults for releases/repositories if not configured
Releases ??= DefaultReleases;
Repositories ??= DefaultRepositories;
if (Releases.Length == 0 || Releases.All(static value => string.IsNullOrWhiteSpace(value)))
{
throw new InvalidOperationException("At least one Alpine release must be configured.");
}
if (Repositories is null || Repositories.Length == 0 || Repositories.All(static value => string.IsNullOrWhiteSpace(value)))
if (Repositories.Length == 0 || Repositories.All(static value => string.IsNullOrWhiteSpace(value)))
{
throw new InvalidOperationException("At least one Alpine repository must be configured.");
}

View File

@@ -1,12 +1,14 @@
namespace StellaOps.Concelier.Merge.Comparers;
using System;
using System.Collections.Immutable;
using StellaOps.Concelier.Normalization.Distro;
using StellaOps.VersionComparison;
/// <summary>
/// Compares Alpine APK package versions using apk-tools ordering rules.
/// </summary>
public sealed class ApkVersionComparer : IComparer<ApkVersion>, IComparer<string>
public sealed class ApkVersionComparer : IVersionComparator, IComparer<ApkVersion>, IComparer<string>
{
public static ApkVersionComparer Instance { get; } = new();
@@ -14,6 +16,9 @@ public sealed class ApkVersionComparer : IComparer<ApkVersion>, IComparer<string
{
}
/// <inheritdoc />
public ComparatorType ComparatorType => ComparatorType.Apk;
public int Compare(string? x, string? y)
{
if (ReferenceEquals(x, y))
@@ -96,6 +101,101 @@ public sealed class ApkVersionComparer : IComparer<ApkVersion>, IComparer<string
return 0;
}
/// <inheritdoc />
public VersionComparisonResult CompareWithProof(string? left, string? right)
{
var proofLines = new List<string>();
if (left is null && right is null)
{
proofLines.Add("Both versions are null: equal");
return new VersionComparisonResult(0, [.. proofLines], ComparatorType.Apk);
}
if (left is null)
{
proofLines.Add("Left version is null: less than right");
return new VersionComparisonResult(-1, [.. proofLines], ComparatorType.Apk);
}
if (right is null)
{
proofLines.Add("Right version is null: left is greater");
return new VersionComparisonResult(1, [.. proofLines], ComparatorType.Apk);
}
var leftParsed = ApkVersion.TryParse(left, out var leftVer);
var rightParsed = ApkVersion.TryParse(right, out var rightVer);
if (!leftParsed || !rightParsed)
{
if (!leftParsed && !rightParsed)
{
var cmp = string.Compare(left, right, StringComparison.Ordinal);
proofLines.Add($"Both versions invalid, fallback to string comparison: {ResultString(cmp)}");
return new VersionComparisonResult(cmp, [.. proofLines], ComparatorType.Apk);
}
if (!leftParsed)
{
proofLines.Add("Left version invalid, right valid: left is less");
return new VersionComparisonResult(-1, [.. proofLines], ComparatorType.Apk);
}
proofLines.Add("Right version invalid, left valid: left is greater");
return new VersionComparisonResult(1, [.. proofLines], ComparatorType.Apk);
}
// Compare version string
var versionCmp = CompareVersionStringWithProof(leftVer!.Version, rightVer!.Version, "Version", proofLines);
if (versionCmp != 0)
{
return new VersionComparisonResult(versionCmp, [.. proofLines], ComparatorType.Apk);
}
// Compare pkgrel
var pkgRelCmp = leftVer.PkgRel.CompareTo(rightVer.PkgRel);
if (pkgRelCmp != 0)
{
proofLines.Add($"Package release: r{leftVer.PkgRel} {CompareSymbol(pkgRelCmp)} r{rightVer.PkgRel} ({ResultString(pkgRelCmp)})");
return new VersionComparisonResult(pkgRelCmp, [.. proofLines], ComparatorType.Apk);
}
proofLines.Add($"Package release: r{leftVer.PkgRel} == r{rightVer.PkgRel} (equal)");
// Compare explicit vs implicit pkgrel
if (!leftVer.HasExplicitPkgRel && rightVer.HasExplicitPkgRel)
{
proofLines.Add("Left has implicit -r0, right has explicit -r0: left is older");
return new VersionComparisonResult(-1, [.. proofLines], ComparatorType.Apk);
}
if (leftVer.HasExplicitPkgRel && !rightVer.HasExplicitPkgRel)
{
proofLines.Add("Left has explicit -r0, right has implicit -r0: left is newer");
return new VersionComparisonResult(1, [.. proofLines], ComparatorType.Apk);
}
return new VersionComparisonResult(0, [.. proofLines], ComparatorType.Apk);
}
private static int CompareVersionStringWithProof(string left, string right, string segmentName, List<string> proofLines)
{
var cmp = CompareVersionString(left, right);
if (cmp == 0)
{
proofLines.Add($"{segmentName}: {left} == {right} (equal)");
}
else
{
proofLines.Add($"{segmentName}: {left} {CompareSymbol(cmp)} {right} ({ResultString(cmp)})");
}
return cmp;
}
private static string CompareSymbol(int cmp) => cmp < 0 ? "<" : cmp > 0 ? ">" : "==";
private static string ResultString(int cmp) => cmp < 0 ? "left is older" : cmp > 0 ? "left is newer" : "equal";
private static int CompareVersionString(string left, string right)
{
var leftIndex = 0;

View File

@@ -1,10 +1,17 @@
namespace StellaOps.Concelier.Merge.Comparers;
using StellaOps.VersionComparison;
/// <summary>
/// Provides version comparison with optional proof output.
/// </summary>
public interface IVersionComparator
{
/// <summary>
/// The type of comparator (for UI display and evidence recording).
/// </summary>
ComparatorType ComparatorType { get; }
/// <summary>
/// Compares two version strings.
/// </summary>

View File

@@ -1,10 +1,37 @@
namespace StellaOps.Concelier.Merge.Comparers;
using System.Collections.Immutable;
using StellaOps.VersionComparison;
/// <summary>
/// Result of a version comparison with explainability proof lines.
/// </summary>
/// <param name="Comparison">Negative if left &lt; right, zero if equal, positive if left &gt; right.</param>
/// <param name="ProofLines">Human-readable explanation of comparison steps.</param>
/// <param name="Comparator">The comparator type used.</param>
public sealed record VersionComparisonResult(
int Comparison,
ImmutableArray<string> ProofLines);
ImmutableArray<string> ProofLines,
ComparatorType Comparator)
{
/// <summary>
/// True if the left version is less than the right version.
/// </summary>
public bool IsLessThan => Comparison < 0;
/// <summary>
/// True if the left version equals the right version.
/// </summary>
public bool IsEqual => Comparison == 0;
/// <summary>
/// True if the left version is greater than the right version.
/// </summary>
public bool IsGreaterThan => Comparison > 0;
/// <summary>
/// True if the left version is greater than or equal to the right version.
/// Useful for checking if installed >= fixed.
/// </summary>
public bool IsGreaterThanOrEqual => Comparison >= 0;
}