This commit is contained in:
StellaOps Bot
2025-12-13 02:22:15 +02:00
parent 564df71bfb
commit 999e26a48e
395 changed files with 25045 additions and 2224 deletions

View File

@@ -0,0 +1,15 @@
# Public Reachability Samples (offline fixtures)
This folder contains a small, public-friendly reachability mini-dataset intended for:
- deterministic fixture validation in CI (hash manifests),
- offline demos and ingestion tests (Signals callgraph/runtime facts),
- documentation examples without pulling external repos.
Layout (mirrors `docs/reachability/corpus-plan.md`):
- `schema/ground-truth.schema.json` — JSON schema for `ground-truth.json`.
- `scripts/update_manifest.py` — deterministic manifest generator.
- `manifest.json` — hashes for required files in each sample directory.
- `samples/<lang>/<case-id>/` — per-sample code + callgraph + SBOM + VEX + ground truth.

View File

@@ -0,0 +1,35 @@
[
{
"files": {
"callgraph.static.json": "1492f2cd51647c6c483f4d8a169f4b0c2ef5b3cc73f280aacd46035793fb07e2",
"ground-truth.json": "34b5500cfb9f14e4d423a3021372286520826e79a29f5405e3b5b4fd7473686f",
"repro.sh": "6be2324a06a4873c80ada305b60602e3f1ca134a39d021f663694974e8e7b20b",
"sbom.cdx.json": "999333d7e6b1c2f96833d532ae9a247cec6589ae936a16e8e8db21bf6885267a",
"vex.openvex.json": "447fa7bf849d61fc59d8892c37918714c7cb878658db27d709001560a173fe0b"
},
"id": "cs-001-binaryformatter-deserialize",
"language": "csharp"
},
{
"files": {
"callgraph.static.json": "32de821ce640554c4cdeac9251c3314e30c934c17783a11b999c54af03f9321c",
"ground-truth.json": "d9493688ae781f32628476b1bf06a45501e08a260f65ae68fd4d3722e01acc46",
"repro.sh": "6be2324a06a4873c80ada305b60602e3f1ca134a39d021f663694974e8e7b20b",
"sbom.cdx.json": "035a9c241e287ff4a52d2e702735649b96ddfec1ffb1ce8de980634b44906694",
"vex.openvex.json": "c1a62ab9bde5a1e60ac48f1fb8e275fa75caeadb29177972808a9abaee2f17f5"
},
"id": "js-002-yaml-unsafe-load",
"language": "js"
},
{
"files": {
"callgraph.static.json": "e55c78a1ea4c3615477bdabe2b069e3d756be2b21c4b359186612818a7213470",
"ground-truth.json": "de5b129c315abb4eae3dabe76c0961fe5f97dff3024805c6705ebb45c809f22c",
"repro.sh": "6be2324a06a4873c80ada305b60602e3f1ca134a39d021f663694974e8e7b20b",
"sbom.cdx.json": "d4b18c13d2d7e7cda0ec7a8f6355e4dc52f0c30eaa8b1eddaea2c0da21620455",
"vex.openvex.json": "df1c795978893c01d64831ff2232aebd66efe9cfe418f461ac125aece1906778"
},
"id": "php-001-phar-deserialize",
"language": "php"
}
]

View File

@@ -0,0 +1,5 @@
$ErrorActionPreference = "Stop"
python (Join-Path $PSScriptRoot "..\\scripts\\update_manifest.py") | Out-Null
Write-Host "samples-public: manifest regenerated"

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
python3 "$(dirname "$0")/../scripts/update_manifest.py" >/dev/null
echo "samples-public: manifest regenerated"

View File

@@ -0,0 +1,13 @@
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
// Fixture-only sample: demonstrates a BinaryFormatter deserialize-style sink.
// Do not deploy.
var payload = Environment.GetEnvironmentVariable("PAYLOAD") ?? string.Empty;
var bytes = Convert.FromBase64String(payload);
using var ms = new MemoryStream(bytes);
var formatter = new BinaryFormatter();
_ = formatter.Deserialize(ms);

View File

@@ -0,0 +1,4 @@
# cs-001-binaryformatter-deserialize
Minimal C# sample used as a public reachability fixture.

View File

@@ -0,0 +1,14 @@
{
"schema_version": "1.0",
"roots": [
{ "id": "sym://dotnet:Program#Main", "phase": "runtime", "source": "static" }
],
"nodes": [
{ "id": "sym://dotnet:Program#Main", "name": "Main", "kind": "function", "language": "dotnet" },
{ "id": "sym://dotnet:System.Runtime.Serialization.Formatters.Binary.BinaryFormatter#Deserialize", "name": "Deserialize", "kind": "function", "language": "dotnet" }
],
"edges": [
{ "from": "sym://dotnet:Program#Main", "to": "sym://dotnet:System.Runtime.Serialization.Formatters.Binary.BinaryFormatter#Deserialize", "kind": "call" }
]
}

View File

@@ -0,0 +1,12 @@
{
"case_id": "cs-001-binaryformatter-deserialize",
"paths": [
[
"sym://dotnet:Program#Main",
"sym://dotnet:System.Runtime.Serialization.Formatters.Binary.BinaryFormatter#Deserialize"
]
],
"schema_version": "reachbench.reachgraph.truth/v1",
"variant": "reachable"
}

View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
echo "Fixture-only sample: no live repro; use callgraph.static.json + ground-truth.json for ingestion/tests."

View File

@@ -0,0 +1,22 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"version": 1,
"metadata": {
"component": {
"type": "application",
"name": "cs-001-binaryformatter-deserialize",
"version": "0.0.0",
"purl": "pkg:nuget/cs-001-binaryformatter-deserialize@0.0.0"
}
},
"components": [
{
"type": "library",
"name": "System.Runtime.Serialization.Formatters",
"version": "4.3.0",
"purl": "pkg:nuget/System.Runtime.Serialization.Formatters@4.3.0"
}
]
}

View File

@@ -0,0 +1,21 @@
{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "urn:stellaops:vex:cs-001-binaryformatter-deserialize",
"author": "StellaOps",
"timestamp": "2025-12-12T00:00:00Z",
"version": 1,
"statements": [
{
"vulnerability": {
"name": "CVE-TEST-0003"
},
"products": [
{
"@id": "pkg:nuget/System.Runtime.Serialization.Formatters@4.3.0"
}
],
"status": "under_investigation"
}
]
}

View File

@@ -0,0 +1,4 @@
# js-002-yaml-unsafe-load
Minimal JavaScript sample used as a public reachability fixture.

View File

@@ -0,0 +1,14 @@
{
"schema_version": "1.0",
"roots": [
{ "id": "sym://js:src/index.js#main", "phase": "runtime", "source": "static" }
],
"nodes": [
{ "id": "sym://js:src/index.js#main", "name": "main", "kind": "function", "file": "src/index.js", "line": 1, "language": "nodejs" },
{ "id": "sym://js:node_modules/js-yaml#load", "name": "load", "kind": "function", "namespace": "js-yaml", "language": "nodejs" }
],
"edges": [
{ "from": "sym://js:src/index.js#main", "to": "sym://js:node_modules/js-yaml#load", "kind": "call" }
]
}

View File

@@ -0,0 +1,12 @@
{
"case_id": "js-002-yaml-unsafe-load",
"paths": [
[
"sym://js:src/index.js#main",
"sym://js:node_modules/js-yaml#load"
]
],
"schema_version": "reachbench.reachgraph.truth/v1",
"variant": "reachable"
}

View File

@@ -0,0 +1,6 @@
// Fixture-only sample: demonstrates an unsafe YAML load-style sink.
// Do not deploy.
const yaml = require("js-yaml");
yaml.load(process.env.PAYLOAD || "");

View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
echo "Fixture-only sample: no live repro; use callgraph.static.json + ground-truth.json for ingestion/tests."

View File

@@ -0,0 +1,22 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"version": 1,
"metadata": {
"component": {
"type": "application",
"name": "js-002-yaml-unsafe-load",
"version": "0.0.0",
"purl": "pkg:npm/js-002-yaml-unsafe-load@0.0.0"
}
},
"components": [
{
"type": "library",
"name": "js-yaml",
"version": "4.1.0",
"purl": "pkg:npm/js-yaml@4.1.0"
}
]
}

View File

@@ -0,0 +1,21 @@
{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "urn:stellaops:vex:js-002-yaml-unsafe-load",
"author": "StellaOps",
"timestamp": "2025-12-12T00:00:00Z",
"version": 1,
"statements": [
{
"vulnerability": {
"name": "CVE-TEST-0002"
},
"products": [
{
"@id": "pkg:npm/js-yaml@4.1.0"
}
],
"status": "under_investigation"
}
]
}

View File

@@ -0,0 +1,6 @@
# php-001-phar-deserialize
Minimal PHP sample used as a public reachability fixture.
This is a fixture only: it is not intended to be deployed.

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
// Fixture-only sample: demonstrates a deserialize-style sink.
// Do not deploy.
$payload = $_GET["payload"] ?? "";
unserialize($payload);

View File

@@ -0,0 +1,16 @@
{
"schema_version": "1.0",
"roots": [
{ "id": "sym://php:public/index.php#main", "phase": "runtime", "source": "static" }
],
"nodes": [
{ "id": "sym://php:public/index.php#main", "name": "main", "kind": "function", "file": "public/index.php", "line": 1, "language": "php" },
{ "id": "sym://php:app/UploadController.php#handle", "name": "handle", "kind": "function", "file": "app/UploadController.php", "line": 1, "language": "php" },
{ "id": "sym://php:php.net#unserialize", "name": "unserialize", "kind": "function", "namespace": "php", "language": "php" }
],
"edges": [
{ "from": "sym://php:public/index.php#main", "to": "sym://php:app/UploadController.php#handle", "kind": "call" },
{ "from": "sym://php:app/UploadController.php#handle", "to": "sym://php:php.net#unserialize", "kind": "call" }
]
}

View File

@@ -0,0 +1,13 @@
{
"case_id": "php-001-phar-deserialize",
"paths": [
[
"sym://php:public/index.php#main",
"sym://php:app/UploadController.php#handle",
"sym://php:php.net#unserialize"
]
],
"schema_version": "reachbench.reachgraph.truth/v1",
"variant": "reachable"
}

View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
echo "Fixture-only sample: no live repro; use callgraph.static.json + ground-truth.json for ingestion/tests."

View File

@@ -0,0 +1,21 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"version": 1,
"metadata": {
"component": {
"type": "application",
"name": "php-001-phar-deserialize",
"version": "0.0.0"
}
},
"components": [
{
"type": "library",
"name": "php",
"version": "8.x",
"purl": "pkg:generic/php@8"
}
]
}

View File

@@ -0,0 +1,21 @@
{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "urn:stellaops:vex:php-001-phar-deserialize",
"author": "StellaOps",
"timestamp": "2025-12-12T00:00:00Z",
"version": 1,
"statements": [
{
"vulnerability": {
"name": "CVE-TEST-0001"
},
"products": [
{
"@id": "pkg:generic/php@8"
}
],
"status": "under_investigation"
}
]
}

View File

@@ -0,0 +1,34 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "stellaops.reachability.ground-truth.schema.json",
"title": "StellaOps Reachability Ground Truth (public samples)",
"type": "object",
"required": ["schema_version", "case_id", "variant", "paths"],
"properties": {
"schema_version": {
"type": "string",
"const": "reachbench.reachgraph.truth/v1"
},
"case_id": {
"type": "string",
"minLength": 1
},
"variant": {
"type": "string",
"enum": ["reachable", "unreachable"]
},
"paths": {
"type": "array",
"items": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"minLength": 1
}
}
}
},
"additionalProperties": true
}

View File

@@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""
Regenerate the public samples manifest deterministically.
Usage: python tests/reachability/samples-public/scripts/update_manifest.py
"""
from __future__ import annotations
import hashlib
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SAMPLES_ROOT = ROOT / "samples"
FILE_LIST = [
"callgraph.static.json",
"ground-truth.json",
"sbom.cdx.json",
"vex.openvex.json",
"repro.sh",
]
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def main() -> int:
entries: list[dict] = []
for lang_dir in sorted(p for p in SAMPLES_ROOT.iterdir() if p.is_dir()):
for case_dir in sorted(p for p in lang_dir.iterdir() if p.is_dir()):
files: dict[str, str] = {}
for name in FILE_LIST:
path = case_dir / name
if not path.exists():
raise SystemExit(f"missing {path}")
files[name] = sha256(path)
entries.append(
{
"id": case_dir.name,
"language": lang_dir.name,
"files": files,
}
)
manifest_path = ROOT / "manifest.json"
manifest_path.write_text(json.dumps(entries, indent=2, sort_keys=True) + "\n")
print(f"wrote {manifest_path} ({len(entries)} entries)")
return 0
if __name__ == "__main__":
raise SystemExit(main())