Merge branch 'stack-oid-validation'
commit 2df663fec9d0fba880b97198c4a163620efe9f0b · parent 00e17472b7 · parent 14dda3a13d
· browse this tree
warpfork, 2026-10-21 16:13:03 UTC
diff --git a/bin/patchette b/bin/patchette index d17e45b..46e9d49 100755 --- a/bin/patchette +++ b/bin/patchette @@ -95,8 +95,9 @@ absdir() { ## - @param $1 path to the STACK file ## - @state STACK_REF, STACK_BASE, STACK_TIP, STACK_SERIES[] -- the parsed fields parse_stack() { - local f=$1 line mode=head - declare -A seen=() + local f=$1 line mode=head key + local oid='^[0-9a-f]{40}([0-9a-f]{24})?$' # SHA-1 or SHA-256, full and lowercase + declare -A seen=() keys_seen=() STACK_REF='' STACK_BASE='' STACK_TIP='' STACK_SERIES=() while IFS= read -r line; do if [[ $mode == series ]]; then @@ -106,10 +107,19 @@ parse_stack() { STACK_SERIES+=("$line") continue fi + key=${line%% *} + [[ -z ${keys_seen[$key]:-} ]] || die "$f: duplicate $key line" + keys_seen[$key]=1 case $line in "ref "*) STACK_REF=${line#ref } ;; - "base "*) STACK_BASE=${line#base } ;; - "tip "*) STACK_TIP=${line#tip } ;; + "base "*) + STACK_BASE=${line#base } + [[ $STACK_BASE =~ $oid ]] || die "$f: base is not a full commit ID: $STACK_BASE" + ;; + "tip "*) + STACK_TIP=${line#tip } + [[ $STACK_TIP =~ $oid ]] || die "$f: tip is not a full commit ID: $STACK_TIP" + ;; series) mode=series ;; *) die "$f: unrecognized line: $line" ;; esac diff --git a/docs/hacking/FORMAT.md b/docs/hacking/FORMAT.md index 96950bd..07f9df8 100644 --- a/docs/hacking/FORMAT.md +++ b/docs/hacking/FORMAT.md @@ -97,6 +97,7 @@ fix-the-frobnicator-2 Several pieces of `key value` format data start the file, then a literal `series` line denotes the end of that section, and then one patch file name is per line, until EOF. +Each key appears at most once. Of the key value entries: @@ -110,6 +111,10 @@ Of the key value entries: It may also be disregarded in some workflows: e.g., when hand-editing patch files, there's no expectable value for tip until the branch has been built. +The `base` and `tip` values are full commit IDs in lowercase hex: +40 characters in a SHA-1 repo, 64 in a SHA-256 one. +Abbreviations and ref names are errors (they can resolve differently from repo to repo, or over time). + In the `series` section: - each line is a patch name. These match `^[a-z0-9-]+$`, diff --git a/tests/test-failclosed.sh b/tests/test-failclosed.sh index 6c77dd5..31a0164 100755 --- a/tests/test-failclosed.sh +++ b/tests/test-failclosed.sh @@ -71,6 +71,27 @@ sed -i '1s/.*/parents no-such-slug/' "$TAMPER/$fancy/add-sprocket-docs.patch" EXPECT refusal "an unknown parent reference is an error" "not BASE or an earlier slug" -- \ "$PATCHETTE" verify "$work/corpus/dev" "$TAMPER" +# base and tip must be full commit IDs: a ref name or an abbreviation could +# resolve differently in another repo, or later, so neither is guessed at. +clone_layout refbase +sed -i 's/^base .*/base main/' "$TAMPER/$fancy/STACK" +EXPECT refusal "a ref name as base is an error" "base is not a full commit ID" -- \ + "$PATCHETTE" verify "$work/corpus/dev" "$TAMPER" +clone_layout shorttip +sed -i 's/^tip \(.\{12\}\).*/tip \1/' "$TAMPER/$fancy/STACK" +EXPECT refusal "an abbreviated tip is an error" "tip is not a full commit ID" -- \ + "$PATCHETTE" verify "$work/corpus/dev" "$TAMPER" +clone_layout emptytip +sed -i 's/^tip .*/tip /' "$TAMPER/$fancy/STACK" +EXPECT refusal "a tip line with no value is an error, not an absent tip" "tip is not a full commit ID" -- \ + "$PATCHETTE" verify "$work/corpus/dev" "$TAMPER" + +# A repeated STACK key is an error: which value is meant can't be known. +clone_layout dupkey +sed -i '1a ref refs/heads/other' "$TAMPER/$fancy/STACK" +EXPECT refusal "a repeated STACK key is an error" "duplicate ref line" -- \ + "$PATCHETTE" verify "$work/corpus/dev" "$TAMPER" + # A duplicate series entry is an error. clone_layout dupseries echo "note-to-self" >>"$TAMPER/$fancy/STACK"