From fb295d24a0129ab0623082873b02828755886d29 Mon Sep 17 00:00:00 2001 From: ci Date: Mon, 31 Aug 2026 16:39:37 -0400 Subject: [PATCH 1/8] Add inactive control policy set validator --- README.md | 11 ++ RESTORE.md | 11 ++ ci/required-files.txt | 5 + control/v1/policy-set.jq | 61 ++++++++ control/v1/validate.sh | 83 +++++++++++ scripts/test/control-policy-set.test.sh | 178 ++++++++++++++++++++++++ 6 files changed, 349 insertions(+) create mode 100644 control/v1/policy-set.jq create mode 100755 control/v1/validate.sh create mode 100755 scripts/test/control-policy-set.test.sh diff --git a/README.md b/README.md index a428b39..d1cd4e7 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,17 @@ fixed limits, and disposable directories, but this test does not provide or clai mechanical network or host-filesystem isolation. Real-adapter sandbox qualification and an external-target smoke remain required. +## Inactive control policy-set validator + +`control/v1/` defines a canonical identity bundle for six later Control foundation +policies: duty separation, sandbox, credentials, risk gates, kill switch, and +immutable evidence. Its validator checks exact immutable policy and decision refs; +it does not contain or evaluate those policies. + +The package stays inactive and fail-closed. It grants no authority, activates no +profile, reads no credential, launches no adapter, and performs no external write. +Later bounded units own each policy body and its enforcement. + ## The current default team You talk **only** to yshifu, in a Claude Code session. yshifu orchestrates the other roles diff --git a/RESTORE.md b/RESTORE.md index 2066160..7bbcf2e 100644 --- a/RESTORE.md +++ b/RESTORE.md @@ -319,6 +319,17 @@ each cell, and checks the accepted 2×2 matrix plus closed negative protocol cas It uses no real adapter or credential and makes no network/host isolation, qualification, activation, or external-target-smoke claim. +Restore the three paths in the manifest's inactive control policy-set block, then +run: + +```sh +bash scripts/test/control-policy-set.test.sh +``` + +The proof validates only the canonical six-section identity bundle. It does not +evaluate a policy, grant authority, activate a profile, or enforce sandbox, +credential, risk, kill-switch, or evidence behavior. + --- ## 5. Smoke test — prove the rebuilt team is alive diff --git a/ci/required-files.txt b/ci/required-files.txt index 8411409..c099a43 100644 --- a/ci/required-files.txt +++ b/ci/required-files.txt @@ -166,3 +166,8 @@ adapter-tests/v1/fakes/forge-b.sh adapter-tests/v1/fakes/protocol-fault.sh adapter-tests/v1/fixture/source.txt scripts/test/portable-adapter-contracts.test.sh + +# Inactive control policy-set identity seam +control/v1/policy-set.jq +control/v1/validate.sh +scripts/test/control-policy-set.test.sh diff --git a/control/v1/policy-set.jq b/control/v1/policy-set.jq new file mode 100644 index 0000000..5aef193 --- /dev/null +++ b/control/v1/policy-set.jq @@ -0,0 +1,61 @@ +def exact($required): + type == "object" and (keys | sort) == ($required | sort); + +def id_ok: + type == "string" and test("\\A[a-z0-9][a-z0-9._:-]{0,127}\\z"); + +def sha256_ok: + type == "string" and test("\\A[0-9a-f]{64}\\z"); + +def content_ref_ok($media_type): + exact(["content_id","media_type","sha256"]) and + (.content_id | id_ok) and .media_type == $media_type and + (.sha256 | sha256_ok); + +def section_shape_ok: + exact(["decision_ref","policy_ref","section_id"]) and + (.section_id | id_ok) and + (.policy_ref | content_ref_ok("application/vnd.ystack.control-policy+json")) and + (.decision_ref | content_ref_ok("application/vnd.ystack.control-decision+json")); + +def core_contract_shape_ok: + exact(["generation_id","package_ref","semantic_identity"]) and + (.semantic_identity | id_ok) and + (.generation_id | test("\\Ag-[0-9a-f]{64}\\z")) and + (.package_ref | content_ref_ok("application/vnd.ystack.core-contract+json")); + +def shape_ok: + exact(["body","id","kind","schema_version"]) and + .schema_version == 1 and .kind == "control_policy_set" and + (.id | id_ok) and + (.body | + exact(["activation_state","core_contract","fail_mode","policy_version","sections"]) and + (.activation_state | type == "string") and + (.fail_mode | type == "string") and + (.policy_version | type == "string") and + (.core_contract | core_contract_shape_ok) and + (.sections | type == "array" and length >= 1 and length <= 16 and + all(.[];section_shape_ok))); + +def expected_sections: + ["credential-policy","duty-separation","evidence-integrity", + "kill-switch","risk-gates","sandbox"]; + +def relations_ok: + .body as $body | + $body.policy_version == "v1" and + $body.activation_state == "inactive" and + $body.fail_mode == "closed" and + ($body.core_contract.semantic_identity | + test("\\Acore\\.contracts\\.v[1-9][0-9]*\\z")) and + ($body.sections | map(.section_id)) == expected_sections and + ($body.sections | all(.[]; + .policy_ref.content_id == ("control-policy:" + .section_id))) and + ($body.sections | map(.policy_ref.sha256) | unique | length) == 6 and + ($body.sections | map(.decision_ref.content_id) | unique | length) == 6 and + ($body.sections | map(.decision_ref.sha256) | unique | length) == 6; + +if (shape_ok | not) then "E_SHAPE" +elif (relations_ok | not) then "E_RELATION" +else empty +end diff --git a/control/v1/validate.sh b/control/v1/validate.sh new file mode 100755 index 0000000..eeebefe --- /dev/null +++ b/control/v1/validate.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +set -uo pipefail +export LC_ALL=C +umask 077 + +emit_error() { + case "${1:-}" in + E_USAGE|E_RUNTIME|E_LIMIT|E_PARSE|E_CANONICAL|E_SHAPE|E_RELATION) + /usr/bin/printf '%s\n' "$1" >&2 + ;; + *) /usr/bin/printf '%s\n' E_RUNTIME >&2 ;; + esac + exit 1 +} + +[ "$#" -eq 2 ] && [ "$1" = validate ] || emit_error E_USAGE +input=$2 +source_path=${BASH_SOURCE[0]} +case "$source_path" in + /*) ;; + *) source_path="$(pwd -P)/$source_path" ;; +esac +[ -f "$source_path" ] && [ ! -L "$source_path" ] || emit_error E_RUNTIME +source_dir=$(CDPATH='' cd -P -- "${source_path%/*}" 2>/dev/null && pwd -P) || + emit_error E_RUNTIME +source_path="$source_dir/${source_path##*/}" +[ "$source_path" = "$source_dir/validate.sh" ] || emit_error E_RUNTIME +policy_program="$source_dir/policy-set.jq" +[ -f "$policy_program" ] && [ ! -L "$policy_program" ] || emit_error E_RUNTIME +[ -f "$input" ] && [ ! -L "$input" ] || emit_error E_RUNTIME +jq_bin=$(command -v jq 2>/dev/null) || emit_error E_RUNTIME +[ -f "$jq_bin" ] && [ -x "$jq_bin" ] && [ ! -L "$jq_bin" ] && + [ "$($jq_bin --version 2>/dev/null)" = jq-1.6 ] || emit_error E_RUNTIME + +scratch=$(/usr/bin/mktemp -d "${TMPDIR:-/tmp}/ystack-control-policy.XXXXXX" 2>/dev/null) || + emit_error E_RUNTIME +cleanup() { /bin/rm -rf -- "$scratch" >/dev/null 2>&1 || :; } +signal_exit() { trap - EXIT HUP INT TERM; cleanup; exit 1; } +trap cleanup EXIT +trap signal_exit HUP INT TERM + +raw="$scratch/raw.json" +/bin/dd if="$input" of="$raw" bs=1048577 count=1 2>/dev/null || emit_error E_RUNTIME +raw_size=$(/usr/bin/wc -c < "$raw" | /usr/bin/tr -d ' ') || emit_error E_RUNTIME +[ "$raw_size" -le 1048576 ] || emit_error E_LIMIT +bom=$(/usr/bin/od -An -tx1 -N3 "$raw" 2>/dev/null | /usr/bin/tr -d ' \n') || + emit_error E_RUNTIME +[ "$bom" != efbbbf ] || emit_error E_PARSE +"$jq_bin" . "$raw" >/dev/null 2>&1 || emit_error E_PARSE +root_count=$("$jq_bin" -s 'length' "$raw" 2>/dev/null) || emit_error E_PARSE +[ "$root_count" -eq 1 ] || emit_error E_PARSE +canonical="$scratch/canonical.json" +"$jq_bin" -S -c . "$raw" > "$canonical" 2>/dev/null || emit_error E_PARSE +/usr/bin/cmp -s "$raw" "$canonical" || emit_error E_CANONICAL + +"$jq_bin" -e ' + def depth: + if type == "array" then + if length == 0 then 1 else 1 + ([.[] | depth] | max) end + elif type == "object" then + if length == 0 then 1 else 1 + ([.[] | depth] | max) end + else 1 end; + def members: + if type == "array" then length + ([.[] | members] | add // 0) + elif type == "object" then (keys_unsorted | length) + ([.[] | members] | add // 0) + else 0 end; + def strings_ok: + if type == "array" then all(.[];strings_ok) + elif type == "object" then + all(keys_unsorted[];utf8bytelength <= 8192) and all(.[];strings_ok) + elif type == "string" then utf8bytelength <= 8192 + else true end; + (depth <= 32) and (members <= 1024) and strings_ok +' "$raw" >/dev/null 2>&1 || emit_error E_LIMIT + +result=$("$jq_bin" -r -f "$policy_program" "$raw" 2>/dev/null) || emit_error E_RUNTIME +case "$result" in + '') ;; + E_SHAPE|E_RELATION) emit_error "$result" ;; + *) emit_error E_RUNTIME ;; +esac +trap - EXIT HUP INT TERM +cleanup diff --git a/scripts/test/control-policy-set.test.sh b/scripts/test/control-policy-set.test.sh new file mode 100755 index 0000000..af9fb4a --- /dev/null +++ b/scripts/test/control-policy-set.test.sh @@ -0,0 +1,178 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2016 +set -euo pipefail +export LC_ALL=C +umask 077 + +root=$(CDPATH='' cd -P -- "${BASH_SOURCE[0]%/*}/../.." && pwd -P) +validator="$root/control/v1/validate.sh" +tmp=$(/usr/bin/mktemp -d "${TMPDIR:-/tmp}/ystack-control-policy-test.XXXXXX") +tmp=$(CDPATH='' cd -P -- "$tmp" && pwd -P) +download='' + +cleanup() { + if [ -n "$download" ] && [ -f "$download" ]; then + /bin/rm -f -- "$download" + fi + /bin/rm -rf -- "$tmp" +} +trap cleanup EXIT +fail() { /usr/bin/printf 'FAIL: %s\n' "$1" >&2; exit 1; } +passes=0 +pass() { passes=$((passes + 1)); /usr/bin/printf 'ok %s - %s\n' "$passes" "$1"; } +sha_file() { /usr/bin/shasum -a 256 "$1" | /usr/bin/awk '{print $1}'; } + +platform=$(/usr/bin/uname -s):$(/usr/bin/uname -m) +case "$platform" in + Darwin:*) jq_asset=jq-osx-amd64; jq_sha=5c0a0a3ea600f302ee458b30317425dd9632d1ad8882259fcaf4e9b868b2b1ef ;; + Linux:x86_64) jq_asset=jq-linux64; jq_sha=af986793a515d500ab2d35f8d2aecd656e764504b789b66d7e1a0b727a124c44 ;; + *) fail "unsupported host $platform" ;; +esac +jq_cache_dir="${TMPDIR:-/tmp}/ystack-portable-core-jq16" +/bin/mkdir -p "$jq_cache_dir" +jq_cache="$jq_cache_dir/$jq_asset" +if [ ! -f "$jq_cache" ] || [ "$(sha_file "$jq_cache")" != "$jq_sha" ]; then + download=$(/usr/bin/mktemp "$jq_cache_dir/.jq-1.6.XXXXXX") + /usr/bin/curl --proto '=https' --tlsv1.2 -fsSL \ + "https://github.com/jqlang/jq/releases/download/jq-1.6/$jq_asset" \ + -o "$download" + [ "$(sha_file "$download")" = "$jq_sha" ] || fail 'jq release digest' + /bin/chmod 0555 "$download" + /bin/mv "$download" "$jq_cache" + download='' +fi +[ "$(sha_file "$jq_cache")" = "$jq_sha" ] || fail 'jq digest' +bin="$tmp/bin" +/bin/mkdir -m 700 "$bin" +/bin/cp "$jq_cache" "$bin/jq" +/bin/chmod 0555 "$bin/jq" +jq_bin="$bin/jq" +[ "$("$jq_bin" --version)" = jq-1.6 ] || fail 'jq identity' + +valid="$tmp/valid.json" +"$jq_bin" -S -c -n ' + def ref($id;$media;$character): + {content_id:$id,media_type:$media,sha256:($character*64)}; + def decision_character($character): + if $character=="1" then "a" elif $character=="2" then "b" + elif $character=="3" then "c" elif $character=="4" then "d" + elif $character=="5" then "e" else "f" end; + def section($id;$character): + {section_id:$id, + policy_ref:ref("control-policy:"+$id;"application/vnd.ystack.control-policy+json";$character), + decision_ref:ref("control-decision:"+$id;"application/vnd.ystack.control-decision+json"; + decision_character($character))}; + {schema_version:1,kind:"control_policy_set",id:"control-policy-set.example", + body:{policy_version:"v1",activation_state:"inactive",fail_mode:"closed", + core_contract:{semantic_identity:"core.contracts.v2",generation_id:("g-"+("7"*64)), + package_ref:ref("core-contract-package.v2";"application/vnd.ystack.core-contract+json";"9")}, + sections:[section("credential-policy";"1"),section("duty-separation";"2"), + section("evidence-integrity";"3"),section("kill-switch";"4"), + section("risk-gates";"5"),section("sandbox";"6")]}} +' > "$valid" + +run_validator() { + local input=$1 out=$2 err=$3 status=0 + PATH="$bin:/usr/bin:/bin" "$validator" validate "$input" > "$out" 2> "$err" || status=$? + RUN_STATUS=$status +} +expect_pass() { + local name=$1 input=$2 + local out="$tmp/$name.out" err="$tmp/$name.err" + run_validator "$input" "$out" "$err" + [ "$RUN_STATUS" -eq 0 ] && [ ! -s "$out" ] && [ ! -s "$err" ] || fail "$name" + pass "$name" +} +expect_error() { + local name=$1 expected=$2 input=$3 + local out="$tmp/$name.out" err="$tmp/$name.err" + run_validator "$input" "$out" "$err" + if [ "$RUN_STATUS" -eq 0 ] || [ -s "$out" ] || + [ "$(/bin/cat "$err")" != "$expected" ] || + /usr/bin/grep -Fq "$tmp" "$err"; then + fail "$name" + fi + pass "$name" +} +mutate() { + local name=$1 filter=$2 + "$jq_bin" -S -c "$filter" "$valid" > "$tmp/$name.json" + /usr/bin/printf '%s\n' "$tmp/$name.json" +} + +expect_pass canonical-valid "$valid" +for field in schema_version kind id body; do + expect_error "missing-envelope-$field" E_SHAPE "$(mutate "missing-envelope-$field" "del(.$field)")" +done +for field in policy_version activation_state fail_mode core_contract sections; do + expect_error "missing-body-$field" E_SHAPE "$(mutate "missing-body-$field" "del(.body.$field)")" +done +for field in semantic_identity generation_id package_ref; do + expect_error "missing-core-$field" E_SHAPE "$(mutate "missing-core-$field" "del(.body.core_contract.$field)")" +done +for field in section_id policy_ref decision_ref; do + expect_error "missing-section-$field" E_SHAPE \ + "$(mutate "missing-section-$field" "del(.body.sections[0].$field)")" +done + +expect_error section-missing E_RELATION "$(mutate section-missing '.body.sections |= .[:-1]')" +expect_error section-extra E_RELATION "$(mutate section-extra '.body.sections += [(.body.sections[0] | .section_id="other" | .policy_ref.content_id="control-policy:other" | .policy_ref.sha256=("8"*64) | .decision_ref.content_id="control-decision:other" | .decision_ref.sha256=("8"*64))]')" +expect_error section-duplicate E_RELATION "$(mutate section-duplicate '.body.sections[1]=.body.sections[0]')" +expect_error section-reordered E_RELATION "$(mutate section-reordered '.body.sections[0:2] |= reverse')" +expect_error section-renamed E_RELATION "$(mutate section-renamed '.body.sections[0].section_id="renamed"')" +expect_error policy-media E_SHAPE "$(mutate policy-media '.body.sections[0].policy_ref.media_type="application/json"')" +expect_error decision-media E_SHAPE "$(mutate decision-media '.body.sections[0].decision_ref.media_type="application/json"')" +expect_error package-media E_SHAPE "$(mutate package-media '.body.core_contract.package_ref.media_type="application/json"')" +expect_error bad-digest E_SHAPE "$(mutate bad-digest '.body.sections[0].policy_ref.sha256="bad"')" +expect_error bad-id E_SHAPE "$(mutate bad-id '.id="Bad ID"')" +expect_error policy-id-link E_RELATION "$(mutate policy-id-link '.body.sections[0].policy_ref.content_id="control-policy:other"')" +expect_error core-identity E_RELATION "$(mutate core-identity '.body.core_contract.semantic_identity="other.contract"')" +expect_error core-generation E_SHAPE "$(mutate core-generation '.body.core_contract.generation_id="g-bad"')" +expect_error active-state E_RELATION "$(mutate active-state '.body.activation_state="active"')" +expect_error mutable-state E_RELATION "$(mutate mutable-state '.body.activation_state="mutable"')" +expect_error fail-open E_RELATION "$(mutate fail-open '.body.fail_mode="open"')" +for field in command url credential vendor executable grant evaluation activation; do + expect_error "injected-$field" E_SHAPE \ + "$(mutate "injected-$field" ".body.$field=\"forbidden\"")" +done + +/usr/bin/printf '{\n' > "$tmp/parse.json" +expect_error parse E_PARSE "$tmp/parse.json" +"$jq_bin" . "$valid" > "$tmp/noncanonical.json" +expect_error noncanonical E_CANONICAL "$tmp/noncanonical.json" +/bin/cat "$valid" "$valid" > "$tmp/multi-root.json" +expect_error multi-root E_PARSE "$tmp/multi-root.json" +/usr/bin/printf '\357\273\277' > "$tmp/bom.json" +/bin/cat "$valid" >> "$tmp/bom.json" +expect_error bom E_PARSE "$tmp/bom.json" +/usr/bin/awk 'BEGIN { for (i=0;i<1048577;i++) printf "x" }' > "$tmp/raw-limit.json" +expect_error raw-limit E_LIMIT "$tmp/raw-limit.json" +"$jq_bin" -S -c -n 'reduce range(0;33) as $i (0;{x:.})' > "$tmp/depth-limit.json" +expect_error depth-limit E_LIMIT "$tmp/depth-limit.json" +"$jq_bin" -S -c -n 'reduce range(0;1025) as $i ({};. + {($i|tostring):0})' > "$tmp/member-limit.json" +expect_error member-limit E_LIMIT "$tmp/member-limit.json" +"$jq_bin" -S -c '.body.extra=("x"*8193)' "$valid" > "$tmp/string-limit.json" +expect_error string-limit E_LIMIT "$tmp/string-limit.json" + +/bin/ln -s "$valid" "$tmp/symlink.json" +expect_error symlink E_RUNTIME "$tmp/symlink.json" +/usr/bin/mkfifo "$tmp/input.fifo" +expect_error nonregular E_RUNTIME "$tmp/input.fifo" +fake_home="$tmp/fake-home" +fake_cwd="$tmp/fake-cwd" +/bin/mkdir -m 700 "$fake_home" "$fake_cwd" +ambient_out="$tmp/ambient.out" +ambient_err="$tmp/ambient.err" +(cd "$fake_cwd" && HOME="$fake_home" JQ_LIBRARY_PATH="$fake_home" \ + PATH="$bin:/usr/bin:/bin" "$validator" validate "$valid" > "$ambient_out" 2> "$ambient_err") +[ ! -s "$ambient_out" ] && [ ! -s "$ambient_err" ] || fail 'ambient independence' +pass 'ambient cwd and environment independence' + +for required in control/v1/policy-set.jq control/v1/validate.sh scripts/test/control-policy-set.test.sh; do + [ "$(/usr/bin/grep -Fxc "$required" "$root/ci/required-files.txt")" -eq 1 ] || + fail "manifest $required" +done +/usr/bin/grep -Fq 'Inactive control policy-set validator' "$root/README.md" || fail 'README docs' +/usr/bin/grep -Fq 'control-policy-set.test.sh' "$root/RESTORE.md" || fail 'RESTORE docs' +pass 'restore manifest and docs' +/usr/bin/printf 'control policy set: %s focused checks passed\n' "$passes" From 7e74fb24f7fec3e583a22feeb440dfb65b0532d2 Mon Sep 17 00:00:00 2001 From: ci Date: Mon, 31 Aug 2026 16:42:07 -0400 Subject: [PATCH 2/8] Bind policy decisions to sections --- control/v1/policy-set.jq | 2 ++ scripts/test/control-policy-set.test.sh | 1 + 2 files changed, 3 insertions(+) diff --git a/control/v1/policy-set.jq b/control/v1/policy-set.jq index 5aef193..f453a4d 100644 --- a/control/v1/policy-set.jq +++ b/control/v1/policy-set.jq @@ -51,6 +51,8 @@ def relations_ok: ($body.sections | map(.section_id)) == expected_sections and ($body.sections | all(.[]; .policy_ref.content_id == ("control-policy:" + .section_id))) and + ($body.sections | all(.[]; + .decision_ref.content_id == ("control-decision:" + .section_id))) and ($body.sections | map(.policy_ref.sha256) | unique | length) == 6 and ($body.sections | map(.decision_ref.content_id) | unique | length) == 6 and ($body.sections | map(.decision_ref.sha256) | unique | length) == 6; diff --git a/scripts/test/control-policy-set.test.sh b/scripts/test/control-policy-set.test.sh index af9fb4a..6df16d1 100755 --- a/scripts/test/control-policy-set.test.sh +++ b/scripts/test/control-policy-set.test.sh @@ -126,6 +126,7 @@ expect_error package-media E_SHAPE "$(mutate package-media '.body.core_contract. expect_error bad-digest E_SHAPE "$(mutate bad-digest '.body.sections[0].policy_ref.sha256="bad"')" expect_error bad-id E_SHAPE "$(mutate bad-id '.id="Bad ID"')" expect_error policy-id-link E_RELATION "$(mutate policy-id-link '.body.sections[0].policy_ref.content_id="control-policy:other"')" +expect_error decision-id-link E_RELATION "$(mutate decision-id-link '.body.sections[0].decision_ref.content_id="control-decision:other"')" expect_error core-identity E_RELATION "$(mutate core-identity '.body.core_contract.semantic_identity="other.contract"')" expect_error core-generation E_SHAPE "$(mutate core-generation '.body.core_contract.generation_id="g-bad"')" expect_error active-state E_RELATION "$(mutate active-state '.body.activation_state="active"')" From f6cbdcba4fb773f5968b4bb244db292089b7a924 Mon Sep 17 00:00:00 2001 From: ci Date: Mon, 31 Aug 2026 17:03:53 -0400 Subject: [PATCH 3/8] Reject non-string core generations --- control/v1/policy-set.jq | 3 ++- scripts/test/control-policy-set.test.sh | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/control/v1/policy-set.jq b/control/v1/policy-set.jq index f453a4d..539cbe9 100644 --- a/control/v1/policy-set.jq +++ b/control/v1/policy-set.jq @@ -21,7 +21,8 @@ def section_shape_ok: def core_contract_shape_ok: exact(["generation_id","package_ref","semantic_identity"]) and (.semantic_identity | id_ok) and - (.generation_id | test("\\Ag-[0-9a-f]{64}\\z")) and + (.generation_id | + if type == "string" then test("\\Ag-[0-9a-f]{64}\\z") else false end) and (.package_ref | content_ref_ok("application/vnd.ystack.core-contract+json")); def shape_ok: diff --git a/scripts/test/control-policy-set.test.sh b/scripts/test/control-policy-set.test.sh index 6df16d1..9ba60cc 100755 --- a/scripts/test/control-policy-set.test.sh +++ b/scripts/test/control-policy-set.test.sh @@ -129,6 +129,10 @@ expect_error policy-id-link E_RELATION "$(mutate policy-id-link '.body.sections[ expect_error decision-id-link E_RELATION "$(mutate decision-id-link '.body.sections[0].decision_ref.content_id="control-decision:other"')" expect_error core-identity E_RELATION "$(mutate core-identity '.body.core_contract.semantic_identity="other.contract"')" expect_error core-generation E_SHAPE "$(mutate core-generation '.body.core_contract.generation_id="g-bad"')" +expect_error core-generation-number E_SHAPE "$(mutate core-generation-number '.body.core_contract.generation_id=1')" +expect_error core-generation-object E_SHAPE "$(mutate core-generation-object '.body.core_contract.generation_id={}')" +expect_error core-generation-array E_SHAPE "$(mutate core-generation-array '.body.core_contract.generation_id=[]')" +expect_error core-generation-null E_SHAPE "$(mutate core-generation-null '.body.core_contract.generation_id=null')" expect_error active-state E_RELATION "$(mutate active-state '.body.activation_state="active"')" expect_error mutable-state E_RELATION "$(mutate mutable-state '.body.activation_state="mutable"')" expect_error fail-open E_RELATION "$(mutate fail-open '.body.fail_mode="open"')" From 823aa9aa0ccc8c8701e4973e31c56236516ab809 Mon Sep 17 00:00:00 2001 From: ci Date: Mon, 31 Aug 2026 20:25:42 -0400 Subject: [PATCH 4/8] Use core-compatible control content IDs --- control/v1/policy-set.jq | 4 ++-- scripts/test/control-policy-set.test.sh | 21 ++++++++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/control/v1/policy-set.jq b/control/v1/policy-set.jq index 539cbe9..bbb60d6 100644 --- a/control/v1/policy-set.jq +++ b/control/v1/policy-set.jq @@ -51,9 +51,9 @@ def relations_ok: test("\\Acore\\.contracts\\.v[1-9][0-9]*\\z")) and ($body.sections | map(.section_id)) == expected_sections and ($body.sections | all(.[]; - .policy_ref.content_id == ("control-policy:" + .section_id))) and + .policy_ref.content_id == ("control-policy." + .section_id))) and ($body.sections | all(.[]; - .decision_ref.content_id == ("control-decision:" + .section_id))) and + .decision_ref.content_id == ("control-decision." + .section_id))) and ($body.sections | map(.policy_ref.sha256) | unique | length) == 6 and ($body.sections | map(.decision_ref.content_id) | unique | length) == 6 and ($body.sections | map(.decision_ref.sha256) | unique | length) == 6; diff --git a/scripts/test/control-policy-set.test.sh b/scripts/test/control-policy-set.test.sh index 9ba60cc..a18be8c 100755 --- a/scripts/test/control-policy-set.test.sh +++ b/scripts/test/control-policy-set.test.sh @@ -59,8 +59,8 @@ valid="$tmp/valid.json" elif $character=="5" then "e" else "f" end; def section($id;$character): {section_id:$id, - policy_ref:ref("control-policy:"+$id;"application/vnd.ystack.control-policy+json";$character), - decision_ref:ref("control-decision:"+$id;"application/vnd.ystack.control-decision+json"; + policy_ref:ref("control-policy."+$id;"application/vnd.ystack.control-policy+json";$character), + decision_ref:ref("control-decision."+$id;"application/vnd.ystack.control-decision+json"; decision_character($character))}; {schema_version:1,kind:"control_policy_set",id:"control-policy-set.example", body:{policy_version:"v1",activation_state:"inactive",fail_mode:"closed", @@ -101,6 +101,17 @@ mutate() { } expect_pass canonical-valid "$valid" +core_generation=g-392d20099dfa99872764009b268c8871914b4dbc0da467ec346baa921818ae3e +core_modules="$root/core/v2/generations/$core_generation/modules" +[ -f "$core_modules/schema.jq" ] || fail 'core schema seam' +"$jq_bin" -L "$core_modules" -e ' + import "schema" as schema; + (.body.sections | length) == 6 and + all(.body.sections[]; + (.policy_ref | schema::content_ref_ok) and + (.decision_ref | schema::content_ref_ok)) +' "$valid" >/dev/null || fail 'core content refs' +pass 'policy and decision refs satisfy core content-ref contract' for field in schema_version kind id body; do expect_error "missing-envelope-$field" E_SHAPE "$(mutate "missing-envelope-$field" "del(.$field)")" done @@ -116,7 +127,7 @@ for field in section_id policy_ref decision_ref; do done expect_error section-missing E_RELATION "$(mutate section-missing '.body.sections |= .[:-1]')" -expect_error section-extra E_RELATION "$(mutate section-extra '.body.sections += [(.body.sections[0] | .section_id="other" | .policy_ref.content_id="control-policy:other" | .policy_ref.sha256=("8"*64) | .decision_ref.content_id="control-decision:other" | .decision_ref.sha256=("8"*64))]')" +expect_error section-extra E_RELATION "$(mutate section-extra '.body.sections += [(.body.sections[0] | .section_id="other" | .policy_ref.content_id="control-policy.other" | .policy_ref.sha256=("8"*64) | .decision_ref.content_id="control-decision.other" | .decision_ref.sha256=("8"*64))]')" expect_error section-duplicate E_RELATION "$(mutate section-duplicate '.body.sections[1]=.body.sections[0]')" expect_error section-reordered E_RELATION "$(mutate section-reordered '.body.sections[0:2] |= reverse')" expect_error section-renamed E_RELATION "$(mutate section-renamed '.body.sections[0].section_id="renamed"')" @@ -125,8 +136,8 @@ expect_error decision-media E_SHAPE "$(mutate decision-media '.body.sections[0]. expect_error package-media E_SHAPE "$(mutate package-media '.body.core_contract.package_ref.media_type="application/json"')" expect_error bad-digest E_SHAPE "$(mutate bad-digest '.body.sections[0].policy_ref.sha256="bad"')" expect_error bad-id E_SHAPE "$(mutate bad-id '.id="Bad ID"')" -expect_error policy-id-link E_RELATION "$(mutate policy-id-link '.body.sections[0].policy_ref.content_id="control-policy:other"')" -expect_error decision-id-link E_RELATION "$(mutate decision-id-link '.body.sections[0].decision_ref.content_id="control-decision:other"')" +expect_error policy-id-link E_RELATION "$(mutate policy-id-link '.body.sections[0].policy_ref.content_id="control-policy.other"')" +expect_error decision-id-link E_RELATION "$(mutate decision-id-link '.body.sections[0].decision_ref.content_id="control-decision.other"')" expect_error core-identity E_RELATION "$(mutate core-identity '.body.core_contract.semantic_identity="other.contract"')" expect_error core-generation E_SHAPE "$(mutate core-generation '.body.core_contract.generation_id="g-bad"')" expect_error core-generation-number E_SHAPE "$(mutate core-generation-number '.body.core_contract.generation_id=1')" From 1b97a8413a7d1fd46d4566c091de19ae21aca8fb Mon Sep 17 00:00:00 2001 From: ci Date: Mon, 31 Aug 2026 20:42:47 -0400 Subject: [PATCH 5/8] Test control refs through public core seam --- scripts/test/control-policy-set.test.sh | 42 ++++++++++++++++++------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/scripts/test/control-policy-set.test.sh b/scripts/test/control-policy-set.test.sh index a18be8c..e915f7c 100755 --- a/scripts/test/control-policy-set.test.sh +++ b/scripts/test/control-policy-set.test.sh @@ -101,17 +101,37 @@ mutate() { } expect_pass canonical-valid "$valid" -core_generation=g-392d20099dfa99872764009b268c8871914b4dbc0da467ec346baa921818ae3e -core_modules="$root/core/v2/generations/$core_generation/modules" -[ -f "$core_modules/schema.jq" ] || fail 'core schema seam' -"$jq_bin" -L "$core_modules" -e ' - import "schema" as schema; - (.body.sections | length) == 6 and - all(.body.sections[]; - (.policy_ref | schema::content_ref_ok) and - (.decision_ref | schema::content_ref_ok)) -' "$valid" >/dev/null || fail 'core content refs' -pass 'policy and decision refs satisfy core content-ref contract' +core_wrapper="$root/scripts/core-contract.sh" +core_fixture_dir="$root/scripts/test" +core_request="$tmp/core-request.json" +[ -x "$core_wrapper" ] || fail 'core public seam' +"$jq_bin" -L "$core_fixture_dir" -S -c -n ' + import "portable-core-stage-request-fixtures" as fixture; + fixture::request_doc("producer";("0"*64)) | + .schema_version=2 | + .body.resolved_profile_ref.schema_version=2 +' > "$core_request" +core_ref_count=0 +for section_index in 0 1 2 3 4 5; do + for ref_field in policy_ref decision_ref; do + core_ref_count=$((core_ref_count + 1)) + core_case="$tmp/core-ref-$section_index-$ref_field.json" + "$jq_bin" -S -c --argjson index "$section_index" --arg field "$ref_field" \ + --slurpfile policy "$valid" \ + '.body.selection_ref.decision_record_ref = + $policy[0].body.sections[$index][$field]' \ + "$core_request" > "$core_case" + core_out="$tmp/core-ref-$core_ref_count.out" + core_err="$tmp/core-ref-$core_ref_count.err" + PATH="$bin:/usr/bin:/bin" /bin/bash "$core_wrapper" \ + validate-document "$core_case" > "$core_out" 2> "$core_err" || + fail "core content ref $core_ref_count" + [ ! -s "$core_out" ] && [ ! -s "$core_err" ] || + fail "core content ref output $core_ref_count" + done +done +[ "$core_ref_count" -eq 12 ] || fail 'core content ref coverage' +pass 'all policy and decision refs pass the public core seam' for field in schema_version kind id body; do expect_error "missing-envelope-$field" E_SHAPE "$(mutate "missing-envelope-$field" "del(.$field)")" done From eaa147ab3628953872305079abd0c2dd973cc8de Mon Sep 17 00:00:00 2001 From: ci Date: Mon, 31 Aug 2026 21:03:54 -0400 Subject: [PATCH 6/8] Align all control content IDs with core --- control/v1/policy-set.jq | 5 ++++- scripts/test/control-policy-set.test.sh | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/control/v1/policy-set.jq b/control/v1/policy-set.jq index bbb60d6..3650576 100644 --- a/control/v1/policy-set.jq +++ b/control/v1/policy-set.jq @@ -4,12 +4,15 @@ def exact($required): def id_ok: type == "string" and test("\\A[a-z0-9][a-z0-9._:-]{0,127}\\z"); +def content_id_ok: + id_ok and (contains(":") | not) and (contains("/") | not); + def sha256_ok: type == "string" and test("\\A[0-9a-f]{64}\\z"); def content_ref_ok($media_type): exact(["content_id","media_type","sha256"]) and - (.content_id | id_ok) and .media_type == $media_type and + (.content_id | content_id_ok) and .media_type == $media_type and (.sha256 | sha256_ok); def section_shape_ok: diff --git a/scripts/test/control-policy-set.test.sh b/scripts/test/control-policy-set.test.sh index e915f7c..6ee3b29 100755 --- a/scripts/test/control-policy-set.test.sh +++ b/scripts/test/control-policy-set.test.sh @@ -130,8 +130,21 @@ for section_index in 0 1 2 3 4 5; do fail "core content ref output $core_ref_count" done done -[ "$core_ref_count" -eq 12 ] || fail 'core content ref coverage' -pass 'all policy and decision refs pass the public core seam' +core_ref_count=$((core_ref_count + 1)) +core_case="$tmp/core-ref-$core_ref_count-package.json" +"$jq_bin" -S -c --slurpfile policy "$valid" \ + '.body.selection_ref.decision_record_ref = + $policy[0].body.core_contract.package_ref' \ + "$core_request" > "$core_case" +core_out="$tmp/core-ref-$core_ref_count.out" +core_err="$tmp/core-ref-$core_ref_count.err" +PATH="$bin:/usr/bin:/bin" /bin/bash "$core_wrapper" \ + validate-document "$core_case" > "$core_out" 2> "$core_err" || + fail 'core package content ref' +[ ! -s "$core_out" ] && [ ! -s "$core_err" ] || + fail 'core package content ref output' +[ "$core_ref_count" -eq 13 ] || fail 'core content ref coverage' +pass 'all content refs pass the public core seam' for field in schema_version kind id body; do expect_error "missing-envelope-$field" E_SHAPE "$(mutate "missing-envelope-$field" "del(.$field)")" done @@ -154,6 +167,8 @@ expect_error section-renamed E_RELATION "$(mutate section-renamed '.body.section expect_error policy-media E_SHAPE "$(mutate policy-media '.body.sections[0].policy_ref.media_type="application/json"')" expect_error decision-media E_SHAPE "$(mutate decision-media '.body.sections[0].decision_ref.media_type="application/json"')" expect_error package-media E_SHAPE "$(mutate package-media '.body.core_contract.package_ref.media_type="application/json"')" +expect_error package-id-colon E_SHAPE "$(mutate package-id-colon '.body.core_contract.package_ref.content_id="core:package"')" +expect_error package-id-path E_SHAPE "$(mutate package-id-path '.body.core_contract.package_ref.content_id="core/package"')" expect_error bad-digest E_SHAPE "$(mutate bad-digest '.body.sections[0].policy_ref.sha256="bad"')" expect_error bad-id E_SHAPE "$(mutate bad-id '.id="Bad ID"')" expect_error policy-id-link E_RELATION "$(mutate policy-id-link '.body.sections[0].policy_ref.content_id="control-policy.other"')" From 136ee7f553e2750c8bdd92a3c671db02b37742b4 Mon Sep 17 00:00:00 2001 From: ci Date: Mon, 31 Aug 2026 21:23:18 -0400 Subject: [PATCH 7/8] Reject relative jq executables --- control/v1/validate.sh | 1 + scripts/test/control-policy-set.test.sh | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/control/v1/validate.sh b/control/v1/validate.sh index eeebefe..814e305 100755 --- a/control/v1/validate.sh +++ b/control/v1/validate.sh @@ -29,6 +29,7 @@ policy_program="$source_dir/policy-set.jq" [ -f "$policy_program" ] && [ ! -L "$policy_program" ] || emit_error E_RUNTIME [ -f "$input" ] && [ ! -L "$input" ] || emit_error E_RUNTIME jq_bin=$(command -v jq 2>/dev/null) || emit_error E_RUNTIME +case "$jq_bin" in /*) ;; *) emit_error E_RUNTIME ;; esac [ -f "$jq_bin" ] && [ -x "$jq_bin" ] && [ ! -L "$jq_bin" ] && [ "$($jq_bin --version 2>/dev/null)" = jq-1.6 ] || emit_error E_RUNTIME diff --git a/scripts/test/control-policy-set.test.sh b/scripts/test/control-policy-set.test.sh index 6ee3b29..a1fa3e5 100755 --- a/scripts/test/control-policy-set.test.sh +++ b/scripts/test/control-policy-set.test.sh @@ -219,6 +219,26 @@ ambient_err="$tmp/ambient.err" [ ! -s "$ambient_out" ] && [ ! -s "$ambient_err" ] || fail 'ambient independence' pass 'ambient cwd and environment independence' +relative_cwd="$tmp/relative-cwd" +relative_sentinel="$tmp/relative-jq-ran" +/bin/mkdir -m 700 "$relative_cwd" +/usr/bin/printf '%s\n' \ + '#!/bin/sh' \ + ': > "${YSTACK_FAKE_JQ_SENTINEL:?}"' \ + 'if [ "${1:-}" = --version ]; then printf "%s\n" jq-1.6; fi' \ + 'exit 0' > "$relative_cwd/jq" +/bin/chmod 0700 "$relative_cwd/jq" +relative_out="$tmp/relative-path.out" +relative_err="$tmp/relative-path.err" +relative_status=0 +(cd "$relative_cwd" && YSTACK_FAKE_JQ_SENTINEL="$relative_sentinel" \ + PATH=".:$bin:/usr/bin:/bin" "$validator" validate "$valid" \ + > "$relative_out" 2> "$relative_err") || relative_status=$? +[ "$relative_status" -ne 0 ] && [ ! -s "$relative_out" ] && + [ "$(/bin/cat "$relative_err")" = E_RUNTIME ] && + [ ! -e "$relative_sentinel" ] || fail 'relative jq path' +pass 'relative jq path is rejected before execution' + for required in control/v1/policy-set.jq control/v1/validate.sh scripts/test/control-policy-set.test.sh; do [ "$(/usr/bin/grep -Fxc "$required" "$root/ci/required-files.txt")" -eq 1 ] || fail "manifest $required" From 25474f5a58d61d655b3ec4380863e5e0a6ae18b9 Mon Sep 17 00:00:00 2001 From: ci Date: Mon, 31 Aug 2026 21:31:32 -0400 Subject: [PATCH 8/8] Fix control validator interpreter path --- control/v1/validate.sh | 2 +- scripts/test/control-policy-set.test.sh | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/control/v1/validate.sh b/control/v1/validate.sh index 814e305..42047c6 100755 --- a/control/v1/validate.sh +++ b/control/v1/validate.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/bash set -uo pipefail export LC_ALL=C umask 077 diff --git a/scripts/test/control-policy-set.test.sh b/scripts/test/control-policy-set.test.sh index a1fa3e5..6632d97 100755 --- a/scripts/test/control-policy-set.test.sh +++ b/scripts/test/control-policy-set.test.sh @@ -220,24 +220,32 @@ ambient_err="$tmp/ambient.err" pass 'ambient cwd and environment independence' relative_cwd="$tmp/relative-cwd" -relative_sentinel="$tmp/relative-jq-ran" +relative_jq_sentinel="$tmp/relative-jq-ran" +relative_bash_sentinel="$tmp/relative-bash-ran" /bin/mkdir -m 700 "$relative_cwd" /usr/bin/printf '%s\n' \ '#!/bin/sh' \ ': > "${YSTACK_FAKE_JQ_SENTINEL:?}"' \ 'if [ "${1:-}" = --version ]; then printf "%s\n" jq-1.6; fi' \ 'exit 0' > "$relative_cwd/jq" -/bin/chmod 0700 "$relative_cwd/jq" +/usr/bin/printf '%s\n' \ + '#!/bin/sh' \ + ': > "${YSTACK_FAKE_BASH_SENTINEL:?}"' \ + 'printf "%s\n" E_RUNTIME >&2' \ + 'exit 1' > "$relative_cwd/bash" +/bin/chmod 0700 "$relative_cwd/jq" "$relative_cwd/bash" relative_out="$tmp/relative-path.out" relative_err="$tmp/relative-path.err" relative_status=0 -(cd "$relative_cwd" && YSTACK_FAKE_JQ_SENTINEL="$relative_sentinel" \ +(cd "$relative_cwd" && YSTACK_FAKE_JQ_SENTINEL="$relative_jq_sentinel" \ + YSTACK_FAKE_BASH_SENTINEL="$relative_bash_sentinel" \ PATH=".:$bin:/usr/bin:/bin" "$validator" validate "$valid" \ > "$relative_out" 2> "$relative_err") || relative_status=$? [ "$relative_status" -ne 0 ] && [ ! -s "$relative_out" ] && [ "$(/bin/cat "$relative_err")" = E_RUNTIME ] && - [ ! -e "$relative_sentinel" ] || fail 'relative jq path' -pass 'relative jq path is rejected before execution' + [ ! -e "$relative_bash_sentinel" ] && [ ! -e "$relative_jq_sentinel" ] || + fail 'relative interpreter or jq path' +pass 'relative interpreter and jq paths are rejected before execution' for required in control/v1/policy-set.jq control/v1/validate.sh scripts/test/control-policy-set.test.sh; do [ "$(/usr/bin/grep -Fxc "$required" "$root/ci/required-files.txt")" -eq 1 ] ||