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