kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

run.sh (9341B)


      1 #!/bin/sh
      2 # Driver-level checks for the shared kit CAS/tree utilities.
      3 
      4 set -u
      5 
      6 script_dir=$(cd "$(dirname "$0")" && pwd)
      7 repo_root=$(cd "$script_dir/../.." && pwd)
      8 
      9 KIT="${KIT:-$repo_root/build/kit}"
     10 
     11 if [ ! -x "$KIT" ]; then
     12     echo "cas: kit binary not found at $KIT" >&2
     13     exit 2
     14 fi
     15 
     16 work=$(mktemp -d "${TMPDIR:-/tmp}/kit-cas-test.XXXXXX")
     17 trap 'rm -rf "$work"' EXIT
     18 
     19 mkdir -p "$work/src/bin" "$work/src/share" "$work/cas" "$work/out"
     20 
     21 # Type-K mode-P kit: ok/run_ok/run_fail/contains/same_file/... + the CAS
     22 # helpers (first_hex_id/cas_object_path/tree_blob_for_path), all recording
     23 # through the unified kit_* counters over $work.
     24 KIT_KIT_DIR="$repo_root/test/lib"
     25 . "$repo_root/test/lib/kit_sh_kit.sh"
     26 kit_report_init
     27 
     28 make_fixtures() {
     29     printf alpha > "$work/src/share/a.txt"
     30     cp "$work/src/share/a.txt" "$work/src/share/a-copy.txt"
     31     : > "$work/src/share/empty.dat"
     32     {
     33         printf '#!/bin/sh\n'
     34         printf 'printf "hello from cas\\n"\n'
     35     } > "$work/src/bin/tool.sh"
     36     chmod +x "$work/src/bin/tool.sh"
     37 }
     38 
     39 make_fixtures
     40 
     41 run_ok "cas-add-blob" "$KIT" cas add-blob --cas "$work/cas" "$work/src/share/a.txt"
     42 blob_id=$(first_hex_id "$work/cas-add-blob.out")
     43 if [ -n "$blob_id" ]; then
     44     ok "cas-add-blob-id"
     45 else
     46     echo "could not parse blob id from add-blob output" > "$work/cas-add-blob-id.diag"
     47     not_ok "cas-add-blob-id" "$work/cas-add-blob-id.diag"
     48 fi
     49 blob_file=$(cas_object_path "$work/cas" blob "$blob_id")
     50 assert_file_exists "cas-add-blob-object" "$blob_file"
     51 same_file "cas-add-blob-content" "$work/src/share/a.txt" "$blob_file"
     52 
     53 run_ok "cas-add-tree-root" "$KIT" cas add-tree --cas "$work/cas" --root "$work/src"
     54 tree_id=$(first_hex_id "$work/cas-add-tree-root.out")
     55 if [ -n "$tree_id" ]; then
     56     ok "cas-add-tree-id"
     57 else
     58     echo "could not parse tree id from add-tree output" > "$work/cas-add-tree-id.diag"
     59     not_ok "cas-add-tree-id" "$work/cas-add-tree-id.diag"
     60 fi
     61 tree_file=$(cas_object_path "$work/cas" tree "$tree_id")
     62 assert_file_exists "cas-add-tree-object" "$tree_file"
     63 contains "cas-tree-magic" "$tree_file" "kit-tree 1"
     64 contains "cas-tree-hash" "$tree_file" "hash = blake2b-256"
     65 contains "cas-tree-blob-format" "$tree_file" "blob = kit-blob-v1"
     66 contains "cas-tree-regular-path" "$tree_file" "path = share/a.txt"
     67 contains "cas-tree-exec-path" "$tree_file" "path = bin/tool.sh"
     68 contains "cas-tree-file-mode" "$tree_file" "mode = -"
     69 contains "cas-tree-exec-mode" "$tree_file" "mode = x"
     70 
     71 run_ok "cas-inspect-tree" "$KIT" cas inspect-tree --cas "$work/cas" "$tree_id"
     72 contains "cas-inspect-tree-magic" "$work/cas-inspect-tree.out" "kit-tree 1"
     73 contains "cas-inspect-tree-path" "$work/cas-inspect-tree.out" "path = share/a.txt"
     74 
     75 run_ok "cas-verify-tree" "$KIT" cas verify-tree --cas "$work/cas" "$tree_id"
     76 
     77 run_ok "cas-materialize-tree" "$KIT" cas materialize --cas "$work/cas" "$tree_id" -C "$work/out/root"
     78 same_file "cas-materialize-a" "$work/src/share/a.txt" "$work/out/root/share/a.txt"
     79 same_file "cas-materialize-a-copy" "$work/src/share/a-copy.txt" "$work/out/root/share/a-copy.txt"
     80 same_file "cas-materialize-empty" "$work/src/share/empty.dat" "$work/out/root/share/empty.dat"
     81 same_file "cas-materialize-tool" "$work/src/bin/tool.sh" "$work/out/root/bin/tool.sh"
     82 is_executable "cas-materialize-exec-mode" "$work/out/root/bin/tool.sh"
     83 
     84 cat > "$work/map.txt" <<EOF
     85 mapped/tool.sh x $work/src/bin/tool.sh
     86 mapped/a.txt - $work/src/share/a.txt
     87 EOF
     88 run_ok "cas-add-tree-map" "$KIT" cas add-tree --cas "$work/cas" --map "$work/map.txt"
     89 map_tree_id=$(first_hex_id "$work/cas-add-tree-map.out")
     90 if [ -n "$map_tree_id" ]; then
     91     ok "cas-add-tree-map-id"
     92 else
     93     echo "could not parse map tree id from add-tree output" > "$work/cas-add-tree-map-id.diag"
     94     not_ok "cas-add-tree-map-id" "$work/cas-add-tree-map-id.diag"
     95 fi
     96 run_ok "cas-verify-map-tree" "$KIT" cas verify-tree --cas "$work/cas" "$map_tree_id"
     97 run_ok "cas-materialize-map-tree" "$KIT" cas materialize --cas "$work/cas" "$map_tree_id" -C "$work/out/map"
     98 same_file "cas-map-materialize-a" "$work/src/share/a.txt" "$work/out/map/mapped/a.txt"
     99 same_file "cas-map-materialize-tool" "$work/src/bin/tool.sh" "$work/out/map/mapped/tool.sh"
    100 is_executable "cas-map-materialize-exec-mode" "$work/out/map/mapped/tool.sh"
    101 
    102 cat > "$work/dup.map" <<EOF
    103 same.txt - $work/src/share/a.txt
    104 same.txt - $work/src/share/a-copy.txt
    105 EOF
    106 run_fail "cas-add-tree-duplicate-path-fails" "$KIT" cas add-tree --cas "$work/cas" --map "$work/dup.map"
    107 
    108 cat > "$work/unsafe.map" <<EOF
    109 ../escape.txt - $work/src/share/a.txt
    110 EOF
    111 run_fail "cas-add-tree-unsafe-path-fails" "$KIT" cas add-tree --cas "$work/cas" --map "$work/unsafe.map"
    112 
    113 cat > "$work/absolute.map" <<EOF
    114 /absolute.txt - $work/src/share/a.txt
    115 EOF
    116 run_fail "cas-add-tree-absolute-path-fails" "$KIT" cas add-tree --cas "$work/cas" --map "$work/absolute.map"
    117 
    118 cat > "$work/empty-component.map" <<EOF
    119 bad//component.txt - $work/src/share/a.txt
    120 EOF
    121 run_fail "cas-add-tree-empty-component-fails" "$KIT" cas add-tree --cas "$work/cas" --map "$work/empty-component.map"
    122 
    123 cat > "$work/dot-component.map" <<EOF
    124 bad/./component.txt - $work/src/share/a.txt
    125 EOF
    126 run_fail "cas-add-tree-dot-component-fails" "$KIT" cas add-tree --cas "$work/cas" --map "$work/dot-component.map"
    127 
    128 cat > "$work/colon.map" <<EOF
    129 bad:name.txt - $work/src/share/a.txt
    130 EOF
    131 run_fail "cas-add-tree-colon-path-fails" "$KIT" cas add-tree --cas "$work/cas" --map "$work/colon.map"
    132 
    133 if [ -n "$tree_id" ]; then
    134     cp -R "$work/cas" "$work/bad-cas-mutated"
    135     a_blob=$(tree_blob_for_path "$tree_file" "share/a.txt")
    136     printf tamper >> "$(cas_object_path "$work/bad-cas-mutated" blob "$a_blob")"
    137     run_fail "cas-verify-mutated-blob-fails" "$KIT" cas verify-tree --cas "$work/bad-cas-mutated" "$tree_id"
    138     run_fail "cas-materialize-mutated-blob-fails" "$KIT" cas materialize --cas "$work/bad-cas-mutated" "$tree_id" -C "$work/out/bad-mutated"
    139 
    140     cp -R "$work/cas" "$work/bad-cas-missing"
    141     rm -f "$(cas_object_path "$work/bad-cas-missing" blob "$a_blob")"
    142     run_fail "cas-verify-missing-blob-fails" "$KIT" cas verify-tree --cas "$work/bad-cas-missing" "$tree_id"
    143 
    144     mkdir -p "$work/empty-cas"
    145     run_fail "cas-verify-missing-tree-fails" "$KIT" cas verify-tree --cas "$work/empty-cas" "$tree_id"
    146 else
    147     skip_test "cas-corruption-tests"
    148 fi
    149 
    150 # ---------------------------------------------------------------------------
    151 # Read-side adversarial trees. A CAS tree object is untrusted input (it may be
    152 # fetched from a remote or unpacked from a package). Crafting a malicious tree
    153 # object directly — bypassing `cas add-tree`, which validates paths at creation
    154 # — must still be rejected at materialize/verify time. This exercises the
    155 # read-path validator (dist_tree_path_valid) and the tree KV NUL-byte check,
    156 # the real attack surface for an untrusted tree.
    157 # ---------------------------------------------------------------------------
    158 blake2b_id() { "$KIT" hash -a blake2b "$1" | awk '{print $1}'; }
    159 
    160 # Store a hand-crafted tree object under its own content id; echoes the id.
    161 place_tree_object() {
    162     src=$1
    163     id=$(blake2b_id "$src")
    164     dest=$(cas_object_path "$work/cas" tree "$id")
    165     mkdir -p "$(dirname "$dest")"
    166     cp "$src" "$dest"
    167     printf '%s\n' "$id"
    168 }
    169 
    170 printf 'good/ok.txt - %s/src/share/a.txt\n' "$work" > "$work/adv.map"
    171 run_ok "cas-adv-base-tree" "$KIT" cas add-tree --cas "$work/cas" --map "$work/adv.map"
    172 adv_tree_id=$(first_hex_id "$work/cas-adv-base-tree.out")
    173 if [ -n "$adv_tree_id" ]; then
    174     adv_base=$(cas_object_path "$work/cas" tree "$adv_tree_id")
    175 
    176     # 1) parent-dir traversal: reject, and write nothing outside the -C target.
    177     sed 's#path = good/ok.txt#path = ../escape.txt#' "$adv_base" > "$work/adv-traverse.tree"
    178     tid=$(place_tree_object "$work/adv-traverse.tree")
    179     run_fail "cas-materialize-traversal-rejected" \
    180         "$KIT" cas materialize --cas "$work/cas" "$tid" -C "$work/out/adv-traverse"
    181     if [ -e "$work/out/escape.txt" ]; then
    182         echo "materialize escaped the target directory via ../" \
    183             > "$work/cas-materialize-traversal-contained.diag"
    184         not_ok "cas-materialize-traversal-contained" \
    185             "$work/cas-materialize-traversal-contained.diag"
    186     else
    187         ok "cas-materialize-traversal-contained"
    188     fi
    189     run_fail "cas-verify-traversal-rejected" \
    190         "$KIT" cas verify-tree --cas "$work/cas" "$tid"
    191 
    192     # 2) absolute path.
    193     sed 's#path = good/ok.txt#path = /tmp/kit-cas-escape.txt#' "$adv_base" > "$work/adv-abs.tree"
    194     tid=$(place_tree_object "$work/adv-abs.tree")
    195     run_fail "cas-materialize-absolute-rejected" \
    196         "$KIT" cas materialize --cas "$work/cas" "$tid" -C "$work/out/adv-abs"
    197 
    198     # 3) embedded NUL in a path: must be rejected, never silently truncated.
    199     if command -v perl >/dev/null 2>&1; then
    200         perl -pe 's{path = good/ok\.txt}{path = good/ok\0evil.txt}' "$adv_base" \
    201             > "$work/adv-nul.tree"
    202         tid=$(place_tree_object "$work/adv-nul.tree")
    203         run_fail "cas-materialize-nul-rejected" \
    204             "$KIT" cas materialize --cas "$work/cas" "$tid" -C "$work/out/adv-nul"
    205         run_fail "cas-verify-nul-rejected" \
    206             "$KIT" cas verify-tree --cas "$work/cas" "$tid"
    207     else
    208         skip_test "cas-materialize-nul-rejected"
    209         skip_test "cas-verify-nul-rejected"
    210     fi
    211 else
    212     skip_test "cas-read-side-adversarial"
    213 fi
    214 
    215 kit_summary cas
    216 kit_exit