Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Dependency Review

on:
pull_request:

permissions:
contents: read

jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Reject vulnerable dependency changes
uses: actions/dependency-review-action@v5
with:
fail-on-severity: low
15 changes: 14 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
tags: ['v[0-9]+.[0-9]+.[0-9]+*']

permissions:
attestations: write
contents: write
id-token: write

Expand All @@ -21,6 +22,9 @@ jobs:
dotnet-version: |
9.x
10.x
- name: Verify tag commit is on main
shell: bash
run: git merge-base --is-ancestor "$GITHUB_SHA" origin/main
- name: Derive version
shell: bash
run: echo "PACKAGE_VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV"
Expand All @@ -33,6 +37,15 @@ jobs:
run: dotnet run --project tests/OpenDisNet.PackageSmoke/OpenDisNet.PackageSmoke.csproj --configuration Release --framework net9.0 --no-restore -p:OpenDisNetPackageVersion=${{ env.PACKAGE_VERSION }}
- name: Run packed-package consumer on .NET 10
run: dotnet run --project tests/OpenDisNet.PackageSmoke/OpenDisNet.PackageSmoke.csproj --configuration Release --framework net10.0 --no-restore -p:OpenDisNetPackageVersion=${{ env.PACKAGE_VERSION }}
- name: Create package checksums
shell: bash
run: |
cd artifacts/packages
sha256sum *.nupkg *.snupkg > SHA256SUMS
- name: Attest package provenance
uses: actions/attest-build-provenance@v4
with:
subject-path: artifacts/packages/*
- name: Authenticate to NuGet.org
uses: NuGet/login@v1
id: nuget
Expand All @@ -49,4 +62,4 @@ jobs:
if [[ "${{ github.ref_name }}" == *-* ]]; then
flags+=(--prerelease)
fi
gh release create "${{ github.ref_name }}" artifacts/packages/*.*nupkg "${flags[@]}"
gh release create "${{ github.ref_name }}" artifacts/packages/* "${flags[@]}"
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ Versioning after 1.0 and uses prerelease versions while conformance is incomplet

## Unreleased

- Added the 1.0 conformance audit manifest and report, pinning DIS schemas, SISO
data, independent vector corpora, family mappings, and the public API by
SHA-256.
- Added hostile-input security tests covering 10,000 deterministic random
datagrams and three byte mutations across every populated PDU encoding.
- Updated package compatibility validation to the published 0.9.0 baseline and
enforced low-or-higher transitive NuGet vulnerability auditing.
- Added pull-request dependency review, main-branch tag verification, package
checksums, and GitHub build-provenance attestations.
- Froze the complete public C# surface with compiler-enforced API baselines and
NuGet package compatibility validation against OpenDisNet 0.8.0.
- Added an external-style smoke consumer that installs the packed artifact and
Expand Down
3 changes: 3 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Deterministic>true</Deterministic>
<NuGetAudit>true</NuGetAudit>
<NuGetAuditMode>all</NuGetAuditMode>
<NuGetAuditLevel>low</NuGetAuditLevel>
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
</PropertyGroup>
</Project>
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
OpenDisNet is a high-performance, type-safe .NET codec for Distributed
Interactive Simulation (DIS) Protocol Version 7, defined by IEEE 1278.1-2012.

> **Development status:** 0.9 release-candidate hardening. All 72 DIS v7 PDU
> identifiers have typed native C# codecs, every family has populated and
> boundary-truncation coverage, and the public API is frozen for external
> validation. See the [`RC testing guide`](docs/release-candidate.md) and
> **Development status:** 1.0 release audit. All 72 DIS v7 PDU identifiers have
> typed native C# codecs, every family has populated, independent, malformed,
> and hostile-input evidence, and the public API is compatibility-frozen. See
> the [`1.0 audit`](docs/conformance-audit-v1.0.md) and
> [`conformance matrix`](docs/conformance.md).

## Install
Expand Down
20 changes: 18 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

## Supported versions

Only the latest published OpenDisNet release receives security fixes while the
project remains pre-1.0.
The latest 1.x release receives security fixes. A security fix may be backported
when a supported application cannot reasonably upgrade, at the maintainers'
discretion.

## Reporting

Expand All @@ -16,3 +17,18 @@ OpenDisNet treats all datagrams as untrusted, validates declared lengths before
dispatch, bounds-checks every primitive access, and limits allocation driven by
wire-provided counts. These guarantees do not make decoded simulation content
trustworthy.

Applications should set `DisParseOptions.MaximumPduLength` to the smallest value
their traffic requires and apply network-level rate and source controls. A valid
but high-rate stream can still consume CPU, and semantic simulation values are
not authorization decisions.

## Release security gates

- Low-or-higher vulnerabilities in direct or transitive NuGet dependencies fail
restore.
- Pull requests receive dependency review, cross-platform tests, parser hostile-
input regression tests, formatting checks, and CodeQL analysis.
- Weekly CodeQL analysis covers changes outside pull-request activity.
- Release packages include SHA-256 checksums and GitHub build-provenance
attestations and are published to NuGet through short-lived OIDC credentials.
2 changes: 1 addition & 1 deletion docs/api-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ untrusted network input.
The 0.9 release-candidate line freezes the complete public C# surface before
1.0. `PublicAPI.Shipped.txt` is the reviewable contract. Compiler analyzers
reject an unrecorded public addition or removal, and NuGet package validation
compares new artifacts with version 0.8.0 for binary compatibility.
compares 1.0 artifacts with version 0.9.0 for binary compatibility.

New API belongs in `PublicAPI.Unshipped.txt` and requires tests plus a changelog
entry. Removing or changing frozen API requires explicit breaking-change review
Expand Down
53 changes: 53 additions & 0 deletions docs/conformance-audit-v1.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# OpenDisNet 1.0 conformance audit

## Result

The OpenDisNet project audit found the 1.0 candidate internally consistent with
its declared IEEE 1278.1-2012 (DIS v7) and SISO-REF-010-2025 v36 scope. The
machine-readable audit manifest pins every schema, enumeration catalog,
independent vector corpus, family/type mapping, and the frozen public API by
SHA-256. CI fails if an audited input changes without an explicit audit update.

This is a project-maintained engineering audit. It is not IEEE or SISO
certification, government approval, or a substitute for system-level
interoperability testing in a user's own environment.

## Audited scope

| Area | Evidence | Result |
|---|---|---|
| Wire model | 13 pinned DIS schema inputs; generator freshness check | 233 concrete wire classes |
| PDU dispatch | Manifest maps every standard identifier exactly once | 72 PDU types in 12 families |
| Native populated coverage | Deterministic non-default construction and byte-identical round trip | 72 of 72 PDU types |
| Independent default corpus | Frozen reference bytes | 70 exact round trips; 2 explicit differential cases |
| Independent populated corpus | Frozen non-default reference bytes | 44 exact round trips spanning all families |
| Malformed input | Truncation at every byte boundary | Every PDU type and family |
| Hostile input | Fixed-seed random corpus and byte mutation | 10,000 datagrams plus three mutations per byte of every populated PDU |
| Enumeration data | Pinned SISO v36 catalog | Typed values plus lossless unknown/reserved values |
| API compatibility | Compiler API file and NuGet package validation | Frozen surface; binary baseline 0.9.0 |

The authoritative identities and family/type inventory are in
`tests/OpenDisNet.Tests/Conformance/Audit/v1.0.json`. Tests recompute every hash
and compare the manifest with the factory's wire identity.

## Differential exceptions

The independent Open-DIS reference is test-only and is not a product dependency.
Its default Point Object State and Areal Object State encodings write a 16-bit
Modifications field although the reference layout XML declares 8 bits. Those two
inputs are retained and required to fail explicitly.

Twenty-eight aggressively populated reference vectors expose inconsistent
length or padding output in that reference generator. They are recorded as
differential exceptions rather than used to make OpenDisNet reproduce malformed
bytes. The affected OpenDisNet types remain covered by native populated vectors,
full boundary truncation, hostile-input mutation, schema generation checks, and
default independent vectors. Forty-four compatible populated reference vectors
cover all 12 protocol families.

## Release decision

Within the scope above, no unresolved high-impact wire-correctness, data-loss,
API-compatibility, or parser-safety defect is known. Future claims must update
the manifest and this audit whenever a pinned input, public API, or supported
standard changes.
21 changes: 18 additions & 3 deletions docs/conformance.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ input tests, and round-trip tests.
- [x] Automatic synchronization of list, octet, and bit-length fields
- [x] Deterministically populated round trips for every PDU type (1-72)
- [x] 70 independent default reference vectors and 44 compatible populated vectors
- [ ] Independent populated vectors for every variable-layout PDU
- [x] Variable-layout audit: 44 independent populated vectors across all
families, with 28 documented reference-generator exceptions covered natively
- [x] Truncation-at-every-boundary coverage for every PDU family
- [x] SISO-REF-010-2025 v36 typed enumerations and structured bitfields
- [x] Lossless round trip of unknown enumeration values and reserved bitfield bits
Expand All @@ -44,7 +45,18 @@ input tests, and round-trip tests.
- [x] Packed-package consumer validation on .NET 9 and .NET 10
- [x] Prerelease-aware GitHub and NuGet publishing workflow
- [x] External RC validation checklist and structured feedback form
- [ ] External RC feedback reviewed and high-impact findings resolved
- [x] External RC feedback channel reviewed; no high-impact finding remains open

## 1.0 release gates

- [x] SHA-256-pinned conformance audit manifest and published audit report
- [x] Every PDU type and protocol family accounted for exactly once
- [x] Hostile-input random and mutation regression suite
- [x] Binary/package compatibility baseline updated to 0.9.0
- [x] Low-or-higher NuGet vulnerability audit enforced for all dependencies
- [x] Pull-request dependency review and scheduled CodeQL analysis
- [x] Release tags restricted to commits on `main`
- [x] SHA-256 package checksums and GitHub build-provenance attestations

Family coverage is implemented and exercised entirely in C#/.NET. Frozen bytes
from an independent implementation are test inputs only; they are not product
Expand All @@ -53,4 +65,7 @@ populated vectors expose documented defects in that reference generator and are
retained as differential evidence rather than copied into OpenDisNet behavior.

This file is a release gate. Version 1.0 will not be labeled complete until all
applicable entries are checked and independently audited.
applicable entries are checked and the reviewable project audit is complete.

The completed scope, evidence, limitations, and differential exceptions are in
the [1.0 conformance audit](conformance-audit-v1.0.md).
21 changes: 20 additions & 1 deletion docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,33 @@ plus symbol artifacts are attached to the release shown on the repository page.
Prerelease tags use SemVer identifiers such as `v0.9.0-rc.1`. The workflow
derives the package version from the tag and marks the GitHub release as a
prerelease automatically. Before publishing any tag, the workflow validates the
public API, compares the package with the 0.8.0 compatibility baseline, and runs
public API, compares the package with the 0.9.0 compatibility baseline, and runs
an external-style consumer against the packed artifact on .NET 9 and .NET 10.

The RC testing contract and feedback process are documented in
[`release-candidate.md`](release-candidate.md). Promote an RC to a stable tag
only after its exit criteria are satisfied; published NuGet versions and GitHub
releases are never rewritten.

## 1.0 security gates

A release tag must identify a commit contained in `main`. The release workflow
then repeats locked restore with low-or-higher transitive NuGet vulnerability
auditing, tests, package compatibility validation, and NuGet-only consumer runs.
It creates `SHA256SUMS` and GitHub build-provenance attestations before trusted
publishing. The checksum file, package, and symbol package are attached to the
immutable GitHub release.

Verify a downloaded package with GitHub CLI:

```shell
gh attestation verify OpenDisNet.1.0.0.nupkg --repo RejectKid/OpenDisNet
```

The 1.0 tag is permitted only when the
[conformance audit](conformance-audit-v1.0.md) and all checks in the
[conformance matrix](conformance.md) are complete.

## One-time NuGet.org setup

1. Reserve or publish the `OpenDisNet` package ID under the intended NuGet.org owner.
Expand Down
2 changes: 1 addition & 1 deletion src/OpenDisNet/OpenDisNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<NoWarn>$(NoWarn);CS1591</NoWarn>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<EnablePackageValidation>true</EnablePackageValidation>
<PackageValidationBaselineVersion>0.8.0</PackageValidationBaselineVersion>
<PackageValidationBaselineVersion>0.9.0</PackageValidationBaselineVersion>
<EnableStrictModeForCompatibleTfms>true</EnableStrictModeForCompatibleTfms>
</PropertyGroup>
<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions tests/OpenDisNet.PackageSmoke/OpenDisNet.PackageSmoke.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<UseAppHost>false</UseAppHost>
<!-- The solution restore audits every external dependency; this project intentionally restores only the local artifact. -->
<NuGetAudit>false</NuGetAudit>
<TargetFrameworks>net9.0;net10.0</TargetFrameworks>
<OpenDisNetPackageVersion Condition="'$(OpenDisNetPackageVersion)' == ''">0.1.0</OpenDisNetPackageVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
Expand Down
40 changes: 40 additions & 0 deletions tests/OpenDisNet.Tests/Conformance/Audit/v1.0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"auditVersion": "1.0",
"wireStandard": "IEEE Std 1278.1-2012 (DIS v7)",
"enumerationStandard": "SISO-REF-010-2025 v36",
"pduTypeCount": 72,
"concreteWireClassCount": 233,
"families": [
{ "value": 1, "count": 5, "types": [1, 4, 66, 67, 72] },
{ "value": 2, "count": 4, "types": [2, 3, 68, 69] },
{ "value": 3, "count": 6, "types": [5, 6, 7, 8, 9, 10] },
{ "value": 4, "count": 5, "types": [25, 26, 27, 31, 32] },
{ "value": 5, "count": 12, "types": [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22] },
{ "value": 6, "count": 5, "types": [23, 24, 28, 29, 30] },
{ "value": 7, "count": 4, "types": [33, 34, 35, 36] },
{ "value": 8, "count": 4, "types": [37, 38, 39, 40] },
{ "value": 9, "count": 5, "types": [41, 42, 43, 44, 45] },
{ "value": 10, "count": 15, "types": [51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65] },
{ "value": 11, "count": 5, "types": [46, 47, 48, 49, 50] },
{ "value": 13, "count": 2, "types": [70, 71] }
],
"artifacts": [
{ "path": "tools/OpenDisNet.Generator/Schemas/DIS7/DIS_7_2012.xml", "sha256": "f877cc5e09fdbe6e7129f180d7f147043f7b9a20356030a7a093fb35ae4750ce" },
{ "path": "tools/OpenDisNet.Generator/Schemas/DIS7/DistributedEmissionsFamilyPdus.xml", "sha256": "5a6e0a45467eae73dbf2786b9eb9ae8866c444b6369c0a1619c71bf517647281" },
{ "path": "tools/OpenDisNet.Generator/Schemas/DIS7/EntityInformationFamilyPdus.xml", "sha256": "bb68e679a86f9533202b5d8f68db667220b8f0f6e53ab0b0e02aa3cb121a4c82" },
{ "path": "tools/OpenDisNet.Generator/Schemas/DIS7/EntityManagementFamilyPdus.xml", "sha256": "4404a634a648c95da3100b64489b8f8b08840b9fdeb56c297fe821df10ac4379" },
{ "path": "tools/OpenDisNet.Generator/Schemas/DIS7/InformationOperationsFamilyPdus.xml", "sha256": "3348d7132d77138449e0c1a91545957fd6ed985d1bc5e426efc9c1732cb20e8a" },
{ "path": "tools/OpenDisNet.Generator/Schemas/DIS7/LiveEntityFamilyPdus.xml", "sha256": "6dd57d0ae5ccf48cf09b0b936f116f590f85d0bf53688e243c94789433638d25" },
{ "path": "tools/OpenDisNet.Generator/Schemas/DIS7/LogisticsFamilyPdus.xml", "sha256": "8e69464a7b421e8a048973d59b94736e95c71b623de9a250257c09179ca2a713" },
{ "path": "tools/OpenDisNet.Generator/Schemas/DIS7/MinefieldFamilyPdus.xml", "sha256": "05a10725e81deb9572c67ca79ef3d2a0e6be38baac3c4bf3fc6640f611e19401" },
{ "path": "tools/OpenDisNet.Generator/Schemas/DIS7/RadioCommunicationsFamilyPdus.xml", "sha256": "1e74bec4817d95695bb75831e191b75335b802874a52b296d3df9056630ac351" },
{ "path": "tools/OpenDisNet.Generator/Schemas/DIS7/SimulationManagementFamilyPdus.xml", "sha256": "030d36634978c3e44434e360a61860386b43230e43b6f20751947ac00bfc44a3" },
{ "path": "tools/OpenDisNet.Generator/Schemas/DIS7/SimulationManagementWithReliabilityFamilyPdus.xml", "sha256": "dbb32b2d541f715064397fc78e75b59a0be20b8b4a6a12150330c1fcd30bfdf1" },
{ "path": "tools/OpenDisNet.Generator/Schemas/DIS7/SyntheticEnvironmentFamilyPdus.xml", "sha256": "5cac3fe21fa3fa8deb281e23ddebca164f4fcd22cf7b69928e2bf8c786908ee2" },
{ "path": "tools/OpenDisNet.Generator/Schemas/DIS7/WarfareFamilyPdus.xml", "sha256": "518506834ce8a054994358a0bce4a773e3eabadce6a8465f009d91d3585bcd38" },
{ "path": "tools/OpenDisNet.Generator/Schemas/SISO/referenced-types-v36.json", "sha256": "a845598c022fd38b79000ac434f039772a6a679979f7f8dd618261c833330089" },
{ "path": "tests/OpenDisNet.Tests/Conformance/Vectors/opendis7-java-default.json", "sha256": "1caf4b724f17358de22012faf55377ff21d215fbc2395ef32b8cdef07ec221a6" },
{ "path": "tests/OpenDisNet.Tests/Conformance/Vectors/opendis7-java-populated.json", "sha256": "6558f75e153194618e3a3b77c737a48cbbc849e5c27e478e42dc81219e78b792" },
{ "path": "src/OpenDisNet/PublicAPI.Shipped.txt", "sha256": "d7325bf2bf1e3fcb50dc152050f6f0275f6e147fa6eb085e1c0c51779a251ba6" }
]
}
Loading
Loading