fix(effort): harden lib/effort-pins.sh (security gate, 4 LOW)
Last map line without newline read; unclosed frontmatter skipped with an err; level re-read after write, mismatch counted as failed; mktemp + cp -p + mv, temp removed on failure; rc 1 on any rejected or failed entry. Cases T11-T14 in the fixture suite; contract criteria 8-9.
This commit is contained in:
+47
-14
@@ -6,12 +6,16 @@
|
||||
# it back after the last vendoring step of install-plugins.sh and
|
||||
# update-all.sh. Idempotent: same level → untouched, other level →
|
||||
# replaced inside the frontmatter only, skill not vendored → skipped,
|
||||
# malformed map line → rejected loudly, never applied. Placement inside the
|
||||
# malformed map line → rejected loudly, never applied. Four hardenings:
|
||||
# a map whose last line lacks a newline is still read; a SKILL.md whose
|
||||
# frontmatter never closes is skipped untouched; the level is re-read after
|
||||
# every write and a mismatch (CRLF, malformed) counts as failed; the write
|
||||
# goes through a mktemp sibling removed on any failure. Placement inside the
|
||||
# frontmatter has no effect on the harness, which reads the key anywhere.
|
||||
#
|
||||
# Usage: source it, then `apply_effort_pins [repo-root]`
|
||||
# or standalone: bash lib/effort-pins.sh [repo-root]
|
||||
# Exit 1 when at least one map line was rejected.
|
||||
# Exit 1 when at least one map line was rejected or a skill failed.
|
||||
|
||||
EFFORT_PINS_REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
EFFORT_PIN_LEVEL_RE='^(low|medium|high|xhigh|max)$'
|
||||
@@ -29,12 +33,19 @@ _effort_pin_current() {
|
||||
| sed -n 's/^effort: //p' | head -1
|
||||
}
|
||||
|
||||
# _effort_pin_closed <skill-file> → rc 0 when the frontmatter has a closing ---
|
||||
_effort_pin_closed() {
|
||||
awk 'NR==1&&/^---$/{p=1;next} p&&/^---$/{f=1;exit} END{exit !f}' "$1"
|
||||
}
|
||||
|
||||
# _effort_pin_write <skill-file> <name> <level> — replace the frontmatter
|
||||
# `effort:` line, or insert one after `name: <name>` (before the closing
|
||||
# `---` when the frontmatter has no name line). Body lines never change.
|
||||
# Writes a mktemp sibling then renames; any failure leaves no temp behind.
|
||||
_effort_pin_write() {
|
||||
local file="$1" name="$2" level="$3"
|
||||
awk -v n="$name" -v lvl="$level" '
|
||||
local file="$1" name="$2" level="$3" tmp
|
||||
tmp="$(mktemp "$file.XXXXXX")" || return 1
|
||||
cp -p "$file" "$tmp" && awk -v n="$name" -v lvl="$level" '
|
||||
NR==1 && /^---$/ { fm=1; print; next }
|
||||
fm && /^---$/ {
|
||||
if (!done) { print "effort: " lvl; done=1 }
|
||||
@@ -43,16 +54,36 @@ _effort_pin_write() {
|
||||
fm && /^effort: / { if (!done) { print "effort: " lvl; done=1 }; next }
|
||||
fm && $0 == "name: " n { print; if (!done) { print "effort: " lvl; done=1 }; next }
|
||||
{ print }
|
||||
' "$file" > "$file.tmp" && mv "$file.tmp" "$file"
|
||||
' "$file" > "$tmp" && mv "$tmp" "$file" && return 0
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
}
|
||||
|
||||
# _effort_pin_apply_one <file> <name> <level> → rc 0 applied, 2 already at
|
||||
# level, 1 failed (err line printed, file untouched or write rolled back)
|
||||
_effort_pin_apply_one() {
|
||||
local file="$1" name="$2" level="$3"
|
||||
if ! _effort_pin_closed "$file"; then
|
||||
err "effort-pins: $file: frontmatter never closed — skipped"; return 1
|
||||
fi
|
||||
[ "$(_effort_pin_current "$file")" = "$level" ] && return 2
|
||||
if ! _effort_pin_write "$file" "$name" "$level"; then
|
||||
err "effort-pins: $file: write failed"; return 1
|
||||
fi
|
||||
if [ "$(_effort_pin_current "$file")" != "$level" ]; then
|
||||
err "effort-pins: $file: level not applied (CRLF or malformed frontmatter?)"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# apply_effort_pins [repo-root] — walk the map, pin every vendored skill
|
||||
apply_effort_pins() {
|
||||
local repo="${1:-$EFFORT_PINS_REPO}" map name level rest file
|
||||
local applied=0 kept=0 rejected=0
|
||||
local repo="${1:-$EFFORT_PINS_REPO}" map name level rest file rc
|
||||
local applied=0 kept=0 rejected=0 failed=0
|
||||
map="$repo/lib/effort-pins.txt"
|
||||
[ -f "$map" ] || { err "effort-pins: map missing: $map"; return 1; }
|
||||
while read -r name level rest; do
|
||||
while read -r name level rest || [ -n "$name" ]; do
|
||||
case "$name" in ''|'#'*) continue ;; esac
|
||||
if [ -n "$rest" ] || ! [[ "$name" =~ $EFFORT_PIN_NAME_RE ]] \
|
||||
|| ! [[ "$level" =~ $EFFORT_PIN_LEVEL_RE ]]; then
|
||||
@@ -61,13 +92,15 @@ apply_effort_pins() {
|
||||
fi
|
||||
file="$repo/skills-external/$name/SKILL.md"
|
||||
[ -f "$file" ] || continue
|
||||
if [ "$(_effort_pin_current "$file")" = "$level" ]; then
|
||||
kept=$((kept + 1)); continue
|
||||
fi
|
||||
_effort_pin_write "$file" "$name" "$level" && applied=$((applied + 1))
|
||||
_effort_pin_apply_one "$file" "$name" "$level"; rc=$?
|
||||
case "$rc" in
|
||||
0) applied=$((applied + 1)) ;;
|
||||
2) kept=$((kept + 1)) ;;
|
||||
*) failed=$((failed + 1)) ;;
|
||||
esac
|
||||
done < "$map"
|
||||
ok "effort-pins: $applied applied, $kept already at level"
|
||||
[ "$rejected" -eq 0 ]
|
||||
ok "effort-pins: $applied applied, $kept already at level, $failed failed"
|
||||
[ "$rejected" -eq 0 ] && [ "$failed" -eq 0 ]
|
||||
}
|
||||
|
||||
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
||||
|
||||
Reference in New Issue
Block a user