consolidate the tests locations

This commit is contained in:
StellaOps Bot
2025-12-26 01:48:24 +02:00
parent 17613acf57
commit 39359da171
2031 changed files with 2607 additions and 476 deletions

View File

@@ -1,24 +0,0 @@
# Invalid Fixtures for Negative Testing
This directory contains intentionally invalid SBOM fixtures used for CI negative testing.
These fixtures MUST fail schema validation to ensure the CI pipeline correctly detects errors.
## CycloneDX Invalid Fixtures
| File | Defect | Expected Error |
|------|--------|----------------|
| `cyclonedx-wrong-version.json` | specVersion "2.0" doesn't exist | Invalid enum value for specVersion |
| `cyclonedx-missing-required.json` | Missing required specVersion field | Missing required property: specVersion |
| `cyclonedx-invalid-component.json` | Component missing name and type | Required properties missing in component |
## CI Usage
The schema validation workflow uses the `tests/fixtures/invalid/` directory for negative test cases.
When `--expect-failures` is passed, the CI expects these files to fail validation.
## Adding New Test Cases
1. Create a new JSON file with an intentional schema violation
2. Add a `$comment` field explaining the defect
3. Update this README with the expected error
4. Ensure the file has the correct format marker (e.g., `"bomFormat": "CycloneDX"`)

View File

@@ -1,15 +0,0 @@
{
"$comment": "INTENTIONALLY INVALID CycloneDX fixture - component missing required 'name' and 'type' fields.",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"version": 1,
"metadata": {
"timestamp": "2025-12-25T00:00:00Z"
},
"components": [
{
"invalid-field": "this-is-not-valid",
"purl": "pkg:npm/missing-required-fields@1.0.0"
}
]
}

View File

@@ -1,9 +0,0 @@
{
"$comment": "INTENTIONALLY INVALID CycloneDX fixture - missing required 'specVersion' field.",
"bomFormat": "CycloneDX",
"version": 1,
"metadata": {
"timestamp": "2025-12-25T00:00:00Z"
},
"components": []
}

View File

@@ -1,11 +0,0 @@
{
"$comment": "INTENTIONALLY INVALID CycloneDX fixture for negative testing.",
"$comment2": "specVersion 2.0 does not exist and should fail schema validation.",
"bomFormat": "CycloneDX",
"specVersion": "2.0",
"version": 1,
"metadata": {
"timestamp": "2025-12-25T00:00:00Z"
},
"components": []
}

View File

@@ -1,89 +0,0 @@
/**
* Micro-interaction test fixtures with deterministic seeds (MI8)
*
* Usage:
* - Import these constants in Storybook stories and Playwright tests
* - Use frozenTimestamp for all date operations
* - Use rngSeed for any randomized content
*/
// Frozen timestamp: 2025-12-04T12:00:00Z (as per advisory)
export const FROZEN_TIMESTAMP = new Date('2025-12-04T12:00:00.000Z');
export const FROZEN_TIMESTAMP_MS = 1733313600000;
// Fixed RNG seed as per advisory: 0x5EED2025
export const RNG_SEED = 0x5EED2025;
// Deterministic UUID generator (seeded)
export function seededUuid(seed: number = RNG_SEED, index: number = 0): string {
const hash = ((seed + index) * 2654435761) >>> 0;
const hex = hash.toString(16).padStart(8, '0');
return `${hex.slice(0, 8)}-${hex.slice(0, 4)}-4${hex.slice(1, 4)}-8${hex.slice(4, 7)}-${hex}0000`.slice(0, 36);
}
// Skeleton state fixture
export const skeletonFixture = {
showAfterMs: 400,
loadingDurationMs: 1200,
state: 'loading' as const,
timestamp: FROZEN_TIMESTAMP,
};
// Error state fixture
export const errorFixture = {
code: 'UI_ERR_001',
message: 'Failed to load data',
retryAvailable: true,
timestamp: FROZEN_TIMESTAMP,
correlationId: seededUuid(RNG_SEED, 1),
};
// Offline state fixture
export const offlineFixture = {
isOffline: true,
lastOnline: new Date(FROZEN_TIMESTAMP_MS - 300000), // 5 minutes ago
cachedDataAge: 'less than 1 hour',
timestamp: FROZEN_TIMESTAMP,
};
// Toast/snackbar fixture
export const toastFixture = {
id: seededUuid(RNG_SEED, 2),
type: 'info' as const,
message: 'Changes saved successfully',
undoAvailable: true,
undoWindowMs: 8000,
timestamp: FROZEN_TIMESTAMP,
};
// Reduced motion test config
export const reducedMotionConfig = {
enabled: true,
emulateQuery: true,
dataAttribute: 'data-reduce-motion',
dataValue: '1',
};
// Playwright/Storybook timer config
export const timerConfig = {
useFakeTimers: true,
now: FROZEN_TIMESTAMP_MS,
shouldAdvanceTime: false,
};
// Sample telemetry event
export const sampleTelemetryEvent = {
schema_version: 'v1.0',
event_type: 'ui.micro.interaction',
timestamp: FROZEN_TIMESTAMP.toISOString(),
tenant_id: 'test-tenant',
surface: 'dashboard',
component: 'button',
action: 'click',
latency_ms: 45,
outcome: 'success',
reduced_motion: false,
offline_mode: false,
error_code: null,
correlation_id: seededUuid(RNG_SEED, 3),
};

View File

@@ -1,75 +0,0 @@
# Offline Bundle Test Fixtures
This directory contains test fixtures for offline/air-gap testing.
## Structure
```
offline-bundle/
├── manifest.json # Bundle manifest
├── feeds/ # Vulnerability feed snapshots
│ ├── nvd-snapshot.json
│ ├── ghsa-snapshot.json
│ └── distro/
│ ├── alpine.json
│ ├── debian.json
│ └── rhel.json
├── policies/ # OPA/Rego policies
│ ├── default.rego
│ └── strict.rego
├── keys/ # Test signing keys
│ ├── signing-key.pem
│ └── signing-key.pub
├── certs/ # Test certificates
│ ├── trust-root.pem
│ └── intermediate.pem
├── vex/ # Sample VEX documents
│ └── vendor-vex.json
└── images/ # Test container image tarballs
├── test-image.tar
├── vuln-image.tar
└── vuln-with-vex.tar
```
## Usage
Set the `STELLAOPS_OFFLINE_BUNDLE` environment variable to point to this directory:
```bash
export STELLAOPS_OFFLINE_BUNDLE=/path/to/tests/fixtures/offline-bundle
```
Tests that extend `NetworkIsolatedTestBase` will automatically use this bundle.
## Generating Test Images
To create test image tarballs:
```bash
# Pull and save test images
docker pull alpine:3.18
docker save alpine:3.18 -o images/test-image.tar
# For vulnerable images
docker pull vulnerables/web-dvwa:latest
docker save vulnerables/web-dvwa:latest -o images/vuln-image.tar
```
## Feed Snapshots
Feed snapshots should be representative samples from real feeds, sufficient for testing but small enough to commit to the repo.
## Test Keys
⚠️ **WARNING:** Keys in this directory are for **testing only**. Never use these in production.
To generate test keys:
```bash
# Generate test signing key
openssl genrsa -out keys/signing-key.pem 2048
openssl rsa -in keys/signing-key.pem -pubout -out keys/signing-key.pub
# Generate test CA
openssl req -new -x509 -key keys/signing-key.pem -out certs/trust-root.pem -days 3650
```

View File

@@ -1,38 +0,0 @@
{
"bundleId": "test-offline-bundle-v1",
"schemaVersion": "1.0.0",
"createdAt": "2025-12-22T00:00:00Z",
"description": "Test offline bundle for air-gap testing",
"contents": {
"feeds": [
"feeds/nvd-snapshot.json",
"feeds/ghsa-snapshot.json",
"feeds/distro/alpine.json",
"feeds/distro/debian.json"
],
"policies": [
"policies/default.rego",
"policies/strict.rego"
],
"keys": [
"keys/signing-key.pem",
"keys/signing-key.pub"
],
"certs": [
"certs/trust-root.pem",
"certs/intermediate.pem"
],
"vex": [
"vex/vendor-vex.json"
],
"images": [
"images/test-image.tar",
"images/vuln-image.tar",
"images/vuln-with-vex.tar"
]
},
"integrity": {
"algorithm": "SHA-256",
"manifestDigest": "placeholder"
}
}

View File

@@ -1,45 +0,0 @@
# SCA Failure Catalogue Fixtures
This directory hosts deterministic fixtures for scanner failure mode regression testing.
Each fixture documents a real-world failure pattern that StellaOps must handle correctly.
## Catalogue Overview
| ID | Name | Failure Mode | Added |
|----|------|--------------|-------|
| FC1 | Credential Leak | Grype credential leak in environment | 2025-11-30 |
| FC2 | Trivy DB Schema | Trivy offline DB schema mismatch | 2025-11-30 |
| FC3 | SBOM Parity | SBOM parity drift between tools | 2025-11-30 |
| FC4 | Grype Version | Grype version divergence | 2025-11-30 |
| FC5 | Inconsistent Detection | Inconsistent detection across runs | 2025-11-30 |
| FC6 | Java Shadow JAR | Fat/uber JARs with shaded dependencies | 2025-12-16 |
| FC7 | .NET Transitive Pinning | Transitive dependency version conflicts | 2025-12-16 |
| FC8 | Docker Multi-Stage Leakage | Build-time deps leaking into runtime | 2025-12-16 |
| FC9 | PURL Namespace Collision | Same package name in different ecosystems | 2025-12-16 |
| FC10 | CVE Split/Merge | CVE split/merge tracking issues | 2025-12-16 |
## Fixture Structure
Each fixture directory (`fc1/`, `fc2/`, etc.) contains:
- `expected.json` - Expected scanner output and test assertions
- `input.txt` - Input description and configuration
- `manifest.dsse.json` - DSSE-signed manifest for integrity verification
## Usage
```bash
# Run all catalogue tests
dotnet test --filter "Category=ScaCatalogue"
# Run specific fixture
dotnet test --filter "FullyQualifiedName~FC6"
```
## Constraints
- All fixtures are deterministic and offline-capable
- Pinned tool versions and feeds are recorded in `inputs.lock`
- No network access; rely on bundled caches only
- All outputs must be normalized before comparison

View File

@@ -1,8 +0,0 @@
{
"id": "fc1-credential-leak",
"scanner": "grype",
"feed": "offline-cache-2025-11-30",
"expected_findings": [
{"purl": "pkg:docker/example@1.0.0", "cve": "CVE-2024-9999", "status": "present"}
]
}

View File

@@ -1 +0,0 @@
input stub for fc1

View File

@@ -1,10 +0,0 @@
{
"payloadType": "application/json",
"payload": "ewogICJpZCI6ICJmYzEtY3JlZGVudGlhbC1sZWFrIiwKICAic2Nhbm5lciI6ICJncnlwZSIsCiAgImZlZWQiOiAib2ZmbGluZS1jYWNoZS0yMDI1LTExLTMwIiwKICAiZXhwZWN0ZWRfZmluZGluZ3MiOiBbCiAgICB7InB1cmwiOiAicGtnOmRvY2tlci9leGFtcGxlQDEuMC4wIiwgImN2ZSI6ICJDVkUtMjAyNC05OTk5IiwgInN0YXR1cyI6ICJwcmVzZW50In0KICBdCn0K",
"signatures": [
{
"keyid": "stub-key-id",
"sig": "stub-signature"
}
]
}

View File

@@ -1 +0,0 @@
stub-signature

View File

@@ -1,62 +0,0 @@
{
"id": "fc10-cve-split-merge",
"name": "CVE Split/Merge Failure Case",
"description": "Single vulnerability split across multiple CVEs or multiple vulnerabilities merged into one. NVD/MITRE sometimes splits or merges CVEs after initial assignment, causing tracking issues.",
"scanner": "grype",
"feed": "offline-cache-2025-12-16",
"failure_mode": {
"category": "cve_tracking",
"root_cause": "CVE reassignment not properly tracked in vulnerability database",
"affected_scanners": ["grype", "trivy", "syft"],
"severity": "high"
},
"input": {
"type": "sbom",
"packages": [
{"purl": "pkg:npm/lodash@4.17.15", "note": "CVE split case"},
{"purl": "pkg:maven/org.springframework/spring-core@5.3.18", "note": "CVE merge case"},
{"purl": "pkg:pypi/pillow@9.0.0", "note": "CVE chain case"}
]
},
"cve_cases": {
"split": {
"description": "Original CVE-2020-8203 was split into CVE-2020-8203, CVE-2020-28500, CVE-2021-23337 for lodash",
"original_cve": "CVE-2020-8203",
"split_cves": ["CVE-2020-8203", "CVE-2020-28500", "CVE-2021-23337"],
"affected_package": "pkg:npm/lodash@4.17.15"
},
"merge": {
"description": "CVE-2022-22965 (Spring4Shell) encompasses what was initially tracked as multiple issues",
"merged_cves": ["CVE-2022-22963", "CVE-2022-22965"],
"canonical_cve": "CVE-2022-22965",
"affected_package": "pkg:maven/org.springframework/spring-core@5.3.18"
},
"chain": {
"description": "Pillow has vulnerability chain where one CVE leads to another",
"cve_chain": ["CVE-2022-22815", "CVE-2022-22816", "CVE-2022-22817"],
"affected_package": "pkg:pypi/pillow@9.0.0"
}
},
"expected_findings": [
{"purl": "pkg:npm/lodash@4.17.15", "cve": "CVE-2020-8203", "status": "present"},
{"purl": "pkg:npm/lodash@4.17.15", "cve": "CVE-2020-28500", "status": "present"},
{"purl": "pkg:npm/lodash@4.17.15", "cve": "CVE-2021-23337", "status": "present"},
{"purl": "pkg:maven/org.springframework/spring-core@5.3.18", "cve": "CVE-2022-22965", "status": "present"},
{"purl": "pkg:pypi/pillow@9.0.0", "cve": "CVE-2022-22815", "status": "present"},
{"purl": "pkg:pypi/pillow@9.0.0", "cve": "CVE-2022-22816", "status": "present"},
{"purl": "pkg:pypi/pillow@9.0.0", "cve": "CVE-2022-22817", "status": "present"}
],
"detection_requirements": {
"track_cve_aliases": true,
"handle_cve_splits": true,
"handle_cve_merges": true,
"track_cve_chains": true,
"use_osv_aliases": true
},
"test_assertions": [
"All CVEs from split vulnerabilities must be reported",
"Merged CVEs should use canonical CVE ID",
"CVE aliases must be tracked (e.g., via OSV)",
"No duplicate findings for same underlying issue"
]
}

View File

@@ -1,33 +0,0 @@
# FC10: CVE Split/Merge Test Case
#
# This fixture tests correct handling of CVEs that have been
# split into multiple CVEs or merged from multiple into one.
#
# Input: Packages affected by split/merged CVEs
# Expected: All applicable CVEs correctly tracked
type: sbom
format: cyclonedx-1.6
# CVE split case: lodash
# CVE-2020-8203 was split into multiple CVEs
package: pkg:npm/lodash@4.17.15
split_cves:
- CVE-2020-8203 (original)
- CVE-2020-28500 (split)
- CVE-2021-23337 (split)
# CVE merge case: Spring
# Multiple issues merged into Spring4Shell
package: pkg:maven/org.springframework/spring-core@5.3.18
merged_cves:
- CVE-2022-22963 (related but separate)
- CVE-2022-22965 (Spring4Shell - canonical)
# CVE chain case: Pillow
# Related CVEs affecting same package
package: pkg:pypi/pillow@9.0.0
chain_cves:
- CVE-2022-22815
- CVE-2022-22816
- CVE-2022-22817

View File

@@ -1,10 +0,0 @@
{
"payloadType": "application/json",
"payload": "ewogICJpZCI6ICJmYzEwLWN2ZS1zcGxpdC1tZXJnZSIsCiAgIm5hbWUiOiAiQ1ZFIFNwbGl0L01lcmdlIEZhaWx1cmUgQ2FzZSIsCiAgImRlc2NyaXB0aW9uIjogIlNpbmdsZSB2dWxuZXJhYmlsaXR5IHNwbGl0IGFjcm9zcyBtdWx0aXBsZSBDVkVzIG9yIG11bHRpcGxlIHZ1bG5lcmFiaWxpdGllcyBtZXJnZWQgaW50byBvbmUuIE5WRC9NSVRSRSBzb21ldGltZXMgc3BsaXRzIG9yIG1lcmdlcyBDVkVzIGFmdGVyIGluaXRpYWwgYXNzaWdubWVudCwgY2F1c2luZyB0cmFja2luZyBpc3N1ZXMuIiwKICAic2Nhbm5lciI6ICJncnlwZSIsCiAgImZlZWQiOiAib2ZmbGluZS1jYWNoZS0yMDI1LTEyLTE2IiwKICAiZmFpbHVyZV9tb2RlIjogewogICAgImNhdGVnb3J5IjogImN2ZV90cmFja2luZyIsCiAgICAicm9vdF9jYXVzZSI6ICJDVkUgcmVhc3NpZ25tZW50IG5vdCBwcm9wZXJseSB0cmFja2VkIGluIHZ1bG5lcmFiaWxpdHkgZGF0YWJhc2UiLAogICAgImFmZmVjdGVkX3NjYW5uZXJzIjogWyJncnlwZSIsICJ0cml2eSIsICJzeWZ0Il0sCiAgICAic2V2ZXJpdHkiOiAiaGlnaCIKICB9LAogICJpbnB1dCI6IHsKICAgICJ0eXBlIjogInNib20iLAogICAgInBhY2thZ2VzIjogWwogICAgICB7InB1cmwiOiAicGtnOm5wbS9sb2Rhc2hANC4xNy4xNSIsICJub3RlIjogIkNWRSBzcGxpdCBjYXNlIn0sCiAgICAgIHsicHVybCI6ICJwa2c6bWF2ZW4vb3JnLnNwcmluZ2ZyYW1ld29yay9zcHJpbmctY29yZUA1LjMuMTgiLCAibm90ZSI6ICJDVkUgbWVyZ2UgY2FzZSJ9LAogICAgICB7InB1cmwiOiAicGtnOnB5cGkvcGlsbG93QDkuMC4wIiwgIm5vdGUiOiAiQ1ZFIGNoYWluIGNhc2UifQogICAgXQogIH0sCiAgImN2ZV9jYXNlcyI6IHsKICAgICJzcGxpdCI6IHsKICAgICAgImRlc2NyaXB0aW9uIjogIk9yaWdpbmFsIENWRS0yMDIwLTgyMDMgd2FzIHNwbGl0IGludG8gQ1ZFLTIwMjAtODIwMywgQ1ZFLTIwMjAtMjg1MDAsIENWRS0yMDIxLTIzMzM3IGZvciBsb2Rhc2giLAogICAgICAib3JpZ2luYWxfY3ZlIjogIkNWRS0yMDIwLTgyMDMiLAogICAgICAic3BsaXRfY3ZlcyI6IFsiQ1ZFLTIwMjAtODIwMyIsICJDVkUtMjAyMC0yODUwMCIsICJDVkUtMjAyMS0yMzMzNyJdLAogICAgICAiYWZmZWN0ZWRfcGFja2FnZSI6ICJwa2c6bnBtL2xvZGFzaEA0LjE3LjE1IgogICAgfSwKICAgICJtZXJnZSI6IHsKICAgICAgImRlc2NyaXB0aW9uIjogIkNWRS0yMDIyLTIyOTY1IChTcHJpbmc0U2hlbGwpIGVuY29tcGFzc2VzIHdoYXQgd2FzIGluaXRpYWxseSB0cmFja2VkIGFzIG11bHRpcGxlIGlzc3VlcyIsCiAgICAgICJtZXJnZWRfY3ZlcyI6IFsiQ1ZFLTIwMjItMjI5NjMiLCAiQ1ZFLTIwMjItMjI5NjUiXSwKICAgICAgImNhbm9uaWNhbF9jdmUiOiAiQ1ZFLTIwMjItMjI5NjUiLAogICAgICAiYWZmZWN0ZWRfcGFja2FnZSI6ICJwa2c6bWF2ZW4vb3JnLnNwcmluZ2ZyYW1ld29yay9zcHJpbmctY29yZUA1LjMuMTgiCiAgICB9LAogICAgImNoYWluIjogewogICAgICAiZGVzY3JpcHRpb24iOiAiUGlsbG93IGhhcyB2dWxuZXJhYmlsaXR5IGNoYWluIHdoZXJlIG9uZSBDVkUgbGVhZHMgdG8gYW5vdGhlciIsCiAgICAgICJjdmVfY2hhaW4iOiBbIkNWRS0yMDIyLTIyODE1IiwgIkNWRS0yMDIyLTIyODE2IiwgIkNWRS0yMDIyLTIyODE3Il0sCiAgICAgICJhZmZlY3RlZF9wYWNrYWdlIjogInBrZzpweXBpL3BpbGxvd0A5LjAuMCIKICAgIH0KICB9LAogICJleHBlY3RlZF9maW5kaW5ncyI6IFsKICAgIHsicHVybCI6ICJwa2c6bnBtL2xvZGFzaEA0LjE3LjE1IiwgImN2ZSI6ICJDVkUtMjAyMC04MjAzIiwgInN0YXR1cyI6ICJwcmVzZW50In0sCiAgICB7InB1cmwiOiAicGtnOm5wbS9sb2Rhc2hANC4xNy4xNSIsICJjdmUiOiAiQ1ZFLTIwMjAtMjg1MDAiLCAic3RhdHVzIjogInByZXNlbnQifSwKICAgIHsicHVybCI6ICJwa2c6bnBtL2xvZGFzaEA0LjE3LjE1IiwgImN2ZSI6ICJDVkUtMjAyMS0yMzMzNyIsICJzdGF0dXMiOiAicHJlc2VudCJ9LAogICAgeyJwdXJsIjogInBrZzptYXZlbi9vcmcuc3ByaW5nZnJhbWV3b3JrL3NwcmluZy1jb3JlQDUuMy4xOCIsICJjdmUiOiAiQ1ZFLTIwMjItMjI5NjUiLCAic3RhdHVzIjogInByZXNlbnQifSwKICAgIHsicHVybCI6ICJwa2c6cHlwaS9waWxsb3dAOS4wLjAiLCAiY3ZlIjogIkNWRS0yMDIyLTIyODE1IiwgInN0YXR1cyI6ICJwcmVzZW50In0sCiAgICB7InB1cmwiOiAicGtnOnB5cGkvcGlsbG93QDkuMC4wIiwgImN2ZSI6ICJDVkUtMjAyMi0yMjgxNiIsICJzdGF0dXMiOiAicHJlc2VudCJ9LAogICAgeyJwdXJsIjogInBrZzpweXBpL3BpbGxvd0A5LjAuMCIsICJjdmUiOiAiQ1ZFLTIwMjItMjI4MTciLCAic3RhdHVzIjogInByZXNlbnQifQogIF0sCiAgImRldGVjdGlvbl9yZXF1aXJlbWVudHMiOiB7CiAgICAidHJhY2tfY3ZlX2FsaWFzZXMiOiB0cnVlLAogICAgImhhbmRsZV9jdmVfc3BsaXRzIjogdHJ1ZSwKICAgICJoYW5kbGVfY3ZlX21lcmdlcyI6IHRydWUsCiAgICAidHJhY2tfY3ZlX2NoYWlucyI6IHRydWUsCiAgICAidXNlX29zdl9hbGlhc2VzIjogdHJ1ZQogIH0sCiAgInRlc3RfYXNzZXJ0aW9ucyI6IFsKICAgICJBbGwgQ1ZFcyBmcm9tIHNwbGl0IHZ1bG5lcmFiaWxpdGllcyBtdXN0IGJlIHJlcG9ydGVkIiwKICAgICJNZXJnZWQgQ1ZFcyBzaG91bGQgdXNlIGNhbm9uaWNhbCBDVkUgSUQiLAogICAgIkNWRSBhbGlhc2VzIG11c3QgYmUgdHJhY2tlZCAoZS5nLiwgdmlhIE9TVikiLAogICAgIk5vIGR1cGxpY2F0ZSBmaW5kaW5ncyBmb3Igc2FtZSB1bmRlcmx5aW5nIGlzc3VlIgogIF0KfQo=",
"signatures": [
{
"sig": "stub-signature",
"keyid": "stub-key-id"
}
]
}

View File

@@ -1,8 +0,0 @@
{
"id": "fc2-trivy-offline-schema",
"scanner": "trivy",
"feed": "offline-cache-2025-11-30",
"expected_errors": [
{"code": "SCHEMA_MISMATCH", "message": "offline DB schema mismatch"}
]
}

View File

@@ -1 +0,0 @@
input stub for fc2

View File

@@ -1,10 +0,0 @@
{
"payloadType": "application/json",
"payload": "ewogICJpZCI6ICJmYzItdHJpdnktb2ZmbGluZS1zY2hlbWEiLAogICJzY2FubmVyIjogInRyaXZ5IiwKICAiZmVlZCI6ICJvZmZsaW5lLWNhY2hlLTIwMjUtMTEtMzAiLAogICJleHBlY3RlZF9lcnJvcnMiOiBbCiAgICB7ImNvZGUiOiAiU0NIRU1BX01JU01BVENIIiwgIm1lc3NhZ2UiOiAib2ZmbGluZSBEQiBzY2hlbWEgbWlzbWF0Y2gifQogIF0KfQo=",
"signatures": [
{
"keyid": "stub-key-id",
"sig": "stub-signature"
}
]
}

View File

@@ -1 +0,0 @@
stub-signature

View File

@@ -1,8 +0,0 @@
{
"id": "fc3-sbom-parity-drift",
"scanner": "syft",
"feed": "offline-cache-2025-11-30",
"expected_findings": [
{"purl": "pkg:docker/example@1.0.0", "issue": "sbom_parity_drift"}
]
}

View File

@@ -1 +0,0 @@
input stub for fc3

View File

@@ -1,10 +0,0 @@
{
"payloadType": "application/json",
"payload": "ewogICJpZCI6ICJmYzMtc2JvbS1wYXJpdHktZHJpZnQiLAogICJzY2FubmVyIjogInN5ZnQiLAogICJmZWVkIjogIm9mZmxpbmUtY2FjaGUtMjAyNS0xMS0zMCIsCiAgImV4cGVjdGVkX2ZpbmRpbmdzIjogWwogICAgeyJwdXJsIjogInBrZzpkb2NrZXIvZXhhbXBsZUAxLjAuMCIsICJpc3N1ZSI6ICJzYm9tX3Bhcml0eV9kcmlmdCJ9CiAgXQp9Cg==",
"signatures": [
{
"keyid": "stub-key-id",
"sig": "stub-signature"
}
]
}

View File

@@ -1 +0,0 @@
stub-signature

View File

@@ -1,8 +0,0 @@
{
"id": "fc4-grype-version-divergence",
"scanner": "grype",
"feed": "offline-cache-2025-11-30",
"expected_warnings": [
{"code": "VERSION_DIVERGENCE", "message": "scanner version drift detected"}
]
}

View File

@@ -1 +0,0 @@
input stub for fc4

View File

@@ -1,10 +0,0 @@
{
"payloadType": "application/json",
"payload": "ewogICJpZCI6ICJmYzQtZ3J5cGUtdmVyc2lvbi1kaXZlcmdlbmNlIiwKICAic2Nhbm5lciI6ICJncnlwZSIsCiAgImZlZWQiOiAib2ZmbGluZS1jYWNoZS0yMDI1LTExLTMwIiwKICAiZXhwZWN0ZWRfd2FybmluZ3MiOiBbCiAgICB7ImNvZGUiOiAiVkVSU0lPTl9ESVZFUkdFTkNFIiwgIm1lc3NhZ2UiOiAic2Nhbm5lciB2ZXJzaW9uIGRyaWZ0IGRldGVjdGVkIn0KICBdCn0K",
"signatures": [
{
"keyid": "stub-key-id",
"sig": "stub-signature"
}
]
}

View File

@@ -1 +0,0 @@
stub-signature

View File

@@ -1,8 +0,0 @@
{
"id": "fc5-inconsistent-detection",
"scanner": "grype",
"feed": "offline-cache-2025-11-30",
"expected_findings": [
{"purl": "pkg:docker/example@1.0.0", "issue": "inconsistent_detection"}
]
}

View File

@@ -1 +0,0 @@
input stub for fc5

View File

@@ -1,10 +0,0 @@
{
"payloadType": "application/json",
"payload": "ewogICJpZCI6ICJmYzUtaW5jb25zaXN0ZW50LWRldGVjdGlvbiIsCiAgInNjYW5uZXIiOiAiZ3J5cGUiLAogICJmZWVkIjogIm9mZmxpbmUtY2FjaGUtMjAyNS0xMS0zMCIsCiAgImV4cGVjdGVkX2ZpbmRpbmdzIjogWwogICAgeyJwdXJsIjogInBrZzpkb2NrZXIvZXhhbXBsZUAxLjAuMCIsICJpc3N1ZSI6ICJpbmNvbnNpc3RlbnRfZGV0ZWN0aW9uIn0KICBdCn0K",
"signatures": [
{
"keyid": "stub-key-id",
"sig": "stub-signature"
}
]
}

View File

@@ -1 +0,0 @@
stub-signature

View File

@@ -1,45 +0,0 @@
{
"id": "fc6-java-shadow-jar",
"name": "Java Shadow JAR Failure Case",
"description": "Fat/uber JARs with shaded dependencies not correctly analyzed. Maven shade plugin or Gradle shadow can relocate classes, causing scanners to miss vulnerable dependencies that have been repackaged under different package names.",
"scanner": "syft",
"feed": "offline-cache-2025-12-16",
"failure_mode": {
"category": "dependency_masking",
"root_cause": "Shaded JAR analysis fails to detect relocated vulnerable classes",
"affected_scanners": ["syft", "grype", "trivy"],
"severity": "high"
},
"input": {
"type": "jar",
"file": "sample-uber.jar",
"build_tool": "maven-shade-plugin",
"original_dependencies": [
{"groupId": "org.apache.logging.log4j", "artifactId": "log4j-core", "version": "2.14.1"},
{"groupId": "com.google.guava", "artifactId": "guava", "version": "20.0"},
{"groupId": "org.yaml", "artifactId": "snakeyaml", "version": "1.26"}
],
"shaded_packages": [
{"original": "org.apache.logging.log4j", "relocated": "com.example.shaded.log4j"},
{"original": "com.google.guava", "relocated": "com.example.shaded.guava"},
{"original": "org.yaml.snakeyaml", "relocated": "com.example.shaded.yaml"}
]
},
"expected_findings": [
{"purl": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1", "cve": "CVE-2021-44228", "status": "present", "severity": "critical", "note": "Log4Shell - must be detected even when shaded"},
{"purl": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1", "cve": "CVE-2021-45046", "status": "present", "severity": "critical"},
{"purl": "pkg:maven/com.google.guava/guava@20.0", "cve": "CVE-2018-10237", "status": "present", "severity": "medium"},
{"purl": "pkg:maven/org.yaml/snakeyaml@1.26", "cve": "CVE-2022-1471", "status": "present", "severity": "high"}
],
"detection_requirements": {
"must_detect_shaded": true,
"analyze_jar_contents": true,
"check_pom_properties": true,
"scan_manifest_mf": true
},
"test_assertions": [
"All expected CVEs must be detected regardless of class relocation",
"Original artifact coordinates must be resolved from META-INF",
"Shaded package names should not prevent vulnerability matching"
]
}

View File

@@ -1,26 +0,0 @@
# FC6: Java Shadow JAR Test Case
#
# This fixture tests detection of vulnerabilities in fat/uber JARs
# where dependencies have been shaded (class packages relocated).
#
# Input: Simulated uber JAR with shaded log4j, guava, and snakeyaml
# Expected: All known CVEs detected despite class relocation
#
# Test command:
# stellaops scan --input sample-uber.jar --offline --deterministic
type: jar
path: sample-uber.jar
sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
# Shaded dependencies (original → relocated)
shaded:
- org.apache.logging.log4j → com.example.shaded.log4j
- com.google.guava → com.example.shaded.guava
- org.yaml.snakeyaml → com.example.shaded.yaml
# Original versions (from pom.properties in META-INF)
versions:
log4j-core: 2.14.1
guava: 20.0
snakeyaml: 1.26

View File

@@ -1,10 +0,0 @@
{
"payloadType": "application/json",
"payload": "ewogICJpZCI6ICJmYzYtamF2YS1zaGFkb3ctamFyIiwKICAibmFtZSI6ICJKYXZhIFNoYWRvdyBKQVIgRmFpbHVyZSBDYXNlIiwKICAiZGVzY3JpcHRpb24iOiAiRmF0L3ViZXIgSkFScyB3aXRoIHNoYWRlZCBkZXBlbmRlbmNpZXMgbm90IGNvcnJlY3RseSBhbmFseXplZC4gTWF2ZW4gc2hhZGUgcGx1Z2luIG9yIEdyYWRsZSBzaGFkb3cgY2FuIHJlbG9jYXRlIGNsYXNzZXMsIGNhdXNpbmcgc2Nhbm5lcnMgdG8gbWlzcyB2dWxuZXJhYmxlIGRlcGVuZGVuY2llcyB0aGF0IGhhdmUgYmVlbiByZXBhY2thZ2VkIHVuZGVyIGRpZmZlcmVudCBwYWNrYWdlIG5hbWVzLiIsCiAgInNjYW5uZXIiOiAic3lmdCIsCiAgImZlZWQiOiAib2ZmbGluZS1jYWNoZS0yMDI1LTEyLTE2IiwKICAiZmFpbHVyZV9tb2RlIjogewogICAgImNhdGVnb3J5IjogImRlcGVuZGVuY3lfbWFza2luZyIsCiAgICAicm9vdF9jYXVzZSI6ICJTaGFkZWQgSkFSIGFuYWx5c2lzIGZhaWxzIHRvIGRldGVjdCByZWxvY2F0ZWQgdnVsbmVyYWJsZSBjbGFzc2VzIiwKICAgICJhZmZlY3RlZF9zY2FubmVycyI6IFsic3lmdCIsICJncnlwZSIsICJ0cml2eSJdLAogICAgInNldmVyaXR5IjogImhpZ2giCiAgfSwKICAiaW5wdXQiOiB7CiAgICAidHlwZSI6ICJqYXIiLAogICAgImZpbGUiOiAic2FtcGxlLXViZXIuamFyIiwKICAgICJidWlsZF90b29sIjogIm1hdmVuLXNoYWRlLXBsdWdpbiIsCiAgICAib3JpZ2luYWxfZGVwZW5kZW5jaWVzIjogWwogICAgICB7Imdyb3VwSWQiOiAib3JnLmFwYWNoZS5sb2dnaW5nLmxvZzRqIiwgImFydGlmYWN0SWQiOiAibG9nNGotY29yZSIsICJ2ZXJzaW9uIjogIjIuMTQuMSJ9LAogICAgICB7Imdyb3VwSWQiOiAiY29tLmdvb2dsZS5ndWF2YSIsICJhcnRpZmFjdElkIjogImd1YXZhIiwgInZlcnNpb24iOiAiMjAuMCJ9LAogICAgICB7Imdyb3VwSWQiOiAib3JnLnlhbWwiLCAiYXJ0aWZhY3RJZCI6ICJzbmFrZXlhbWwiLCAidmVyc2lvbiI6ICIxLjI2In0KICAgIF0sCiAgICAic2hhZGVkX3BhY2thZ2VzIjogWwogICAgICB7Im9yaWdpbmFsIjogIm9yZy5hcGFjaGUubG9nZ2luZy5sb2c0aiIsICJyZWxvY2F0ZWQiOiAiY29tLmV4YW1wbGUuc2hhZGVkLmxvZzRqIn0sCiAgICAgIHsib3JpZ2luYWwiOiAiY29tLmdvb2dsZS5ndWF2YSIsICJyZWxvY2F0ZWQiOiAiY29tLmV4YW1wbGUuc2hhZGVkLmd1YXZhIn0sCiAgICAgIHsib3JpZ2luYWwiOiAib3JnLnlhbWwuc25ha2V5YW1sIiwgInJlbG9jYXRlZCI6ICJjb20uZXhhbXBsZS5zaGFkZWQueWFtbCJ9CiAgICBdCiAgfSwKICAiZXhwZWN0ZWRfZmluZGluZ3MiOiBbCiAgICB7InB1cmwiOiAicGtnOm1hdmVuL29yZy5hcGFjaGUubG9nZ2luZy5sb2c0ai9sb2c0ai1jb3JlQDIuMTQuMSIsICJjdmUiOiAiQ1ZFLTIwMjEtNDQyMjgiLCAic3RhdHVzIjogInByZXNlbnQiLCAic2V2ZXJpdHkiOiAiY3JpdGljYWwiLCAibm90ZSI6ICJMb2c0U2hlbGwgLSBtdXN0IGJlIGRldGVjdGVkIGV2ZW4gd2hlbiBzaGFkZWQifSwKICAgIHsicHVybCI6ICJwa2c6bWF2ZW4vb3JnLmFwYWNoZS5sb2dnaW5nLmxvZzRqL2xvZzRqLWNvcmVAMi4xNC4xIiwgImN2ZSI6ICJDVkUtMjAyMS00NTA0NiIsICJzdGF0dXMiOiAicHJlc2VudCIsICJzZXZlcml0eSI6ICJjcml0aWNhbCJ9LAogICAgeyJwdXJsIjogInBrZzptYXZlbi9jb20uZ29vZ2xlLmd1YXZhL2d1YXZhQDIwLjAiLCAiY3ZlIjogIkNWRS0yMDE4LTEwMjM3IiwgInN0YXR1cyI6ICJwcmVzZW50IiwgInNldmVyaXR5IjogIm1lZGl1bSJ9LAogICAgeyJwdXJsIjogInBrZzptYXZlbi9vcmcueWFtbC9zbmFrZXlhbWxAMS4yNiIsICJjdmUiOiAiQ1ZFLTIwMjItMTQ3MSIsICJzdGF0dXMiOiAicHJlc2VudCIsICJzZXZlcml0eSI6ICJoaWdoIn0KICBdLAogICJkZXRlY3Rpb25fcmVxdWlyZW1lbnRzIjogewogICAgIm11c3RfZGV0ZWN0X3NoYWRlZCI6IHRydWUsCiAgICAiYW5hbHl6ZV9qYXJfY29udGVudHMiOiB0cnVlLAogICAgImNoZWNrX3BvbV9wcm9wZXJ0aWVzIjogdHJ1ZSwKICAgICJzY2FuX21hbmlmZXN0X21mIjogdHJ1ZQogIH0sCiAgInRlc3RfYXNzZXJ0aW9ucyI6IFsKICAgICJBbGwgZXhwZWN0ZWQgQ1ZFcyBtdXN0IGJlIGRldGVjdGVkIHJlZ2FyZGxlc3Mgb2YgY2xhc3MgcmVsb2NhdGlvbiIsCiAgICAiT3JpZ2luYWwgYXJ0aWZhY3QgY29vcmRpbmF0ZXMgbXVzdCBiZSByZXNvbHZlZCBmcm9tIE1FVEEtSU5GIiwKICAgICJTaGFkZWQgcGFja2FnZSBuYW1lcyBzaG91bGQgbm90IHByZXZlbnQgdnVsbmVyYWJpbGl0eSBtYXRjaGluZyIKICBdCn0K",
"signatures": [
{
"sig": "stub-signature",
"keyid": "stub-key-id"
}
]
}

View File

@@ -1,51 +0,0 @@
{
"id": "fc7-dotnet-transitive-pinning",
"name": ".NET Transitive Pinning Failure Case",
"description": "Transitive dependency version conflicts in .NET projects where packages.lock.json pins different versions than what's actually resolved. Central Package Management (CPM) and transitive pinning can cause discrepancies.",
"scanner": "syft",
"feed": "offline-cache-2025-12-16",
"failure_mode": {
"category": "version_mismatch",
"root_cause": "Transitive dependency resolution differs between restore and scan",
"affected_scanners": ["syft", "trivy", "grype"],
"severity": "high"
},
"input": {
"type": "dotnet_project",
"files": ["SampleApp.csproj", "packages.lock.json", "Directory.Packages.props"],
"framework": "net8.0",
"direct_dependencies": [
{"id": "Microsoft.EntityFrameworkCore", "version": "8.0.0"},
{"id": "Newtonsoft.Json", "version": "13.0.1"}
],
"transitive_conflicts": [
{
"package": "System.Text.Json",
"lock_file_version": "8.0.0",
"actual_resolved": "8.0.1",
"reason": "CPM override"
},
{
"package": "Microsoft.Extensions.Logging",
"lock_file_version": "8.0.0",
"actual_resolved": "7.0.0",
"reason": "Transitive from older package"
}
]
},
"expected_findings": [
{"purl": "pkg:nuget/System.Text.Json@8.0.1", "cve": "CVE-2024-XXXX", "status": "present", "note": "Must use actual resolved version"},
{"purl": "pkg:nuget/Microsoft.Extensions.Logging@7.0.0", "cve": "CVE-2023-YYYY", "status": "present", "note": "Transitive downgrade detection"}
],
"detection_requirements": {
"use_lock_file": true,
"verify_transitive_resolution": true,
"check_cpm_overrides": true,
"resolve_version_conflicts": true
},
"test_assertions": [
"Scanner must use actual resolved versions, not lock file versions when they conflict",
"Transitive downgrades must be detected and flagged",
"CPM overrides must be respected in version resolution"
]
}

View File

@@ -1,31 +0,0 @@
# FC7: .NET Transitive Pinning Test Case
#
# This fixture tests detection of vulnerabilities when lock file
# versions differ from actually resolved transitive dependencies.
#
# Input: .NET 8 project with CPM and transitive version conflicts
# Expected: Vulnerabilities detected using actual resolved versions
type: dotnet_project
framework: net8.0
# Direct dependencies
direct:
- Microsoft.EntityFrameworkCore@8.0.0
- Newtonsoft.Json@13.0.1
# Transitive conflicts (lock vs actual)
conflicts:
- package: System.Text.Json
lock_version: 8.0.0
actual_version: 8.0.1
- package: Microsoft.Extensions.Logging
lock_version: 8.0.0
actual_version: 7.0.0
# Files to analyze
files:
- SampleApp.csproj
- packages.lock.json
- Directory.Packages.props

View File

@@ -1,10 +0,0 @@
{
"payloadType": "application/json",
"payload": "ewogICJpZCI6ICJmYzctZG90bmV0LXRyYW5zaXRpdmUtcGlubmluZyIsCiAgIm5hbWUiOiAiLk5FVCBUcmFuc2l0aXZlIFBpbm5pbmcgRmFpbHVyZSBDYXNlIiwKICAiZGVzY3JpcHRpb24iOiAiVHJhbnNpdGl2ZSBkZXBlbmRlbmN5IHZlcnNpb24gY29uZmxpY3RzIGluIC5ORVQgcHJvamVjdHMgd2hlcmUgcGFja2FnZXMubG9jay5qc29uIHBpbnMgZGlmZmVyZW50IHZlcnNpb25zIHRoYW4gd2hhdCdzIGFjdHVhbGx5IHJlc29sdmVkLiBDZW50cmFsIFBhY2thZ2UgTWFuYWdlbWVudCAoQ1BNKSBhbmQgdHJhbnNpdGl2ZSBwaW5uaW5nIGNhbiBjYXVzZSBkaXNjcmVwYW5jaWVzLiIsCiAgInNjYW5uZXIiOiAic3lmdCIsCiAgImZlZWQiOiAib2ZmbGluZS1jYWNoZS0yMDI1LTEyLTE2IiwKICAiZmFpbHVyZV9tb2RlIjogewogICAgImNhdGVnb3J5IjogInZlcnNpb25fbWlzbWF0Y2giLAogICAgInJvb3RfY2F1c2UiOiAiVHJhbnNpdGl2ZSBkZXBlbmRlbmN5IHJlc29sdXRpb24gZGlmZmVycyBiZXR3ZWVuIHJlc3RvcmUgYW5kIHNjYW4iLAogICAgImFmZmVjdGVkX3NjYW5uZXJzIjogWyJzeWZ0IiwgInRyaXZ5IiwgImdyeXBlIl0sCiAgICAic2V2ZXJpdHkiOiAiaGlnaCIKICB9LAogICJpbnB1dCI6IHsKICAgICJ0eXBlIjogImRvdG5ldF9wcm9qZWN0IiwKICAgICJmaWxlcyI6IFsiU2FtcGxlQXBwLmNzcHJvaiIsICJwYWNrYWdlcy5sb2NrLmpzb24iLCAiRGlyZWN0b3J5LlBhY2thZ2VzLnByb3BzIl0sCiAgICAiZnJhbWV3b3JrIjogIm5ldDguMCIsCiAgICAiZGlyZWN0X2RlcGVuZGVuY2llcyI6IFsKICAgICAgeyJpZCI6ICJNaWNyb3NvZnQuRW50aXR5RnJhbWV3b3JrQ29yZSIsICJ2ZXJzaW9uIjogIjguMC4wIn0sCiAgICAgIHsiaWQiOiAiTmV3dG9uc29mdC5Kc29uIiwgInZlcnNpb24iOiAiMTMuMC4xIn0KICAgIF0sCiAgICAidHJhbnNpdGl2ZV9jb25mbGljdHMiOiBbCiAgICAgIHsKICAgICAgICAicGFja2FnZSI6ICJTeXN0ZW0uVGV4dC5Kc29uIiwKICAgICAgICAibG9ja19maWxlX3ZlcnNpb24iOiAiOC4wLjAiLAogICAgICAgICJhY3R1YWxfcmVzb2x2ZWQiOiAiOC4wLjEiLAogICAgICAgICJyZWFzb24iOiAiQ1BNIG92ZXJyaWRlIgogICAgICB9LAogICAgICB7CiAgICAgICAgInBhY2thZ2UiOiAiTWljcm9zb2Z0LkV4dGVuc2lvbnMuTG9nZ2luZyIsCiAgICAgICAgImxvY2tfZmlsZV92ZXJzaW9uIjogIjguMC4wIiwgCiAgICAgICAgImFjdHVhbF9yZXNvbHZlZCI6ICI3LjAuMCIsCiAgICAgICAgInJlYXNvbiI6ICJUcmFuc2l0aXZlIGZyb20gb2xkZXIgcGFja2FnZSIKICAgICAgfQogICAgXQogIH0sCiAgImV4cGVjdGVkX2ZpbmRpbmdzIjogWwogICAgeyJwdXJsIjogInBrZzpudWdldC9TeXN0ZW0uVGV4dC5Kc29uQDguMC4xIiwgImN2ZSI6ICJDVkUtMjAyNC1YWFhYIiwgInN0YXR1cyI6ICJwcmVzZW50IiwgIm5vdGUiOiAiTXVzdCB1c2UgYWN0dWFsIHJlc29sdmVkIHZlcnNpb24ifSwKICAgIHsicHVybCI6ICJwa2c6bnVnZXQvTWljcm9zb2Z0LkV4dGVuc2lvbnMuTG9nZ2luZ0A3LjAuMCIsICJjdmUiOiAiQ1ZFLTIwMjMtWVlZWSIsICJzdGF0dXMiOiAicHJlc2VudCIsICJub3RlIjogIlRyYW5zaXRpdmUgZG93bmdyYWRlIGRldGVjdGlvbiJ9CiAgXSwKICAiZGV0ZWN0aW9uX3JlcXVpcmVtZW50cyI6IHsKICAgICJ1c2VfbG9ja19maWxlIjogdHJ1ZSwKICAgICJ2ZXJpZnlfdHJhbnNpdGl2ZV9yZXNvbHV0aW9uIjogdHJ1ZSwKICAgICJjaGVja19jcG1fb3ZlcnJpZGVzIjogdHJ1ZSwKICAgICJyZXNvbHZlX3ZlcnNpb25fY29uZmxpY3RzIjogdHJ1ZQogIH0sCiAgInRlc3RfYXNzZXJ0aW9ucyI6IFsKICAgICJTY2FubmVyIG11c3QgdXNlIGFjdHVhbCByZXNvbHZlZCB2ZXJzaW9ucywgbm90IGxvY2sgZmlsZSB2ZXJzaW9ucyB3aGVuIHRoZXkgY29uZmxpY3QiLAogICAgIlRyYW5zaXRpdmUgZG93bmdyYWRlcyBtdXN0IGJlIGRldGVjdGVkIGFuZCBmbGFnZ2VkIiwKICAgICJDUE0gb3ZlcnJpZGVzIG11c3QgYmUgcmVzcGVjdGVkIGluIHZlcnNpb24gcmVzb2x1dGlvbiIKICBdCn0K",
"signatures": [
{
"sig": "stub-signature",
"keyid": "stub-key-id"
}
]
}

View File

@@ -1,18 +0,0 @@
# FC8: Docker Multi-Stage Leakage fixture
#
# Purpose: Ensure scanners attribute packages to the final stage only.
# Note: This file is a deterministic text fixture; it is not executed in tests.
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS builder
WORKDIR /src
# Build-stage only tools (examples): dotnet-sdk-8.0, build-essential, git
RUN dotnet --info > /dev/null
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
# Only artifacts copied from builder should appear in final stage.
COPY --from=builder /src/ /app/
ENTRYPOINT ["dotnet", "SampleApp.dll"]

View File

@@ -1,52 +0,0 @@
{
"id": "fc8-docker-multistage-leakage",
"name": "Docker Multi-Stage Leakage Failure Case",
"description": "Build-time dependencies leaking into runtime image analysis. Multi-stage Docker builds should only report vulnerabilities for packages in the final stage, but some scanners incorrectly include build-stage dependencies.",
"scanner": "trivy",
"feed": "offline-cache-2025-12-16",
"failure_mode": {
"category": "scope_confusion",
"root_cause": "Scanner analyzes all layers instead of final image state",
"affected_scanners": ["trivy", "grype", "syft"],
"severity": "medium"
},
"input": {
"type": "dockerfile",
"file": "Dockerfile.multistage",
"stages": [
{
"name": "builder",
"base": "mcr.microsoft.com/dotnet/sdk:8.0",
"packages": [
{"name": "dotnet-sdk-8.0", "type": "os", "scope": "build"},
{"name": "build-essential", "type": "os", "scope": "build"}
]
},
{
"name": "runtime",
"base": "mcr.microsoft.com/dotnet/aspnet:8.0",
"packages": [
{"name": "aspnetcore-runtime-8.0", "type": "os", "scope": "runtime"},
{"name": "libssl3", "type": "os", "scope": "runtime"}
],
"is_final": true
}
]
},
"expected_findings": [
{"purl": "pkg:deb/debian/libssl3@3.0.11", "cve": "CVE-2024-RUNTIME", "status": "present", "note": "Runtime image vulnerability - should be reported"},
{"purl": "pkg:deb/debian/build-essential@12.9", "cve": "CVE-2024-BUILD", "status": "absent", "note": "Build stage only - should NOT be reported"}
],
"detection_requirements": {
"analyze_final_stage_only": true,
"track_layer_provenance": true,
"exclude_build_dependencies": true,
"respect_copy_from_directives": true
},
"test_assertions": [
"Only vulnerabilities in final stage packages should be reported",
"Build-stage-only packages must not appear in findings",
"COPY --from directives must be traced correctly",
"Layer squashing must not leak intermediate content"
]
}

View File

@@ -1,32 +0,0 @@
# FC8: Docker Multi-Stage Leakage Test Case
#
# This fixture tests that scanners correctly analyze only the final
# stage of multi-stage Docker builds, not intermediate build stages.
#
# Input: Multi-stage Dockerfile with build and runtime stages
# Expected: Only runtime stage vulnerabilities reported
type: dockerfile
file: Dockerfile.multistage
# Stage definitions
stages:
- name: builder
base: mcr.microsoft.com/dotnet/sdk:8.0
scope: build
packages:
- dotnet-sdk-8.0
- build-essential
- git
- name: runtime
base: mcr.microsoft.com/dotnet/aspnet:8.0
scope: runtime
is_final: true
packages:
- aspnetcore-runtime-8.0
- libssl3
# Expected behavior
should_report: runtime stage packages only
should_not_report: build stage packages

View File

@@ -1,10 +0,0 @@
{
"payloadType": "application/json",
"payload": "ewogICJpZCI6ICJmYzgtZG9ja2VyLW11bHRpc3RhZ2UtbGVha2FnZSIsCiAgIm5hbWUiOiAiRG9ja2VyIE11bHRpLVN0YWdlIExlYWthZ2UgRmFpbHVyZSBDYXNlIiwKICAiZGVzY3JpcHRpb24iOiAiQnVpbGQtdGltZSBkZXBlbmRlbmNpZXMgbGVha2luZyBpbnRvIHJ1bnRpbWUgaW1hZ2UgYW5hbHlzaXMuIE11bHRpLXN0YWdlIERvY2tlciBidWlsZHMgc2hvdWxkIG9ubHkgcmVwb3J0IHZ1bG5lcmFiaWxpdGllcyBmb3IgcGFja2FnZXMgaW4gdGhlIGZpbmFsIHN0YWdlLCBidXQgc29tZSBzY2FubmVycyBpbmNvcnJlY3RseSBpbmNsdWRlIGJ1aWxkLXN0YWdlIGRlcGVuZGVuY2llcy4iLAogICJzY2FubmVyIjogInRyaXZ5IiwKICAiZmVlZCI6ICJvZmZsaW5lLWNhY2hlLTIwMjUtMTItMTYiLAogICJmYWlsdXJlX21vZGUiOiB7CiAgICAiY2F0ZWdvcnkiOiAic2NvcGVfY29uZnVzaW9uIiwKICAgICJyb290X2NhdXNlIjogIlNjYW5uZXIgYW5hbHl6ZXMgYWxsIGxheWVycyBpbnN0ZWFkIG9mIGZpbmFsIGltYWdlIHN0YXRlIiwKICAgICJhZmZlY3RlZF9zY2FubmVycyI6IFsidHJpdnkiLCAiZ3J5cGUiLCAic3lmdCJdLAogICAgInNldmVyaXR5IjogIm1lZGl1bSIKICB9LAogICJpbnB1dCI6IHsKICAgICJ0eXBlIjogImRvY2tlcmZpbGUiLAogICAgImZpbGUiOiAiRG9ja2VyZmlsZS5tdWx0aXN0YWdlIiwKICAgICJzdGFnZXMiOiBbCiAgICAgIHsKICAgICAgICAibmFtZSI6ICJidWlsZGVyIiwKICAgICAgICAiYmFzZSI6ICJtY3IubWljcm9zb2Z0LmNvbS9kb3RuZXQvc2RrOjguMCIsCiAgICAgICAgInBhY2thZ2VzIjogWwogICAgICAgICAgeyJuYW1lIjogImRvdG5ldC1zZGstOC4wIiwgInR5cGUiOiAib3MiLCAic2NvcGUiOiAiYnVpbGQifSwKICAgICAgICAgIHsibmFtZSI6ICJidWlsZC1lc3NlbnRpYWwiLCAidHlwZSI6ICJvcyIsICJzY29wZSI6ICJidWlsZCJ9CiAgICAgICAgXQogICAgICB9LAogICAgICB7CiAgICAgICAgIm5hbWUiOiAicnVudGltZSIsCiAgICAgICAgImJhc2UiOiAibWNyLm1pY3Jvc29mdC5jb20vZG90bmV0L2FzcG5ldDo4LjAiLAogICAgICAgICJwYWNrYWdlcyI6IFsKICAgICAgICAgIHsibmFtZSI6ICJhc3BuZXRjb3JlLXJ1bnRpbWUtOC4wIiwgInR5cGUiOiAib3MiLCAic2NvcGUiOiAicnVudGltZSJ9LAogICAgICAgICAgeyJuYW1lIjogImxpYnNzbDMiLCAidHlwZSI6ICJvcyIsICJzY29wZSI6ICJydW50aW1lIn0KICAgICAgICBdLAogICAgICAgICJpc19maW5hbCI6IHRydWUKICAgICAgfQogICAgXQogIH0sCiAgImV4cGVjdGVkX2ZpbmRpbmdzIjogWwogICAgeyJwdXJsIjogInBrZzpkZWIvZGViaWFuL2xpYnNzbDNAMy4wLjExIiwgImN2ZSI6ICJDVkUtMjAyNC1SVU5USU1FIiwgInN0YXR1cyI6ICJwcmVzZW50IiwgIm5vdGUiOiAiUnVudGltZSBpbWFnZSB2dWxuZXJhYmlsaXR5IC0gc2hvdWxkIGJlIHJlcG9ydGVkIn0sCiAgICB7InB1cmwiOiAicGtnOmRlYi9kZWJpYW4vYnVpbGQtZXNzZW50aWFsQDEyLjkiLCAiY3ZlIjogIkNWRS0yMDI0LUJVSUxEIiwgInN0YXR1cyI6ICJhYnNlbnQiLCAibm90ZSI6ICJCdWlsZCBzdGFnZSBvbmx5IC0gc2hvdWxkIE5PVCBiZSByZXBvcnRlZCJ9CiAgXSwKICAiZGV0ZWN0aW9uX3JlcXVpcmVtZW50cyI6IHsKICAgICJhbmFseXplX2ZpbmFsX3N0YWdlX29ubHkiOiB0cnVlLAogICAgInRyYWNrX2xheWVyX3Byb3ZlbmFuY2UiOiB0cnVlLAogICAgImV4Y2x1ZGVfYnVpbGRfZGVwZW5kZW5jaWVzIjogdHJ1ZSwKICAgICJyZXNwZWN0X2NvcHlfZnJvbV9kaXJlY3RpdmVzIjogdHJ1ZQogIH0sCiAgInRlc3RfYXNzZXJ0aW9ucyI6IFsKICAgICJPbmx5IHZ1bG5lcmFiaWxpdGllcyBpbiBmaW5hbCBzdGFnZSBwYWNrYWdlcyBzaG91bGQgYmUgcmVwb3J0ZWQiLAogICAgIkJ1aWxkLXN0YWdlLW9ubHkgcGFja2FnZXMgbXVzdCBub3QgYXBwZWFyIGluIGZpbmRpbmdzIiwKICAgICJDT1BZIC0tZnJvbSBkaXJlY3RpdmVzIG11c3QgYmUgdHJhY2VkIGNvcnJlY3RseSIsCiAgICAiTGF5ZXIgc3F1YXNoaW5nIG11c3Qgbm90IGxlYWsgaW50ZXJtZWRpYXRlIGNvbnRlbnQiCiAgXQp9Cg==",
"signatures": [
{
"sig": "stub-signature",
"keyid": "stub-key-id"
}
]
}

View File

@@ -1,41 +0,0 @@
{
"id": "fc9-purl-namespace-collision",
"name": "PURL Namespace Collision Failure Case",
"description": "Different ecosystems with same package names causing incorrect vulnerability attribution. For example, 'requests' exists in both npm and pypi with completely different codebases and vulnerabilities.",
"scanner": "grype",
"feed": "offline-cache-2025-12-16",
"failure_mode": {
"category": "identity_confusion",
"root_cause": "Package name matched without ecosystem qualifier",
"affected_scanners": ["grype", "trivy", "syft"],
"severity": "critical"
},
"input": {
"type": "mixed_sbom",
"ecosystems": ["npm", "pypi", "cargo", "nuget"],
"packages": [
{"name": "requests", "version": "2.28.0", "ecosystem": "pypi", "purl": "pkg:pypi/requests@2.28.0"},
{"name": "requests", "version": "0.3.0", "ecosystem": "npm", "purl": "pkg:npm/requests@0.3.0"},
{"name": "json", "version": "11.0.0", "ecosystem": "npm", "purl": "pkg:npm/json@11.0.0"},
{"name": "json", "version": "0.1.0", "ecosystem": "cargo", "purl": "pkg:cargo/json@0.1.0"},
{"name": "System.Text.Json", "version": "8.0.0", "ecosystem": "nuget", "purl": "pkg:nuget/System.Text.Json@8.0.0"}
]
},
"expected_findings": [
{"purl": "pkg:pypi/requests@2.28.0", "cve": "CVE-2023-PYPI", "status": "present", "note": "PyPI requests vulnerability"},
{"purl": "pkg:npm/requests@0.3.0", "cve": "CVE-2023-NPM", "status": "present", "note": "npm requests vulnerability - different package"},
{"purl": "pkg:pypi/requests@2.28.0", "cve": "CVE-2023-NPM", "status": "absent", "note": "MUST NOT cross-match npm CVE to pypi package"}
],
"detection_requirements": {
"ecosystem_qualified_matching": true,
"purl_type_enforcement": true,
"no_cross_ecosystem_matching": true,
"strict_namespace_validation": true
},
"test_assertions": [
"Vulnerabilities must only match packages with correct ecosystem",
"pkg:pypi/X must never match advisories for pkg:npm/X",
"PURL type must be part of vulnerability matching",
"Cross-ecosystem false positives are critical failures"
]
}

View File

@@ -1,29 +0,0 @@
# FC9: PURL Namespace Collision Test Case
#
# This fixture tests that scanners correctly differentiate between
# packages with the same name in different ecosystems.
#
# Input: SBOM with same-name packages from different ecosystems
# Expected: No cross-ecosystem vulnerability matching
type: mixed_sbom
format: spdx-2.3
# Packages with name collisions across ecosystems
packages:
# "requests" exists in both npm and pypi
- purl: pkg:pypi/requests@2.28.0
ecosystem: pypi
- purl: pkg:npm/requests@0.3.0
ecosystem: npm
# "json" exists in npm and cargo
- purl: pkg:npm/json@11.0.0
ecosystem: npm
- purl: pkg:cargo/json@0.1.0
ecosystem: cargo
# Critical requirement
rule: CVEs must only match within same ecosystem

View File

@@ -1,10 +0,0 @@
{
"payloadType": "application/json",
"payload": "ewogICJpZCI6ICJmYzktcHVybC1uYW1lc3BhY2UtY29sbGlzaW9uIiwKICAibmFtZSI6ICJQVVJMIE5hbWVzcGFjZSBDb2xsaXNpb24gRmFpbHVyZSBDYXNlIiwKICAiZGVzY3JpcHRpb24iOiAiRGlmZmVyZW50IGVjb3N5c3RlbXMgd2l0aCBzYW1lIHBhY2thZ2UgbmFtZXMgY2F1c2luZyBpbmNvcnJlY3QgdnVsbmVyYWJpbGl0eSBhdHRyaWJ1dGlvbi4gRm9yIGV4YW1wbGUsICdyZXF1ZXN0cycgZXhpc3RzIGluIGJvdGggbnBtIGFuZCBweXBpIHdpdGggY29tcGxldGVseSBkaWZmZXJlbnQgY29kZWJhc2VzIGFuZCB2dWxuZXJhYmlsaXRpZXMuIiwKICAic2Nhbm5lciI6ICJncnlwZSIsCiAgImZlZWQiOiAib2ZmbGluZS1jYWNoZS0yMDI1LTEyLTE2IiwKICAiZmFpbHVyZV9tb2RlIjogewogICAgImNhdGVnb3J5IjogImlkZW50aXR5X2NvbmZ1c2lvbiIsCiAgICAicm9vdF9jYXVzZSI6ICJQYWNrYWdlIG5hbWUgbWF0Y2hlZCB3aXRob3V0IGVjb3N5c3RlbSBxdWFsaWZpZXIiLAogICAgImFmZmVjdGVkX3NjYW5uZXJzIjogWyJncnlwZSIsICJ0cml2eSIsICJzeWZ0Il0sCiAgICAic2V2ZXJpdHkiOiAiY3JpdGljYWwiCiAgfSwKICAiaW5wdXQiOiB7CiAgICAidHlwZSI6ICJtaXhlZF9zYm9tIiwKICAgICJlY29zeXN0ZW1zIjogWyJucG0iLCAicHlwaSIsICJjYXJnbyIsICJudWdldCJdLAogICAgInBhY2thZ2VzIjogWwogICAgICB7Im5hbWUiOiAicmVxdWVzdHMiLCAidmVyc2lvbiI6ICIyLjI4LjAiLCAiZWNvc3lzdGVtIjogInB5cGkiLCAicHVybCI6ICJwa2c6cHlwaS9yZXF1ZXN0c0AyLjI4LjAifSwKICAgICAgeyJuYW1lIjogInJlcXVlc3RzIiwgInZlcnNpb24iOiAiMC4zLjAiLCAiZWNvc3lzdGVtIjogIm5wbSIsICJwdXJsIjogInBrZzpucG0vcmVxdWVzdHNAMC4zLjAifSwKICAgICAgeyJuYW1lIjogImpzb24iLCAidmVyc2lvbiI6ICIxMS4wLjAiLCAiZWNvc3lzdGVtIjogIm5wbSIsICJwdXJsIjogInBrZzpucG0vanNvbkAxMS4wLjAifSwKICAgICAgeyJuYW1lIjogImpzb24iLCAidmVyc2lvbiI6ICIwLjEuMCIsICJlY29zeXN0ZW0iOiAiY2FyZ28iLCAicHVybCI6ICJwa2c6Y2FyZ28vanNvbkAwLjEuMCJ9LAogICAgICB7Im5hbWUiOiAiU3lzdGVtLlRleHQuSnNvbiIsICJ2ZXJzaW9uIjogIjguMC4wIiwgImVjb3N5c3RlbSI6ICJudWdldCIsICJwdXJsIjogInBrZzpudWdldC9TeXN0ZW0uVGV4dC5Kc29uQDguMC4wIn0KICAgIF0KICB9LAogICJleHBlY3RlZF9maW5kaW5ncyI6IFsKICAgIHsicHVybCI6ICJwa2c6cHlwaS9yZXF1ZXN0c0AyLjI4LjAiLCAiY3ZlIjogIkNWRS0yMDIzLVBZUEkiLCAic3RhdHVzIjogInByZXNlbnQiLCAibm90ZSI6ICJQeVBJIHJlcXVlc3RzIHZ1bG5lcmFiaWxpdHkifSwKICAgIHsicHVybCI6ICJwa2c6bnBtL3JlcXVlc3RzQDAuMy4wIiwgImN2ZSI6ICJDVkUtMjAyMy1OUE0iLCAic3RhdHVzIjogInByZXNlbnQiLCAibm90ZSI6ICJucG0gcmVxdWVzdHMgdnVsbmVyYWJpbGl0eSAtIGRpZmZlcmVudCBwYWNrYWdlIn0sCiAgICB7InB1cmwiOiAicGtnOnB5cGkvcmVxdWVzdHNAMi4yOC4wIiwgImN2ZSI6ICJDVkUtMjAyMy1OUE0iLCAic3RhdHVzIjogImFic2VudCIsICJub3RlIjogIk1VU1QgTk9UIGNyb3NzLW1hdGNoIG5wbSBDVkUgdG8gcHlwaSBwYWNrYWdlIn0KICBdLAogICJkZXRlY3Rpb25fcmVxdWlyZW1lbnRzIjogewogICAgImVjb3N5c3RlbV9xdWFsaWZpZWRfbWF0Y2hpbmciOiB0cnVlLAogICAgInB1cmxfdHlwZV9lbmZvcmNlbWVudCI6IHRydWUsCiAgICAibm9fY3Jvc3NfZWNvc3lzdGVtX21hdGNoaW5nIjogdHJ1ZSwKICAgICJzdHJpY3RfbmFtZXNwYWNlX3ZhbGlkYXRpb24iOiB0cnVlCiAgfSwKICAidGVzdF9hc3NlcnRpb25zIjogWwogICAgIlZ1bG5lcmFiaWxpdGllcyBtdXN0IG9ubHkgbWF0Y2ggcGFja2FnZXMgd2l0aCBjb3JyZWN0IGVjb3N5c3RlbSIsCiAgICAicGtnOnB5cGkvWCBtdXN0IG5ldmVyIG1hdGNoIGFkdmlzb3JpZXMgZm9yIHBrZzpucG0vWCIsCiAgICAiUFVSTCB0eXBlIG11c3QgYmUgcGFydCBvZiB2dWxuZXJhYmlsaXR5IG1hdGNoaW5nIiwKICAgICJDcm9zcy1lY29zeXN0ZW0gZmFsc2UgcG9zaXRpdmVzIGFyZSBjcml0aWNhbCBmYWlsdXJlcyIKICBdCn0K",
"signatures": [
{
"sig": "stub-signature",
"keyid": "stub-key-id"
}
]
}

View File

@@ -1,55 +0,0 @@
scanner_versions:
grype: "0.76.1"
trivy: "0.49.1"
syft: "1.1.0"
feed_snapshot: "offline-cache-2025-12-16"
seeds:
default: 20251216
os:
distro: "ubuntu-22.04"
kernel: "5.15"
notes: "Offline-only; normalize outputs before comparison"
# Fixture catalogue (FC1-FC10)
fixtures:
fc1:
id: "fc1-credential-leak"
description: "Grype credential leak in environment"
added: "2025-11-30"
fc2:
id: "fc2-trivy-db-schema"
description: "Trivy offline DB schema mismatch"
added: "2025-11-30"
fc3:
id: "fc3-sbom-parity"
description: "SBOM parity drift between tools"
added: "2025-11-30"
fc4:
id: "fc4-grype-version"
description: "Grype version divergence"
added: "2025-11-30"
fc5:
id: "fc5-inconsistent-detection"
description: "Inconsistent detection across runs"
added: "2025-11-30"
fc6:
id: "fc6-java-shadow-jar"
description: "Fat/uber JARs with shaded dependencies"
added: "2025-12-16"
fc7:
id: "fc7-dotnet-transitive-pinning"
description: ".NET transitive dependency version conflicts"
added: "2025-12-16"
fc8:
id: "fc8-docker-multistage-leakage"
description: "Build-time deps leaking into runtime analysis"
added: "2025-12-16"
fc9:
id: "fc9-purl-namespace-collision"
description: "Same package name in different ecosystems"
added: "2025-12-16"
fc10:
id: "fc10-cve-split-merge"
description: "CVE split/merge tracking"
added: "2025-12-16"