mirror of
https://github.com/Colorado-Mesh/meshcore-bot-firmware.git
synced 2026-08-11 16:20:29 +00:00
116 lines
5.2 KiB
Bash
116 lines
5.2 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/meshcore-env.sh"
|
|
|
|
bot_sources=(
|
|
"${MESHCORE_DIR}/examples/companion_radio/BotTypes.h"
|
|
"${MESHCORE_DIR}/examples/companion_radio/FirmwareBot.h"
|
|
"${MESHCORE_DIR}/examples/companion_radio/FirmwareBot.cpp"
|
|
"${MESHCORE_DIR}/examples/companion_radio/BotCommands.h"
|
|
"${MESHCORE_DIR}/examples/companion_radio/BotCommands.cpp"
|
|
"${MESHCORE_DIR}/examples/companion_radio/BotCommandRegistry.h"
|
|
"${MESHCORE_DIR}/examples/companion_radio/BotCommandRegistry.cpp"
|
|
"${MESHCORE_DIR}/examples/companion_radio/BotPolicy.h"
|
|
"${MESHCORE_DIR}/examples/companion_radio/BotPolicy.cpp"
|
|
"${MESHCORE_DIR}/examples/companion_radio/BotPrefs.h"
|
|
"${MESHCORE_DIR}/examples/companion_radio/BotPrefs.cpp"
|
|
"${MESHCORE_DIR}/examples/companion_radio/EmergencyForwarder.h"
|
|
"${MESHCORE_DIR}/examples/companion_radio/EmergencyForwarder.cpp"
|
|
"${MESHCORE_DIR}/examples/companion_radio/KnownBotRegistry.h"
|
|
"${MESHCORE_DIR}/examples/companion_radio/KnownBotRegistry.cpp"
|
|
"${MESHCORE_DIR}/examples/companion_radio/ResponseCoordinator.h"
|
|
"${MESHCORE_DIR}/examples/companion_radio/ResponseCoordinator.cpp"
|
|
)
|
|
|
|
missing=0
|
|
for file in "${bot_sources[@]}"; do
|
|
if [ ! -f "$file" ]; then
|
|
echo "Missing bot source: ${file#${MESHCORE_FW_ROOT}/}" >&2
|
|
missing=1
|
|
fi
|
|
done
|
|
if [ "$missing" -ne 0 ]; then
|
|
exit 1
|
|
fi
|
|
|
|
forbidden='\bString\b|std::|\bnew[[:space:]]|\bdelete[[:space:]]|\b(malloc|calloc|realloc|free)[[:space:]]*\(|ArduinoJson|JsonDocument|HTTPClient|WiFiClient|WebServer|AsyncWebServer|socket[[:space:]]*\('
|
|
if grep -n -E "$forbidden" "${bot_sources[@]}"; then
|
|
echo "Forbidden dynamic allocation, container, JSON, or network API found in firmware bot sources." >&2
|
|
exit 1
|
|
fi
|
|
|
|
forbidden_timing='\b(txdelay|rxdelay)\b|direct[._-]?tx[._-]?delay|airtime_factor|rx_delay_base'
|
|
if grep -n -E -i "$forbidden_timing" "${bot_sources[@]}"; then
|
|
echo "Forbidden lower-layer timing dependency found in firmware bot sources." >&2
|
|
exit 1
|
|
fi
|
|
|
|
require_pattern() {
|
|
local pattern="$1"
|
|
shift
|
|
local label="$1"
|
|
shift
|
|
if ! grep -q -E "$pattern" "$@"; then
|
|
echo "Missing safety marker: $label" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
require_pattern 'BOT_POLICY_IGNORE' 'normal Public bot traffic is ignored silently' \
|
|
"${MESHCORE_DIR}/examples/companion_radio/BotPolicy.cpp" \
|
|
"${MESHCORE_DIR}/examples/companion_radio/BotTypes.h"
|
|
require_pattern 'BOT_POLICY_EMERGENCY_FORWARD' 'emergency traffic forwarding policy exists' \
|
|
"${MESHCORE_DIR}/examples/companion_radio/BotPolicy.cpp" \
|
|
"${MESHCORE_DIR}/examples/companion_radio/BotPolicy.h"
|
|
require_pattern 'recordBotObservation\(|sendQueuedEmergencyForwards\(|tickBot\(' 'emergency path is wired in MyMesh' \
|
|
"${MESHCORE_DIR}/examples/companion_radio/MyMesh.cpp"
|
|
require_pattern '_prefs\.path_hash_mode[[:space:]]*=[[:space:]]*1' 'bot firmware defaults to two-byte path hashes' \
|
|
"${MESHCORE_DIR}/examples/companion_radio/MyMesh.cpp"
|
|
require_pattern 'command\.args_len > 0 \? botConfiguredTraceHashSize\(_prefs\.path_hash_mode\) : message\.path_hash_size' 'explicit bot trace paths use configured hash width' \
|
|
"${MESHCORE_DIR}/examples/companion_radio/MyMesh.cpp"
|
|
if grep -R -n -E 'BOT_EMERGENCY_RATE_LIMIT|isEmergencyRateLimited|recordEmergencyRateLimitEvent|emergency_rate_window_started|emergency_rate_count' \
|
|
"${MESHCORE_DIR}/examples/companion_radio"; then
|
|
echo "Emergency forwarding must not be globally rate limited or dropped by a bot-local quota." >&2
|
|
exit 1
|
|
fi
|
|
if ! python3 - "${MESHCORE_DIR}/examples/companion_radio/MyMesh.cpp" <<'PY'
|
|
import re
|
|
import sys
|
|
text = open(sys.argv[1], encoding="utf-8").read()
|
|
start = text.find("void MyMesh::sendQueuedEmergencyForwards() {")
|
|
end = text.find("bool MyMesh::sendBotSelfAdvert", start)
|
|
if start < 0 or end < 0:
|
|
sys.exit(1)
|
|
body = text[start:end]
|
|
success_block = re.search(r"if \(success\) \{(?P<block>.*?)\n \} else \{", body, re.S)
|
|
if not success_block or "pending->active = false;" not in success_block.group("block"):
|
|
sys.exit(1)
|
|
else_block = re.search(r"\} else \{(?P<block>.*?)\n \}\n \}", body, re.S)
|
|
if else_block and "pending->active = false;" in else_block.group("block"):
|
|
sys.exit(1)
|
|
PY
|
|
then
|
|
echo "Emergency forwards must stay queued after failed public sends." >&2
|
|
exit 1
|
|
fi
|
|
require_pattern 'CMESH_BOT_ENABLED=1' 'production bot build flag is enabled' \
|
|
"${MESHCORE_DIR}/platformio.ini"
|
|
require_pattern 'ENABLE_PRIVATE_KEY_IMPORT=0' 'private key import disabled in production bot flags' \
|
|
"${MESHCORE_DIR}/platformio.ini"
|
|
require_pattern 'ENABLE_PRIVATE_KEY_EXPORT=0' 'private key export disabled in production bot flags' \
|
|
"${MESHCORE_DIR}/platformio.ini"
|
|
require_pattern '\$\{cmesh_bot_production\.build_flags\}' 'representative envs include production bot flags' \
|
|
"${MESHCORE_DIR}/variants/heltec_v3/platformio.ini" \
|
|
"${MESHCORE_DIR}/variants/rak4631/platformio.ini"
|
|
|
|
count="$(grep -h -E '\$\{cmesh_bot_production\.build_flags\}' \
|
|
"${MESHCORE_DIR}/variants/heltec_v3/platformio.ini" \
|
|
"${MESHCORE_DIR}/variants/rak4631/platformio.ini" | wc -l | tr -d ' ')"
|
|
if [ "$count" -lt 4 ]; then
|
|
echo "Expected production bot flags in four representative envs, found $count." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Firmware bot safety checks passed."
|