J94/bit-vector-tensor-control-policy
0
1#!/usr/bin/env bash2set -euo pipefail3 4ROOT="$(cd "$(dirname "$0")/.." && pwd)"5MANIFEST_PATH="${1:-}"6POLICY_PATH="$ROOT/policy/exec_allowlist_v0.json"7 8if [ -z "$MANIFEST_PATH" ]; then9 echo "usage: ./runtime/execute_manifest.sh <manifest.json>" >&210 exit 111fi12 13if [ ! -f "$MANIFEST_PATH" ]; then14 echo "manifest not found: $MANIFEST_PATH" >&215 exit 116fi17 18MANIFEST_ABS="$(cd "$(dirname "$MANIFEST_PATH")" && pwd)/$(basename "$MANIFEST_PATH")"19INPUT_SHA256="$(shasum -a 256 "$MANIFEST_ABS" | awk '{print $1}')"20DOC_KIND="$(jq -r 'if .version == "work_manifest_v0" then "work_manifest" elif has("task_id") and has("operations") then "utir" else "unknown" end' "$MANIFEST_ABS")"21if [ "$DOC_KIND" = "unknown" ]; then22 echo "unsupported manifest packet: $MANIFEST_ABS" >&223 exit 124fi25 26DOC_ID="$(jq -r 'if has("manifest_id") then .manifest_id else .task_id end' "$MANIFEST_ABS")"27DOC_GOAL="$(jq -r 'if has("goal") then .goal else .description end' "$MANIFEST_ABS")"28DOC_LANE="$(jq -r 'if has("lane") then .lane else "execution" end' "$MANIFEST_ABS")"29RUN_ID="$(date -u +"%Y%m%dT%H%M%SZ")-$DOC_ID"30OUT_DIR="$ROOT/runs/runtime/$RUN_ID"31RECEIPT_PATH="$OUT_DIR/receipt.json"32SUMMARY_PATH="$OUT_DIR/summary.json"33GRAPH_PATH="$OUT_DIR/graph_state.json"34EFFECTS_NDJSON="$OUT_DIR/effects.ndjson"35 36mkdir -p "$OUT_DIR"37: > "$EFFECTS_NDJSON"38 39if [ "$DOC_KIND" = "work_manifest" ]; then40 EXECUTION_GATE="$(jq -r '.execution_gate' "$MANIFEST_ABS")"41 if [ "$EXECUTION_GATE" != "true" ]; then42 echo "execution gate is closed in manifest: $MANIFEST_ABS" >&243 exit 144 fi45fi46 47append_effect() {48 printf '%s\n' "$1" >> "$EFFECTS_NDJSON"49}50 51file_sha_or_null() {52 local file="$1"53 if [ -s "$file" ]; then54 shasum -a 256 "$file" | awk '{print $1}'55 else56 echo ""57 fi58}59 60is_command_allowed() {61 local cmd="$1"62 local first_word63 first_word="$(printf '%s' "$cmd" | awk '{print $1}')"64 if [ -z "$first_word" ]; then65 return 166 fi67 if jq -e --arg cmd "$first_word" '.allowed_commands | index($cmd)' "$POLICY_PATH" >/dev/null; then68 return 069 fi70 return 171}72 73has_blocked_pattern() {74 local cmd="$1"75 while IFS= read -r pattern; do76 [ -z "$pattern" ] && continue77 case "$cmd" in78 *"$pattern"*) return 0 ;;79 esac80 done < <(jq -r '.blocked_patterns[]' "$POLICY_PATH")81 return 182}83 84is_path_allowed() {85 local rel="$1"86 while IFS= read -r prefix; do87 [ -z "$prefix" ] && continue88 case "$rel" in89 "$prefix"*) return 0 ;;90 esac91 done < <(jq -r '.sandbox_prefixes[]' "$POLICY_PATH")92 return 193}94 95ACTION_COUNT="$(jq '.actions | length' "$MANIFEST_ABS")"96if [ "$DOC_KIND" = "utir" ]; then97 ACTION_COUNT="$(jq '.operations | length' "$MANIFEST_ABS")"98fi99 100normalize_type() {101 local raw="$1"102 case "$DOC_KIND:$raw" in103 work_manifest:write_file) echo "write_file" ;;104 work_manifest:exec) echo "exec" ;;105 utir:fs.write) echo "write_file" ;;106 utir:shell) echo "exec" ;;107 *) echo "unsupported" ;;108 esac109}110 111if [ "$ACTION_COUNT" -eq 0 ]; then112 echo "no actions to execute: $MANIFEST_ABS" >&2113 exit 1114fi115 116for idx in $(seq 0 $((ACTION_COUNT - 1))); do117 ACTION="$(jq -c "if \"$DOC_KIND\" == \"work_manifest\" then .actions[$idx] else .operations[$idx] end" "$MANIFEST_ABS")"118 RAW_TYPE="$(printf '%s' "$ACTION" | jq -r '.type')"119 TYPE="$(normalize_type "$RAW_TYPE")"120 121 case "$TYPE" in122 write_file)123 REL_PATH="$(printf '%s' "$ACTION" | jq -r '.path')"124 if ! is_path_allowed "$REL_PATH"; then125 append_effect "$(jq -n \126 --arg path "$REL_PATH" \127 '{kind:"blocked",op:("write_file:" + $path),reason:"path blocked by local policy"}')"128 continue129 fi130 CONTENT="$(printf '%s' "$ACTION" | jq -r '.content')"131 TARGET_PATH="$ROOT/$REL_PATH"132 mkdir -p "$(dirname "$TARGET_PATH")"133 printf '%s' "$CONTENT" > "$TARGET_PATH"134 BYTES="$(wc -c < "$TARGET_PATH" | tr -d ' ')"135 SHA="$(shasum -a 256 "$TARGET_PATH" | awk '{print $1}')"136 append_effect "$(jq -n \137 --arg path "$REL_PATH" \138 --arg sha256 "$SHA" \139 --argjson bytes "$BYTES" \140 '{kind:"write_file",path:$path,bytes:$bytes,sha256:$sha256,ok:true,error:null}')"141 ;;142 exec)143 CMD="$(printf '%s' "$ACTION" | jq -r 'if has("cmd") then .cmd else .command end')"144 if has_blocked_pattern "$CMD"; then145 append_effect "$(jq -n \146 --arg cmd "$CMD" \147 '{kind:"blocked",op:("exec:" + $cmd),reason:"command blocked by local policy pattern"}')"148 elif ! is_command_allowed "$CMD"; then149 append_effect "$(jq -n \150 --arg cmd "$CMD" \151 '{kind:"blocked",op:("exec:" + $cmd),reason:"command not present in local allowlist"}')"152 else153 STDOUT_FILE="$OUT_DIR/exec-$idx.stdout"154 STDERR_FILE="$OUT_DIR/exec-$idx.stderr"155 set +e156 sh -lc "$CMD" >"$STDOUT_FILE" 2>"$STDERR_FILE"157 STATUS="$?"158 set -e159 STDOUT_SHA="$(file_sha_or_null "$STDOUT_FILE")"160 STDERR_SHA="$(file_sha_or_null "$STDERR_FILE")"161 append_effect "$(jq -n \162 --arg cmd "$CMD" \163 --argjson ok "$([ "$STATUS" -eq 0 ] && echo true || echo false)" \164 --argjson status "$STATUS" \165 --arg stdout_sha256 "$STDOUT_SHA" \166 --arg stderr_sha256 "$STDERR_SHA" \167 '{168 kind:"exec",169 cmd:$cmd,170 ok:$ok,171 status:$status,172 stdout_sha256:(if $stdout_sha256 == "" then null else $stdout_sha256 end),173 stderr_sha256:(if $stderr_sha256 == "" then null else $stderr_sha256 end),174 error:(if $ok then null else "command failed" end)175 }')"176 fi177 ;;178 *)179 append_effect "$(jq -n \180 --arg op "$RAW_TYPE" \181 '{kind:"blocked",op:$op,reason:"unsupported action type for local runtime"}')"182 ;;183 esac184done185 186EFFECTS_JSON="$(jq -s '.' "$EFFECTS_NDJSON")"187 188jq -n \189 --arg version "receipt_v1" \190 --argjson deterministic false \191 --arg input_sha256 "$INPUT_SHA256" \192 --argjson effects "$EFFECTS_JSON" \193 '{194 version:$version,195 deterministic:$deterministic,196 input_sha256:$input_sha256,197 generated_at_ms:null,198 effects:$effects199 }' > "$RECEIPT_PATH"200 201jq -n \202 --arg manifest "$MANIFEST_ABS" \203 --arg receipt "$RECEIPT_PATH" \204 --arg graph "$GRAPH_PATH" \205 --arg run_id "$RUN_ID" \206 --arg doc_kind "$DOC_KIND" \207 --argjson action_count "$ACTION_COUNT" \208 '{209 run_id:$run_id,210 manifest:$manifest,211 document_kind:$doc_kind,212 receipt:$receipt,213 graph_state:$graph,214 action_count:$action_count215 }' > "$SUMMARY_PATH"216 217jq -n \218 --arg run_id "$RUN_ID" \219 --arg manifest_id "$DOC_ID" \220 --arg goal "$DOC_GOAL" \221 --arg lane "$DOC_LANE" \222 --arg receipt "$RECEIPT_PATH" \223 --argjson execution_allowed true \224 --arg doc_kind "$DOC_KIND" \225 --argjson effects "$EFFECTS_JSON" \226 '{227 run_id:$run_id,228 manifest_id:$manifest_id,229 document_kind:$doc_kind,230 goal:$goal,231 lane:$lane,232 execution_gate:{allowed:$execution_allowed},233 receipt:$receipt,234 effects:$effects235 }' > "$GRAPH_PATH"236 237printf '%s\n' "$RECEIPT_PATH"238printf '%s\n' "$SUMMARY_PATH"239 