fix(review-loop-run): counts parses annotated cells, declaration-only lines, suspect derived zero (5352fe72-242f-4fef-85a0-b7851a1110e5)
This commit is contained in:
@@ -57,26 +57,75 @@ _counts_scope() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# _counts_extract <scope-text> <sev> → last matching value for SEV in scope,
|
# _is_declaration_line <line> → 0 (true) iff the line is a declaration-only
|
||||||
# accepting: "SEV: n", "SEV = n", "- SEV: n", "**SEV**: n", "| SEV | n |",
|
# line. After stripping `*` emphasis markers, the line must consist solely of
|
||||||
# "SEV (n)", "n SEV". Empty if none found.
|
# declaration tokens (CRITICAL/HIGH/MEDIUM/LOW, any severity) separated by
|
||||||
|
# [.,;\s]+, with nothing else except label-style parentheticals matching
|
||||||
|
# [A-Z][0-9]+ (e.g. "(C1)" labelling the count) which are stripped before the
|
||||||
|
# check. Non-label parentheticals (finding IDs like "(H-R3-1)", annotations
|
||||||
|
# like "(see §3.2)", trailing prose after the count) disqualify the line.
|
||||||
|
_is_declaration_line() {
|
||||||
|
local line="$1"
|
||||||
|
# Strip `*` emphasis markers
|
||||||
|
local clean="${line//\*/}"
|
||||||
|
# Reduce declaration tokens to placeholders (any severity)
|
||||||
|
local reduced
|
||||||
|
reduced=$(printf '%s\n' "$clean" | sed -E 's/(CRITICAL|HIGH|MEDIUM|LOW)[[:space:]]*[:=][[:space:]]*[0-9]+/__TOK__/g')
|
||||||
|
reduced=$(printf '%s\n' "$reduced" | sed -E 's/(CRITICAL|HIGH|MEDIUM|LOW)[[:space:]]*\([0-9]+\)/__TOK__/g')
|
||||||
|
reduced=$(printf '%s\n' "$reduced" | sed -E 's/[0-9]+[[:space:]]+(CRITICAL|HIGH|MEDIUM|LOW)/__TOK__/g')
|
||||||
|
# Strip label-style parentheticals (single uppercase letter + digits)
|
||||||
|
reduced=$(printf '%s\n' "$reduced" | sed -E 's/[[:space:]]*\([A-Z][0-9]+\)//g')
|
||||||
|
# Remaining must be only separators + __TOK__s.
|
||||||
|
# Use a variable for the pattern to keep bash from mis-parsing the
|
||||||
|
# embedded `;` and `[[:space:]]` inside `[[ =~ ]]`.
|
||||||
|
local re='^[[:space:].;,]*__TOK__([.,;[:space:]]+__TOK__)*[[:space:].;,]*$'
|
||||||
|
[[ "$reduced" =~ $re ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# _counts_extract <scope-text> <sev> → last matching value for SEV in scope.
|
||||||
|
# CR-7: Table rows accept annotated value cells ("| SEV | n (...) |") — the
|
||||||
|
# leading integer in the value cell is the count.
|
||||||
|
# CR-8: Declaration forms ("SEV: n", "SEV = n", "SEV (n)", "n SEV", and their
|
||||||
|
# bulleted/bold variants) match only on declaration-only lines — a line
|
||||||
|
# that, after stripping `*` and any leading bullet/heading prefix,
|
||||||
|
# consists solely of declaration tokens separated by [.,;\s]+ (label-
|
||||||
|
# style parentheticals like "(C1)" are accepted; other parentheticals
|
||||||
|
# disqualify the line).
|
||||||
_counts_extract() {
|
_counts_extract() {
|
||||||
local scope="$1" sev="$2" line n
|
local scope="$1" sev="$2"
|
||||||
# Strip markdown emphasis markers (bold/italic) — they can appear before
|
# CR-7: Table row branch (exempt from declaration-only rule).
|
||||||
# or after the colon (e.g. "**SEV:**" as well as "**SEV**:").
|
# Accept "| SEV | n |" and "| SEV | n (<annotation>) |".
|
||||||
local clean; clean=$(sed 's/\*//g' <<<"$scope")
|
local table_match
|
||||||
line=$(grep -inE "\\| *${sev} *\\| *[0-9]+ *\\||${sev}[[:space:]]*[:=][[:space:]]*[0-9]+|${sev}[[:space:]]*\\([0-9]+\\)|[0-9]+[[:space:]]+${sev}\\b" <<<"$clean" | tail -1) || true
|
table_match=$(grep -inE "\\|[[:space:]]*${sev}[[:space:]]*\\|[[:space:]]*[0-9]+[^|]*\\|" <<<"$scope" | tail -1) || true
|
||||||
[[ -z "$line" ]] && { echo ""; return; }
|
if [[ -n "$table_match" ]]; then
|
||||||
if [[ "$line" =~ \|[[:space:]]*${sev}[[:space:]]*\|[[:space:]]*([0-9]+)[[:space:]]*\| ]]; then
|
if [[ "$table_match" =~ \|[[:space:]]*${sev}[[:space:]]*\|[[:space:]]*([0-9]+)[^|]*\| ]]; then
|
||||||
n="${BASH_REMATCH[1]}"
|
echo "${BASH_REMATCH[1]}"
|
||||||
elif [[ "$line" =~ ${sev}[[:space:]]*[:=][[:space:]]*([0-9]+) ]]; then
|
return
|
||||||
n="${BASH_REMATCH[1]}"
|
fi
|
||||||
elif [[ "$line" =~ ${sev}[[:space:]]*\(([0-9]+)\) ]]; then
|
|
||||||
n="${BASH_REMATCH[1]}"
|
|
||||||
elif [[ "$line" =~ ([0-9]+)[[:space:]]+${sev} ]]; then
|
|
||||||
n="${BASH_REMATCH[1]}"
|
|
||||||
fi
|
fi
|
||||||
echo "${n:-}"
|
# CR-8: Declaration line branch — only lines that pass _is_declaration_line
|
||||||
|
# contribute; across all such lines the LAST match wins.
|
||||||
|
local last_n=""
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[[ -z "$line" ]] && continue
|
||||||
|
# Strip leading bullet (-, *, +) or heading (#+) prefix
|
||||||
|
local stripped
|
||||||
|
stripped=$(printf '%s\n' "$line" | sed -E 's/^[[:space:]]*([-*+]+|#{1,6})[[:space:]]*//')
|
||||||
|
# Reject the line if it's not declaration-only
|
||||||
|
_is_declaration_line "$stripped" || continue
|
||||||
|
# Strip `*` markers for value extraction
|
||||||
|
local clean_line="${stripped//\*/}"
|
||||||
|
local n=""
|
||||||
|
if [[ "$clean_line" =~ (^|[^[:alnum:]])${sev}[[:space:]]*[:=][[:space:]]*([0-9]+) ]]; then
|
||||||
|
n="${BASH_REMATCH[2]}"
|
||||||
|
elif [[ "$clean_line" =~ (^|[^[:alnum:]])${sev}[[:space:]]*\(([0-9]+)\) ]]; then
|
||||||
|
n="${BASH_REMATCH[2]}"
|
||||||
|
elif [[ "$clean_line" =~ ([0-9]+)[[:space:]]+${sev}([^[:alnum:]]|$) ]]; then
|
||||||
|
n="${BASH_REMATCH[1]}"
|
||||||
|
fi
|
||||||
|
if [[ -n "$n" ]]; then last_n="$n"; fi
|
||||||
|
done <<<"$scope"
|
||||||
|
echo "$last_n"
|
||||||
}
|
}
|
||||||
|
|
||||||
# counts <file> → prints CRITICAL=n HIGH=n MEDIUM=n LOW=n ; exit 4 if C+H>0
|
# counts <file> → prints CRITICAL=n HIGH=n MEDIUM=n LOW=n ; exit 4 if C+H>0
|
||||||
@@ -103,6 +152,16 @@ counts() {
|
|||||||
n=$(grep -cE "^[[:space:]]*(#+[[:space:]]*)?(\*\*)?${sev}(\*\*)?[[:space:]]*(:|—|-)" "$f") || n=0
|
n=$(grep -cE "^[[:space:]]*(#+[[:space:]]*)?(\*\*)?${sev}(\*\*)?[[:space:]]*(:|—|-)" "$f") || n=0
|
||||||
vals[$sev]="$n"
|
vals[$sev]="$n"
|
||||||
done
|
done
|
||||||
|
# CR-9: Suspect derived zero. If the derived fallback ran and yielded
|
||||||
|
# CRITICAL=0 and HIGH=0, but the file contains a CRITICAL/HIGH section
|
||||||
|
# header anywhere (case-insensitive), the derived count is untrustworthy
|
||||||
|
# — mark "(suspect)" instead of "(derived)" and force exit 4. Suspect
|
||||||
|
# and genuine C+H>0 share exit 4; distinguish via the marker.
|
||||||
|
if [[ "${vals[CRITICAL]:-0}" -eq 0 && "${vals[HIGH]:-0}" -eq 0 ]]; then
|
||||||
|
if grep -qiE "^#{1,6}[[:space:]]*(\*\*)?(CRITICAL|HIGH)\b" "$f"; then
|
||||||
|
derived=" (suspect)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
shopt -u nocasematch
|
shopt -u nocasematch
|
||||||
|
|
||||||
@@ -113,7 +172,7 @@ counts() {
|
|||||||
if [[ "$sev" == "CRITICAL" || "$sev" == "HIGH" ]]; then total=$((total + n)); fi
|
if [[ "$sev" == "CRITICAL" || "$sev" == "HIGH" ]]; then total=$((total + n)); fi
|
||||||
done
|
done
|
||||||
echo "${out% }${derived}"
|
echo "${out% }${derived}"
|
||||||
if [[ "$total" -gt 0 ]]; then return 4; fi
|
if [[ "$total" -gt 0 || "$derived" == " (suspect)" ]]; then return 4; fi
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user