Files
git.stella-ops.org/docs/modules/scanner/design/competitor-fallback-hierarchy.md
StellaOps Bot 8768c27f30
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Signals DSSE Sign & Evidence Locker / sign-signals-artifacts (push) Has been cancelled
Signals DSSE Sign & Evidence Locker / verify-signatures (push) Has been cancelled
Add signal contracts for reachability, exploitability, trust, and unknown symbols
- Introduced `ReachabilityState`, `RuntimeHit`, `ExploitabilitySignal`, `ReachabilitySignal`, `SignalEnvelope`, `SignalType`, `TrustSignal`, and `UnknownSymbolSignal` records to define various signal types and their properties.
- Implemented JSON serialization attributes for proper data interchange.
- Created project files for the new signal contracts library and corresponding test projects.
- Added deterministic test fixtures for micro-interaction testing.
- Included cryptographic keys for secure operations with cosign.
2025-12-05 00:27:00 +02:00

7.8 KiB

Competitor Ingest Fallback Hierarchy (CM6)

Status: Draft · Date: 2025-12-04 Scope: Establish fallback hierarchy when external SBOM/scan data is incomplete, with explicit decision traces.

Objectives

  • Define clear fallback levels for incomplete data.
  • Ensure transparent decision tracking.
  • Enable policy-based confidence scoring.
  • Support offline fallback evaluation.

Fallback Levels

Hierarchy Definition

Level 1: Signed SBOM with valid provenance
    │
    └──► Level 2: Unsigned SBOM with tool metadata
            │
            └──► Level 3: Scan-only results
                    │
                    └──► Level 4: Reject (no evidence)

Level Details

Level Source Confidence Requirements Warnings
1 Signed SBOM 1.0 Valid signature, valid provenance None
2 Unsigned SBOM 0.7 Tool metadata, component purl, scan timestamp provenance_unknown
3 Scan-only 0.5 Scan timestamp degraded_confidence, no_sbom
4 Reject 0.0 None met -

Level 1: Signed SBOM

Requirements:

  • DSSE/COSE/JWS signature present
  • Signature verification passes
  • Signer key in trusted keyring
  • Provenance metadata valid
{
  "fallback": {
    "level": 1,
    "source": "signed_sbom",
    "confidence": 1.0,
    "decision": {
      "reason": "Valid signature and provenance",
      "checks": {
        "signaturePresent": true,
        "signatureValid": true,
        "keyTrusted": true,
        "provenanceValid": true
      }
    }
  }
}

Level 2: Unsigned SBOM

Requirements (all must be present):

  • Tool name and version
  • Component list with PURLs
  • At least one SHA-256 hash per component
  • Scan timestamp
{
  "fallback": {
    "level": 2,
    "source": "unsigned_sbom",
    "confidence": 0.7,
    "decision": {
      "reason": "Valid SBOM without signature",
      "checks": {
        "signaturePresent": false,
        "toolMetadata": true,
        "componentPurls": true,
        "componentHashes": true,
        "scanTimestamp": true
      },
      "warnings": ["provenance_unknown"]
    }
  }
}

Level 3: Scan-only

Requirements:

  • Scan timestamp present
  • At least one finding or component
{
  "fallback": {
    "level": 3,
    "source": "scan_only",
    "confidence": 0.5,
    "decision": {
      "reason": "Scan results without SBOM",
      "checks": {
        "signaturePresent": false,
        "toolMetadata": false,
        "scanTimestamp": true,
        "hasFindings": true
      },
      "warnings": ["degraded_confidence", "no_sbom"]
    }
  }
}

Level 4: Reject

When no requirements met:

{
  "fallback": {
    "level": 4,
    "source": "reject",
    "confidence": 0.0,
    "decision": {
      "reason": "No acceptable evidence found",
      "checks": {
        "signaturePresent": false,
        "toolMetadata": false,
        "scanTimestamp": false,
        "hasFindings": false
      },
      "action": "reject",
      "errorCode": "E2010"
    }
  }
}

Decision Evaluation

Evaluation Algorithm

def evaluate_fallback(input_data: dict) -> FallbackDecision:
    checks = {
        "signaturePresent": has_signature(input_data),
        "signatureValid": False,
        "keyTrusted": False,
        "provenanceValid": False,
        "toolMetadata": has_tool_metadata(input_data),
        "componentPurls": has_component_purls(input_data),
        "componentHashes": has_component_hashes(input_data),
        "scanTimestamp": has_scan_timestamp(input_data),
        "hasFindings": has_findings(input_data)
    }

    # Level 1 check
    if checks["signaturePresent"]:
        sig_result = verify_signature(input_data)
        checks["signatureValid"] = sig_result.valid
        checks["keyTrusted"] = sig_result.key_trusted
        checks["provenanceValid"] = verify_provenance(input_data)

        if all([checks["signatureValid"], checks["keyTrusted"], checks["provenanceValid"]]):
            return FallbackDecision(level=1, confidence=1.0, checks=checks)

    # Level 2 check
    if all([checks["toolMetadata"], checks["componentPurls"],
            checks["componentHashes"], checks["scanTimestamp"]]):
        return FallbackDecision(
            level=2, confidence=0.7, checks=checks,
            warnings=["provenance_unknown"]
        )

    # Level 3 check
    if checks["scanTimestamp"] and checks["hasFindings"]:
        return FallbackDecision(
            level=3, confidence=0.5, checks=checks,
            warnings=["degraded_confidence", "no_sbom"]
        )

    # Level 4: Reject
    return FallbackDecision(
        level=4, confidence=0.0, checks=checks,
        action="reject", error_code="E2010"
    )

Decision Trace

Trace Format

{
  "trace": {
    "id": "trace-12345",
    "timestamp": "2025-12-04T12:00:00Z",
    "input": {
      "hash": "b3:...",
      "size": 12345,
      "format": "cyclonedx-1.6"
    },
    "evaluation": {
      "steps": [
        {
          "check": "signaturePresent",
          "result": false,
          "details": "No DSSE/COSE/JWS envelope found"
        },
        {
          "check": "toolMetadata",
          "result": true,
          "details": "Found tool: syft v1.0.0"
        },
        {
          "check": "componentPurls",
          "result": true,
          "details": "42 components with valid PURLs"
        },
        {
          "check": "componentHashes",
          "result": true,
          "details": "42 components with SHA-256 hashes"
        },
        {
          "check": "scanTimestamp",
          "result": true,
          "details": "Timestamp: 2025-12-04T00:00:00Z"
        }
      ],
      "decision": {
        "level": 2,
        "confidence": 0.7,
        "warnings": ["provenance_unknown"]
      }
    }
  }
}

Trace Persistence

Decision traces are:

  • Stored with normalized output
  • Included in API responses
  • Available for audit queries
  • Deterministic (same input = same trace)

Policy Integration

Confidence Thresholds

{
  "policy": {
    "minConfidence": {
      "production": 0.8,
      "staging": 0.5,
      "development": 0.0
    },
    "allowedLevels": {
      "production": [1],
      "staging": [1, 2],
      "development": [1, 2, 3]
    }
  }
}

Policy Evaluation

# policy/ingest/fallback.rego
package ingest.fallback

import rego.v1

default allow = false

allow if {
    input.fallback.level <= max_allowed_level
    input.fallback.confidence >= min_confidence
}

max_allowed_level := data.policy.allowedLevels[input.environment][_]
min_confidence := data.policy.minConfidence[input.environment]

deny contains msg if {
    input.fallback.level > max_allowed_level
    msg := sprintf("Fallback level %d not allowed in %s", [input.fallback.level, input.environment])
}

warn contains msg if {
    warning := input.fallback.decision.warnings[_]
    msg := sprintf("Fallback warning: %s", [warning])
}

Override Mechanism

Manual Override

# Accept unsigned SBOM in production (requires approval)
stellaops ingest import \
    --input external-sbom.json \
    --allow-unsigned \
    --override-reason "Emergency import per ticket INC-12345" \
    --override-approver security-admin@example.com

Override Record

{
  "override": {
    "enabled": true,
    "level": 2,
    "originalDecision": {
      "level": 4,
      "reason": "Would normally reject"
    },
    "overrideReason": "Emergency import per ticket INC-12345",
    "approver": "security-admin@example.com",
    "approvedAt": "2025-12-04T12:00:00Z",
    "expiresAt": "2025-12-05T12:00:00Z"
  }
}
  • Sprint: docs/implplan/SPRINT_0186_0001_0001_record_deterministic_execution.md (CM6)
  • Verification: docs/modules/scanner/design/competitor-signature-verification.md (CM2)
  • Normalization: docs/modules/scanner/design/competitor-ingest-normalization.md (CM1)