Skip to content

About

Language-neutral OpenSpec contract, schemas, and conformance corpus for OSCAL data bindings

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

OSCAL bindings specs

The language-neutral contract for OSCAL data bindings: OpenSpec capability specs, the vendored NIST OSCAL JSON Schema, the naming manifest, and the conformance corpus. Each language binding implements the contract and passes the corpus.

This repo is an OpenSpec store.

Layout

  1. openspec/: the capability specs (specs/), the changes (changes/), the config, the identifier ledger spec-ids.yaml, and the requirement index requirements.json. npm run requirements:write generates the index from the main specs. It lists the identifier, capability, and title of each requirement. traceability.schema.json is the JSON Schema of the traceability record that each binding keeps (see binding-surface).
  2. schemas/<release>/: the NIST OSCAL JSON Schema files of one release, byte-for-byte, with sha256 sums.
  3. manifest/<release>/: the naming manifest. names.json is generated and holds the canonical name, kind, and schema pointers of each type. overrides.json is the hand-edited naming input. manifest/*.schema.json are the JSON Schemas of both files (see manifest/1.2.3/README.md).
  4. corpus/<release>/: the conformance corpus.
  5. docs/: shared documents that the specs cite, such as canonical JSON for conformance comparison.
  6. scripts/: the store tooling.

Use the store

  1. Clone this repo.

  2. Register it: openspec store register <path>.

  3. In a binding repo, add the store to openspec/config.yaml:

    references:
      - { id: oscal-bindings-specs, remote: "https://github.com/cairn-proofs/oscal-bindings-specs.git" }

A reference is planning context only. Builds and conformance runs use the tagged archive, pinned by its sha256.

Known implementations

  1. oscal-bindings-python, in Python: repository, issue tracker.
  2. oscal-bindings-typescript, in TypeScript: repository, issue tracker.

Each binding keeps its own expected conformance failures, in its own repository. This store holds no data about the state of a binding (see schema-versioning). npm run binding-links:check reads this list.

Develop

CONTRIBUTING.md describes how to change a requirement and how a binding adds or promotes an operation.

mise pins Node and the OpenSpec CLI. package-lock.json pins the npm dependencies.

  1. mise install
  2. npm ci
  3. npm run check

npm run check runs these checks. CI runs the same command on each pull request.

  1. The TypeScript typecheck.
  2. The identifier checker and its self-test.
  3. The requirement index generator self-test, which also checks the traceability record JSON Schema on sample records, and npm run requirements:check: it fails when openspec/requirements.json differs from the generator output.
  4. The VERSION check and its self-test.
  5. The schema sha256 check.
  6. The canonical JSON self-test.
  7. The YAML reader self-test. scripts/yaml-reader.ts is the reference reader that the corpus check uses for YAML inputs.
  8. The corpus check (see corpus/1.2.3/README.md).
  9. The binding link check and its self-test, npm run binding-links:check. It fails when a tracked file outside openspec/changes/ links an individual issue or pull request in a binding repository. The binding repositories are the repositories in Known implementations and each oscal-bindings-* repository other than this store.
  10. The names generator self-test, and npm run names:check: it validates each manifest file and fails when names.json differs from the generator output.
  11. The release checks and their self-test, npm run releases:check. A released name that changes needs a breaking VERSION increment, and a released schemas/<release>/ file must not change. These checks read git tags and history, so they fail in a shallow clone or a clone without tags. They also list each name change against the base ref (SPEC_IDS_BASE, default HEAD), and CI writes that list to the job summary.
  12. openspec validate --all --strict.

To change a canonical name, edit manifest/<release>/overrides.json, run npm run names:write, and commit both files.

To add, rename, or remove a requirement in the main specs, run npm run requirements:write, and commit the index with the specs.

VERSION holds the spec artifact version: a Semantic Version with the OSCAL release as build metadata, for example 0.1.0+oscal.1.2.3.

Threat model

This repo's threat model is a Threat Composer .tc.json file, kept in a separate private repo, oscal-bindings-specs-threat-models, rather than in this repo. Access to it is optional: you do not need it to build, test or contribute.

If you have access, clone it beside this repo, not inside it:

gh repo clone cairn-proofs/oscal-bindings-specs-threat-models ../oscal-bindings-specs-threat-models

Open the .tc.json file there with the Threat Composer VS Code extension or web app.

Release

  1. Set the new version in VERSION, and merge the pull request.
  2. Push the tag v<VERSION> on that commit of main, for example v0.1.0+oscal.1.2.3.
  3. CI runs npm run check, builds the archive with npm run archive, and attaches the archive and SHA256SUMS to a draft GitHub release. CI fails when the tag is not on main or does not match VERSION.
  4. Review the draft and publish it.

Verify a release

Pin the archive by the sha256 in SHA256SUMS, not by the tag. Before you pin or update a pin, check the archive you downloaded. In the commands below, <version> is the release version, for example 0.8.0+oscal.1.2.3.

  1. The archive matches SHA256SUMS:

    shasum -a 256 -c SHA256SUMS
    
  2. This repo's CI built the archive from the tag (SLSA build provenance, logged in the public Sigstore transparency log):

    gh attestation verify oscal-bindings-specs-<version>.tar.gz --repo cairn-proofs/oscal-bindings-specs \
      --cert-identity "https://github.com/cairn-proofs/oscal-bindings-specs/.github/workflows/build.yml@refs/tags/v<version>"
    
  3. The archive is the asset published in the release (GitHub release attestation):

    gh release verify-asset v<version> oscal-bindings-specs-<version>.tar.gz --repo cairn-proofs/oscal-bindings-specs
    
  4. The archive holds exactly the tagged files. In a clone of this repo with its tags, run scripts/verify-release.sh v<version>. It rebuilds the archive with git archive from the tag, compares it with the published archive, and runs checks 1 to 3, skipping a check the release cannot pass (see below). It runs no code from the tag. It needs gh signed in.

Older releases cannot pass every check. Releases up to and including v0.7.0+oscal.1.2.3 were built before CI attested provenance, so check 2 fails for them. v0.1.0+oscal.1.2.3 and v0.2.0+oscal.1.2.3 are not immutable releases, so check 3 fails for them. Checks 1 and 4 pass for every release.

These checks show that the archive matches the tagged commit. They do not show that the tagged commit is free of unwanted changes.

About

Language-neutral OpenSpec contract, schemas, and conformance corpus for OSCAL data bindings

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages