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-grpcand 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:
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-grpchas 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-certand-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-mcprequires a bearer token on a non-loopback address —-token-file,-tokenorOPENSYSML_MCP_TOKEN— and refuses to start without one; every request must carry it. Seesysml-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.