up
This commit is contained in:
15
tests/reachability/samples-public/README.md
Normal file
15
tests/reachability/samples-public/README.md
Normal 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.
|
||||
|
||||
35
tests/reachability/samples-public/manifest.json
Normal file
35
tests/reachability/samples-public/manifest.json
Normal 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"
|
||||
}
|
||||
]
|
||||
5
tests/reachability/samples-public/runners/run_all.ps1
Normal file
5
tests/reachability/samples-public/runners/run_all.ps1
Normal file
@@ -0,0 +1,5 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
python (Join-Path $PSScriptRoot "..\\scripts\\update_manifest.py") | Out-Null
|
||||
Write-Host "samples-public: manifest regenerated"
|
||||
|
||||
6
tests/reachability/samples-public/runners/run_all.sh
Normal file
6
tests/reachability/samples-public/runners/run_all.sh
Normal 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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# cs-001-binaryformatter-deserialize
|
||||
|
||||
Minimal C# sample used as a public reachability fixture.
|
||||
|
||||
@@ -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" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# js-002-yaml-unsafe-load
|
||||
|
||||
Minimal JavaScript sample used as a public reachability fixture.
|
||||
|
||||
@@ -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" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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 || "");
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
53
tests/reachability/samples-public/scripts/update_manifest.py
Normal file
53
tests/reachability/samples-public/scripts/update_manifest.py
Normal 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())
|
||||
|
||||
Reference in New Issue
Block a user