fix(P16): aggregate.sh JSON injection + drift-gate fix (REQ-131, F11, F18)

---ci---
project: orca
phase: 16
milestone: v0.12
status: execute
---/ci---

orca-aggregate.sh: peer output validated via jq before JSON
interpolation (prevents injection from malicious peer). Peer name
escaped. Fallback: JSON shape validation via grep.
orca-pull.sh: R-020 drift gate now uses jq for accurate JSON parsing
(replaces fragile grep-based parsing). Fallback to grep if jq absent.
Build green.
This commit is contained in:
Jon Chery
2026-08-07 11:26:04 +00:00
parent c5ce851fc7
commit 39172c4f35
2 changed files with 33 additions and 4 deletions
+19 -1
View File
@@ -69,7 +69,25 @@ while IFS= read -r peer; do
orca_log_warn "aggregate" "$peer" "skipped" "failed to read state snapshot"
continue
fi
printf '{"peer":"%s","state":%s}\n' "$peer" "$snapshot" >>"$merge_tmp"
# REQ-131 / F11: use jq to safely construct JSON (prevents JSON
# injection from malicious peer output). $peer is sanitized; $snapshot
# is parsed as raw JSON by jq, so control chars can't break out.
if command -v jq >/dev/null 2>&1; then
snapshot_json="$(printf '%s' "$snapshot" | jq -c '.' 2>/dev/null)" || {
orca_log_warn "aggregate" "$peer" "skipped" "peer returned invalid JSON"
continue
}
peer_escaped="${peer//\"/\\\"}"
printf '{"peer":"%s","state":%s}\n' "$peer_escaped" "$snapshot_json" >>"$merge_tmp"
else
# Fallback: validate $snapshot looks like JSON before interpolation.
if ! printf '%s' "$snapshot" | grep -qE '^\s*\{.*\}\s*$'; then
orca_log_warn "aggregate" "$peer" "skipped" "peer returned non-JSON"
continue
fi
peer_escaped="${peer//\"/\\\"}"
printf '{"peer":"%s","state":%s}\n' "$peer_escaped" "$snapshot" >>"$merge_tmp"
fi
orca_log_info "aggregate" "$peer" "ok" "snapshot=$latest_json"
done < <(read_peers)
+14 -3
View File
@@ -115,10 +115,21 @@ if [ "$FORCE" != "true" ]; then
if [ -f "$DRIFT_AGG_JSON" ]; then
NS_FILTER="${NAMESPACE:-}"
NS_REGEX="${NS_FILTER//\//.}"
if [ -n "$NS_FILTER" ]; then
DRIFT_HITS="$(grep -o '"path"[[:space:]]*:[[:space:]]*"[^"]*"' "$DRIFT_AGG_JSON" 2>/dev/null | sed 's/.*: *"//;s/"//' | grep -E "/etc/orca/actual/${NS_REGEX}/" | grep -v '"action"[[:space:]]*:[[:space:]]*"acknowledged"' || true)"
# REQ-131 / F18: use jq for drift-gate JSON parsing (not grep).
if command -v jq >/dev/null 2>&1; then
if [ -n "$NS_FILTER" ]; then
DRIFT_HITS="$(jq -r --arg ns "$NS_FILTER" '[.events[]? | select((.path|test("/etc/orca/actual/\($ns)/")) and (.action != "acknowledged"))] | length' "$DRIFT_AGG_JSON" 2>/dev/null || true)"
else
DRIFT_HITS="$(jq -r '[.events[]? | select(.drift_confirmed == true and .action != "acknowledged")] | length' "$DRIFT_AGG_JSON" 2>/dev/null || true)"
fi
else
DRIFT_HITS="$(grep -o '"drift_confirmed"[[:space:]]*:[[:space:]]*true' "$DRIFT_AGG_JSON" 2>/dev/null || true)"
# Fallback: grep (less accurate; the ack filter may not
# match the same line as the path filter).
if [ -n "$NS_FILTER" ]; then
DRIFT_HITS="$(grep -o '"path"[[:space:]]*:[[:space:]]*"[^"]*"' "$DRIFT_AGG_JSON" 2>/dev/null | sed 's/.*: *"//;s/"//' | grep -E "/etc/orca/actual/${NS_REGEX}/" || true)"
else
DRIFT_HITS="$(grep -o '"drift_confirmed"[[:space:]]*:[[:space:]]*true' "$DRIFT_AGG_JSON" 2>/dev/null || true)"
fi
fi
if [ -n "$DRIFT_HITS" ]; then
orca_log_error "orca-pull" "$TXN_DIR" "drift-detected" "namespace=${NAMESPACE:-cluster-wide}"