fix(e2e): confine scenario card validation

Reuse the bounded fence filter for every scenario-card structural check so headings, coverage text, and falsification prose inside examples cannot satisfy the gate. Valid cards may still contain unrelated fenced examples.\n\nTreat each Card cell as a filename stem: remove at most one enclosing backtick pair, require lowercase kebab case, and reject invalid values before they can be stored or joined to the cards directory. This blocks parent traversal, subdirectories, uppercase names, and empty segments.\n\nAdd real-process RED/GREEN coverage for fenced-only cards, traversal, invalid stems, preserved backtick behavior, and the explicit diagnostic for intentionally unsupported pipe-less tables.
This commit is contained in:
Drew Ritter
2026-08-06 14:50:46 -07:00
parent 2a2e0b41c2
commit 7dff0b2d48
2 changed files with 89 additions and 6 deletions

View File

@@ -73,7 +73,7 @@ expected_section() {
if (insec) exit
}
insec { print }
' "$1"
'
}
# --- extract the first table under the (case-insensitive) heading ----------
@@ -90,6 +90,7 @@ TABLE="$(awk '
if [ -z "$TABLE" ]; then
echo "no scenario table: $SPEC has no \"E2E scenario cards\" heading with a table under it" >&2
echo "(heading must be exactly \"E2E scenario cards\" — no numbering or extra words)" >&2
echo "(scenario table rows must use leading and trailing outer pipes)" >&2
exit 2
fi
@@ -121,7 +122,6 @@ while IFS= read -r line; do
c="$(printf '%s' "$c" | normalize)"
trimmed+=("$c")
done
# cells[0] is empty (before first |); last may be empty too
if [ "$lineno" -eq 1 ]; then
for i in "${!trimmed[@]}"; do
low="$(printf '%s' "${trimmed[$i]}" | tr '[:upper:]' '[:lower:]')"
@@ -153,11 +153,20 @@ while IFS= read -r line; do
fi
card="${trimmed[$CARD_COL]:-}"
falsif="${trimmed[$FALS_COL]:-}"
card="${card//\`/}" # tolerate `card-name` backticks in the cell
if [ -z "$card" ] || [ -z "$falsif" ]; then
fail "row $lineno: empty Card or Falsification cell"
continue
fi
case "$card" in
\`*\`)
card="${card#\`}"
card="${card%\`}"
;;
esac
if ! [[ "$card" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
fail "row $lineno: invalid Card value: $card"
continue
fi
ROW_CARD[$ROWS]="$card"; ROW_FALS[$ROWS]="$falsif"; ROWS=$((ROWS + 1))
done <<< "$TABLE"
@@ -174,15 +183,16 @@ while [ "$i" -lt "$ROWS" ]; do
fail "missing card file: $f"
i=$((i + 1)); continue
fi
hay="$(expected_section "$f" | normalize)"
card_text="$(without_fenced_code "$f")"
hay="$(printf '%s\n' "$card_text" | expected_section | normalize)"
case "$hay" in
*"$falsif"*) : ;;
*) fail "$f: falsification line not present verbatim in the ## Expected section.
expected (normalized): $falsif" ;;
esac
grep -q '\*\*What this covers\*\*' "$f" || fail "$f: missing **What this covers**"
grep -q '\*\*What this covers\*\*' <<< "$card_text" || fail "$f: missing **What this covers**"
for sec in Pre-state Steps Expected Cleanup; do
grep -Eiq "^#{2,}[[:space:]]*${sec}[[:space:]]*$" "$f" || fail "$f: missing ## ${sec} section"
grep -Eiq "^#{2,}[[:space:]]*${sec}[[:space:]]*$" <<< "$card_text" || fail "$f: missing ## ${sec} section"
done
i=$((i + 1))
done