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
453 changes: 452 additions & 1 deletion Cargo.lock

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,24 @@ chrono = { version = "0.4", default-features = false, features = ["std"] }
# machine and adds no crate.
uuid = { version = "1", default-features = false, features = ["v8"] }

# The JSON Schema of the policy file, derived from the structs that read it
# rather than written beside them: a hand-written schema is a second list of
# the keys, which is the drift `uphold schema` exists to remove. It reads the
# same `#[serde(...)]` attributes the deserializer does, so `deny_unknown_fields`,
# `rename` and `default` mean to the schema what they mean to the reader.
schemars = { version = "1", features = ["derive"] }

[target.'cfg(unix)'.dependencies]
# Git discovers hooks with access(X_OK), which checks the caller's permission
# class and ACLs. Use the same OS check through a safe API.
rustix = { version = "1", features = ["fs"] }

[dev-dependencies]
# Checks the committed schema against the 2020-12 metaschema and then runs
# this repository's own policy files through it. A schema nothing validates
# against is a claim; this is the standard's own test for it.
jsonschema = { version = "0.60", default-features = false }

# `strip` is deliberately absent: stripping is a packaging step, and a stripped
# binary with no build-id cannot be symbolicated from a core. `debug = 1` keeps
# the line tables so a crash names the line it happened on.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ uphold check # the claims in policy/upheld.toml still hold
uphold check --probe # add fresh behavior evidence without changing acceptance
uphold check --coverage # which rules here carry a principle
uphold rules --effective # every rule inheritance resolved to, and where each runs
uphold schema # the JSON Schema of principles.toml, for editors
uphold guard --stage pre-push # the guards for that Git hook
uphold shim gh pr create ... # stand in front of a command, then exec
uphold shim --install # link this binary under each command's name
Expand Down
10 changes: 10 additions & 0 deletions dist-workspace.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,13 @@ host = "x86_64-unknown-linux-gnu"
[dist.github-custom-runners.x86_64-unknown-linux-gnu]
runner = "ubuntu-latest"
host = "x86_64-unknown-linux-gnu"

# The policy file's JSON Schema, as a release asset with a `.sha256` beside it,
# so a `#:schema` line in a consumer's policy can name an address that is
# immutable per release. dist writes a `.sha256` for the archives and the source
# tarball only, and its unified `sha256.sum` lists those too; an extra artifact
# gets neither, so the command writes its own. `sha256sum` prints the name as
# given, which is why it runs where the copy was written.
[[dist.extra-artifacts]]
artifacts = ["principles.schema.json", "principles.schema.json.sha256"]
build = ["sh", "-c", "cp docs/schema/principles.schema.json principles.schema.json && sha256sum principles.schema.json > principles.schema.json.sha256"]
45 changes: 45 additions & 0 deletions docs/REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace — that is what lets a claim in
- [`uphold hooks --install` — the hooks git actually runs](#uphold-hooks---install--the-hooks-git-actually-runs)
- [`uphold probe` — can each hook refuse?](#uphold-probe--can-each-hook-refuse)
- [`uphold check --coverage` and `--oscal`](#uphold-check---coverage-and---oscal)
- [`uphold schema` — the policy file's JSON Schema](#uphold-schema--the-policy-files-json-schema)
- [The review tier](#the-review-tier)
- [Controls: can a record produce a finding at all?](#controls-can-a-record-produce-a-finding-at-all)

Expand Down Expand Up @@ -4146,6 +4147,50 @@ along as props in this repository's namespace: the rule id, the seam, the
record's `enforcement.level` and its `automatable`. The catalog itself does not
cross over — see [DESIGN.md](DESIGN.md#why-oscal-and-why-only-the-mapping).

## `uphold schema` — the policy file's JSON Schema

```sh
uphold schema > principles.schema.json
```

Prints a JSON Schema (draft 2020-12) for `principles.toml`, and for the bundled
sets and `inherit.paths` files, which are read by the same deserializer. It is
derived from the structs that read the file, so an unknown key is
`additionalProperties: false` because the reader refuses it, and a key's
description is the doc comment on its field. The `[override.<id>]` table is read
by hand and is described by a stand-in type that a test holds to the reader's
keys. Output is deterministic and takes no arguments; exit `0`.

The schema states the shape and not the whole of loading. A rule that carries
two checks, a `[set]` header outside a bundled set, an `owner` beside an
`owner_from`, and a regular expression that does not compile are refused after
the read, with the rule's id in the message. A document that validates has the
right shape; `uphold scan` or `uphold rules --effective` is what says it loads.

The committed copy is `docs/schema/principles.schema.json`, and
`tests/schema_cli.rs` fails while it differs from what the binary prints. After
a change to a policy struct, regenerate it and read the diff:

```sh
UPHOLD_SCHEMA_GOLDEN=bless cargo test --test schema_cli
```

The same test holds the file to the 2020-12 metaschema, validates this
repository's `policy/principles.toml` and every bundled set against it, and
checks that documents the deserializer refuses are refused by the schema.

Each release carries the schema as an asset, `principles.schema.json`, with
`principles.schema.json.sha256` beside it. To get completion and diagnostics in
an editor that speaks the Even Better TOML language server (taplo), make the
first line of the policy file a schema directive naming the asset:

```toml
#:schema https://github.com/HackingGate/uphold/releases/latest/download/principles.schema.json
```

Replace `latest` with a tag to pin the schema to the release a repository's
`uphold` is pinned to, so the editor and the binary agree on what is a key.

## The review tier

`enforcement.automatable` routes each record to a static rule, to a reviewer, or
Expand Down
Loading
Loading