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,73 @@
// -----------------------------------------------------------------------------
// IVerdictLedger.cs
// Sprint: SPRINT_4300_0001_0001_oci_verdict_attestation_push
// Task: VERDICT-018
// Description: Interface for storing verdict metadata in the findings ledger.
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace StellaOps.Zastava.Core.Verdicts;
/// <summary>
/// Service for persisting verdict observation metadata.
/// </summary>
public interface IVerdictLedger
{
/// <summary>
/// Record an observed verdict in the ledger.
/// </summary>
/// <param name="entry">The verdict ledger entry to store.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The stored entry with assigned ID.</returns>
Task<VerdictLedgerEntry> RecordVerdictAsync(
VerdictLedgerEntry entry,
CancellationToken cancellationToken = default);
/// <summary>
/// Query verdicts for an image digest.
/// </summary>
/// <param name="imageDigest">The image digest to query.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Matching ledger entries.</returns>
Task<IReadOnlyList<VerdictLedgerEntry>> QueryByImageAsync(
string imageDigest,
CancellationToken cancellationToken = default);
/// <summary>
/// Get a verdict entry by its ID.
/// </summary>
/// <param name="entryId">The entry ID.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The entry if found.</returns>
Task<VerdictLedgerEntry?> GetByIdAsync(
string entryId,
CancellationToken cancellationToken = default);
/// <summary>
/// Query verdicts observed within a time range.
/// </summary>
/// <param name="from">Start of time range (inclusive).</param>
/// <param name="to">End of time range (exclusive).</param>
/// <param name="limit">Maximum entries to return.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Matching ledger entries.</returns>
Task<IReadOnlyList<VerdictLedgerEntry>> QueryByTimeRangeAsync(
DateTimeOffset from,
DateTimeOffset to,
int limit = 100,
CancellationToken cancellationToken = default);
/// <summary>
/// Get the most recent verdict for an image.
/// </summary>
/// <param name="imageDigest">The image digest.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The most recent verdict if any.</returns>
Task<VerdictLedgerEntry?> GetLatestForImageAsync(
string imageDigest,
CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,65 @@
// -----------------------------------------------------------------------------
// IVerdictObserver.cs
// Sprint: SPRINT_4300_0001_0001_oci_verdict_attestation_push
// Tasks: VERDICT-016, VERDICT-019
// Description: Interface for observing verdict referrer artifacts from OCI registries.
// -----------------------------------------------------------------------------
using System.Threading;
using System.Threading.Tasks;
namespace StellaOps.Zastava.Core.Verdicts;
/// <summary>
/// Service for discovering verdict attestations attached to container images.
/// </summary>
public interface IVerdictObserver
{
/// <summary>
/// Discover verdict referrers for an image.
/// </summary>
/// <param name="imageReference">Full image reference (registry/repo@sha256:digest).</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Discovery result containing any found verdicts.</returns>
Task<VerdictDiscoveryResult> DiscoverVerdictsAsync(
string imageReference,
CancellationToken cancellationToken = default);
/// <summary>
/// Fetch a specific verdict by digest.
/// </summary>
/// <param name="imageReference">Full image reference.</param>
/// <param name="verdictDigest">Digest of the verdict to fetch.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The verdict payload bytes if found.</returns>
Task<VerdictFetchResult> FetchVerdictAsync(
string imageReference,
string verdictDigest,
CancellationToken cancellationToken = default);
}
/// <summary>
/// Result of fetching a verdict's content.
/// </summary>
public sealed record VerdictFetchResult
{
/// <summary>
/// Whether the fetch was successful.
/// </summary>
public bool Success { get; init; }
/// <summary>
/// The DSSE envelope bytes.
/// </summary>
public byte[]? EnvelopeBytes { get; init; }
/// <summary>
/// Media type of the fetched content.
/// </summary>
public string? MediaType { get; init; }
/// <summary>
/// Error message if fetch failed.
/// </summary>
public string? Error { get; init; }
}

View File

@@ -0,0 +1,81 @@
// -----------------------------------------------------------------------------
// IVerdictValidator.cs
// Sprint: SPRINT_4300_0001_0001_oci_verdict_attestation_push
// Task: VERDICT-017
// Description: Interface for validating verdict attestation signatures.
// -----------------------------------------------------------------------------
using System.Threading;
using System.Threading.Tasks;
namespace StellaOps.Zastava.Core.Verdicts;
/// <summary>
/// Service for validating verdict attestation signatures.
/// </summary>
public interface IVerdictValidator
{
/// <summary>
/// Validate a verdict attestation signature.
/// </summary>
/// <param name="envelopeBytes">The DSSE envelope bytes.</param>
/// <param name="options">Validation options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Validation result.</returns>
Task<VerdictValidationResult> ValidateAsync(
byte[] envelopeBytes,
VerdictValidationOptions? options = null,
CancellationToken cancellationToken = default);
}
/// <summary>
/// Options for verdict validation.
/// </summary>
public sealed record VerdictValidationOptions
{
/// <summary>
/// Whether to require a valid signature.
/// </summary>
public bool RequireValidSignature { get; init; } = true;
/// <summary>
/// Allowed signing key identities (issuer/subject pairs).
/// </summary>
public IReadOnlyList<TrustedIdentity>? TrustedIdentities { get; init; }
/// <summary>
/// Whether to verify the payload type matches expected verdict type.
/// </summary>
public bool VerifyPayloadType { get; init; } = true;
/// <summary>
/// Maximum age of the verdict to accept.
/// </summary>
public TimeSpan? MaxAge { get; init; }
/// <summary>
/// Whether to verify against Rekor transparency log.
/// </summary>
public bool VerifyRekor { get; init; }
}
/// <summary>
/// A trusted signing identity.
/// </summary>
public sealed record TrustedIdentity
{
/// <summary>
/// OIDC issuer for keyless signing.
/// </summary>
public string? Issuer { get; init; }
/// <summary>
/// Subject identity (email, workflow URI, etc.).
/// </summary>
public string? Subject { get; init; }
/// <summary>
/// Public key fingerprint for keyed signing.
/// </summary>
public string? KeyFingerprint { get; init; }
}

View File

@@ -0,0 +1,245 @@
// -----------------------------------------------------------------------------
// VerdictObserverContracts.cs
// Sprint: SPRINT_4300_0001_0001_oci_verdict_attestation_push
// Tasks: VERDICT-016, VERDICT-017, VERDICT-018, VERDICT-019
// Description: Contracts for observing and validating verdict referrer artifacts.
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace StellaOps.Zastava.Core.Verdicts;
/// <summary>
/// Result of discovering verdict referrers for an image.
/// </summary>
public sealed record VerdictDiscoveryResult
{
/// <summary>
/// The image digest that was queried.
/// </summary>
public required string ImageDigest { get; init; }
/// <summary>
/// List of discovered verdict attestations.
/// </summary>
public IReadOnlyList<DiscoveredVerdict> Verdicts { get; init; } = [];
/// <summary>
/// Whether the discovery was successful.
/// </summary>
public bool Success { get; init; }
/// <summary>
/// Error message if discovery failed.
/// </summary>
public string? Error { get; init; }
/// <summary>
/// When the discovery was performed.
/// </summary>
public DateTimeOffset DiscoveredAt { get; init; }
}
/// <summary>
/// A verdict attestation discovered via OCI referrers API.
/// </summary>
public sealed record DiscoveredVerdict
{
/// <summary>
/// Digest of the verdict manifest.
/// </summary>
public required string Digest { get; init; }
/// <summary>
/// Media type of the artifact.
/// </summary>
public required string MediaType { get; init; }
/// <summary>
/// Artifact type from the manifest.
/// </summary>
public required string ArtifactType { get; init; }
/// <summary>
/// Size of the verdict manifest in bytes.
/// </summary>
public long Size { get; init; }
/// <summary>
/// Annotations from the verdict manifest.
/// </summary>
public IReadOnlyDictionary<string, string> Annotations { get; init; } = new Dictionary<string, string>();
/// <summary>
/// The verdict decision (pass, warn, block).
/// </summary>
[JsonPropertyName("decision")]
public string? Decision { get; init; }
/// <summary>
/// When the verdict was created.
/// </summary>
[JsonPropertyName("timestamp")]
public DateTimeOffset? Timestamp { get; init; }
/// <summary>
/// SBOM digest used for the verdict.
/// </summary>
[JsonPropertyName("sbomDigest")]
public string? SbomDigest { get; init; }
/// <summary>
/// Feeds digest used for the verdict.
/// </summary>
[JsonPropertyName("feedsDigest")]
public string? FeedsDigest { get; init; }
/// <summary>
/// Policy digest used for the verdict.
/// </summary>
[JsonPropertyName("policyDigest")]
public string? PolicyDigest { get; init; }
/// <summary>
/// Proof bundle digest for the verdict.
/// </summary>
[JsonPropertyName("proofBundleDigest")]
public string? ProofBundleDigest { get; init; }
/// <summary>
/// Uncertainty statement digest (Sprint: SPRINT_4300_0002_0002).
/// </summary>
[JsonPropertyName("uncertaintyDigest")]
public string? UncertaintyDigest { get; init; }
/// <summary>
/// Uncertainty budget digest (Sprint: SPRINT_4300_0002_0002).
/// </summary>
[JsonPropertyName("uncertaintyBudgetDigest")]
public string? UncertaintyBudgetDigest { get; init; }
}
/// <summary>
/// Result of validating a verdict signature.
/// </summary>
public sealed record VerdictValidationResult
{
/// <summary>
/// Digest of the verdict that was validated.
/// </summary>
public required string VerdictDigest { get; init; }
/// <summary>
/// Whether the signature is valid.
/// </summary>
public bool IsValid { get; init; }
/// <summary>
/// Signature algorithm used.
/// </summary>
public string? SignatureAlgorithm { get; init; }
/// <summary>
/// Key ID or certificate subject.
/// </summary>
public string? KeyIdentifier { get; init; }
/// <summary>
/// When the validation was performed.
/// </summary>
public DateTimeOffset ValidatedAt { get; init; }
/// <summary>
/// Validation error details if not valid.
/// </summary>
public string? Error { get; init; }
/// <summary>
/// Individual validation checks performed.
/// </summary>
public IReadOnlyList<ValidationCheck> Checks { get; init; } = [];
}
/// <summary>
/// A single validation check performed during verdict validation.
/// </summary>
public sealed record ValidationCheck
{
/// <summary>
/// Name of the check.
/// </summary>
public required string Name { get; init; }
/// <summary>
/// Whether the check passed.
/// </summary>
public bool Passed { get; init; }
/// <summary>
/// Additional details about the check.
/// </summary>
public string? Details { get; init; }
}
/// <summary>
/// Verdict metadata stored in the findings ledger.
/// </summary>
public sealed record VerdictLedgerEntry
{
/// <summary>
/// Unique identifier for this entry.
/// </summary>
public required string EntryId { get; init; }
/// <summary>
/// Image digest this verdict applies to.
/// </summary>
public required string ImageDigest { get; init; }
/// <summary>
/// Digest of the verdict attestation.
/// </summary>
public required string VerdictDigest { get; init; }
/// <summary>
/// The verdict decision.
/// </summary>
public required string Decision { get; init; }
/// <summary>
/// When the verdict was observed.
/// </summary>
public required DateTimeOffset ObservedAt { get; init; }
/// <summary>
/// Whether the signature was validated.
/// </summary>
public bool SignatureValidated { get; init; }
/// <summary>
/// SBOM digest used for the verdict.
/// </summary>
public string? SbomDigest { get; init; }
/// <summary>
/// Feeds digest used for the verdict.
/// </summary>
public string? FeedsDigest { get; init; }
/// <summary>
/// Policy digest used for the verdict.
/// </summary>
public string? PolicyDigest { get; init; }
/// <summary>
/// Source registry where the verdict was discovered.
/// </summary>
public string? SourceRegistry { get; init; }
/// <summary>
/// Graph revision ID associated with the verdict.
/// </summary>
public string? GraphRevisionId { get; init; }
}