Practical examples for semantic validation with scheck.
Each example includes a JSON rule file, input documents, and CLI invocations.
All files live under etc/examples/.
DSL equivalents (.scheck files) are provided alongside each JSON rule file
for those who prefer a concise hand-written format.
Ensure a document contains mandatory fields.
Rules: required-fields.json
| DSL
{
"title": "required fields",
"patterns": [
{
"name": "basics",
"rules": [
{
"context": "$",
"checks": [
{
"kind": "assert",
"test": { "type": "exists", "path": "$.name" },
"message": "name is required"
},
{
"kind": "assert",
"test": { "type": "exists", "path": "$.email" },
"message": "email is required"
}
]
}
]
}
]
}$ scheck validate etc/examples/required-fields-pass.json --rules etc/examples/required-fields.json
OK: all checks passed
$ scheck validate etc/examples/required-fields-fail.json --rules etc/examples/required-fields.json
[error] basics at $: email is required
1 error(s), 0 warning(s), 0 info(s)
Reject documents containing sensitive fields.
Rules: forbidden-fields.json
| DSL
{
"title": "no secrets",
"patterns": [
{
"name": "security",
"rules": [
{
"context": "$",
"checks": [
{
"kind": "assert",
"test": { "type": "not_exists", "path": "$.password" },
"message": "must not contain password",
"flag": "security"
},
{
"kind": "assert",
"test": { "type": "not_exists", "path": "$.secret" },
"message": "must not contain secret",
"flag": "security"
}
]
}
]
}
]
}$ scheck validate etc/examples/forbidden-fields-pass.json --rules etc/examples/forbidden-fields.json
OK: all checks passed
$ scheck validate etc/examples/forbidden-fields-fail.json --rules etc/examples/forbidden-fields.json
[error] security at $: must not contain password
1 error(s), 0 warning(s), 0 info(s)
Validate string values match an expected format.
Rules: regex-matching.json
| DSL
{
"title": "format checks",
"patterns": [
{
"name": "identifiers",
"rules": [
{
"context": "$",
"checks": [
{
"kind": "assert",
"test": {
"type": "matches",
"path": "$.cve",
"pattern": "^CVE-\\d{4}-\\d{4,}$"
},
"message": "CVE ID must match standard format"
}
]
}
]
}
]
}$ scheck validate etc/examples/regex-matching-pass.json --rules etc/examples/regex-matching.json
OK: all checks passed
$ scheck validate etc/examples/regex-matching-fail.json --rules etc/examples/regex-matching.json
[error] identifiers at $: CVE ID must match standard format
1 error(s), 0 warning(s), 0 info(s)
Assert a field has a specific value.
{
"title": "status check",
"patterns": [
{
"name": "state",
"rules": [
{
"context": "$",
"checks": [
{
"kind": "assert",
"test": { "type": "equals", "path": "$.status", "value": "active" },
"message": "status must be active"
}
]
}
]
}
]
}Pass: {"status": "active"}
Fail: {"status": "draft"} -- [error] status must be active
Require a minimum (or exact) number of elements.
{
"title": "array checks",
"patterns": [
{
"name": "tags",
"rules": [
{
"context": "$",
"checks": [
{
"kind": "assert",
"test": { "type": "count", "path": "$.tags[*]", "cmp": ">=", "expected": 1 },
"message": "need at least one tag"
},
{
"kind": "assert",
"test": { "type": "count", "path": "$.authors[*]", "cmp": ">=", "expected": 1 },
"message": "need at least one author",
"severity": "warning"
}
]
}
]
}
]
}Pass: {"tags": ["security"], "authors": ["Alice"]}
Fail: {"tags": []} -- [error] need at least one tag
Supported comparison operators: ==, !=, <, <=, >, >=.
Combine predicates with and, or, and not.
{
"title": "contact info",
"patterns": [
{
"name": "contact",
"rules": [
{
"context": "$",
"checks": [
{
"kind": "assert",
"test": {
"type": "or",
"left": { "type": "exists", "path": "$.phone" },
"right": { "type": "exists", "path": "$.email" }
},
"message": "need phone or email"
},
{
"kind": "assert",
"test": {
"type": "and",
"left": { "type": "exists", "path": "$.name" },
"right": { "type": "exists", "path": "$.age" }
},
"message": "need both name and age"
},
{
"kind": "assert",
"test": {
"type": "not",
"inner": { "type": "exists", "path": "$.deprecated" }
},
"message": "document must not be deprecated"
}
]
}
]
}
]
}Pass: {"name": "Alice", "age": 30, "email": "a@b.com"}
Fail: {"name": "Alice", "age": 30} -- [error] need phone or email
Use $..field to find fields at any depth.
{
"title": "deep search",
"patterns": [
{
"name": "nested",
"rules": [
{
"context": "$",
"checks": [
{
"kind": "assert",
"test": { "type": "exists", "path": "$..email" },
"message": "email must exist somewhere in document"
}
]
}
]
}
]
}Pass: {"user": {"profile": {"email": "a@b.com"}}}
Fail: {"user": {"profile": {"name": "Alice"}}} -- [error] email must exist somewhere in document
Use a context path to apply checks to each array element.
Rules: array-context.json
| DSL
{
"title": "item checks",
"patterns": [
{
"name": "items",
"rules": [
{
"context": "$.items[*]",
"checks": [
{
"kind": "assert",
"test": { "type": "exists", "path": "$.name" },
"message": "every item must have a name"
},
{
"kind": "assert",
"test": { "type": "exists", "path": "$.price" },
"message": "every item must have a price"
}
]
}
]
}
]
}$ scheck validate etc/examples/array-context-pass.json --rules etc/examples/array-context.json
OK: all checks passed
$ scheck validate etc/examples/array-context-fail.json --rules etc/examples/array-context.json
[error] items at $.items[*]: every item must have a price
[error] items at $.items[*]: every item must have a name
2 error(s), 0 warning(s), 0 info(s)
Control how findings are classified: fatal, error (default), warning, info.
Rules: severity-levels.json
| DSL
{
"title": "severity demo",
"patterns": [
{
"name": "checks",
"rules": [
{
"context": "$",
"checks": [
{
"kind": "assert",
"test": { "type": "exists", "path": "$.id" },
"message": "id is required",
"severity": "fatal"
},
{
"kind": "assert",
"test": { "type": "exists", "path": "$.name" },
"message": "name is required"
},
{
"kind": "assert",
"test": { "type": "exists", "path": "$.description" },
"message": "description is recommended",
"severity": "warning"
},
{
"kind": "assert",
"test": { "type": "exists", "path": "$.metadata" },
"message": "metadata is optional",
"severity": "info"
}
]
}
]
}
]
}fatal and error cause validation to fail. warning and info are
reported but do not fail validation.
$ scheck validate etc/examples/severity-levels-pass.json --rules etc/examples/severity-levels.json
OK: all checks passed
$ scheck validate etc/examples/severity-levels-fail.json --rules etc/examples/severity-levels.json
[fatal] checks at $: id is required
[warning] checks at $: description is recommended
[info] checks at $: metadata is optional
1 fatal(s), 0 error(s), 1 warning(s), 1 info(s)
assert fires on failure (test must be true). report fires on
success (surfaces a positive finding when test is true).
{
"title": "mixed checks",
"patterns": [
{
"name": "audit",
"rules": [
{
"context": "$",
"checks": [
{
"kind": "assert",
"test": { "type": "exists", "path": "$.id" },
"message": "id required"
},
{
"kind": "report",
"test": { "type": "exists", "path": "$.metadata" },
"message": "document has metadata",
"severity": "info"
},
{
"kind": "report",
"test": { "type": "exists", "path": "$.deprecated" },
"message": "document is marked deprecated",
"severity": "warning"
}
]
}
]
}
]
}Given {"id": "1", "metadata": {}}, validation passes and reports:
[info] document has metadata.
Group patterns into named phases and run subsets selectively.
Rules: phases.json
| DSL
{
"title": "phased checks",
"default_phase": "quick",
"phases": [
{ "name": "quick", "active_patterns": ["required"] },
{ "name": "full", "active_patterns": ["required", "format"] }
],
"patterns": [
{
"name": "required",
"rules": [
{
"context": "$",
"checks": [
{
"kind": "assert",
"test": { "type": "exists", "path": "$.id" },
"message": "id required"
}
]
}
]
},
{
"name": "format",
"rules": [
{
"context": "$",
"checks": [
{
"kind": "assert",
"test": { "type": "matches", "path": "$.id", "pattern": "^[A-Z]" },
"message": "id must start uppercase"
}
]
}
]
}
]
}$ scheck validate etc/examples/phases-pass.json --rules etc/examples/phases.json
OK: all checks passed
$ scheck validate etc/examples/phases-fail.json --rules etc/examples/phases.json
OK: all checks passed
$ scheck validate etc/examples/phases-fail.json --rules etc/examples/phases.json --phase full
[error] format at $: id must start uppercase
1 error(s), 0 warning(s), 0 info(s)
$ scheck validate etc/examples/phases-fail.json --rules etc/examples/phases.json --phase all
[error] format at $: id must start uppercase
1 error(s), 0 warning(s), 0 info(s)
Attach reusable explanations to checks.
Rules: diagnostics.json
| DSL
{
"title": "with diagnostics",
"diagnostics": [
{ "id": "spec-4.2", "message": "See CSAF 2.0 spec, section 4.2" }
],
"patterns": [
{
"name": "spec-compliance",
"rules": [
{
"context": "$",
"checks": [
{
"kind": "assert",
"test": { "type": "exists", "path": "$.document" },
"message": "document root required",
"diagnostics": ["spec-4.2"]
}
]
}
]
}
]
}$ scheck validate etc/examples/diagnostics-fail.json --rules etc/examples/diagnostics.json
[error] spec-compliance at $: document root required
See CSAF 2.0 spec, section 4.2
1 error(s), 0 warning(s), 0 info(s)
Categorize checks with flags for filtering and reporting.
{
"title": "flagged checks",
"patterns": [
{
"name": "security",
"rules": [
{
"context": "$",
"checks": [
{
"kind": "assert",
"test": { "type": "not_exists", "path": "$.password" },
"message": "must not contain password",
"flag": "security"
},
{
"kind": "assert",
"test": { "type": "exists", "path": "$.auth_token" },
"message": "auth token required",
"flag": "security"
}
]
}
]
}
]
}Flags appear in JSON output:
$ scheck validate doc.json --rules flagged.json --format json
scheck validates YAML documents identically to JSON. Format is auto-detected.
Rules: yaml-input.json
| DSL
$ scheck validate etc/examples/yaml-input-pass.yaml --rules etc/examples/yaml-input.json
OK: all checks passed
$ scheck validate etc/examples/yaml-input-fail.yaml --rules etc/examples/yaml-input.json
[error] required at $: version is required
1 error(s), 0 warning(s), 0 info(s)
Use scheck check to validate a rule file without running it against a document:
$ scheck check etc/examples/csaf-advisory.json
OK: schema "CSAF Advisory Checks" — 3 pattern(s), 2 phase(s) [json]
Get structured SVRL-inspired JSON output for programmatic consumption:
$ scheck validate etc/examples/required-fields-fail.json \
--rules etc/examples/required-fields.json \
--format json
Build rules programmatically with type safety.
use scheck::builder::*;
use scheck::{Comparison, Severity, validate_json};
let schema = schema("API validation")
.pattern("request", |p| p
.title("Request validation")
.rule("$", |r| r
.assert(exists("$.method"), "method is required")
.assert(
matches("$.method", "^(GET|POST|PUT|DELETE)$"),
"method must be a valid HTTP verb",
)
.assert(exists("$.url"), "url is required")
.assert_with(
count("$.headers[*]", Comparison::Ge, 1),
"should have at least one header",
Severity::Warning,
)
.report(exists("$.body"), "request has a body")
)
)
.build();
let doc = r#"{"method": "GET", "url": "/api/users"}"#;
let report = validate_json(&schema, doc)?;
assert!(report.is_ok());
// Export as JSON for other tools
let json = serde_json::to_string_pretty(&schema)?;A complete example validating a CSAF security advisory.
Rules: csaf-advisory.json
| DSL
$ scheck validate etc/examples/csaf-advisory-pass.json \
--rules etc/examples/csaf-advisory.json --phase full
OK: all checks passed
$ scheck validate etc/examples/csaf-advisory-fail.json \
--rules etc/examples/csaf-advisory.json --phase full
[error] required-fields at $: tracking ID is required
[error] cve-format at $..vulnerabilities[*]: CVE ID must match standard format
2 error(s), 0 warning(s), 0 info(s)
With JSON output:
$ scheck validate etc/examples/csaf-advisory-fail.json \
--rules etc/examples/csaf-advisory.json --phase full --format json
Built-in validators for common formats. Use "type": "named" instead of
writing regex patterns by hand.
{
"title": "named type checks",
"patterns": [
{
"name": "formats",
"rules": [
{
"context": "$",
"checks": [
{
"kind": "assert",
"test": { "type": "named", "name": "email", "path": "$.contact" },
"message": "contact must be a valid email"
},
{
"kind": "assert",
"test": { "type": "named", "name": "url", "path": "$.homepage" },
"message": "homepage must be a valid URL"
},
{
"kind": "assert",
"test": { "type": "named", "name": "semver", "path": "$.version" },
"message": "version must be semver"
},
{
"kind": "assert",
"test": { "type": "named", "name": "cve_id", "path": "$.cve" },
"message": "must be a valid CVE ID"
},
{
"kind": "assert",
"test": { "type": "named", "name": "uuid", "path": "$.id" },
"message": "id must be a UUID"
},
{
"kind": "assert",
"test": { "type": "named", "name": "iso_date", "path": "$.date" },
"message": "date must be YYYY-MM-DD"
}
]
}
]
}
]
}Supported types: email, url, cve_id (or cve-id), purl, semver,
uuid, iso_date (or iso-date), iso_datetime (or iso-datetime).
In the Rust builder API:
use scheck::builder::*;
let schema = schema("types")
.pattern("p", |p| p
.rule("$", |r| r
.assert(is_email("$.contact"), "bad email")
.assert(is_url("$.homepage"), "bad url")
.assert(is_semver("$.version"), "bad version")
.assert(is_cve_id("$.cve"), "bad CVE")
.assert(is_purl("$.package"), "bad PURL")
.assert(is_uuid("$.id"), "bad UUID")
.assert(is_iso_date("$.date"), "bad date")
.assert(is_iso_datetime("$.timestamp"), "bad datetime")
)
)
.build();Validate only a subtree of a document with --context. Rules run against
each node matched by the context path instead of the full document root.
$ scheck validate users.json --rules user-rules.json --context '$.users[*]'
Given users.json:
{
"users": [
{"name": "Alice", "email": "alice@example.com"},
{"name": "Bob"},
{"age": 30}
]
}And rules that check for $.name and $.email at the root, partial
validation runs those checks against each user object individually.
In the Rust API:
let report = scheck::validate_context(&schema, &doc, "$.users[*]", "");The Validated wrapper guarantees at the type level that a document
passed all validation rules. It cannot be constructed directly -- only
through try_validate().
use scheck::try_validate;
let doc = scheck::from_json(r#"{"name": "Alice"}"#)?;
match try_validate(&schema, doc) {
Ok(validated) => {
// proven valid -- safe to process
let doc = validated.document();
let report = validated.report();
}
Err(failed) => {
// ValidationFailed implements Error + Display
eprintln!("{}", failed.report().to_text());
}
}try_validate_phase() accepts a phase name for phased validation.