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

@@ -0,0 +1,269 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (c) StellaOps
using System.Collections.Immutable;
using StellaOps.Scanner.Explainability.Assumptions;
using StellaOps.Scanner.Explainability.Confidence;
using StellaOps.Scanner.Explainability.Falsifiability;
namespace StellaOps.Scanner.Explainability;
/// <summary>
/// A comprehensive risk report that includes all explainability data for a finding.
/// </summary>
public sealed record RiskReport
{
/// <summary>Unique report identifier</summary>
public required string Id { get; init; }
/// <summary>The finding this report explains</summary>
public required string FindingId { get; init; }
/// <summary>The vulnerability ID (CVE, GHSA, etc.)</summary>
public required string VulnerabilityId { get; init; }
/// <summary>Package name</summary>
public required string PackageName { get; init; }
/// <summary>Package version</summary>
public required string PackageVersion { get; init; }
/// <summary>Assumptions made during analysis</summary>
public AssumptionSet? Assumptions { get; init; }
/// <summary>Falsifiability criteria and status</summary>
public FalsifiabilityCriteria? Falsifiability { get; init; }
/// <summary>Evidence density confidence score</summary>
public EvidenceDensityScore? ConfidenceScore { get; init; }
/// <summary>Human-readable explanation of the finding</summary>
public required string Explanation { get; init; }
/// <summary>Detailed narrative explaining the risk</summary>
public string? DetailedNarrative { get; init; }
/// <summary>Recommended actions</summary>
public ImmutableArray<RecommendedAction> RecommendedActions { get; init; } = [];
/// <summary>When this report was generated</summary>
public required DateTimeOffset GeneratedAt { get; init; }
/// <summary>Version of the explainability engine</summary>
public required string EngineVersion { get; init; }
}
/// <summary>
/// A recommended action to address a finding.
/// </summary>
/// <param name="Priority">Action priority (1 = highest)</param>
/// <param name="Action">The recommended action</param>
/// <param name="Rationale">Why this action is recommended</param>
/// <param name="Effort">Estimated effort level</param>
public sealed record RecommendedAction(
int Priority,
string Action,
string Rationale,
EffortLevel Effort
);
/// <summary>
/// Effort level for a recommended action.
/// </summary>
public enum EffortLevel
{
/// <summary>Quick configuration change or update</summary>
Low,
/// <summary>Moderate code changes or testing required</summary>
Medium,
/// <summary>Significant refactoring or architectural changes</summary>
High
}
/// <summary>
/// Generates comprehensive risk reports.
/// </summary>
public interface IRiskReportGenerator
{
/// <summary>
/// Generates a risk report for a finding.
/// </summary>
RiskReport Generate(RiskReportInput input);
}
/// <summary>
/// Input for generating a risk report.
/// </summary>
public sealed record RiskReportInput
{
public required string FindingId { get; init; }
public required string VulnerabilityId { get; init; }
public required string PackageName { get; init; }
public required string PackageVersion { get; init; }
public string? Severity { get; init; }
public string? Description { get; init; }
public string? FixedVersion { get; init; }
public AssumptionSet? Assumptions { get; init; }
public FalsifiabilityCriteria? Falsifiability { get; init; }
public EvidenceFactors? EvidenceFactors { get; init; }
}
/// <summary>
/// Default implementation of <see cref="IRiskReportGenerator"/>.
/// </summary>
public sealed class RiskReportGenerator : IRiskReportGenerator
{
private const string EngineVersionValue = "1.0.0";
private readonly IEvidenceDensityScorer _scorer;
public RiskReportGenerator(IEvidenceDensityScorer scorer)
{
_scorer = scorer;
}
/// <inheritdoc />
public RiskReport Generate(RiskReportInput input)
{
// Calculate confidence score if evidence factors provided
EvidenceDensityScore? confidenceScore = null;
if (input.EvidenceFactors is not null)
{
confidenceScore = _scorer.Calculate(input.EvidenceFactors);
}
var explanation = GenerateExplanation(input);
var narrative = GenerateNarrative(input, confidenceScore);
var actions = GenerateRecommendedActions(input);
return new RiskReport
{
Id = Guid.NewGuid().ToString("N"),
FindingId = input.FindingId,
VulnerabilityId = input.VulnerabilityId,
PackageName = input.PackageName,
PackageVersion = input.PackageVersion,
Assumptions = input.Assumptions,
Falsifiability = input.Falsifiability,
ConfidenceScore = confidenceScore,
Explanation = explanation,
DetailedNarrative = narrative,
RecommendedActions = [.. actions],
GeneratedAt = DateTimeOffset.UtcNow,
EngineVersion = EngineVersionValue
};
}
private static string GenerateExplanation(RiskReportInput input)
{
var parts = new List<string>
{
$"Vulnerability {input.VulnerabilityId} affects {input.PackageName}@{input.PackageVersion}."
};
if (input.Severity is not null)
{
parts.Add($"Severity: {input.Severity}.");
}
if (input.Falsifiability?.Status == FalsifiabilityStatus.Falsified)
{
parts.Add("This finding has been falsified and may not be exploitable in your environment.");
}
else if (input.Assumptions?.HasContradictions == true)
{
parts.Add("Some analysis assumptions have been contradicted by observed evidence.");
}
return string.Join(" ", parts);
}
private static string GenerateNarrative(RiskReportInput input, EvidenceDensityScore? score)
{
var sections = new List<string>();
// Overview
sections.Add($"## Overview\n{input.Description ?? "No description available."}");
// Assumptions section
if (input.Assumptions is not null && input.Assumptions.Assumptions.Length > 0)
{
var assumptionLines = input.Assumptions.Assumptions
.Select(a => $"- **{a.Category}**: {a.Key} = {a.AssumedValue}" +
(a.ObservedValue is not null ? $" (observed: {a.ObservedValue})" : ""));
sections.Add($"## Assumptions\n{string.Join("\n", assumptionLines)}");
}
// Falsifiability section
if (input.Falsifiability is not null)
{
sections.Add($"## Falsifiability\n**Status**: {input.Falsifiability.Status}\n\n{input.Falsifiability.Summary}");
}
// Confidence section
if (score is not null)
{
sections.Add($"## Confidence Assessment\n{score.Explanation}");
if (score.ImprovementRecommendations.Count > 0)
{
var recs = score.ImprovementRecommendations.Select(r => $"- {r}");
sections.Add($"### Recommendations to Improve Confidence\n{string.Join("\n", recs)}");
}
}
return string.Join("\n\n", sections);
}
private static List<RecommendedAction> GenerateRecommendedActions(RiskReportInput input)
{
var actions = new List<RecommendedAction>();
int priority = 1;
// Action: Update package if fix available
if (input.FixedVersion is not null)
{
actions.Add(new RecommendedAction(
priority++,
$"Update {input.PackageName} to version {input.FixedVersion} or later",
"A fixed version is available that addresses this vulnerability",
EffortLevel.Low));
}
// Action: Validate assumptions
if (input.Assumptions is not null && input.Assumptions.ValidatedCount < input.Assumptions.Assumptions.Length)
{
actions.Add(new RecommendedAction(
priority++,
"Validate analysis assumptions with runtime observations",
$"Only {input.Assumptions.ValidatedCount}/{input.Assumptions.Assumptions.Length} assumptions are validated",
EffortLevel.Medium));
}
// Action: Evaluate falsifiability criteria
if (input.Falsifiability?.Status == FalsifiabilityStatus.PartiallyEvaluated)
{
var pendingCount = input.Falsifiability.Criteria.Count(c => c.Status == CriterionStatus.Pending);
actions.Add(new RecommendedAction(
priority++,
"Complete falsifiability evaluation",
$"{pendingCount} criteria are pending evaluation",
EffortLevel.Medium));
}
// Default action if no fix available
if (input.FixedVersion is null)
{
actions.Add(new RecommendedAction(
priority,
"Monitor for vendor patch or implement compensating controls",
"No fixed version is currently available",
EffortLevel.High));
}
return actions;
}
}