Skip to content

Security

How to report a vulnerability and what to expect, which versions get fixes, how to verify a release before running it, how to harden a served instance, and where advisories are published. The policy below is SECURITY.md at the repository root, embedded when this site is built, and the site also serves it as /.well-known/security.txt (RFC 9116).

The response times and the disclosure process below are the commitments reporters can hold us to. They are set to the bar OpenSysML will be held to as a tool used on safety-critical projects, not to the project's current release stage.

Reporting a vulnerability

Report vulnerabilities privately, through GitHub's private vulnerability reporting: Security → Report a vulnerability on this repository. Do not open a public issue, pull request or discussion for anything you believe is a security problem, and do not include a working exploit in a public place before the advisory is published.

If that form is unavailable (it depends on a repository setting) or you get no acknowledgement within the deadline below, e-mail the responder directly at jason.han@jpl.nasa.gov with [OpenSysML security] in the subject; a report sent that way gets the same handling.

A report is most useful when it has:

  • the affected component (sysml, sysml-lsp, sysml-grpc, sysml-mcp, sysml-jupyter-kernel, the Python or Node client, the VS Code extension, install.sh/install.ps1, or a release workflow), and the version (sysml --version, a release tag, or a nightly's commit);
  • a minimal reproducer: the model, command line, request or input that triggers the problem;
  • the impact as you understand it (what an attacker gains, and from where).

Reports are handled by the release authority, Jason Han (@HuiJun), who is the code owner of this repository (see .github/CODEOWNERS). If a report has gone a week without an acknowledgement, reach the same person through an @-mention on any issue of this repository, saying only that a private report is waiting.

What to expect

Step Deadline, from receipt
Acknowledgement 3 business days
Triage: confirmed or not, severity (CVSS v3.1), affected versions 7 calendar days
Fix released, critical severity 14 calendar days
Fix released, high or medium severity 60 calendar days
Fix released, low severity next scheduled release
Advisory published with the fix release

Severity is scored with CVSS v3.1 and may be revised during triage; the reporter is told the score and the reasoning. A fix that cannot meet its deadline is not hidden: the reporter is told why, given the new date, and the advisory says what to do in the meantime.

Fixes ship as a release of the affected component (a patch release of the current line, or a hotfix cut from main, see docs/project/releasing.md) and a GitHub Security Advisory that names the affected and fixed versions, the CVE (requested through GitHub), the reporter if they want credit, and any mitigation for users who cannot update at once. Nightly builds get the fix as soon as it merges, before the advisory.

Coordinated disclosure

We ask reporters to keep the report private until the advisory is published. We publish within 90 days of receipt whether or not a fix is ready, unless the reporter and the project agree in writing to a longer embargo; a vulnerability already being exploited is published, with mitigations, as soon as there is something actionable to say. Reporters are credited in the advisory unless they ask not to be. There is no bug bounty.

Automated and AI-assisted reports

Reports produced by scanners, fuzzers or AI assistants are welcome on the same terms as any other, with one requirement: the report must contain a reproducer that we can run against a current release or nightly and that demonstrates the claimed impact. A report that consists of a tool's unverified output, a theoretical pattern with no reproducer, or a reproducer that does not demonstrate the stated impact is closed as unverified without triage, and the reporter is told so within the acknowledgement deadline. Repeated unverified submissions from one source are not acknowledged individually. Please say in the report when it was produced or assisted by a tool; it does not count against the report.

Supported versions

Version Supported
The latest release (vX.Y.Z on Releases) Yes: fixes ship as a patch release of that line
Earlier releases No: update to the latest release
nightly prereleases Fixed on develop as soon as they merge; no advisory covers a nightly

Until 1.0.0, each release line is supported only until the next release. From 1.0.0 the previous minor release line is also supported for 90 days after its successor ships, so that a fix can be taken without a feature update.

What counts as a vulnerability

In scope:

  • memory safety, code execution, path traversal or file disclosure from a crafted model, project file, import, XMI/RDF input, FMU, template or query — the parser and the runtime must fail with a diagnostic, never execute or disclose;
  • the network surfaces: sysml-grpc and the MCP server's HTTP mode (sysml-mcp -http), including authentication, authorization and request handling;
  • the supply chain: install.sh/install.ps1, the release workflows, the signed checksum manifest and provenance, the Python and Node clients' download and verification path, the VS Code extension and the published packages (PyPI, npm, Maven Central, Homebrew);
  • secrets or credentials in the repository, releases, or logs;
  • a denial of service that a crafted input causes in a server (sysml-grpc, sysml-lsp, sysml-mcp) — a crash of the command-line tool on a bad model is a bug, not a vulnerability, unless it breaks an invariant above.

Out of scope: the SysML v2 and KerML specifications and the OMG pilot implementation (report to their maintainers), the contents of a user's own models, and findings that require an already-compromised host or user account.

Verifying what you run

Every release ships a checksum manifest, SHA256SUMS.txt, signed with cosign (keyless, from the release pipeline's identity) as SHA256SUMS.txt.bundle, and a SLSA provenance statement signed the same way as provenance.intoto.json.bundle. Windows builds are Authenticode signed through SignPath Foundation once the project's application is approved. How to verify each of these, and the exact signer identity to pin, is in docs/guide/security.md § Verifying a release (the same page as this policy on the documentation site); the installer's --verify-signature flag does the manifest check for you (see docs/guide/01-install.md). A release asset that does not verify is a vulnerability report in itself: send it through the channel above.

Dependencies and static analysis

Dependabot alerts and the code-scanning results that SonarCloud uploads to this repository are watched by the code owners and remediated on the deadlines in the table above (critical within 14 days, high and medium within 60). Dependency updates that fix a published vulnerability are released on the same schedule as a vulnerability in our own code.

Verifying a release

Every stable release publishes one checksum manifest, SHA256SUMS.txt, covering every asset of the release — archives, bare binaries, the Python wheel and sdist, the WebAssembly builds — and signs it with cosign from the release pipeline's identity (keyless; the certificate names the CircleCI project, not a person), as SHA256SUMS.txt.bundle. A SLSA provenance statement, provenance.intoto.json, is signed the same way. Verify the manifest first; everything else is checked against it. The commands below were run against v0.11.0; substitute the tag you downloaded.

The checksum manifest and its signature

R=https://github.com/Open-MBEE/OpenSysML/releases/download/v0.11.0
curl -fsSLO $R/SHA256SUMS.txt
curl -fsSLO $R/SHA256SUMS.txt.bundle
cosign verify-blob SHA256SUMS.txt --bundle SHA256SUMS.txt.bundle \
  --certificate-oidc-issuer https://oidc.circleci.com/org/1169df8b-0b59-400f-82d2-c9d8e98bdb62 \
  --certificate-identity-regexp '^https://circleci\.com/api/v2/projects/eeb0dddd-237f-4f02-9e51-8e24caef589d/pipeline-definitions/[0-9a-f-]+$'
sha256sum -c SHA256SUMS.txt --ignore-missing

cosign prints Verified OK when the manifest was signed by this project's release pipeline; sha256sum prints one OK per downloaded asset it finds in the manifest. The issuer and the identity pattern are the pipeline's and do not change between releases; a bundle that verifies under a different identity is not a release of this project. The installer does this check for you with --verify-signature (see Install). The nightly prereleases are signed by GitHub Actions instead; their identity is on the nightly snapshot page.

The provenance statement

provenance.intoto.json is an in-toto Statement with a SLSA v1 provenance predicate: the tag and commit the assets were built from, and the digest of every asset. Its signature covers the statement, and cosign checks that the asset you name is one of its subjects:

curl -fsSLO $R/provenance.intoto.json
curl -fsSLO $R/provenance.intoto.json.bundle
curl -fsSLO $R/opensysml-linux-amd64.tar.gz
cosign verify-blob-attestation opensysml-linux-amd64.tar.gz \
  --bundle provenance.intoto.json.bundle --type slsaprovenance1 \
  --certificate-oidc-issuer https://oidc.circleci.com/org/1169df8b-0b59-400f-82d2-c9d8e98bdb62 \
  --certificate-identity-regexp '^https://circleci\.com/api/v2/projects/eeb0dddd-237f-4f02-9e51-8e24caef589d/pipeline-definitions/[0-9a-f-]+$'
jq -r '.predicate.buildDefinition.buildType,
       (.predicate.buildDefinition.resolvedDependencies[] | "\(.uri) \(.digest.gitCommit)")' \
  provenance.intoto.json

For v0.11.0 the build type is https://github.com/Open-MBEE/OpenSysML/.circleci/build-release/v1 and the one resolved dependency is git+https://github.com/Open-MBEE/OpenSysML@refs/tags/v0.11.0 at commit ddf4df00037d6ddbfb1fa9d743a4389f75c9e2dc — the commit the tag points at on GitHub.

The .sha256 sidecars

The bare service binaries (sysml-grpc-*, sysml-jupyter-kernel-*) and the WebAssembly builds carry a <asset>.sha256 sidecar for clients that fetch one file. A sidecar is a convenience, not a trust anchor: it is served from the same place as the asset, so check it against the signed manifest.

curl -fsSLO $R/sysml-grpc-linux-amd64.sha256
grep ' sysml-grpc-linux-amd64$' SHA256SUMS.txt | diff - sysml-grpc-linux-amd64.sha256

The Python and Node clients, which download sysml-grpc for you, use pinned digests stamped into the package at release time or the signed manifest, never a sidecar alone; a digest mismatch or an invalid signature is an error, not a fallback to unverified bytes (Python client § The service).

Python packages

The wheel and sdist uploaded to PyPI are the same files as on the GitHub release, and the manifest lists them, so a package downloaded from PyPI can be checked against the signed manifest:

curl -fsSLO $R/opensysml-0.11.0-py3-none-any.whl
curl -fsSLO $R/opensysml-0.11.0.tar.gz
sha256sum -c SHA256SUMS.txt --ignore-missing

Pin the same digest in a requirements file (opensysml==0.11.0 --hash=sha256:a4bb6cb481bf008f336bc4dce71b9fd345de971268ebc5874e0a36b84eb5503d for the v0.11.0 wheel) and pip install --require-hashes refuses anything else. The Jupyter kernel's platform wheels (jupyter_opensysml_kernel-*.whl) are on the release and in the manifest too.

npm packages

@openmbee/opensysml and its platform packages are published to the npm registry, which records a subresource integrity digest per tarball that npm install checks on every download; a lockfile pins it. Read the registry's record before pinning:

npm view @openmbee/opensysml@0.11.0 dist.integrity

The platform packages that carry sysml-grpc embed the binary from the release's assets.

Maven Central

org.openmbee:opensysml is on Maven Central from 0.10.1 (pkg:maven/org.openmbee/opensysml@0.11.0). Central publishes a detached GPG signature (.asc) and checksums beside every artifact, as it requires of every publisher, and Maven and Gradle check the checksums on download; pin the version in the build file so a resolved artifact cannot change under it.

crates.io

Cargo verifies every crate it downloads against the checksum recorded in the registry index, so cargo add opensysml@0.11.0 with a committed Cargo.lock pins the exact crate. The registry reports the same checksum:

curl -fsS -A 'opensysml-docs (curl)' https://crates.io/api/v1/crates/opensysml/0.11.0 \
  | jq -r '.version | .num, .checksum, .yanked'

The container image

openmbee/opensysml:<version> is a multi-platform image whose index carries a BuildKit provenance attestation and an SPDX SBOM per platform. Pin the index digest and read the attestations with docker buildx imagetools:

docker buildx imagetools inspect openmbee/opensysml:0.11.0
docker buildx imagetools inspect openmbee/opensysml:0.11.0 \
  --format '{{ range $k, $v := .Provenance }}{{ $k }}: {{ json $v.SLSA.buildDefinition.externalParameters.request.root.request.args }}{{ "\n" }}{{ end }}'
docker buildx imagetools inspect openmbee/opensysml:0.11.0 \
  --format '{{ range $k, $v := .SBOM }}{{ $k }}: {{ $v.SPDX.spdxVersion }} {{ len $v.SPDX.packages }} packages{{ "\n" }}{{ end }}'

The first command prints the index digest (sha256:e3c7a9dc… for v0.11.0) and one manifest per platform plus one attestation manifest each. The provenance names the source the image was built from — vcs:source https://github.com/Open-MBEE/OpenSysML.git, vcs:revision ddf4df00037d6ddbfb1fa9d743a4389f75c9e2dc, build-arg:VERSION v0.11.0 for v0.11.0 — which must match the release's provenance above. Run the image by digest (openmbee/opensysml@sha256:…) once you have checked it.

Windows installer and signed binaries

The MSI and the Windows executables are Authenticode signed through SignPath Foundation once the project's application is approved, and published as separate *-signed* assets with their digests in SHA256SUMS-windows-signed.txt; the unsigned MSI's digest is in SHA256SUMS-windows-msi.txt. Both digest files are produced by the GitHub Actions workflow that builds the installer, not by the CircleCI pipeline, so they are not covered by the cosign signature above — check the signature on the file itself (Get-AuthenticodeSignature in PowerShell, or the file's Properties → Digital Signatures) and see the code signing policy for who may approve a signature. macOS binaries are not Developer ID signed or notarized.

Hardening a served instance

sysml-grpc and sysml-mcp -http bind loopback unless the address says otherwise. Before an address other machines can reach:

  • sysml-grpc has no authentication. Nothing in the service tells one caller from another: every client that can reach the address can call every RPC the capability list advertises, edits and runs included. Keep it on loopback, or reach it only through something that does authenticate — a reverse proxy or gateway, a VPN, a network policy — and never from an address the public internet routes to.
  • TLS. -tls-cert and -tls-key (both or neither) serve every protocol over HTTPS on the one port, TLS 1.2 or later; it encrypts the connection and proves the server, not the client. Service transports is the reference, including the browser-origin allow-list, which is not authentication either.
  • sysml-mcp requires a bearer token on a non-loopback address — -token-file, -token or OPENSYSML_MCP_TOKEN — and refuses to start without one; every request must carry it. See sysml-mcp § Transports.
  • A crafted model, project file, import or query must never execute or disclose anything on the host; a case where one does is in scope above, and a denial of service against a served instance is too.

Advisories

Fixes are announced as GitHub Security Advisories on this repository, with a CVE requested through GitHub, and the fix release's notes link the advisory. Dependabot alerts and SonarCloud code-scanning results are watched by the code owners on the deadlines in the policy above. Every release's notes end with the support period in force and a link back to Verifying a release.