save progress
This commit is contained in:
545
docs-archived/product/advisories/08-Jan-2026 - AI moats.md
Normal file
545
docs-archived/product/advisories/08-Jan-2026 - AI moats.md
Normal file
@@ -0,0 +1,545 @@
|
||||
Below is a cohesive set of **7 product advisories** that together define an “AI-native” Stella Ops with defensible moats. Each advisory follows the same structure:
|
||||
|
||||
* **Problem** (what hurts today)
|
||||
* **Why** (why Stella should solve it)
|
||||
* **What we ship** (capabilities, boundaries)
|
||||
* **How we achieve** (proposed `AdvisoryAI` backend modules + key UI components)
|
||||
* **Guardrails** (safety / trust / determinism)
|
||||
* **KPIs** (how you prove it works)
|
||||
|
||||
I’m assuming your canonical object model already includes **Runs** (incident/escalation/change investigation runs) and a system-of-record in **PostgreSQL** with **Valkey** as a non-authoritative accelerator.
|
||||
|
||||
---
|
||||
|
||||
# ADVISORY-AI-000 — AdvisoryAI Foundation: Chat + Workbench + Runs (the “AI OS surface”)
|
||||
|
||||
## Problem
|
||||
|
||||
Most “AI in ops” fails because it’s only a chat box. Chat is not:
|
||||
|
||||
* auditable
|
||||
* repeatable
|
||||
* actionable with guardrails
|
||||
* collaborative (handoffs, approvals, artifacts)
|
||||
|
||||
Operators need a place where AI output becomes **objects** (runs, decisions, patches, evidence packs), not ephemeral text.
|
||||
|
||||
## Why we do it
|
||||
|
||||
This advisory is the substrate for all other moats. Without it, your other features remain demos.
|
||||
|
||||
## What we ship
|
||||
|
||||
1. **AdvisoryAI Orchestrator** that can:
|
||||
|
||||
* read Stella objects (runs, services, policies, evidence)
|
||||
* propose plans
|
||||
* call tools/actions (within policy)
|
||||
* produce structured artifacts (patches, decision records, evidence packs)
|
||||
|
||||
2. **AI Workbench UI**:
|
||||
|
||||
* Chat panel for intent
|
||||
* Artifact cards (Run, Playbook Patch, Decision, Evidence Pack)
|
||||
* Run Timeline view (what happened, tool calls, approvals, outputs)
|
||||
|
||||
## How we achieve (modules + UI)
|
||||
|
||||
### Backend modules (suggested)
|
||||
|
||||
* `StellaOps.AdvisoryAI.WebService`
|
||||
|
||||
* Conversation/session orchestration
|
||||
* Tool routing + action execution requests
|
||||
* Artifact creation (Run notes, patches, decisions)
|
||||
* `StellaOps.AdvisoryAI.Prompting`
|
||||
|
||||
* Prompt templates versioned + hashed
|
||||
* Guarded system prompts per “mode”
|
||||
* `StellaOps.AdvisoryAI.Tools`
|
||||
|
||||
* Tool contracts (read-only queries, action requests)
|
||||
* `StellaOps.AdvisoryAI.Eval`
|
||||
|
||||
* Regression tests for tool correctness + safety
|
||||
|
||||
### UI components
|
||||
|
||||
* `AiChatPanelComponent`
|
||||
* `AiArtifactCardComponent` (Run/Decision/Patch/Evidence Pack)
|
||||
* `RunTimelineComponent` (with “AI steps” and “human steps”)
|
||||
* `ModeSelectorComponent` (Analyst / Operator / Autopilot)
|
||||
|
||||
### Canonical flow
|
||||
|
||||
```
|
||||
User intent (chat)
|
||||
-> AdvisoryAI proposes plan (steps)
|
||||
-> executes read-only tools
|
||||
-> generates artifact(s)
|
||||
-> requests approvals for risky actions
|
||||
-> records everything on Run timeline
|
||||
```
|
||||
|
||||
## Guardrails
|
||||
|
||||
* Every AI interaction writes to a **Run** (or attaches to an existing Run).
|
||||
* Prompt templates are **versioned + hashed**.
|
||||
* Tool calls and outputs are **persisted** (for audit and replay).
|
||||
|
||||
## KPIs
|
||||
|
||||
* % AI sessions attached to Runs
|
||||
* “Time to first useful artifact”
|
||||
* Operator adoption (weekly active users of Workbench)
|
||||
|
||||
---
|
||||
|
||||
# ADVISORY-AI-001 — Evidence-First Outputs (trust-by-construction)
|
||||
|
||||
## Problem
|
||||
|
||||
In ops, an answer without evidence is a liability. LLMs are persuasive even when wrong. Operators waste time verifying or, worse, act on incorrect claims.
|
||||
|
||||
## Why we do it
|
||||
|
||||
Evidence-first output is the trust prerequisite for:
|
||||
|
||||
* automation
|
||||
* playbook learning
|
||||
* org memory
|
||||
* executive reporting
|
||||
|
||||
## What we ship
|
||||
|
||||
* A **Claim → Evidence** constraint:
|
||||
|
||||
* Each material claim must be backed by an `EvidenceRef` (query snapshot, ticket, pipeline run, commit, config state).
|
||||
* An **Evidence Pack** artifact:
|
||||
|
||||
* A shareable bundle of evidence for an incident/change/review.
|
||||
|
||||
## How we achieve (modules + UI)
|
||||
|
||||
### Backend modules
|
||||
|
||||
* `StellaOps.AdvisoryAI.Evidence`
|
||||
|
||||
* Claim extraction from model output
|
||||
* Evidence retrieval + snapshotting
|
||||
* Citation enforcement (or downgrade claim confidence)
|
||||
* `StellaOps.EvidenceStore`
|
||||
|
||||
* Immutable (or content-addressed) snapshots
|
||||
* Hashes, timestamps, query parameters
|
||||
|
||||
### UI components
|
||||
|
||||
* `EvidenceSidePanelComponent` (opens from inline citations)
|
||||
* `EvidencePackViewerComponent`
|
||||
* `ConfidenceBadgeComponent` (Verified / Inferred / Unknown)
|
||||
|
||||
### Implementation pattern
|
||||
|
||||
* For each answer:
|
||||
|
||||
1. Draft response
|
||||
2. Extract claims
|
||||
3. Attach evidence refs
|
||||
4. If evidence missing: label as uncertain + propose verification steps
|
||||
|
||||
## Guardrails
|
||||
|
||||
* If evidence is missing, Stella must **not** assert certainty.
|
||||
* Evidence snapshots must capture:
|
||||
|
||||
* query inputs
|
||||
* time range
|
||||
* raw result (or hash + storage pointer)
|
||||
|
||||
## KPIs
|
||||
|
||||
* Citation coverage (% of answers with evidence refs)
|
||||
* Reduced back-and-forth (“how do you know?” rate)
|
||||
* Adoption of automation after evidence-first rollout
|
||||
|
||||
---
|
||||
|
||||
# ADVISORY-AI-002 — Policy-Aware Automation (safe actions, not just suggestions)
|
||||
|
||||
## Problem
|
||||
|
||||
The main blocker to “AI that acts” is governance:
|
||||
|
||||
* wrong environment
|
||||
* insufficient permission
|
||||
* missing approvals
|
||||
* non-idempotent actions
|
||||
* unclear accountability
|
||||
|
||||
## Why we do it
|
||||
|
||||
If Stella can’t safely execute actions, it will remain a read-only assistant. Policy-aware automation is a hard moat because it requires real engineering discipline and operational maturity.
|
||||
|
||||
## What we ship
|
||||
|
||||
* A typed **Action Registry**:
|
||||
|
||||
* schemas, risk levels, idempotency, rollback/compensation
|
||||
* A **Policy decision point** (PDP) before any action:
|
||||
|
||||
* allow / allow-with-approvals / deny
|
||||
* An **Approval workflow** linked to Runs
|
||||
|
||||
## How we achieve (modules + UI)
|
||||
|
||||
### Backend modules
|
||||
|
||||
* `StellaOps.ActionRegistry`
|
||||
|
||||
* Action definitions + schemas + risk metadata
|
||||
* `StellaOps.PolicyEngine`
|
||||
|
||||
* Rules: environment protections, freeze windows, role constraints
|
||||
* `StellaOps.AdvisoryAI.Automation`
|
||||
|
||||
* Converts intent → action proposals
|
||||
* Submits action requests after approvals
|
||||
* `StellaOps.RunLedger`
|
||||
|
||||
* Every action request + result is a ledger entry
|
||||
|
||||
### UI components
|
||||
|
||||
* `ActionProposalCardComponent`
|
||||
* `ApprovalModalComponent` (scoped approval: this action/this run/this window)
|
||||
* `PolicyExplanationComponent` (human-readable “why allowed/denied”)
|
||||
* `RollbackPanelComponent`
|
||||
|
||||
## Guardrails
|
||||
|
||||
* Default: propose actions; only auto-execute in explicitly configured “Autopilot scopes.”
|
||||
* Every action must support:
|
||||
|
||||
* idempotency key
|
||||
* audit fields (why, ticket/run linkage)
|
||||
* reversible/compensating action where feasible
|
||||
|
||||
## KPIs
|
||||
|
||||
* % actions proposed vs executed
|
||||
* “Policy prevented incident” count
|
||||
* Approval latency and action success rate
|
||||
|
||||
---
|
||||
|
||||
# ADVISORY-AI-003 — Ops Memory (structured, durable, queryable)
|
||||
|
||||
## Problem
|
||||
|
||||
Teams repeat incidents because knowledge lives in:
|
||||
|
||||
* chat logs
|
||||
* tribal memory
|
||||
* scattered tickets
|
||||
* unwritten heuristics
|
||||
|
||||
Chat history is not an operational knowledge base: it’s unstructured and hard to reuse safely.
|
||||
|
||||
## Why we do it
|
||||
|
||||
Ops memory reduces repeat work and accelerates diagnosis. It also becomes a defensible dataset because it’s tied to your Runs, artifacts, and outcomes.
|
||||
|
||||
## What we ship
|
||||
|
||||
A set of typed memory objects (not messages):
|
||||
|
||||
* `DecisionRecord`
|
||||
* `KnownIssue`
|
||||
* `Tactic`
|
||||
* `Constraint`
|
||||
* `PostmortemSummary`
|
||||
|
||||
Memory is written on:
|
||||
|
||||
* Run closure
|
||||
* approvals (policy events)
|
||||
* explicit “save as org memory” actions
|
||||
|
||||
## How we achieve (modules + UI)
|
||||
|
||||
### Backend modules
|
||||
|
||||
* `StellaOps.AdvisoryAI.Memory`
|
||||
|
||||
* Write: extract structured memory from run artifacts
|
||||
* Read: retrieve memory relevant to current context (service/env/symptoms)
|
||||
* Conflict handling: “superseded by”, timestamps, confidence
|
||||
* `StellaOps.MemoryStore` (Postgres tables + full-text index as needed)
|
||||
|
||||
### UI components
|
||||
|
||||
* `MemoryPanelComponent` (contextual suggestions during a run)
|
||||
* `MemoryBrowserComponent` (search + filters)
|
||||
* `MemoryDiffComponent` (when superseding prior memory)
|
||||
|
||||
## Guardrails
|
||||
|
||||
* Memory entries have:
|
||||
|
||||
* scope (service/env/team)
|
||||
* confidence (verified vs anecdotal)
|
||||
* review/expiry policies for tactics/constraints
|
||||
* Never “learn” from unresolved or low-confidence runs by default.
|
||||
|
||||
## KPIs
|
||||
|
||||
* Repeat incident rate reduction
|
||||
* Time-to-diagnosis delta when memory exists
|
||||
* Memory reuse rate inside Runs
|
||||
|
||||
---
|
||||
|
||||
# ADVISORY-AI-004 — Playbook Learning (Run → Patch → Approved Playbook)
|
||||
|
||||
## Problem
|
||||
|
||||
Runbooks/playbooks drift. Operators improvise. The playbook never improves, and the organization pays the same “tuition” repeatedly.
|
||||
|
||||
## Why we do it
|
||||
|
||||
Playbook learning is the compounding loop that turns daily operations into a proprietary advantage. Competitors can generate playbooks; they struggle to continuously improve them from real run traces with review + governance.
|
||||
|
||||
## What we ship
|
||||
|
||||
* Versioned playbooks as structured objects
|
||||
* **Playbook Patch** proposals generated from Run traces:
|
||||
|
||||
* coverage patches, repair patches, optimization patches, safety patches, detection patches
|
||||
* Owner review + approval workflow
|
||||
|
||||
## How we achieve (modules + UI)
|
||||
|
||||
### Backend modules
|
||||
|
||||
* `StellaOps.Playbooks`
|
||||
|
||||
* Playbook schema + versioning
|
||||
* `StellaOps.AdvisoryAI.PlaybookLearning`
|
||||
|
||||
* Extract “what we did” from Run timeline
|
||||
* Compare to playbook steps
|
||||
* Propose a patch with evidence links
|
||||
* `StellaOps.DiffService`
|
||||
|
||||
* Human-friendly diff output for UI
|
||||
|
||||
### UI components
|
||||
|
||||
* `PlaybookPatchCardComponent`
|
||||
* `DiffViewerComponent` (Monaco diff or equivalent)
|
||||
* `PlaybookApprovalFlowComponent`
|
||||
* `PlaybookCoverageHeatmapComponent` (optional, later)
|
||||
|
||||
## Guardrails
|
||||
|
||||
* Never auto-edit canonical playbooks; only patches + review.
|
||||
* Require evidence links for each proposed step.
|
||||
* Prevent one-off contamination by marking patches as:
|
||||
|
||||
* “generalizable” vs “context-specific”
|
||||
|
||||
## KPIs
|
||||
|
||||
* % incidents with a playbook
|
||||
* Patch acceptance rate
|
||||
* MTTR improvement for playbook-backed incidents
|
||||
|
||||
---
|
||||
|
||||
# ADVISORY-AI-005 — Integration Concierge (setup + health + “how-to” that is actually correct)
|
||||
|
||||
## Problem
|
||||
|
||||
Integrations are where tools die:
|
||||
|
||||
* users ask “how do I integrate X”
|
||||
* assistant answers generically
|
||||
* setup fails because of environment constraints, permissions, webhooks, scopes, retries, or missing prerequisites
|
||||
* no one can debug it later
|
||||
|
||||
## Why we do it
|
||||
|
||||
Integration handling becomes a moat when it is:
|
||||
|
||||
* deterministic (wizard truth)
|
||||
* auditable (events + actions traced)
|
||||
* self-healing (retries, backfills, health checks)
|
||||
* explainable (precise steps, not generic docs)
|
||||
|
||||
## What we ship
|
||||
|
||||
1. **Integration Setup Wizard** per provider (GitLab, Jira, Slack, etc.)
|
||||
2. **Integration Health** dashboard:
|
||||
|
||||
* last event received
|
||||
* last action executed
|
||||
* failure reasons + next steps
|
||||
* token expiry warnings
|
||||
|
||||
3. **Chat-driven guidance** that drives the same wizard backend:
|
||||
|
||||
* when user asks “how to integrate GitLab,” Stella replies with the exact steps for the instance type, auth mode, and required permissions, and can pre-fill a setup plan.
|
||||
|
||||
## How we achieve (modules + UI)
|
||||
|
||||
### Backend modules
|
||||
|
||||
* `StellaOps.Integrations`
|
||||
|
||||
* Provider contracts: inbound events + outbound actions
|
||||
* Normalization into Stella `Signals` and `Actions`
|
||||
* `StellaOps.Integrations.Reliability`
|
||||
|
||||
* Webhook dedupe, replay, dead-letter, backfill polling
|
||||
* `StellaOps.AdvisoryAI.Integrations`
|
||||
|
||||
* Retrieves provider-specific setup templates
|
||||
* Asks only for missing parameters
|
||||
* Produces a “setup checklist” artifact attached to a Run or Integration record
|
||||
|
||||
### UI components
|
||||
|
||||
* `IntegrationWizardComponent`
|
||||
* `IntegrationHealthComponent`
|
||||
* `IntegrationEventLogComponent` (raw payload headers + body stored securely)
|
||||
* `SetupChecklistArtifactComponent` (generated by AdvisoryAI)
|
||||
|
||||
## Guardrails
|
||||
|
||||
* Store inbound webhook payloads for replay/debug, with redaction where required.
|
||||
* Always support reconciliation/backfill (webhooks are never perfectly lossless).
|
||||
* Use least-privilege token scopes by default, with clear permission error guidance.
|
||||
|
||||
## KPIs
|
||||
|
||||
* Time-to-first-successful-event
|
||||
* Integration “healthy” uptime
|
||||
* Setup completion rate without human support
|
||||
|
||||
---
|
||||
|
||||
# ADVISORY-AI-006 — Outcome Analytics (prove ROI with credible attribution)
|
||||
|
||||
## Problem
|
||||
|
||||
AI features are easy to cut in budgeting because value is vague. “It feels faster” doesn’t survive scrutiny.
|
||||
|
||||
## Why we do it
|
||||
|
||||
Outcome analytics makes Stella defensible to leadership and helps prioritize what to automate next. It also becomes a dataset for continuous improvement.
|
||||
|
||||
## What we ship
|
||||
|
||||
* Baseline metrics (before Stella influence):
|
||||
|
||||
* MTTA, MTTR, escalation count, repeat incidents, deploy failure rate (as relevant)
|
||||
* Attribution model (only count impact when Stella materially contributed):
|
||||
|
||||
* playbook patch accepted
|
||||
* evidence pack used
|
||||
* policy-gated action executed
|
||||
* memory entry reused
|
||||
* Monthly/weekly impact reports
|
||||
|
||||
## How we achieve (modules + UI)
|
||||
|
||||
### Backend modules
|
||||
|
||||
* `StellaOps.Analytics`
|
||||
|
||||
* Metric computation + cohorts (by service/team/severity)
|
||||
* `StellaOps.AdvisoryAI.Attribution`
|
||||
|
||||
* Joins outcomes to AI artifacts and actions in the Run ledger
|
||||
* `StellaOps.Reporting`
|
||||
|
||||
* Scheduled report generation (exportable)
|
||||
|
||||
### UI components
|
||||
|
||||
* `OutcomeDashboardComponent`
|
||||
* `AttributionBreakdownComponent`
|
||||
* `ExecutiveReportExportComponent`
|
||||
|
||||
## Guardrails
|
||||
|
||||
* Avoid vanity metrics (“number of chats”).
|
||||
* Always show confidence/limitations in attribution (correlation vs causation).
|
||||
|
||||
## KPIs
|
||||
|
||||
* MTTR delta (with Stella artifacts vs without)
|
||||
* Repeat incident reduction
|
||||
* Escalation reduction
|
||||
* Automation coverage growth
|
||||
|
||||
---
|
||||
|
||||
## One unifying implementation note: “AdvisoryAI” should output objects, not prose
|
||||
|
||||
To make all seven advisories work together, standardize on a small set of **AI-produced artifacts**:
|
||||
|
||||
* `Plan` (step list with tools/actions)
|
||||
* `EvidencePack`
|
||||
* `DecisionRecord`
|
||||
* `PlaybookPatch`
|
||||
* `IntegrationSetupChecklist`
|
||||
* `RunSummary` (postmortem-ready)
|
||||
|
||||
Every artifact is:
|
||||
|
||||
* versioned
|
||||
* evidence-linked
|
||||
* attached to a Run
|
||||
* subject to policy gates when it triggers actions
|
||||
|
||||
This gives you:
|
||||
|
||||
* auditability
|
||||
* deterministic replay of the *inputs and tool outputs*
|
||||
* compounding “Ops memory” and “Playbook learning” data
|
||||
|
||||
---
|
||||
|
||||
## Example: how this maps to “AdvisoryAI module + Chat UI component”
|
||||
|
||||
Minimum viable architecture that is coherent:
|
||||
|
||||
**Backend**
|
||||
|
||||
* `StellaOps.AdvisoryAI.WebService` (orchestrator)
|
||||
* `StellaOps.AdvisoryAI.Evidence` (citations)
|
||||
* `StellaOps.AdvisoryAI.Memory` (structured memory)
|
||||
* `StellaOps.AdvisoryAI.PlaybookLearning` (patch proposals)
|
||||
* `StellaOps.AdvisoryAI.Integrations` (setup guidance + checklists)
|
||||
* `StellaOps.PolicyEngine` + `StellaOps.ActionRegistry` (gated actions)
|
||||
* `StellaOps.RunLedger` + `StellaOps.EvidenceStore` (audit + snapshots)
|
||||
|
||||
**UI**
|
||||
|
||||
* AI chat panel that emits/accepts **artifact cards**
|
||||
* Evidence side panel
|
||||
* Approval modal
|
||||
* Diff viewer for patches
|
||||
* Integration wizard + health
|
||||
|
||||
---
|
||||
|
||||
If you want, I can translate these advisories into a single internal spec pack:
|
||||
|
||||
* database tables (Postgres)
|
||||
* event contracts (signals/actions)
|
||||
* JSON schemas for artifacts/actions
|
||||
* UI navigation and component tree
|
||||
* the first 10 “golden workflows” you should ship with the Workbench
|
||||
@@ -0,0 +1,639 @@
|
||||
# GitHub Code Scanning Integration via SARIF
|
||||
|
||||
> **Status:** Revised
|
||||
> **Original:** 09-Jan-2026 (Lighting Up GitHub with SARIF)
|
||||
> **Revision:** 09-Jan-2026
|
||||
> **Author:** Product/Engineering
|
||||
> **Epic:** Platform Integrations
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This advisory defines the complete integration between StellaOps Scanner and **GitHub Code Scanning** via SARIF 2.1.0. The integration enables StellaOps findings to appear natively in GitHub's Security tab with zero custom UI, leveraging GitHub's existing annotation, filtering, and alerting infrastructure.
|
||||
|
||||
### Current State
|
||||
|
||||
| Component | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| SARIF 2.1.0 Models | **Implemented** | Full schema in `Scanner.SmartDiff` |
|
||||
| SmartDiff SARIF Export | **Implemented** | Binary diff findings, production-ready |
|
||||
| Findings SARIF Export | **Not Implemented** | Main vulnerability findings |
|
||||
| GitHub App Connector | **Implemented** | Auth + health checks working |
|
||||
| GitHub Code Scanning Upload | **Not Implemented** | REST API client needed |
|
||||
| GitHub Actions Workflow | **Not Implemented** | Template generation needed |
|
||||
|
||||
### Business Value
|
||||
|
||||
- **Zero Custom UI:** GitHub renders findings, annotations, and PR decorations
|
||||
- **Native Integration:** Findings appear in Security tab alongside Dependabot/CodeQL
|
||||
- **Alert Management:** GitHub's existing dismiss/reopen/severity workflow
|
||||
- **PR Blocking:** Branch protection rules can require scan results
|
||||
- **Enterprise Ready:** Supports GitHub.com and GitHub Enterprise Server
|
||||
|
||||
---
|
||||
|
||||
## What SARIF Is
|
||||
|
||||
**SARIF** (Static Analysis Results Interchange Format) is an OASIS standard (version 2.1.0) for representing static analysis results. GitHub Code Scanning accepts a **subset** of SARIF 2.1.0 and renders it as security alerts.
|
||||
|
||||
### SARIF Structure
|
||||
|
||||
```
|
||||
SarifLog
|
||||
├── $schema: "https://json.schemastore.org/sarif-2.1.0.json"
|
||||
├── version: "2.1.0"
|
||||
└── runs[]
|
||||
├── tool
|
||||
│ └── driver
|
||||
│ ├── name: "StellaOps Scanner"
|
||||
│ ├── version: "1.0.0"
|
||||
│ └── rules[]
|
||||
│ ├── id: "STELLA-VULN-001"
|
||||
│ ├── name: "Critical Vulnerability"
|
||||
│ └── properties (CWE, CVSS, etc.)
|
||||
├── results[]
|
||||
│ ├── ruleId: "STELLA-VULN-001"
|
||||
│ ├── level: "error" | "warning" | "note"
|
||||
│ ├── message.text
|
||||
│ ├── locations[]
|
||||
│ │ └── physicalLocation
|
||||
│ │ ├── artifactLocation.uri
|
||||
│ │ └── region.startLine
|
||||
│ └── fingerprints (deduplication)
|
||||
└── versionControlProvenance (git metadata)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Three Upload Options
|
||||
|
||||
### Option 1: GitHub Actions (Recommended)
|
||||
|
||||
```yaml
|
||||
# .github/workflows/stellaops-scan.yml
|
||||
name: StellaOps Scan
|
||||
on:
|
||||
push:
|
||||
branches: [main, release/*]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
schedule:
|
||||
- cron: "0 3 * * 1" # Weekly Monday 3 AM
|
||||
|
||||
jobs:
|
||||
scan:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write # Required for Code Scanning
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run StellaOps Scanner
|
||||
uses: stellaops/scanner-action@v1
|
||||
with:
|
||||
image: ${{ github.repository }}:${{ github.sha }}
|
||||
output-format: sarif
|
||||
output-file: results.sarif
|
||||
# Optional: filter by severity
|
||||
min-severity: medium
|
||||
|
||||
- name: Upload SARIF to Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: stellaops-scanner
|
||||
# Optional: wait for processing
|
||||
wait-for-processing: true
|
||||
```
|
||||
|
||||
### Option 2: REST API
|
||||
|
||||
For scans running outside GitHub Actions:
|
||||
|
||||
```bash
|
||||
# Gzip + base64 encode the SARIF file
|
||||
gzip -c results.sarif | base64 -w0 > sarif.b64
|
||||
|
||||
# Upload to GitHub Code Scanning API
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"https://api.github.com/repos/OWNER/REPO/code-scanning/sarifs" \
|
||||
-d "{
|
||||
\"commit_sha\": \"$(git rev-parse HEAD)\",
|
||||
\"ref\": \"refs/heads/main\",
|
||||
\"sarif\": \"$(cat sarif.b64)\",
|
||||
\"tool_name\": \"StellaOps Scanner\"
|
||||
}"
|
||||
```
|
||||
|
||||
**Required Scopes:**
|
||||
- Public repos: `public_repo`
|
||||
- Private repos: `security_events`
|
||||
|
||||
### Option 3: StellaOps CLI
|
||||
|
||||
```bash
|
||||
# Scan and upload in one command
|
||||
stella scan \
|
||||
--image myregistry/myapp:latest \
|
||||
--sarif results.sarif \
|
||||
--github-upload \
|
||||
--github-token $GITHUB_TOKEN \
|
||||
--github-repo owner/repo \
|
||||
--github-ref refs/heads/main \
|
||||
--github-sha $(git rev-parse HEAD)
|
||||
|
||||
# Or: scan first, upload separately
|
||||
stella scan --image myregistry/myapp:latest --sarif results.sarif
|
||||
stella github upload \
|
||||
--sarif results.sarif \
|
||||
--repo owner/repo \
|
||||
--ref refs/heads/main
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## StellaOps SARIF Rule Taxonomy
|
||||
|
||||
### Vulnerability Rules (STELLA-VULN-*)
|
||||
|
||||
| Rule ID | Name | Level | Description |
|
||||
|---------|------|-------|-------------|
|
||||
| STELLA-VULN-001 | Critical Vulnerability | error | CVSS >= 9.0 or KEV-listed |
|
||||
| STELLA-VULN-002 | High Vulnerability | error | CVSS 7.0-8.9 |
|
||||
| STELLA-VULN-003 | Medium Vulnerability | warning | CVSS 4.0-6.9 |
|
||||
| STELLA-VULN-004 | Low Vulnerability | note | CVSS < 4.0 |
|
||||
| STELLA-VULN-005 | Reachable Vulnerability | error | Runtime-confirmed reachable |
|
||||
| STELLA-VULN-006 | Static Reachable Vulnerability | warning | Static-only reachable |
|
||||
|
||||
### Secret Detection Rules (STELLA-SEC-*)
|
||||
|
||||
| Rule ID | Name | Level | Description |
|
||||
|---------|------|-------|-------------|
|
||||
| STELLA-SEC-001 | Hardcoded Secret | error | API key, password, token in code |
|
||||
| STELLA-SEC-002 | Private Key Exposure | error | PEM, PKCS#8 private key |
|
||||
| STELLA-SEC-003 | Credential Pattern | warning | Potential credential pattern |
|
||||
|
||||
### Supply Chain Rules (STELLA-SC-*)
|
||||
|
||||
| Rule ID | Name | Level | Description |
|
||||
|---------|------|-------|-------------|
|
||||
| STELLA-SC-001 | Unsigned Package | warning | Package lacks signature |
|
||||
| STELLA-SC-002 | Unknown Provenance | warning | No SLSA provenance |
|
||||
| STELLA-SC-003 | Typosquat Candidate | error | Potential typosquatting |
|
||||
| STELLA-SC-004 | Deprecated Package | note | Package marked deprecated |
|
||||
|
||||
### Binary Hardening Rules (STELLA-BIN-*)
|
||||
|
||||
| Rule ID | Name | Level | Description |
|
||||
|---------|------|-------|-------------|
|
||||
| STELLA-BIN-001 | Missing RELRO | warning | No full RELRO |
|
||||
| STELLA-BIN-002 | No Stack Canary | warning | Stack protection disabled |
|
||||
| STELLA-BIN-003 | No PIE | warning | Position-independent disabled |
|
||||
| STELLA-BIN-004 | No Fortify | note | FORTIFY_SOURCE not used |
|
||||
|
||||
### SmartDiff Rules (SDIFF-*) - Already Implemented
|
||||
|
||||
| Rule ID | Name | Level | Description |
|
||||
|---------|------|-------|-------------|
|
||||
| SDIFF001 | Material Risk Change | warning | Risk profile changed |
|
||||
| SDIFF002 | Binary Hardening Regression | error | Security control removed |
|
||||
| SDIFF003 | VEX Candidate | note | VEX status changed |
|
||||
| SDIFF004 | Reachability Change | warning | Reachability status changed |
|
||||
|
||||
---
|
||||
|
||||
## SARIF Schema for Findings
|
||||
|
||||
### Complete Finding Example
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
|
||||
"runs": [{
|
||||
"tool": {
|
||||
"driver": {
|
||||
"name": "StellaOps Scanner",
|
||||
"version": "3.2.1",
|
||||
"informationUri": "https://stellaops.io/scanner",
|
||||
"rules": [{
|
||||
"id": "STELLA-VULN-001",
|
||||
"name": "CriticalVulnerability",
|
||||
"shortDescription": {
|
||||
"text": "Critical vulnerability detected"
|
||||
},
|
||||
"fullDescription": {
|
||||
"text": "A critical severity vulnerability (CVSS >= 9.0) was detected in a dependency."
|
||||
},
|
||||
"helpUri": "https://stellaops.io/rules/STELLA-VULN-001",
|
||||
"properties": {
|
||||
"precision": "high",
|
||||
"problem.severity": "error",
|
||||
"security-severity": "9.8",
|
||||
"tags": ["security", "vulnerability", "critical"]
|
||||
}
|
||||
}],
|
||||
"supportedTaxonomies": [{
|
||||
"name": "CWE",
|
||||
"guid": "d4c8a3c4-8f5e-4f3a-9a6b-2c7d8e9f0a1b"
|
||||
}]
|
||||
}
|
||||
},
|
||||
"taxonomies": [{
|
||||
"name": "CWE",
|
||||
"guid": "d4c8a3c4-8f5e-4f3a-9a6b-2c7d8e9f0a1b",
|
||||
"taxa": [{
|
||||
"id": "502",
|
||||
"name": "Deserialization of Untrusted Data"
|
||||
}]
|
||||
}],
|
||||
"results": [{
|
||||
"ruleId": "STELLA-VULN-001",
|
||||
"ruleIndex": 0,
|
||||
"level": "error",
|
||||
"message": {
|
||||
"text": "Critical vulnerability CVE-2021-44228 (Log4Shell) in org.apache.logging.log4j:log4j-core@2.14.1. CVSS: 10.0. This vulnerability allows remote code execution via JNDI injection.",
|
||||
"markdown": "**Critical vulnerability** [CVE-2021-44228](https://nvd.nist.gov/vuln/detail/CVE-2021-44228) (Log4Shell) in `org.apache.logging.log4j:log4j-core@2.14.1`.\n\n**CVSS:** 10.0 (Critical)\n\n**Description:** Remote code execution via JNDI injection.\n\n**Remediation:** Upgrade to log4j-core >= 2.17.0"
|
||||
},
|
||||
"locations": [{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "pom.xml",
|
||||
"uriBaseId": "%SRCROOT%"
|
||||
},
|
||||
"region": {
|
||||
"startLine": 45,
|
||||
"startColumn": 1,
|
||||
"endLine": 49,
|
||||
"endColumn": 1,
|
||||
"snippet": {
|
||||
"text": "<dependency>\n <groupId>org.apache.logging.log4j</groupId>\n <artifactId>log4j-core</artifactId>\n <version>2.14.1</version>\n</dependency>"
|
||||
}
|
||||
}
|
||||
},
|
||||
"logicalLocations": [{
|
||||
"name": "org.apache.logging.log4j:log4j-core",
|
||||
"kind": "package",
|
||||
"fullyQualifiedName": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1"
|
||||
}]
|
||||
}],
|
||||
"fingerprints": {
|
||||
"stellaops/v1": "sha256:a1b2c3d4e5f6...",
|
||||
"primaryLocationLineHash": "abc123..."
|
||||
},
|
||||
"partialFingerprints": {
|
||||
"primaryLocationLineHash": "abc123..."
|
||||
},
|
||||
"taxa": [{
|
||||
"id": "502",
|
||||
"toolComponent": {
|
||||
"name": "CWE"
|
||||
}
|
||||
}],
|
||||
"properties": {
|
||||
"stellaops.finding.id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
||||
"stellaops.component.purl": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1",
|
||||
"stellaops.vulnerability.cve": "CVE-2021-44228",
|
||||
"stellaops.vulnerability.cvss": 10.0,
|
||||
"stellaops.vulnerability.severity": "critical",
|
||||
"stellaops.vulnerability.epss": 0.975,
|
||||
"stellaops.vulnerability.kev": true,
|
||||
"stellaops.reachability.state": "RuntimeObserved",
|
||||
"stellaops.reachability.confidence": 0.92,
|
||||
"stellaops.vex.status": "affected",
|
||||
"stellaops.evidence.uris": [
|
||||
"stella://reachgraph/blake3:abc123",
|
||||
"stella://signals/runtime/tenant/sha256:def456"
|
||||
]
|
||||
}
|
||||
}],
|
||||
"artifacts": [{
|
||||
"location": {
|
||||
"uri": "pom.xml",
|
||||
"uriBaseId": "%SRCROOT%"
|
||||
},
|
||||
"mimeType": "application/xml",
|
||||
"hashes": {
|
||||
"sha-256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
}
|
||||
}],
|
||||
"versionControlProvenance": [{
|
||||
"repositoryUri": "https://github.com/example/myapp",
|
||||
"revisionId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
|
||||
"branch": "main",
|
||||
"mappedTo": {
|
||||
"uriBaseId": "%SRCROOT%"
|
||||
}
|
||||
}],
|
||||
"properties": {
|
||||
"stellaops.scan.id": "scan-12345",
|
||||
"stellaops.scan.artifact": "sha256:abc123...",
|
||||
"stellaops.scan.timestamp": "2026-01-09T10:30:00Z",
|
||||
"stellaops.scan.version": "3.2.1",
|
||||
"stellaops.attestation": {
|
||||
"digest": "sha256:sig789...",
|
||||
"predicateType": "https://stellaops.io/attestation/scan/v1",
|
||||
"rekorLogId": 12345678
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fingerprinting Strategy
|
||||
|
||||
Fingerprints enable GitHub to deduplicate alerts across scans:
|
||||
|
||||
### Primary Fingerprint (stellaops/v1)
|
||||
|
||||
```
|
||||
SHA-256(
|
||||
ruleId + "|" +
|
||||
component_purl + "|" +
|
||||
vulnerability_id + "|" +
|
||||
artifact_digest
|
||||
)
|
||||
```
|
||||
|
||||
### Partial Fingerprints (GitHub-computed fallback)
|
||||
|
||||
When source code is available, provide:
|
||||
- `primaryLocationLineHash`: Hash of code at finding location
|
||||
- `primaryLocationContextHash`: Hash of surrounding context
|
||||
|
||||
### Deduplication Behavior
|
||||
|
||||
| Scenario | GitHub Behavior |
|
||||
|----------|----------------|
|
||||
| Same fingerprint, new scan | Updates existing alert |
|
||||
| New fingerprint | Creates new alert |
|
||||
| Missing fingerprint in new scan | Closes alert as fixed |
|
||||
| Fingerprint reappears | Reopens alert |
|
||||
|
||||
---
|
||||
|
||||
## GitHub Code Scanning API Integration
|
||||
|
||||
### Upload Endpoint
|
||||
|
||||
```
|
||||
POST /repos/{owner}/{repo}/code-scanning/sarifs
|
||||
```
|
||||
|
||||
### Request Format
|
||||
|
||||
```json
|
||||
{
|
||||
"commit_sha": "a1b2c3d4e5f6...",
|
||||
"ref": "refs/heads/main",
|
||||
"sarif": "<gzip+base64 encoded SARIF>",
|
||||
"checkout_uri": "file:///home/runner/work/repo/repo",
|
||||
"started_at": "2026-01-09T10:00:00Z",
|
||||
"tool_name": "StellaOps Scanner"
|
||||
}
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "47177e22-5596-11eb-80a1-c1e54ef945c6",
|
||||
"url": "https://api.github.com/repos/owner/repo/code-scanning/sarifs/47177e22-5596-11eb-80a1-c1e54ef945c6"
|
||||
}
|
||||
```
|
||||
|
||||
### Status Polling
|
||||
|
||||
```
|
||||
GET /repos/{owner}/{repo}/code-scanning/sarifs/{sarif_id}
|
||||
```
|
||||
|
||||
Response includes `processing_status`: `pending` | `complete` | `failed`
|
||||
|
||||
---
|
||||
|
||||
## GitHub Enterprise Server Support
|
||||
|
||||
The existing `GitHubAppConnectorPlugin` supports GHES:
|
||||
|
||||
```csharp
|
||||
// Endpoint resolution
|
||||
var apiBase = isEnterprise
|
||||
? $"https://{hostname}/api/v3"
|
||||
: "https://api.github.com";
|
||||
```
|
||||
|
||||
### GHES Configuration
|
||||
|
||||
```yaml
|
||||
# etc/integrations.yaml
|
||||
github:
|
||||
type: github_enterprise
|
||||
hostname: github.mycompany.com
|
||||
api_version: "2022-11-28"
|
||||
app_id: 12345
|
||||
private_key_path: /secrets/github-app.pem
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Important Gotchas
|
||||
|
||||
### 1. One Tool Per Run (June 2025 Deadline)
|
||||
|
||||
GitHub is deprecating combined runs. Each tool must have its own run:
|
||||
|
||||
```json
|
||||
{
|
||||
"runs": [
|
||||
{ "tool": { "driver": { "name": "StellaOps Scanner" } }, "results": [...] },
|
||||
{ "tool": { "driver": { "name": "StellaOps SmartDiff" } }, "results": [...] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Not:**
|
||||
```json
|
||||
{
|
||||
"runs": [
|
||||
{
|
||||
"tool": { "driver": { "name": "StellaOps" } },
|
||||
"results": [/* mixed scanner + smartdiff */]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Permissions
|
||||
|
||||
| Context | Required Permission |
|
||||
|---------|---------------------|
|
||||
| GitHub Actions | `security-events: write` |
|
||||
| REST API (public) | `public_repo` scope |
|
||||
| REST API (private) | `security_events` scope |
|
||||
| GitHub App | `security_events: write` |
|
||||
|
||||
### 3. PR Uploads from Forks
|
||||
|
||||
- Direct API uploads from forks have restrictions
|
||||
- Use `github/codeql-action/upload-sarif` action instead
|
||||
- The action handles fork context correctly
|
||||
|
||||
### 4. SARIF Size Limits
|
||||
|
||||
| Limit | Value |
|
||||
|-------|-------|
|
||||
| Uncompressed | 10 MB |
|
||||
| Compressed (gzip) | Recommended for API |
|
||||
| Results per run | 10,000 (soft limit) |
|
||||
|
||||
### 5. Rate Limits
|
||||
|
||||
- 1000 requests/hour for Code Scanning API
|
||||
- Use conditional requests (`If-None-Match`) where possible
|
||||
|
||||
---
|
||||
|
||||
## Integration Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ StellaOps Scanner │
|
||||
│ ┌─────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │
|
||||
│ │ Scan Engine │──>│ Findings Ledger │──>│ SARIF Export Service │ │
|
||||
│ └─────────────┘ └─────────────────┘ │ - FindingsSarifMapper │ │
|
||||
│ │ - SarifRuleRegistry │ │
|
||||
│ │ - FingerprintGenerator │ │
|
||||
│ └─────────────┬───────────────┘ │
|
||||
└─────────────────────────────────────────────────────────┼───────────────────┘
|
||||
│
|
||||
│ SARIF 2.1.0
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ GitHub Integration Service │
|
||||
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────┐ │
|
||||
│ │ GitHubAppConnector │ │ CodeScanningClient │ │ SarifUploader │ │
|
||||
│ │ (existing) │ │ (new) │ │ (new) │ │
|
||||
│ └─────────────────────┘ └─────────────────────┘ └─────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────┬───────────────────┘
|
||||
│
|
||||
│ REST API / Actions
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ GitHub Code Scanning │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │
|
||||
│ │ Security Tab │ │ PR Annotations │ │ Branch Protection │ │
|
||||
│ │ Alerts │ │ Check Runs │ │ Rules │ │
|
||||
│ └─────────────────┘ └─────────────────┘ └─────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CLI Commands
|
||||
|
||||
### Scan with SARIF Output
|
||||
|
||||
```bash
|
||||
# Basic scan with SARIF output
|
||||
stella scan --image myapp:latest --format sarif --output results.sarif
|
||||
|
||||
# With severity filter
|
||||
stella scan --image myapp:latest --format sarif --output results.sarif \
|
||||
--min-severity high
|
||||
|
||||
# With reachability evidence
|
||||
stella scan --image myapp:latest --format sarif --output results.sarif \
|
||||
--include-reachability
|
||||
|
||||
# Pretty-printed for debugging
|
||||
stella scan --image myapp:latest --format sarif --output results.sarif \
|
||||
--pretty
|
||||
```
|
||||
|
||||
### GitHub Upload
|
||||
|
||||
```bash
|
||||
# Upload SARIF to GitHub Code Scanning
|
||||
stella github upload-sarif \
|
||||
--sarif results.sarif \
|
||||
--repo owner/repo \
|
||||
--ref refs/heads/main \
|
||||
--sha $(git rev-parse HEAD)
|
||||
|
||||
# With GitHub Enterprise
|
||||
stella github upload-sarif \
|
||||
--sarif results.sarif \
|
||||
--repo owner/repo \
|
||||
--ref refs/heads/main \
|
||||
--sha $(git rev-parse HEAD) \
|
||||
--github-url https://github.mycompany.com
|
||||
|
||||
# Wait for processing
|
||||
stella github upload-sarif \
|
||||
--sarif results.sarif \
|
||||
--repo owner/repo \
|
||||
--wait --timeout 5m
|
||||
```
|
||||
|
||||
### Generate Workflow
|
||||
|
||||
```bash
|
||||
# Generate GitHub Actions workflow
|
||||
stella github generate-workflow \
|
||||
--repo owner/repo \
|
||||
--output .github/workflows/stellaops-scan.yml \
|
||||
--triggers push,pull_request,schedule \
|
||||
--schedule "0 3 * * 1"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### Quantitative
|
||||
|
||||
| Metric | Target |
|
||||
|--------|--------|
|
||||
| SARIF schema validation | 100% pass rate |
|
||||
| Upload success rate | > 99% |
|
||||
| Processing time (1000 findings) | < 30 seconds |
|
||||
| Fingerprint stability | 100% (same input = same fingerprint) |
|
||||
|
||||
### Qualitative
|
||||
|
||||
- Findings appear correctly in GitHub Security tab
|
||||
- PR annotations show at correct line numbers
|
||||
- Alert deduplication works across scans
|
||||
- Branch protection rules can gate on scan results
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [SARIF 2.1.0 Specification (OASIS)](https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html)
|
||||
- [GitHub SARIF Support](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning)
|
||||
- [GitHub Code Scanning REST API](https://docs.github.com/en/rest/code-scanning)
|
||||
- [upload-sarif Action](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github)
|
||||
|
||||
---
|
||||
|
||||
## Sprint Index
|
||||
|
||||
| Sprint ID | Title | Status |
|
||||
|-----------|-------|--------|
|
||||
| SPRINT_20260109_010_000 | INDEX: GitHub Code Scanning | Planning |
|
||||
| SPRINT_20260109_010_001 | Findings SARIF Exporter | Planning |
|
||||
| SPRINT_20260109_010_002 | GitHub Code Scanning Client | Planning |
|
||||
| SPRINT_20260109_010_003 | CI/CD Workflow Templates | Planning |
|
||||
|
||||
---
|
||||
|
||||
_Last updated: 09-Jan-2026_
|
||||
@@ -0,0 +1,828 @@
|
||||
# Hybrid Reachability and VEX Integration
|
||||
|
||||
> **Status:** Revised
|
||||
> **Original:** 09-Jan-2026
|
||||
> **Revision:** 09-Jan-2026
|
||||
> **Author:** Product/Engineering
|
||||
> **Epic:** Evidence-First Vulnerability Triage
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This advisory defines the **Hybrid Reachability System** - a unified approach to determining vulnerability exploitability by combining **static call-graph analysis** with **runtime execution evidence** to produce **high-confidence VEX verdicts**. The system enables StellaOps to answer: "Is this CVE actually reachable in my running application?"
|
||||
|
||||
### Key Differentiators
|
||||
|
||||
1. **Evidence-First:** Every VEX verdict backed by auditable, reproducible evidence
|
||||
2. **Lattice-Based Decisions:** Mathematically sound state transitions (never weaker than strongest evidence)
|
||||
3. **Offline-Capable:** Full functionality in air-gapped environments via bundle replay
|
||||
4. **Deterministic:** Same inputs always produce same verdicts (auditable, reproducible)
|
||||
|
||||
---
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Current vulnerability scanners generate excessive noise:
|
||||
- **70-90% of CVEs** flagged are not reachable in actual code paths
|
||||
- **Static-only analysis** over-approximates (flags dead code, feature-flagged paths)
|
||||
- **Runtime-only analysis** under-approximates (misses rarely-executed paths)
|
||||
- **No evidence chain** connecting scanner output to VEX justification
|
||||
|
||||
### Current State in StellaOps
|
||||
|
||||
| Component | Status | Gap |
|
||||
|-----------|--------|-----|
|
||||
| Static call-graph extraction | Implemented (5 languages) | No unified query interface |
|
||||
| ReachGraph storage | Implemented | No runtime evidence merge |
|
||||
| Runtime fact ingestion | Contract defined | No collection agent |
|
||||
| VEX decision emission | Implemented | No reachability-aware justification |
|
||||
| Evidence-weighted scoring | Implemented (6 dimensions) | RTS dimension needs runtime feed |
|
||||
| Signals module | Implemented | Missing runtime trace pipeline |
|
||||
|
||||
---
|
||||
|
||||
## Solution Architecture
|
||||
|
||||
### Conceptual Model
|
||||
|
||||
```
|
||||
STATIC ANALYSIS RUNTIME ANALYSIS
|
||||
| |
|
||||
+--------------------+--------------------+ +---------------+---------------+
|
||||
| | | |
|
||||
v v v v
|
||||
+--------+ +----------+ +---------+ +----------+ +--------+ +--------+
|
||||
| SBOM |--->| CallGraph|--->|ReachGraph| | Runtime |--->| Symbol |--->|Reachability|
|
||||
|Generator| | Extractor| | Service | | Agent | |Normalizer| | Index |
|
||||
+--------+ +----------+ +---------+ +----------+ +--------+ +--------+
|
||||
| |
|
||||
+---------------+---------------+
|
||||
|
|
||||
v
|
||||
+----------------+
|
||||
| VEX Decision |
|
||||
| Filter |
|
||||
+----------------+
|
||||
|
|
||||
v
|
||||
+----------------+
|
||||
| OpenVEX Output |
|
||||
| + Evidence |
|
||||
+----------------+
|
||||
```
|
||||
|
||||
### Component Responsibilities
|
||||
|
||||
#### 1. Reachability Core Library (NEW)
|
||||
|
||||
**Location:** `src/__Libraries/StellaOps.Reachability.Core/`
|
||||
|
||||
Provides the unified `IReachabilityIndex` interface that facades over:
|
||||
- ReachGraph (static call-graph queries)
|
||||
- Signals (runtime facts)
|
||||
- CVE-Symbol mapping corpus
|
||||
|
||||
```csharp
|
||||
public interface IReachabilityIndex
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if symbol exists in static call graph from any entrypoint.
|
||||
/// </summary>
|
||||
Task<StaticReachabilityResult> QueryStaticAsync(
|
||||
SymbolRef symbol,
|
||||
string artifactDigest,
|
||||
CancellationToken ct);
|
||||
|
||||
/// <summary>
|
||||
/// Check if symbol was observed at runtime within observation window.
|
||||
/// </summary>
|
||||
Task<RuntimeReachabilityResult> QueryRuntimeAsync(
|
||||
SymbolRef symbol,
|
||||
string artifactDigest,
|
||||
TimeSpan observationWindow,
|
||||
CancellationToken ct);
|
||||
|
||||
/// <summary>
|
||||
/// Compute hybrid reachability score combining static + runtime evidence.
|
||||
/// </summary>
|
||||
Task<HybridReachabilityResult> QueryHybridAsync(
|
||||
SymbolRef symbol,
|
||||
string artifactDigest,
|
||||
HybridQueryOptions options,
|
||||
CancellationToken ct);
|
||||
|
||||
/// <summary>
|
||||
/// Batch query for multiple symbols (CVE-related).
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<HybridReachabilityResult>> QueryBatchAsync(
|
||||
IEnumerable<SymbolRef> symbols,
|
||||
string artifactDigest,
|
||||
HybridQueryOptions options,
|
||||
CancellationToken ct);
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Symbol Canonicalization Service (NEW)
|
||||
|
||||
**Location:** `src/__Libraries/StellaOps.Reachability.Core/Symbols/`
|
||||
|
||||
Normalizes symbols across different sources:
|
||||
- Static: Roslyn (`Namespace.Type.Method`), ASM (`class.method(descriptor)`)
|
||||
- Runtime: JIT names, ETW method IDs, eBPF uprobe symbols
|
||||
- Native: Mangled C++, demangled, ELF symbols
|
||||
|
||||
```csharp
|
||||
public interface ISymbolCanonicalizer
|
||||
{
|
||||
/// <summary>
|
||||
/// Canonicalize symbol to portable format.
|
||||
/// </summary>
|
||||
CanonicalSymbol Canonicalize(RawSymbol raw, SymbolSource source);
|
||||
|
||||
/// <summary>
|
||||
/// Match two symbols with fuzzy tolerance for minor variations.
|
||||
/// </summary>
|
||||
SymbolMatchResult Match(CanonicalSymbol a, CanonicalSymbol b, MatchOptions options);
|
||||
}
|
||||
|
||||
public sealed record CanonicalSymbol
|
||||
{
|
||||
public required string Purl { get; init; } // pkg:npm/lodash@4.17.21
|
||||
public required string Namespace { get; init; } // lodash
|
||||
public required string Type { get; init; } // _ (for JS) or class name
|
||||
public required string Method { get; init; } // get
|
||||
public required string Signature { get; init; } // (object, string)
|
||||
public required string CanonicalId { get; init; } // SHA-256 of above
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. CVE-Symbol Mapping Corpus (NEW)
|
||||
|
||||
**Location:** `src/__Libraries/StellaOps.Reachability.Core/CveMapping/`
|
||||
|
||||
Maps CVE identifiers to vulnerable symbols:
|
||||
|
||||
```csharp
|
||||
public interface ICveSymbolMappingService
|
||||
{
|
||||
/// <summary>
|
||||
/// Get vulnerable symbols for a CVE.
|
||||
/// </summary>
|
||||
Task<CveSymbolMapping?> GetMappingAsync(string cveId, CancellationToken ct);
|
||||
|
||||
/// <summary>
|
||||
/// Ingest mapping from patch analysis or manual curation.
|
||||
/// </summary>
|
||||
Task IngestMappingAsync(CveSymbolMapping mapping, CancellationToken ct);
|
||||
}
|
||||
|
||||
public sealed record CveSymbolMapping
|
||||
{
|
||||
public required string CveId { get; init; }
|
||||
public required ImmutableArray<VulnerableSymbol> Symbols { get; init; }
|
||||
public required MappingSource Source { get; init; }
|
||||
public required double Confidence { get; init; }
|
||||
public required DateTimeOffset ExtractedAt { get; init; }
|
||||
public string? PatchCommitUrl { get; init; }
|
||||
public string? DeltaSigDigest { get; init; }
|
||||
}
|
||||
|
||||
public sealed record VulnerableSymbol
|
||||
{
|
||||
public required CanonicalSymbol Symbol { get; init; }
|
||||
public required VulnerabilityType Type { get; init; } // Sink, TaintSource, GadgetEntry
|
||||
public string? Condition { get; init; } // e.g., "when input > 1024"
|
||||
}
|
||||
|
||||
public enum MappingSource
|
||||
{
|
||||
PatchAnalysis, // Automated extraction from git diff
|
||||
OsvDatabase, // OSV affected ranges with functions
|
||||
ManualCuration, // Security researcher input
|
||||
DeltaSignature, // Binary diff signature match
|
||||
AiExtraction // LLM-assisted extraction from description
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. Runtime Agent Framework (NEW)
|
||||
|
||||
**Location:** `src/Signals/StellaOps.Signals.RuntimeAgent/`
|
||||
|
||||
Pluggable runtime collection agents:
|
||||
|
||||
| Platform | Agent Type | Collection Method | Overhead |
|
||||
|----------|-----------|-------------------|----------|
|
||||
| .NET | EventPipe | CLR method enter/leave | <2% |
|
||||
| .NET | CLR Profiler | ICorProfiler callbacks | <5% |
|
||||
| Java | JFR | Flight Recorder events | <1% |
|
||||
| Java | JVMTI | Agent attach | <3% |
|
||||
| Node.js | V8 Profiler | CPU profiler | <2% |
|
||||
| Python | sys.settrace | Frame tracing | <10% |
|
||||
| Native | eBPF | uprobe/uretprobe | <1% |
|
||||
| Windows | ETW | CLR/JScript providers | <1% |
|
||||
|
||||
```csharp
|
||||
public interface IRuntimeAgent
|
||||
{
|
||||
string AgentId { get; }
|
||||
RuntimePlatform Platform { get; }
|
||||
RuntimePosture Posture { get; }
|
||||
|
||||
Task StartAsync(RuntimeAgentOptions options, CancellationToken ct);
|
||||
Task StopAsync(CancellationToken ct);
|
||||
|
||||
IAsyncEnumerable<RuntimeMethodEvent> StreamEventsAsync(CancellationToken ct);
|
||||
}
|
||||
|
||||
public sealed record RuntimeMethodEvent
|
||||
{
|
||||
public required string SymbolId { get; init; }
|
||||
public required string MethodName { get; init; }
|
||||
public required string TypeName { get; init; }
|
||||
public required string AssemblyOrModule { get; init; }
|
||||
public required DateTimeOffset Timestamp { get; init; }
|
||||
public required RuntimeEventKind Kind { get; init; } // Enter, Leave, Tail
|
||||
public string? ContainerId { get; init; }
|
||||
public int? ProcessId { get; init; }
|
||||
public string? ThreadId { get; init; }
|
||||
public IReadOnlyDictionary<string, string>? Context { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
#### 5. VEX Decision Filter Enhancement (ENHANCEMENT)
|
||||
|
||||
**Location:** `src/Policy/StellaOps.Policy.Engine/Vex/`
|
||||
|
||||
Enhance existing `IVexDecisionEmitter` with reachability-aware justification:
|
||||
|
||||
```csharp
|
||||
public interface IReachabilityAwareVexEmitter
|
||||
{
|
||||
/// <summary>
|
||||
/// Emit VEX verdict with hybrid reachability evidence.
|
||||
/// </summary>
|
||||
Task<VexDecisionDocument> EmitVerdictAsync(
|
||||
Finding finding,
|
||||
HybridReachabilityResult reachability,
|
||||
VexEmissionOptions options,
|
||||
CancellationToken ct);
|
||||
}
|
||||
```
|
||||
|
||||
### Reachability Lattice (8-State Model)
|
||||
|
||||
The system uses an 8-state lattice for evidence strength:
|
||||
|
||||
```
|
||||
X (Contested)
|
||||
/ \
|
||||
/ \
|
||||
CR (Confirmed CU (Confirmed
|
||||
Reachable) Unreachable)
|
||||
| \ / |
|
||||
| \ / |
|
||||
RO (Runtime RU (Runtime
|
||||
Observed) Unobserved)
|
||||
| |
|
||||
| |
|
||||
SR (Static SU (Static
|
||||
Reachable) Unreachable)
|
||||
\ /
|
||||
\ /
|
||||
U (Unknown)
|
||||
```
|
||||
|
||||
**State Transitions:**
|
||||
|
||||
| From | Evidence | To | Confidence Delta |
|
||||
|------|----------|-----|-----------------|
|
||||
| U | Static analysis finds path | SR | +0.3 |
|
||||
| U | Static analysis proves no path | SU | +0.4 |
|
||||
| SR | Runtime observes execution | RO | +0.3 |
|
||||
| SR | Runtime window expires with no observation | RU | +0.2 |
|
||||
| SU | Runtime observes execution (unexpected!) | X | -0.2 (conflict) |
|
||||
| RO | Multiple sources confirm | CR | +0.2 |
|
||||
| RU | Multiple sources confirm | CU | +0.2 |
|
||||
|
||||
### VEX Justification Mapping
|
||||
|
||||
| Lattice State | VEX Status | Justification | Confidence Range |
|
||||
|---------------|------------|---------------|------------------|
|
||||
| CU | not_affected | vulnerable_code_not_in_execute_path | 0.90-1.00 |
|
||||
| RU | not_affected | vulnerable_code_not_in_execute_path | 0.70-0.89 |
|
||||
| SU | not_affected | vulnerable_code_not_in_execute_path | 0.50-0.69 |
|
||||
| CR | affected | - | 0.90-1.00 |
|
||||
| RO | affected | - | 0.70-0.89 |
|
||||
| SR | under_investigation | - | 0.30-0.69 |
|
||||
| U | under_investigation | - | 0.00-0.29 |
|
||||
| X | under_investigation | - | requires_manual_review |
|
||||
|
||||
---
|
||||
|
||||
## Data Contracts
|
||||
|
||||
### 1. Static Reachability (existing, enhanced)
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "reachability.static@v2",
|
||||
"artifactDigest": "sha256:abc123...",
|
||||
"component": "pkg:npm/lodash@4.17.21",
|
||||
"symbols": [
|
||||
{
|
||||
"canonicalId": "sha256:def456...",
|
||||
"namespace": "lodash",
|
||||
"type": "_",
|
||||
"method": "get",
|
||||
"signature": "(object, string)",
|
||||
"entrypoints": ["main", "handleRequest"],
|
||||
"pathLength": 3,
|
||||
"guards": [
|
||||
{"type": "FeatureFlag", "key": "ENABLE_LODASH", "value": "true"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"extractedAt": "2026-01-09T10:00:00Z",
|
||||
"extractorVersion": "scanner.callgraph@3.2.1"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Runtime Reachability (new)
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "reachability.runtime@v1",
|
||||
"artifactDigest": "sha256:abc123...",
|
||||
"observationWindow": {
|
||||
"start": "2026-01-01T00:00:00Z",
|
||||
"end": "2026-01-09T00:00:00Z",
|
||||
"durationDays": 8
|
||||
},
|
||||
"trafficProfile": {
|
||||
"requestCount": 1250000,
|
||||
"percentile": "p95",
|
||||
"environments": ["production"]
|
||||
},
|
||||
"symbols": [
|
||||
{
|
||||
"canonicalId": "sha256:def456...",
|
||||
"hitCount": 45230,
|
||||
"firstSeen": "2026-01-02T14:23:00Z",
|
||||
"lastSeen": "2026-01-08T22:15:00Z",
|
||||
"contexts": [
|
||||
{
|
||||
"containerId": "abc123",
|
||||
"processId": 1234,
|
||||
"route": "/api/v1/users",
|
||||
"frequency": 0.82
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"collectedAt": "2026-01-09T00:05:00Z",
|
||||
"agentVersion": "signals.runtime@1.0.0",
|
||||
"agentPosture": "ActiveTracing"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Hybrid Reachability Result
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "reachability.hybrid@v1",
|
||||
"artifactDigest": "sha256:abc123...",
|
||||
"symbol": {
|
||||
"canonicalId": "sha256:def456...",
|
||||
"displayName": "lodash.get(object, string)"
|
||||
},
|
||||
"latticeState": "RO",
|
||||
"confidence": 0.85,
|
||||
"staticEvidence": {
|
||||
"present": true,
|
||||
"pathCount": 3,
|
||||
"shortestPath": 2,
|
||||
"guards": []
|
||||
},
|
||||
"runtimeEvidence": {
|
||||
"present": true,
|
||||
"hitCount": 45230,
|
||||
"lastSeen": "2026-01-08T22:15:00Z",
|
||||
"windowDays": 8
|
||||
},
|
||||
"verdict": {
|
||||
"status": "affected",
|
||||
"justification": null,
|
||||
"confidenceBucket": "high"
|
||||
},
|
||||
"evidenceUris": [
|
||||
"stella://reachgraph/sha256:abc123/slice?symbol=sha256:def456",
|
||||
"stella://signals/runtime/sha256:abc123?symbol=sha256:def456"
|
||||
],
|
||||
"computedAt": "2026-01-09T10:30:00Z",
|
||||
"computedBy": "reachability.index@1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. OpenVEX with StellaOps Evidence Extension
|
||||
|
||||
```json
|
||||
{
|
||||
"@context": "https://openvex.dev/ns/v0.2.0",
|
||||
"author": "StellaOps Policy Engine",
|
||||
"timestamp": "2026-01-09T10:35:00Z",
|
||||
"version": 1,
|
||||
"statements": [
|
||||
{
|
||||
"vulnerability": {
|
||||
"@id": "CVE-2021-44228",
|
||||
"name": "Log4Shell",
|
||||
"description": "Apache Log4j2 JNDI injection"
|
||||
},
|
||||
"products": [
|
||||
{
|
||||
"@id": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1",
|
||||
"subcomponents": [
|
||||
{"@id": "pkg:maven/org.apache.logging.log4j/log4j-api@2.14.1"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"status": "not_affected",
|
||||
"justification": "vulnerable_code_not_in_execute_path",
|
||||
"impact_statement": "The vulnerable JNDI lookup is not reachable from any application entrypoint.",
|
||||
"action_statement": "No action required. Monitor for code changes that may introduce reachability.",
|
||||
"x-stellaops-evidence": {
|
||||
"schemaVersion": "stellaops.evidence@v1",
|
||||
"latticeState": "CU",
|
||||
"confidence": 0.95,
|
||||
"staticAnalysis": {
|
||||
"graphDigest": "blake3:abc123...",
|
||||
"pathCount": 0,
|
||||
"analyzerVersion": "scanner.callgraph@3.2.1"
|
||||
},
|
||||
"runtimeAnalysis": {
|
||||
"observationWindowDays": 14,
|
||||
"trafficPercentile": "p95",
|
||||
"hitCount": 0,
|
||||
"agentPosture": "EbpfDeep"
|
||||
},
|
||||
"cveSymbolMapping": {
|
||||
"source": "PatchAnalysis",
|
||||
"vulnerableSymbols": [
|
||||
"org.apache.logging.log4j.core.lookup.JndiLookup::lookup"
|
||||
],
|
||||
"mappingConfidence": 0.98
|
||||
},
|
||||
"evidenceUris": [
|
||||
"stella://reachgraph/blake3:abc123",
|
||||
"stella://signals/runtime/tenant123/sha256:def456"
|
||||
],
|
||||
"attestation": {
|
||||
"dsseDigest": "sha256:sig789...",
|
||||
"rekorLogIndex": 12345678
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Evidence URI Scheme
|
||||
|
||||
Define `stella://` URI scheme for evidence references:
|
||||
|
||||
| Pattern | Description | Example |
|
||||
|---------|-------------|---------|
|
||||
| `stella://reachgraph/{digest}` | Full reachability graph | `stella://reachgraph/blake3:abc123` |
|
||||
| `stella://reachgraph/{digest}/slice?symbol={id}` | Symbol slice | `stella://reachgraph/blake3:abc123/slice?symbol=sha256:def` |
|
||||
| `stella://signals/runtime/{tenant}/{artifact}` | Runtime facts | `stella://signals/runtime/acme/sha256:abc` |
|
||||
| `stella://signals/runtime/{tenant}/{artifact}?symbol={id}` | Symbol runtime facts | `stella://signals/runtime/acme/sha256:abc?symbol=sha256:def` |
|
||||
| `stella://cvemap/{cveId}` | CVE-symbol mapping | `stella://cvemap/CVE-2021-44228` |
|
||||
| `stella://attestation/{digest}` | DSSE attestation | `stella://attestation/sha256:sig789` |
|
||||
|
||||
---
|
||||
|
||||
## Integration Architecture
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Scanner Module │
|
||||
│ ┌─────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │
|
||||
│ │ SBOM │──>│ CallGraph │──>│ ReachGraph Service │ │
|
||||
│ │ Generator │ │ Extractor │ │ (static reachability store) │ │
|
||||
│ └─────────────┘ └─────────────────┘ └─────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ Static edges + nodes
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Reachability Core Library │
|
||||
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
|
||||
│ │ Symbol │ │ CVE-Symbol │ │ IReachabilityIndex │ │
|
||||
│ │ Canonicalizer │ │ Mapping Service │ │ (unified query facade) │ │
|
||||
│ └──────────────────┘ └──────────────────┘ └──────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
▲
|
||||
│ Runtime facts
|
||||
│
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Signals Module │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │
|
||||
│ │ Runtime Agent │──>│ Symbol │──>│ RuntimeFacts Store │ │
|
||||
│ │ (.NET/Java/etc) │ │ Normalizer │ │ (Valkey + PostgreSQL) │ │
|
||||
│ └─────────────────┘ └─────────────────┘ └─────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ Hybrid queries
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Policy Engine │
|
||||
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
|
||||
│ │ VEX Decision │ │ Reachability- │ │ Evidence-Weighted │ │
|
||||
│ │ Emitter │<─│ Aware Filter │<─│ Score Calculator │ │
|
||||
│ └──────────────────┘ └──────────────────┘ └──────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ VEX verdicts
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ VexLens Module │
|
||||
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐ │
|
||||
│ │ Consensus │ │ Trust Weight │ │ Conflict │ │
|
||||
│ │ Engine │ │ Engine │ │ Resolution │ │
|
||||
│ └──────────────────┘ └──────────────────┘ └──────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### API Endpoints (New/Enhanced)
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| POST | `/v1/reachability/query` | Query hybrid reachability for symbol |
|
||||
| POST | `/v1/reachability/query/batch` | Batch query for CVE symbols |
|
||||
| GET | `/v1/reachability/artifact/{digest}/summary` | Artifact reachability summary |
|
||||
| POST | `/v1/runtime/ingest` | Ingest runtime facts from agent |
|
||||
| GET | `/v1/runtime/facts/{artifact}` | Get runtime facts for artifact |
|
||||
| POST | `/v1/cvemap/ingest` | Ingest CVE-symbol mapping |
|
||||
| GET | `/v1/cvemap/{cveId}` | Get CVE-symbol mapping |
|
||||
| POST | `/v1/vex/emit/reachability-aware` | Emit VEX with reachability evidence |
|
||||
|
||||
---
|
||||
|
||||
## Air-Gap Support
|
||||
|
||||
### Offline Bundle Format
|
||||
|
||||
```
|
||||
hybrid-reachability-bundle/
|
||||
├── manifest.json # Bundle metadata, digests
|
||||
├── static/
|
||||
│ ├── reachgraphs.jsonl # CallGraph snapshots
|
||||
│ └── reachgraphs.sig # DSSE signatures
|
||||
├── runtime/
|
||||
│ ├── facts.jsonl # Runtime observations
|
||||
│ └── facts.sig # DSSE signatures
|
||||
├── cvemap/
|
||||
│ ├── mappings.jsonl # CVE-symbol mappings
|
||||
│ └── mappings.sig # DSSE signatures
|
||||
├── verdicts/
|
||||
│ ├── vex-decisions.jsonl # Pre-computed VEX verdicts
|
||||
│ └── vex-decisions.sig # DSSE signatures
|
||||
└── signatures/
|
||||
└── bundle.dsse # Bundle attestation
|
||||
```
|
||||
|
||||
### Offline Workflow
|
||||
|
||||
1. **Export** (connected environment):
|
||||
```bash
|
||||
stella reachability export --artifact sha256:abc123 --output bundle.zip
|
||||
```
|
||||
|
||||
2. **Transfer** bundle to air-gapped environment
|
||||
|
||||
3. **Import** (air-gapped environment):
|
||||
```bash
|
||||
stella reachability import --bundle bundle.zip --verify
|
||||
```
|
||||
|
||||
4. **Query** (air-gapped, uses cached data):
|
||||
```bash
|
||||
stella reachability query --cve CVE-2021-44228 --artifact sha256:abc123
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Determinism Guarantees
|
||||
|
||||
### Reproducibility Requirements
|
||||
|
||||
1. **Canonical Serialization:** RFC 8785 JSON (sorted keys, no nulls, minimal escaping)
|
||||
2. **Stable Symbol IDs:** SHA-256 of canonical symbol representation
|
||||
3. **Time Injection:** All timestamps via `TimeProvider` (testable, replayable)
|
||||
4. **Culture Invariance:** `InvariantCulture` for all string operations
|
||||
5. **Ordered Collections:** `ImmutableSortedSet` / `ImmutableSortedDictionary` for deterministic iteration
|
||||
|
||||
### Replay Verification
|
||||
|
||||
```csharp
|
||||
public interface IReachabilityReplayService
|
||||
{
|
||||
/// <summary>
|
||||
/// Replay hybrid reachability computation from inputs.
|
||||
/// </summary>
|
||||
Task<ReplayResult> ReplayAsync(
|
||||
HybridReachabilityInputs inputs,
|
||||
HybridReachabilityResult expected,
|
||||
CancellationToken ct);
|
||||
}
|
||||
|
||||
public sealed record ReplayResult
|
||||
{
|
||||
public required bool Match { get; init; }
|
||||
public required string ExpectedDigest { get; init; }
|
||||
public required string ActualDigest { get; init; }
|
||||
public IReadOnlyList<string>? Differences { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Access Control
|
||||
|
||||
| Resource | Read | Write | Admin |
|
||||
|----------|------|-------|-------|
|
||||
| Static reachability | `reachability:read` | `reachability:write` | - |
|
||||
| Runtime facts | `runtime:read` | `runtime:write` | - |
|
||||
| CVE mappings | `cvemap:read` | `cvemap:write` | `cvemap:admin` |
|
||||
| VEX verdicts | `vex:read` | `vex:write` | - |
|
||||
|
||||
### Data Sensitivity
|
||||
|
||||
- **Runtime traces expose code paths:** May reveal internal architecture
|
||||
- **Symbol names may be sensitive:** Obfuscation support planned
|
||||
- **Tenant isolation:** RLS policies enforce strict separation
|
||||
|
||||
### Threat Model
|
||||
|
||||
| Threat | Mitigation |
|
||||
|--------|------------|
|
||||
| Malicious runtime agent | Agent authentication via mTLS, signed events |
|
||||
| CVE mapping poisoning | Mapping provenance tracking, multi-source consensus |
|
||||
| Evidence tampering | DSSE attestations, Rekor transparency log |
|
||||
| Information leakage | Tenant RLS, encrypted storage, audit logs |
|
||||
|
||||
---
|
||||
|
||||
## Observability
|
||||
|
||||
### Metrics
|
||||
|
||||
| Metric | Description |
|
||||
|--------|-------------|
|
||||
| `reachability_query_duration_seconds` | Query latency histogram |
|
||||
| `reachability_lattice_state_total` | Count by lattice state |
|
||||
| `runtime_facts_ingested_total` | Runtime facts ingested |
|
||||
| `runtime_agent_connected_gauge` | Connected agents |
|
||||
| `cvemap_mappings_total` | CVE mappings count |
|
||||
| `vex_verdicts_by_reachability_total` | VEX verdicts by lattice state |
|
||||
|
||||
### Traces
|
||||
|
||||
| Span | Description |
|
||||
|------|-------------|
|
||||
| `reachability.query.static` | Static graph query |
|
||||
| `reachability.query.runtime` | Runtime facts query |
|
||||
| `reachability.query.hybrid` | Combined computation |
|
||||
| `reachability.canonicalize` | Symbol canonicalization |
|
||||
| `cvemap.lookup` | CVE-symbol lookup |
|
||||
|
||||
### Alerts
|
||||
|
||||
| Alert | Condition | Severity |
|
||||
|-------|-----------|----------|
|
||||
| `ReachabilityQuerySlow` | P95 > 500ms | warning |
|
||||
| `RuntimeAgentDisconnected` | No heartbeat 5min | warning |
|
||||
| `CveMappingStale` | No updates 7 days | info |
|
||||
| `LatticeConflictRate` | X state > 5% | warning |
|
||||
|
||||
---
|
||||
|
||||
## Performance Targets
|
||||
|
||||
| Operation | Target | Notes |
|
||||
|-----------|--------|-------|
|
||||
| Hybrid query (single symbol) | P95 < 50ms | Cached |
|
||||
| Hybrid query (batch, 100 symbols) | P95 < 500ms | Parallel |
|
||||
| Runtime fact ingestion | 10,000 events/sec | Per agent |
|
||||
| Symbol canonicalization | < 1ms | In-memory |
|
||||
| CVE mapping lookup | P95 < 10ms | Cached |
|
||||
| VEX emission | P95 < 100ms | Includes signing |
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Internal Modules
|
||||
|
||||
| Module | Dependency Type | Purpose |
|
||||
|--------|-----------------|---------|
|
||||
| ReachGraph | Data source | Static call-graph queries |
|
||||
| Signals | Data source + sink | Runtime fact storage |
|
||||
| Scanner.CallGraph | Data producer | Call-graph extraction |
|
||||
| Policy | Consumer | VEX decision emission |
|
||||
| VexLens | Consumer | Consensus computation |
|
||||
| Attestor | Integration | DSSE signing |
|
||||
| Authority | Integration | Access control |
|
||||
|
||||
### External Dependencies
|
||||
|
||||
| Dependency | Purpose | Offline Alternative |
|
||||
|------------|---------|---------------------|
|
||||
| OSV API | CVE-symbol enrichment | Bundled corpus |
|
||||
| NVD API | CVE details | Bundled corpus |
|
||||
| Rekor | Transparency log | Local log or skip |
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Foundation (Weeks 1-2)
|
||||
- Reachability Core library with `IReachabilityIndex`
|
||||
- Symbol canonicalization service
|
||||
- Integration with existing ReachGraph
|
||||
|
||||
### Phase 2: CVE Mapping (Weeks 3-4)
|
||||
- CVE-symbol mapping service
|
||||
- Patch analysis extractor (git diff parsing)
|
||||
- Initial corpus from high-profile CVEs
|
||||
|
||||
### Phase 3: Runtime Collection (Weeks 5-8)
|
||||
- Runtime agent framework
|
||||
- .NET EventPipe agent
|
||||
- Java JFR agent
|
||||
- Symbol normalization pipeline
|
||||
|
||||
### Phase 4: VEX Integration (Weeks 9-10)
|
||||
- Reachability-aware VEX emitter
|
||||
- Evidence extension schema
|
||||
- Policy Engine integration
|
||||
|
||||
### Phase 5: UI & Observability (Weeks 11-12)
|
||||
- Evidence panel enhancements
|
||||
- Metrics and alerts
|
||||
- Documentation and runbooks
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### Quantitative
|
||||
|
||||
| Metric | Target | Measurement |
|
||||
|--------|--------|-------------|
|
||||
| False positive reduction | >60% | CVEs marked NA with reachability evidence |
|
||||
| Confidence accuracy | >90% | Verdicts validated against manual review |
|
||||
| Query latency | P95 < 100ms | Hybrid queries |
|
||||
| Coverage | >80% | Artifacts with both static + runtime evidence |
|
||||
|
||||
### Qualitative
|
||||
|
||||
- Security teams trust VEX verdicts backed by evidence
|
||||
- Developers understand why CVE is/isn't relevant
|
||||
- Auditors can verify verdict provenance
|
||||
- Air-gapped deployments have full functionality
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [ReachGraph Architecture](../../modules/reach-graph/architecture.md)
|
||||
- [Signals Architecture](../../modules/signals/architecture.md)
|
||||
- [VexLens Architecture](../../modules/vex-lens/architecture.md)
|
||||
- [Evidence-Weighted Scoring](../../modules/signals/evidence-weighted-scoring.md)
|
||||
- [DSSE Attestation](../../modules/attestor/dsse-integration.md)
|
||||
|
||||
---
|
||||
|
||||
## Sprint Index
|
||||
|
||||
| Sprint ID | Title | Status |
|
||||
|-----------|-------|--------|
|
||||
| SPRINT_20260109_009_000 | INDEX: Hybrid Reachability | Planning |
|
||||
| SPRINT_20260109_009_001 | Reachability Core Library | Planning |
|
||||
| SPRINT_20260109_009_002 | Symbol Canonicalization | Planning |
|
||||
| SPRINT_20260109_009_003 | CVE-Symbol Mapping | Planning |
|
||||
| SPRINT_20260109_009_004 | Runtime Agent Framework | Planning |
|
||||
| SPRINT_20260109_009_005 | VEX Decision Integration | Planning |
|
||||
| SPRINT_20260109_009_006 | Evidence Panel UI | Planning |
|
||||
|
||||
---
|
||||
|
||||
_Last updated: 09-Jan-2026_
|
||||
@@ -0,0 +1,161 @@
|
||||
Here’s a simple way to make vulnerability triage almost noise‑free: combine **static SBOM call‑graph pruning** with **runtime reachability hints** before making a VEX decision.
|
||||
|
||||
---
|
||||
|
||||
# Why this matters (plain English)
|
||||
|
||||
* Static scanners flag lots of CVEs that your app never calls.
|
||||
* Runtimes (traces, signals) know what actually executed.
|
||||
* Merging the two yields “only the CVEs that matter,” so your VEX is credible and quiet.
|
||||
|
||||
---
|
||||
|
||||
# The hybrid model (static → dynamic → VEX)
|
||||
|
||||
1. **Static stage (Sbomer + Feedser assist)**
|
||||
|
||||
* Build SBOM (CycloneDX/SPDX).
|
||||
* Generate a **per‑package call graph** (library → functions → entrypoints).
|
||||
* Compute a **pruned reachable set** using import graphs, symbol tables, and package metadata.
|
||||
* Output: `reachability_static.json` (component → callable IDs).
|
||||
|
||||
2. **Dynamic stage (Signals + Scheduler traces)**
|
||||
|
||||
* Ingest **runtime traces** (e.g., eBPF/ETW/CLR Profiler/JFR) from Scheduler‑managed jobs.
|
||||
* Normalize to **reachability vectors**: `(component, symbol, freq, last_seen, context)`.
|
||||
* Output: `reachability_runtime.json`.
|
||||
|
||||
3. **Decision stage (Vexer)**
|
||||
|
||||
* For each CVE → vulnerable symbols (from advisories/OSVs, delta‑sigs if available).
|
||||
* If vulnerable symbol ∉ static set → **Not Affected (NA: not reachable)**.
|
||||
* If ∈ static but ∉ runtime → **Under Investigation / Likely NA** (confidence < 1).
|
||||
* If ∈ runtime → **Affected** with confidence and evidence URIs.
|
||||
* Emit **VEX** entries with justification (`not_provided_code_path`, `requires_config`, `fixed`, etc.) and confidence scores.
|
||||
|
||||
---
|
||||
|
||||
# Minimal PoC plan (2 weeks of evening work)
|
||||
|
||||
**Goal:** Let **Vexer** import runtime reachability vectors from **Scheduler** traces and use them to gate VEX verdicts.
|
||||
|
||||
### 0) Contracts (day 1)
|
||||
|
||||
* **Schema:**
|
||||
|
||||
* `reachability_static.jsonl`: `{ component, version, symbols:[...] }`
|
||||
* `reachability_runtime.jsonl`: `{ component, symbol, last_seen, count, context:{pid, container, route}}`
|
||||
* **Evidence URIs:** `stella://signals/<trace-id>#symbol=<name>`
|
||||
|
||||
### 1) Static extractor (days 2–4)
|
||||
|
||||
* Sbomer plugin: emit **symbol list + call graph** per artifact.
|
||||
* Start with **.NET**: use Roslyn/IL metadata to map `assembly → type → method`.
|
||||
* Heuristic fallbacks for native: ELF/PE export tables.
|
||||
|
||||
### 2) Runtime collector (days 3–6)
|
||||
|
||||
* Signals module:
|
||||
|
||||
* .NET CLR Profiler or EventPipe→method enter/leave (sampling ok).
|
||||
* Map back to `(assembly, type, method)` + container tags.
|
||||
* Scheduler: batch traces into **reachability vectors** and push to **Router**.
|
||||
|
||||
### 3) Vexer importer (days 6–8)
|
||||
|
||||
* New `Vexer.Reachability` package:
|
||||
|
||||
* Merge static + runtime with **component coordinate normalization** (purl).
|
||||
* `bool IsRuntimeReachable(component, symbol)`; `ReachabilityScore 0..1`.
|
||||
|
||||
### 4) VEX decision filter (days 8–10)
|
||||
|
||||
* For each CVE advisory record (from Feedser): vulnerable symbols → lookup.
|
||||
* Decision table:
|
||||
|
||||
* `Runtime=1` → **Affected (A)**.
|
||||
* `Static=1, Runtime=0` → **NA (Not reachable) with low confidence** unless policy overrides.
|
||||
* `Static=0` → **NA (No code path)**.
|
||||
* Emit CycloneDX VEX or CSAF with **justifications + evidence links**.
|
||||
|
||||
### 5) UI & Ops (days 10–14)
|
||||
|
||||
* **Stella UI:** badge per CVE: `Affected / NA / Probable NA`, hover shows symbols and last‑seen.
|
||||
* **Policy Engine:** org rules like “treat Probable NA as NA after 14 days of runtime with p95 traffic.”
|
||||
* **Notify:** only alert on `Affected` or `Probable NA → A` transitions.
|
||||
|
||||
---
|
||||
|
||||
# Data model & code stubs (illustrative)
|
||||
|
||||
**Vexer reachability interface**
|
||||
|
||||
```csharp
|
||||
public record SymbolRef(string Purl, string Assembly, string Type, string Method);
|
||||
|
||||
public interface IReachabilityIndex {
|
||||
bool InStatic(SymbolRef s);
|
||||
bool InRuntime(SymbolRef s, TimeSpan since, out int count);
|
||||
double Score(SymbolRef s); // 0..1 weighted by freq/recency
|
||||
}
|
||||
```
|
||||
|
||||
**Decision kernel**
|
||||
|
||||
```csharp
|
||||
VexVerdict Decide(Cve cve, IEnumerable<SymbolRef> vulnSyms, IReachabilityIndex idx) {
|
||||
var anyRuntime = vulnSyms.Any(s => idx.InRuntime(s, TimeSpan.FromDays(14), out _));
|
||||
var anyStatic = vulnSyms.Any(s => idx.InStatic(s));
|
||||
|
||||
return anyRuntime ? VexVerdict.Affected
|
||||
: anyStatic ? VexVerdict.ProbableNotAffected // gated by policy window
|
||||
: VexVerdict.NotAffected;
|
||||
}
|
||||
```
|
||||
|
||||
**Evidence snippet in VEX (CycloneDX)**
|
||||
|
||||
```json
|
||||
{
|
||||
"vulnerability": { "id": "CVE-2025-12345" },
|
||||
"analysis": {
|
||||
"state": "not_affected",
|
||||
"justification": "Vulnerable_code_not_in_execute_path",
|
||||
"response": [ "will_not_fix" ],
|
||||
"detail": "Symbols present but not executed across 14d p95 traffic",
|
||||
"evidence": [
|
||||
"stella://signals/trace-9f12#symbol=Contoso.Crypto.Rsa::Sign"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Where it plugs into Stella Ops
|
||||
|
||||
* **Sbomer**: adds call‑graph/symbol export plugin.
|
||||
* **Signals**: runtime tracer + compressor → reachability vectors.
|
||||
* **Scheduler**: tags traces by job/tenant/env; hands them to Router.
|
||||
* **Vexer**: new Reachability importer + decision filter.
|
||||
* **Feedser**: enriches CVEs with symbol hints (from OSV, delta‑sigs).
|
||||
* **Policy Engine**: organization rules for confidence windows.
|
||||
* **Notify/UI/Timeline**: surface verdicts, evidence, and flips over time.
|
||||
|
||||
---
|
||||
|
||||
# Guardrails & edge cases
|
||||
|
||||
* **Backports:** pair with your existing *binary‑delta signatures* so “fixed but looks vulnerable” becomes **NA (fixed by backport)** even if symbol name matches.
|
||||
* **Lazy paths & feature flags:** allow **context filters** (route, tenant, env) before declaring NA.
|
||||
* **Air‑gapped:** ship traces via offline bundle (`*.signals.zip`) and replay.
|
||||
|
||||
---
|
||||
|
||||
# Next steps I can do now
|
||||
|
||||
1. Draft the JSON schemas + purl mapping table.
|
||||
2. Add the `Vexer.Reachability` package and wire the decision filter.
|
||||
3. Provide a tiny sample repo (toy service + vulnerable lib) to demo **A → Probable NA → NA** as traffic evolves.
|
||||
|
||||
If you want, I’ll generate the initial schemas and a .NET tracer checklist so you can drop them into `docs/modules/`.
|
||||
@@ -0,0 +1,102 @@
|
||||
Here’s a quick, practical path to make your scanner’s findings show up in GitHub’s **Code scanning** UI with almost no integration work: emit **SARIF 2.1.0** and upload it.
|
||||
|
||||
---
|
||||
|
||||
### What SARIF is (and what GitHub expects)
|
||||
|
||||
* **SARIF** = Static Analysis Results Interchange Format, a JSON standard for static-analysis results. GitHub Code Scanning accepts a **subset** of **SARIF 2.1.0** and turns it into alerts in the Security tab. ([GitHub Docs][1])
|
||||
|
||||
---
|
||||
|
||||
### Three upload options (pick one)
|
||||
|
||||
1. **GitHub Actions**: add a step that uploads your SARIF file(s).
|
||||
|
||||
```yaml
|
||||
# .github/workflows/upload-sarif.yml
|
||||
name: Upload SARIF
|
||||
on:
|
||||
push:
|
||||
schedule: [{cron: "0 3 * * 1"}] # optional weekly recrawl
|
||||
jobs:
|
||||
sarif:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write # required to publish code scanning alerts
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run your scanner
|
||||
run: ./your_scanner --format sarif --out results.sarif
|
||||
- name: Upload SARIF to Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: your-scanner
|
||||
```
|
||||
|
||||
This uses GitHub’s official **upload-sarif** action and is the easiest route. ([GitHub Docs][2])
|
||||
|
||||
2. **REST API**: gzip + base64 your SARIF and POST to `code-scanning/sarifs` (needs `security_events` scope for private repos). Useful if you run scans outside Actions. ([GitHub Docs][3])
|
||||
|
||||
3. **CodeQL CLI**: `codeql github upload-results --sarif-file results.sarif` (also needs proper token). Handy if you’re already using the CLI in your CI. ([GitHub Docs][4])
|
||||
|
||||
---
|
||||
|
||||
### Minimal SARIF you should emit
|
||||
|
||||
At minimum, include:
|
||||
|
||||
* `version: "2.1.0"`
|
||||
* one `run` with your `tool.driver` (name, version)
|
||||
* `results[]` with each finding’s `ruleId`, `message.text`, and at least one `location` (file/region) so GitHub can anchor it to code. (Empty or missing locations often breaks uploads.) ([GitHub Docs][1])
|
||||
|
||||
Skeleton:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
|
||||
"runs": [{
|
||||
"tool": { "driver": { "name": "StellaOps Scanner", "version": "1.0.0" } },
|
||||
"results": [{
|
||||
"ruleId": "STELLA001",
|
||||
"message": { "text": "Vulnerability XYZ in package foo@1.2.3" },
|
||||
"locations": [{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": { "uri": "src/app/foo.js" },
|
||||
"region": { "startLine": 42 }
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
(Full schema: OASIS SARIF 2.1.0.) ([OASIS Open][5])
|
||||
|
||||
---
|
||||
|
||||
### Gotchas (save yourself time)
|
||||
|
||||
* **One tool/category per run**: GitHub is deprecating combining multiple runs with the same tool+category in a single upload; by **June 2025** such uploads will fail. Keep runs distinct or separate files. ([The GitHub Blog][6])
|
||||
* **Partial fingerprints**: If you don’t provide them, the upload action can compute them (prevents duplicate alerts) when source is present. ([GitHub Docs][2])
|
||||
* **Permissions**: in Actions, grant `security-events: write`. For API/CLI, use tokens with the right scopes. ([GitHub Docs][3])
|
||||
* **PRs from forks**: public API uploads have restrictions; the `upload-sarif` action is the usual workaround in PR contexts. ([GitHub][7])
|
||||
|
||||
---
|
||||
|
||||
### Why this helps your rollout
|
||||
|
||||
* **Zero custom UI**: GitHub draws findings, file annotations, and PR decorations for you. ([GitHub Docs][1])
|
||||
* **Fits any CI**: Emit SARIF once, then upload via Actions, REST, or CLI—works for GitHub-hosted or external runners. ([GitHub Docs][2])
|
||||
|
||||
If you want, I can tailor a ready-to-drop workflow for one of your Stella Ops repos (mono-repo vs multi-repo, language mix, and desired categories).
|
||||
|
||||
[1]: https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning?utm_source=chatgpt.com "SARIF support for code scanning"
|
||||
[2]: https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/uploading-a-sarif-file-to-github?utm_source=chatgpt.com "Uploading a SARIF file to GitHub"
|
||||
[3]: https://docs.github.com/en/rest/code-scanning?utm_source=chatgpt.com "REST API endpoints for code scanning"
|
||||
[4]: https://docs.github.com/en/code-security/codeql-cli/codeql-cli-manual/github-upload-results?utm_source=chatgpt.com "github upload-results"
|
||||
[5]: https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html?utm_source=chatgpt.com "Static Analysis Results Interchange Format (SARIF) Version ..."
|
||||
[6]: https://github.blog/changelog/2024-05-06-code-scanning-will-stop-combining-runs-from-a-single-upload/?utm_source=chatgpt.com "Code Scanning will stop combining runs from a single upload"
|
||||
[7]: https://github.com/orgs/community/discussions/54013?utm_source=chatgpt.com "Uploading SARIF files for pull requests #54013"
|
||||
Reference in New Issue
Block a user