cleanup, rip of Q02, fixes

This commit is contained in:
Michael Messner
2026-05-22 09:19:31 +02:00
parent ecc908d62a
commit 9facf4f88b
14 changed files with 136 additions and 412 deletions
+1 -2
View File
@@ -175,8 +175,7 @@ set_defaults() {
export SVG_BUILD_TIMEOUT="12h"
export SVG_FILE="${LOG_PATH_MODULE}/EMBA-dependency-map.svg"
DEPENDENCY_TRACK_ENABLED=0
OPENAI_API_KEY=""
export DEPENDENCY_TRACK_ENABLED=0
# we can enable/disable the s08 submodules with the following array configuration
# -> just comment the submodule that should not be used
+1 -69
View File
@@ -300,7 +300,7 @@ dependency_check() {
echo -e "${RED}not ok${NC}"
print_output "[!] Warning: EMBA has NO internet connection!" "no_log"
print_output "[!] Warning: Update checks and multiple EMBA modules are disabled!" "no_log"
print_output "[!] Warning: GPT (Q02), kernel verification (S26) and further online modules are disabled!" "no_log"
print_output "[!] Warning: Kernel verification (S26) and further online modules are disabled!" "no_log"
fi
fi
@@ -335,74 +335,6 @@ dependency_check() {
# in our containers we have mounted our current EMBA dir to /emba, this includes the host ./external with the onlinechecker
local lONLINE_CHECK_FILE="/emba/external/onlinechecker/EMBA_VERSION.txt"
fi
# as we first check the onlinechecker/EMBA_VERSION.txt file we know if we are online or not:
if ! [[ -f "${lONLINE_CHECK_FILE}" ]] && [[ -n "${OPENAI_API_KEY}" ]]; then
# if we have no EMBA_VERSION identified, we do not need to check our GPT key now -> there is no internet
print_output "$(indent "${ORANGE}As there is no Internet connection available, no GPT checks performed.${NC}")" "no_log"
elif [[ -z "${OPENAI_API_KEY}" ]]; then
print_output "$(indent "ChatGPT-API key not set - ${ORANGE}see https://github.com/e-m-b-a/emba/wiki/AI-supported-firmware-analysis for more information${NC}")" "no_log"
# The following if clause is currently not working! We have not loaded the profile in this stage
# TODO: Find a workaround!
if [[ "${AI_OPTION}" -eq 1 ]]; then
DEP_ERROR=1
fi
else
local lRETRIES_=0
# on the host we try it only 10 times:
local lMAX_RETRIES=10
if [[ "${IN_DOCKER}" -eq 1 ]]; then
# within the Quest container we can keep trying it as it does not matter if the container starts up later
lMAX_RETRIES=200
fi
local lSLEEPTIME=30
while true; do
local lHTTP_CODE_=400
print_output " OpenAI-API key - \\c" "no_log"
lHTTP_CODE_=$(curl -sS https://api.openai.com/v1/chat/completions -H "Content-Type: application/json" \
-H "Authorization: Bearer ${OPENAI_API_KEY}" \
-d @"${CONFIG_DIR}/gpt_template.json" --write-out "%{http_code}" -o /tmp/chatgpt-test.json 2>/dev/null)
if [[ "${lHTTP_CODE_}" -eq 200 ]]; then
echo -e "${GREEN}""ok""${NC}"
rm /tmp/chatgpt-test.json
break
else
if [[ -f /tmp/chatgpt-test.json ]]; then
if jq '.error.code' /tmp/chatgpt-test.json | grep -q "rate_limit_exceeded"; then
# rate limit handling - if we got a response like:
# Please try again in 20s
echo -e "${RED}""not ok (rate limit issues)""${NC}"
if jq '.error.message' /tmp/chatgpt-test.json | grep -q "Please try again in "; then
# print_output "GPT API test #${lRETRIES_} - \\c" "no_log"
sleep "${lSLEEPTIME}"s
# sleeptime gets adjusted on every failure
lSLEEPTIME=$((lSLEEPTIME + 5))
((lRETRIES_ += 1))
[[ "${lRETRIES_}" -lt "${lMAX_RETRIES}" ]] && continue
fi
fi
if jq '.error.code' /tmp/chatgpt-test.json | grep -q "insufficient_quota"; then
echo -e "${RED}""not ok (quota limit issues)""${NC}"
break
fi
fi
echo -e "${RED}""not ok""${NC}"
print_output "[-] ChatGPT error while testing the API-Key: ${OPENAI_API_KEY}" "no_log"
if [[ -f /tmp/chatgpt-test.json ]]; then
print_output "[-] ERROR response: $(cat /tmp/chatgpt-test.json)" "no_log"
fi
# Note: we are running into issues in the case where the key can't be verified, but GPT is not enabled at all
# In such a case we will fail the check without the need of GPT
# DEP_ERROR=1
fi
if grep -q "Testing phase ended" "${LOG_DIR}"/"${MAIN_LOG_FILE}" 2>/dev/null; then
print_output " Testing phase ended - \\c" "no_log"
echo -e "${RED}""exit now""${NC}"
DEP_ERROR=1
fi
done
fi
else
print_output " Isolation - ${GREEN}""ok""${NC}" "no_log"
fi
+2 -2
View File
@@ -116,11 +116,11 @@ add_link_tags() {
# in some cases we link to files from different modules. Now we check if the html report file is already
# generated and we can directly use the already available html report file
lORIG_SRC_MODULE_DIR=$(echo "${lREF_LINK}" | tr '/' '\n' | grep -E "^[psqfl][0-9]{2,3}_.*")
lORIG_SRC_MODULE_DIR=$(echo "${lREF_LINK}" | tr '/' '\n' | grep -E "^[psqfl][0-9]{2,3}_.*" | head -n 1 || print_error "[-] HTML report linking issue for ${lREF_LINK}")
print_debug "[*] REF link ${lREF_LINK} / BACK_LINK ${lBACK_LINK} / lORIG_SRC_MODULE_DIR: ${lORIG_SRC_MODULE_DIR} / lLINK_FILE: ${lLINK_FILE}" "no_log"
if [[ -f "${ABS_HTML_PATH%/}/${lORIG_SRC_MODULE_DIR}/${lMD5_REF_LINK}" ]]; then
print_debug "[*] Found already generated log file in ${ABS_HTML_PATH%/}/${lORIG_SRC_MODULE_DIR}/${lMD5_REF_LINK}" "no_log"
if [[ "$(basename ${lLINK_FILE})" =~ ^(d|p|l|s|q|f){1}[0-9]{2,3}_.*$ ]]; then
if [[ "$(basename "${lLINK_FILE}")" =~ ^(d|p|l|s|q|f){1}[0-9]{2,3}_.*$ ]]; then
# link to the already available report file without generating a new one:
# we need to link to the original module directory of the file if we are in a main module log file
lBACK_LINK_NEW="${lORIG_SRC_MODULE_DIR}/${lMD5_REF_LINK}"
+3 -3
View File
@@ -65,6 +65,8 @@ F05_qs_resolver() {
print_output "[*] AI resolver - testing ${ORANGE}${CSV_DIR}/ai_question.csv${NC}"
gpt_resolver_csv "${lGPT_INPUT_FILE_}" "${lAI_ANCHOR}" "${l_GPT_PRIO_}" "${lGPT_QUESTION_}" "${lGPT_OUTPUT_FILE_}" "${lGPT_TOKENS_}" "${lAI_RESPONSE_FILE}" &
lWAIT_PIDS_F05_ARR+=("$!")
store_kill_pids "${lWAIT_PIDS_F05_ARR[-1]}"
done <"${CSV_DIR}/ai_question.csv"
wait_for_pid "${lWAIT_PIDS_F05_ARR[@]}"
@@ -85,6 +87,7 @@ F05_qs_resolver() {
gpt_resolver_csv_tmp "${lGPT_INPUT_FILE_}" "${lAI_ANCHOR}" "${l_GPT_PRIO_}" "${lGPT_QUESTION_}" "${lGPT_OUTPUT_FILE_}" "${lGPT_TOKENS_}" "${lGPT_RESPONSE_}" &
local lTMP_PID="$!"
lWAIT_PIDS_F05_ARR+=("${lTMP_PID}")
store_kill_pids "${lTMP_PID}"
# max_pids_protection "${MAX_MOD_THREADS}" "${lWAIT_PIDS_F05_ARR[@]}"
done <"${CSV_DIR}/ai_question.csv.tmp"
@@ -171,9 +174,6 @@ gpt_resolver_csv_tmp() {
print_output "[*] AI module didn't check ${lGPT_INPUT_FILE_}, linking to the GPT module page instead"
local lMD5_OF_ORIG_SRC_FILE=""
lMD5_OF_ORIG_SRC_FILE=$(md5sum "${lGPT_OUTPUT_FILE_}" | awk '{print $1}')
readarray -t lGPT_OUTPUT_FILE_HTML_ARR_ < <(grep -r -l "\[ASK_AI\]\ ${lAI_ANCHOR}" "${HTML_PATH}" 2>/dev/null || true)
for lHTML_FILE_ in "${lGPT_OUTPUT_FILE_HTML_ARR_[@]}"; do
+7
View File
@@ -95,6 +95,13 @@ output_overview() {
fi
print_output "[+] EMBA version: ""${ORANGE}""${lSBOM_TOOL_VERS}""${NC}"
write_csv_log "EMBA_version" "${lSBOM_TOOL_VERS}" "NA" "NA" "NA" "NA" "NA" "NA" "NA"
if [[ -f "${LOG_DIR}/emba_error.log" ]]; then
if grep -q Location "${LOG_DIR}/emba_error.log"; then
print_output "[-] WARNING: EMBA ${ORANGE}errors${NC} to review" "" ""
else
print_output "[*] INFO: EMBA ${ORANGE}warnings${NC} to review" "" ""
fi
fi
if [[ -n "${ARCH}" ]] && [[ "${ARCH}" != "NA" ]]; then
if [[ -n "${D_END:-"NA"}" ]]; then
-300
View File
@@ -1,300 +0,0 @@
#!/bin/bash -p
# EMBA - EMBEDDED LINUX ANALYZER
#
# Copyright 2020-2026 Siemens Energy AG
#
# EMBA comes with ABSOLUTELY NO WARRANTY. This is free software, and you are
# welcome to redistribute it under the terms of the GNU General Public License.
# See LICENSE file for usage of this software.
#
# EMBA is licensed under GPLv3
# SPDX-License-Identifier: GPL-3.0-only
#
# Author(s): Benedikt Kuehne
# Description: Openai questioning module for container #2
# Note: Important requirement for Q-modules is the self termination when a certain phase ends
Q02_openai_question() {
module_log_init "${FUNCNAME[0]}"
export CHATGPT_RESULT_CNT=0
if [[ "${AI_OPTION}" -gt 0 ]] && [[ -n "${OPENAI_API_KEY}" ]]; then
# Prints title to CLI and into log
module_title "AI analysis via OpenAI"
pre_module_reporter "${FUNCNAME[0]}"
# we wait until there arer entries in the question csv
while ! [[ -f "${LOG_DIR}"/"${MAIN_LOG_FILE}" ]]; do
if ! [[ -d "${LOG_DIR}" ]]; then
# this usually happens if we automate analysis and remove the logging directory while this module was not finished at all
return
fi
sleep 10
done
if [[ -f "${LOG_DIR}"/"${MAIN_LOG_FILE}" ]]; then
while ! [[ -f "${CSV_DIR}/ai_question.csv.tmp" ]]; do
sleep 3
done
fi
export GTP_CHECKED_ARR=()
while ! grep -q "Reporting phase started" "${LOG_DIR}"/"${MAIN_LOG_FILE}" && [[ "${lMODULE_RUNTIME}" -le "${LOCAL_AI_MIN_RUNTIME}" ]]; do
if [[ "${CHATGPT_RESULT_CNT}" -ge 0 ]]; then
ask_chatgpt
fi
sleep 2
done
unset OPENAI_API_KEY
fi
module_end_log "${FUNCNAME[0]}" "${CHATGPT_RESULT_CNT}"
}
# looks through the modules and finds chatgpt questions inside the csv
ask_chatgpt() {
local lGPT_FILE_DIR="${LOG_PATH_MODULE}/gpt_files"
local lGPT_PRIO=3
# default vars
local lGPT_QUESTION=""
local lCHATGPT_CODE=""
local lGPT_RESPONSE=""
local lGPT_RESPONSE_CLEANED=""
local lGPT_TOKENS=0
local lHTTP_CODE=200
local lORIGIN_MODULE=""
local lGPT_SERVER_ERROR_CNT=0
local lELE_INDEX=0
local lGPT_ANCHOR=""
local lGPT_INPUT_FILE=""
local lGPT_INPUT_FILE_mod=""
local lGPT_OUTPUT_FILE=""
local lSCRIPT_PATH_TMP=""
print_output "[*] Checking scripts with ChatGPT that have priority ${ORANGE}${MINIMUM_GPT_PRIO}${NC} or lower" "no_log"
if ! [[ -d "${lGPT_FILE_DIR}" ]]; then
mkdir "${lGPT_FILE_DIR}"
fi
# generating Array for GPT requests - sorting according the prio in field 3
# this array gets regenerated on every round
readarray -t Q02_OPENAI_QUESTIONS < <(sort -k 3 -t ';' -r "${CSV_DIR}/ai_question.csv.tmp")
for ((lELE_INDEX = 0; lELE_INDEX < "${#Q02_OPENAI_QUESTIONS[@]}"; lELE_INDEX++)); do
local lELEM="${Q02_OPENAI_QUESTIONS["${lELE_INDEX}"]}"
lSCRIPT_PATH_TMP="$(echo "${lELEM}" | cut -d\; -f1)"
# as we always start with the highest rated entry, we need to check if this entry was already tested:
if [[ " ${GTP_CHECKED_ARR[*]} " =~ ${lSCRIPT_PATH_TMP} ]]; then
# print_output "[*] GPT - Already tested ${lSCRIPT_PATH_TMP}" "no_log"
# lets test the next entry
continue
fi
lGPT_ANCHOR="$(echo "${lELEM}" | cut -d\; -f2)"
lGPT_PRIO="$(echo "${lELEM}" | cut -d\; -f3)"
# lGPT_PRIO="${lGPT_PRIO//GPT-Prio-/}"
lGPT_QUESTION="$(echo "${lELEM}" | cut -d\; -f4)"
lGPT_OUTPUT_FILE="$(echo "${lELEM}" | cut -d\; -f5)"
lGPT_TOKENS="$(echo "${lELEM}" | cut -d\; -f6)"
lGPT_TOKENS="${lGPT_TOKENS//cost\=/}"
lGPT_RESPONSE="$(echo "${lELEM}" | cut -d\; -f7)"
lGPT_INPUT_FILE="$(basename "${lSCRIPT_PATH_TMP}")"
lGPT_INPUT_FILE_mod="${lGPT_INPUT_FILE//\./}"
# in case we have nothing we are going to move on
[[ -z "${lSCRIPT_PATH_TMP}" ]] && continue
if [[ "${lSCRIPT_PATH_TMP}" == *"s16_ghidra_decompile_checks"* ]]; then
# our ghidra check stores the decompiled code in the log directory. We need to copy it to the gpt log directory for further processing
print_output "[*] Ghidra decompiled code found ${lSCRIPT_PATH_TMP}" "no_log"
[[ -f "${lSCRIPT_PATH_TMP}" ]] && cp "${lSCRIPT_PATH_TMP}" "${lGPT_FILE_DIR}/${lGPT_INPUT_FILE_mod}.log"
else
# this is currently the usual case for scripts
print_output "[*] Identification of ${ORANGE}${lSCRIPT_PATH_TMP} / ${lGPT_INPUT_FILE}${NC} inside ${ORANGE}${LOG_DIR}/firmware${NC}" "no_log"
if [[ "${lSCRIPT_PATH_TMP}" == ".""${LOG_DIR}"* ]]; then
print_output "[*] Warning: System path is not stripped with the root directory - we try to fix it now" "no_log"
# remove the '.'
lSCRIPT_PATH_TMP="${lSCRIPT_PATH_TMP:1}"
# remove the LOG_DIR
# shellcheck disable=SC2001
lSCRIPT_PATH_TMP="$(echo "${lSCRIPT_PATH_TMP}" | sed 's#'"${LOG_DIR}"'##')"
print_output "[*] Stripped path ${lSCRIPT_PATH_TMP}" "no_log"
fi
# dirty fix - Todo: use array in future
lSCRIPT_PATH_TMP="$(find "${LOG_DIR}/firmware" -wholename "*${lSCRIPT_PATH_TMP}" | head -1)"
# in case we have nothing we are going to move on
! [[ -f "${lSCRIPT_PATH_TMP}" ]] && continue
[[ -f "${lSCRIPT_PATH_TMP}" ]] && cp "${lSCRIPT_PATH_TMP}" "${lGPT_FILE_DIR}/${lGPT_INPUT_FILE_mod}.log"
fi
print_output "[*] AI-Assisted analysis of script ${ORANGE}${lSCRIPT_PATH_TMP}${NC} with question ${ORANGE}${lGPT_QUESTION}${NC}" "no_log"
print_output "[*] Current priority for testing is ${lGPT_PRIO}" "no_log"
if [[ -z ${lGPT_RESPONSE} ]] && [[ ${lGPT_PRIO} -ge ${MINIMUM_GPT_PRIO} ]] && [[ "${lSCRIPT_PATH_TMP}" != '' ]]; then
if [[ -f "${lSCRIPT_PATH_TMP}" ]]; then
# add navbar-item for file
sub_module_title "AI analysis for ${lGPT_INPUT_FILE}"
# print_output "[*] AI-Assisted analysis for ${ORANGE}${lGPT_INPUT_FILE}${NC}" "" "${lGPT_FILE_DIR}/${lGPT_INPUT_FILE_mod}.log"
print_output "[*] AI-Assisted analysis for ${lGPT_INPUT_FILE_mod}" "" "${lGPT_FILE_DIR}/${lGPT_INPUT_FILE_mod}.log"
print_output "$(indent "$(orange "$(print_path "${lSCRIPT_PATH_TMP}")")")"
head -n -2 "${CONFIG_DIR}/gpt_template.json" >"${TMP_DIR}/chat.json" || print_error "[-] Tmp file create error for ${lSCRIPT_PATH_TMP}"
if [[ ! -f "${TMP_DIR}/chat.json" ]]; then
print_output "[-] Temp file ${TMP_DIR}/chat.json for further analysis of ${lSCRIPT_PATH_TMP} was not created ... some Error occured"
return
fi
lCHATGPT_CODE=$(sed 's/\\//g;s/"/\\\"/g' "${lSCRIPT_PATH_TMP}" | tr -d '[:space:]' | sed 's/\[ASK_GPT\].*//')
if [[ "${#lCHATGPT_CODE}" -gt 4561 ]]; then
print_output "[*] GPT request is too big ... stripping it now" "no_log"
lCHATGPT_CODE=$(sed 's/\\//g;s/"/\\\"/g' "${lSCRIPT_PATH_TMP}" | tr -d '[:space:]' | cut -c-4560 | sed 's/\[ASK_GPT\].*//')
fi
strip_color_codes "$(printf '"%s %s"\n}]}' "${lGPT_QUESTION}" "${lCHATGPT_CODE}")" >>"${TMP_DIR}/chat.json"
print_output "[*] Testing the following code with ChatGPT:" "no_log"
cat "${lSCRIPT_PATH_TMP}"
print_ln "no_log"
print_output "[*] Adjusted the code under test to send it to ChatGPT:" "no_log"
cat "${TMP_DIR}/chat.json"
print_ln "no_log"
print_output "[*] The combined cost of the OpenAI request / the length is: ${ORANGE}${#lGPT_QUESTION} + ${#lCHATGPT_CODE}${NC}" "no_log"
lHTTP_CODE=$(curl https://api.openai.com/v1/chat/completions -H "Content-Type: application/json" \
-H "Authorization: Bearer ${OPENAI_API_KEY}" \
-d @"${TMP_DIR}/chat.json" -o "${TMP_DIR}/${lGPT_INPUT_FILE_mod}_response.json" --write-out "%{http_code}" || true)
if [[ "${lHTTP_CODE}" -ne 200 ]]; then
print_output "[-] Something went wrong with the ChatGPT requests"
if [[ -f "${TMP_DIR}/${lGPT_INPUT_FILE_mod}_response.json" ]]; then
print_output "[-] ERROR response: $(cat "${TMP_DIR}/${lGPT_INPUT_FILE_mod}_response.json")"
if jq '.error.type' "${TMP_DIR}/${lGPT_INPUT_FILE_mod}_response.json" | grep -q "insufficient_quota"; then
print_output "[-] Stopping OpenAI requests since the API key has reached its quota limit"
CHATGPT_RESULT_CNT=-1
sleep 20
break
elif jq '.error.type' "${TMP_DIR}/${lGPT_INPUT_FILE_mod}_response.json" | grep -q "server_error"; then
((lGPT_SERVER_ERROR_CNT += 1))
if [[ "${lGPT_SERVER_ERROR_CNT}" -ge 5 ]]; then
# more than 5 failes we stop trying until the newxt round
print_output "[-] Stopping OpenAI requests since the Server seems to be overloaded"
CHATGPT_RESULT_CNT=-1
sleep 20
break
fi
elif jq '.error.code' "${TMP_DIR}/${lGPT_INPUT_FILE_mod}_response.json" | grep -q "rate_limit_exceeded"; then
# rate limit handling - if we got a response like:
# Please try again in 7m12s.
# then we will wate ~10mins and try it afterwards again
# in this time we need to check if the Testing phase is running or not
if jq '.error.message' "${TMP_DIR}/${lGPT_INPUT_FILE_mod}_response.json" | grep -q "Please try again in "; then
local lCNT=0
while [[ "${lCNT}" -lt 1000 ]]; do
lCNT=$((lCNT + 1))
local lTEMP_VAR="$(("${lCNT}" % 100))"
(("${lTEMP_VAR}" == 0)) && print_output "[*] Rate limit handling ... sleep mode - ${lCNT}" "no_log"
if grep -q "Reporting phase started" "${LOG_DIR}"/"${MAIN_LOG_FILE}" && [[ "${lMODULE_RUNTIME}" -ge "${LOCAL_AI_MIN_RUNTIME}" ]]; then
break 2
fi
sleep 1
done
# TODO: now we should redo the last test
else
print_output "[-] Stopping OpenAI requests since the API key has reached its rate_limit"
CHATGPT_RESULT_CNT=-1
break
fi
fi
cat "${TMP_DIR}/${lGPT_INPUT_FILE_mod}_response.json" >>"${lGPT_FILE_DIR}/openai_server_errors.log"
readarray -t Q02_OPENAI_QUESTIONS < <(sort -k 3 -t ';' -r "${CSV_DIR}/ai_question.csv.tmp")
# reset the array index to start again with the highest rated entry
lELE_INDEX=0
if grep -q "Reporting phase started" "${LOG_DIR}"/"${MAIN_LOG_FILE}" && [[ "${lMODULE_RUNTIME}" -ge "${LOCAL_AI_MIN_RUNTIME}" ]]; then
return
fi
sleep 30s
continue
fi
fi
if ! [[ -f "${TMP_DIR}/${lGPT_INPUT_FILE_mod}_response.json" ]]; then
# catches: (56) Recv failure: Connection reset by peer
print_output "[-] Something went wrong with the ChatGPT request for ${lGPT_INPUT_FILE}"
break
fi
lGPT_RESPONSE=("$(jq '.choices[] | .message.content' "${TMP_DIR}/${lGPT_INPUT_FILE_mod}_response.json")")
lGPT_RESPONSE_CLEANED="${lGPT_RESPONSE[*]//\;/}" #remove ; from response
lGPT_TOKENS=$(jq '.usage.total_tokens' "${TMP_DIR}/${lGPT_INPUT_FILE_mod}_response.json")
if [[ ${lGPT_TOKENS} -ne 0 ]]; then
GTP_CHECKED_ARR+=("${lSCRIPT_PATH_TMP}")
# write new into done csv
write_csv_AI "${lGPT_INPUT_FILE}" "${lGPT_ANCHOR}" "${lGPT_PRIO}" "${lGPT_QUESTION}" "${lGPT_OUTPUT_FILE}" "cost=${lGPT_TOKENS}" "'${lGPT_RESPONSE_CLEANED//\'/}'"
# we store the answers in dedicated files for further interlinking within the report
if ! [[ -d "${LOG_PATH_MODULE}"/gpt_answers ]]; then
mkdir "${LOG_PATH_MODULE}"/gpt_answers || true
fi
echo "${lGPT_RESPONSE_CLEANED}" >"${LOG_PATH_MODULE}"/gpt_answers/gpt_response_"${lGPT_INPUT_FILE_mod}".log
# print openai response
print_ln
# print_output "[*] ${ORANGE}AI-assisted analysis results via OpenAI ChatGPT:${NC}\\n"
echo -e "${lGPT_RESPONSE[*]}" | tee -a "${LOG_FILE}"
# add proper module link
if [[ "${lGPT_OUTPUT_FILE}" == *'/csv_logs/'* ]]; then
# if we have a csv_logs path we need to adjust the cut
lORIGIN_MODULE="$(echo "${lGPT_OUTPUT_FILE}" | cut -d / -f4 | cut -d_ -f1)"
elif [[ "${lGPT_OUTPUT_FILE}" == '/logs/'* ]]; then
lORIGIN_MODULE="$(echo "${lGPT_OUTPUT_FILE}" | cut -d / -f3 | cut -d_ -f1)"
else
lORIGIN_MODULE="$(basename "$(dirname "${lGPT_OUTPUT_FILE}")" | cut -d_ -f1)"
fi
print_ln
print_output "[+] Further results for ${ORANGE}${lGPT_INPUT_FILE_mod}${GREEN} available in module ${ORANGE}${lORIGIN_MODULE}${NC}" "" "${lORIGIN_MODULE}"
print_output "[+] Analysed source file ${ORANGE}${lGPT_INPUT_FILE_mod}${GREEN}" "" "${lGPT_FILE_DIR}/${lGPT_INPUT_FILE_mod}.log"
# print_output "[+] Analysed source script popup ${ORANGE}${lGPT_INPUT_FILE_mod}${GREEN} script"
# write_local_overlay_link "${lGPT_FILE_DIR}/${lGPT_INPUT_FILE_mod}.log"
if [[ -f "${LOG_PATH_MODULE}"/gpt_answers/gpt_response_"${lGPT_INPUT_FILE_mod}".log ]]; then
print_output "[+] GPT answer file for ${ORANGE}${lGPT_INPUT_FILE_mod}${NC}" "" "${LOG_PATH_MODULE}"/gpt_answers/gpt_response_"${lGPT_INPUT_FILE_mod}".log
fi
print_ln
((CHATGPT_RESULT_CNT += 1))
fi
else
print_output "[-] Couldn't find ${ORANGE}$(print_path "${lSCRIPT_PATH_TMP}")${NC}"
fi
fi
if grep -q "Reporting phase started" "${LOG_DIR}"/"${MAIN_LOG_FILE}" && [[ "${lMODULE_RUNTIME}" -ge "${LOCAL_AI_MIN_RUNTIME}" ]]; then
break
fi
if [[ "${AI_OPTION}" -ne 2 ]]; then
sleep 20s
fi
# reload q02 results:
print_output "[*] Regenerate analysis array ..." "no_log"
readarray -t Q02_OPENAI_QUESTIONS < <(sort -k 3 -t ';' -r "${CSV_DIR}/ai_question.csv.tmp")
# reset the array index to start again with the highest rated entry
lELE_INDEX=0
done
if [[ -f "${CSV_DIR}/ai_question.csv" ]]; then
local lGPT_ENTRY_LINE=""
while read -r lGPT_ENTRY_LINE; do
lGPT_ANCHOR="$(echo "${lGPT_ENTRY_LINE}" | cut -d ';' -f2)"
sed -i "/${lGPT_ANCHOR}/d" "${CSV_DIR}/ai_question.csv.tmp"
done <"${CSV_DIR}/ai_question.csv"
fi
}
+28 -13
View File
@@ -66,6 +66,10 @@ Q03_localai_connector() {
fi
export SECONDS=0
if [[ "${AI_MIN_RUNTIME}" == *"d" ]]; then
AI_MIN_RUNTIME=${AI_MIN_RUNTIME//d/}
AI_MIN_RUNTIME=$((AI_MIN_RUNTIME*24*3600))
fi
if [[ "${AI_MIN_RUNTIME}" == *"h" ]]; then
AI_MIN_RUNTIME=${AI_MIN_RUNTIME//h/}
AI_MIN_RUNTIME=$((AI_MIN_RUNTIME*3600))
@@ -74,9 +78,8 @@ Q03_localai_connector() {
AI_MIN_RUNTIME=${AI_MIN_RUNTIME//m/}
AI_MIN_RUNTIME=$((AI_MIN_RUNTIME*60))
fi
if [[ "${AI_MIN_RUNTIME}" == *"d" ]]; then
AI_MIN_RUNTIME=${AI_MIN_RUNTIME//d/}
AI_MIN_RUNTIME=$((AI_MIN_RUNTIME*24*3600))
if [[ "${AI_MIN_RUNTIME}" == *"s" ]]; then
AI_MIN_RUNTIME=${AI_MIN_RUNTIME//s/}
fi
export GTP_CHECKED_ARR=()
@@ -470,15 +473,20 @@ generate_prompt_script() {
print_output "[*] Build prompt for ${lSCRIPT_PATH_TMP} - lSCRIPT_NAME: ${lBINARY_NAME} - script type: ${lSCRIPT_TYPE}" "no_log"
# Read the file content and remove the ASK_AI marker
lCODE_CONTENT_tmp=$(grep -v "ASK_AI" "${lSCRIPT_PATH_TMP}")
lCODE_CONTENT_tmp=$(grep -v "ASK_AI" "${lSCRIPT_PATH_TMP}" || true)
# remove REF entries for HTML links
lCODE_CONTENT_tmp=$(sed '/^\[REF\] .*/d' <<< "${lCODE_CONTENT_tmp}")
lCODE_CONTENT_tmp=$(sed '/^\[REF\] .*/d' <<< "${lCODE_CONTENT_tmp}" || print_error "[-] Code parsing issue for ${lSCRIPT_PATH_TMP} - 1")
# remove semgrep identifiers
lCODE_CONTENT_tmp=$(sed 's/\/\/possible issue identified -.*//' <<< "${lCODE_CONTENT_tmp}")
lCODE_CONTENT_tmp=$(sed 's/\/\/possible issue identified -.*//' <<< "${lCODE_CONTENT_tmp}" || print_error "[-] Code parsing issue for ${lSCRIPT_PATH_TMP} - 2")
# remove color codes
lCODE_CONTENT_tmp=$(strip_color_codes "${lCODE_CONTENT_tmp}")
# remove shell comments and empty lines:
lCODE_CONTENT=$(sed '/^[[:blank:]]*#/d;s/[[:blank:]]*#.*//' <<< "${lCODE_CONTENT_tmp}")
# remove shell comment lines:
lCODE_CONTENT_tmp=$(sed '/^[[:blank:]]*#/d' <<< "${lCODE_CONTENT_tmp}" || print_error "[-] Code parsing issue for ${lSCRIPT_PATH_TMP} - 4")
# remove shell comments:
lCODE_CONTENT_tmp=$(sed 's/[[:blank:]]*#.*//' <<< "${lCODE_CONTENT_tmp}" || print_error "[-] Code parsing issue for ${lSCRIPT_PATH_TMP} - 5")
# remove empty lines (includes also empty lines with spaces):
lCODE_CONTENT=$(sed -r '/^\s*$/d' <<< "${lCODE_CONTENT_tmp}" || print_error "[-] Code parsing issue for ${lSCRIPT_PATH_TMP} - 3")
lPROMPT="You are an expert software architect. Analyze the provided source code."
lPROMPT+=" The following source code is from a Linux based firmware ${lSCRIPT_TYPE} script called ${lBINARY_NAME}."
@@ -495,7 +503,7 @@ generate_prompt_script() {
fi
lPROMPT="${lPROMPT}\nHere is the code:\n\n${lCODE_CONTENT:0:${AI_MAX_CHARS_TO_ANALYSE}}\n"
echo -n "${lPROMPT}" | iconv -f ISO-8859-1 -t UTF-8 > "${AI_PROMPT_DIR}/prompt_localai_${lBINARY_NAME}_${lSCRIPT_TYPE}.txt"
echo -n "${lPROMPT}" > "${AI_PROMPT_DIR}/prompt_localai_${lBINARY_NAME}_${lSCRIPT_TYPE}.txt"
}
generate_prompt_binary() {
@@ -535,14 +543,21 @@ generate_prompt_binary() {
fi
# Read the file content and remove the ASK_AI marker
lCODE_CONTENT_tmp=$(grep -v "ASK_AI" "${lSCRIPT_PATH_TMP}")
lCODE_CONTENT_tmp=$(grep -v "ASK_AI" "${lSCRIPT_PATH_TMP}" || true)
# remove REF entries for HTML links
lCODE_CONTENT_tmp=$(sed '/^\[REF\] .*/d' <<< "${lCODE_CONTENT_tmp}")
lCODE_CONTENT_tmp=$(sed '/^\[REF\] .*/d' <<< "${lCODE_CONTENT_tmp}" || print_error "[-] Code parsing issue for ${lSCRIPT_PATH_TMP} - 1")
# remove semgrep identifiers
lCODE_CONTENT_tmp=$(sed 's/\/\/possible issue identified -.*//' <<< "${lCODE_CONTENT_tmp}")
lCODE_CONTENT_tmp=$(sed 's/\/\/possible issue identified -.*//' <<< "${lCODE_CONTENT_tmp}" || print_error "[-] Code parsing issue for ${lSCRIPT_PATH_TMP} - 2")
# remove color codes
lCODE_CONTENT=$(strip_color_codes "${lCODE_CONTENT_tmp}")
# remove shell comment lines:
lCODE_CONTENT_tmp=$(sed '/^[[:blank:]]*#/d' <<< "${lCODE_CONTENT_tmp}" || print_error "[-] Code parsing issue for ${lSCRIPT_PATH_TMP} - 4")
# remove shell comments:
lCODE_CONTENT_tmp=$(sed 's/[[:blank:]]*#.*//' <<< "${lCODE_CONTENT_tmp}" || print_error "[-] Code parsing issue for ${lSCRIPT_PATH_TMP} - 5")
# remove empty lines (includes also empty lines with spaces):
lCODE_CONTENT=$(sed -r '/^\s*$/d' <<< "${lCODE_CONTENT_tmp}" || print_error "[-] Code parsing issue for ${lSCRIPT_PATH_TMP} - 3")
lPROMPT="You are an expert software architect. Analyze the provided source code."
lPROMPT+=" The following source code is from a Linux based firmware binary called ${lBINARY_NAME}."
lPROMPT+=" It was automatically generated with the reverse engineering framework Ghidra."
@@ -588,5 +603,5 @@ generate_prompt_binary() {
# print_output "[*] Wrting prompt for ${lSCRIPT_PATH_TMP} - ${lPROMPT} to ${AI_PROMPT_DIR}/prompt_localai_${lBINARY_NAME}_${lFUNCTION_NAME}.txt"
echo -n "${lPROMPT}" | iconv -f ISO-8859-1 -t UTF-8 > "${AI_PROMPT_DIR}/prompt_localai_${lBINARY_NAME}_${lFUNCTION_NAME}.txt"
echo -n "${lPROMPT}" > "${AI_PROMPT_DIR}/prompt_localai_${lBINARY_NAME}_${lFUNCTION_NAME}.txt"
}
+2 -2
View File
@@ -142,10 +142,10 @@ setup_environment() {
if [[ "${#ALL_EXEC_FILES_ARR[@]}" -gt "${MAX_MAP_FILES}" ]]; then
print_output "[*] INFO: A huge number of files (${#ALL_EXEC_FILES_ARR[@]} -gt ${MAX_MAP_FILES}) detected ... limit the module to a maximum of ${MAX_MAP_FILES} executables only"
if [[ -f "${P99_CSV_LOG}" ]]; then
mapfile -t ALL_EXEC_FILES_ARR < <(grep "ELF\|executable\|script" "${P99_CSV_LOG}" | cut -d ';' -f2 | grep -v "\.raw" | head -n "${MAX_MAP_FILES}")
mapfile -t ALL_EXEC_FILES_ARR < <(grep "ELF\|executable\|script" "${P99_CSV_LOG}" | cut -d ';' -f2 | grep -v "\.raw" | head -n "${MAX_MAP_FILES}" || true)
else
# quick and dirty backup solution to not handle all the executable files
mapfile -t ALL_EXEC_FILES_ARR < <(find "${FIRMWARE_PATH}" -type f -executable ! -name "*.raw" 2>/dev/null | sort -u | head -n "${MAX_MAP_FILES}")
mapfile -t ALL_EXEC_FILES_ARR < <(find "${FIRMWARE_PATH}" -type f -executable ! -name "*.raw" 2>/dev/null | sort -u | head -n "${MAX_MAP_FILES}" || true)
fi
fi
print_ln ""
+2 -2
View File
@@ -259,8 +259,8 @@ s22_vuln_check() {
write_csv_AI_tmp "${lS22_SOURCE_DIR}/${lPHP_SCRIPT_NAME}.log" "${lAI_ANCHOR}" "${lGPT_PRIO_}" "${GPT_QUESTION}" "${lPHP_SCRIPT_}" "" ""
# add AI link
printf '%s\n\n' "" >>"${lPHP_SCRIPT_}"
write_anchor_AI "${lAI_ANCHOR}" "${lPHP_SCRIPT_}"
printf '%s\n\n' "" >>"${lPHP_LOG}"
write_anchor_AI "${lAI_ANCHOR}" "${lPHP_LOG}"
printf '%s\n\n' "" >>"${lS22_SOURCE_DIR}/${lPHP_SCRIPT_NAME}.log"
write_anchor_AI "${lAI_ANCHOR}" "${lS22_SOURCE_DIR}/${lPHP_SCRIPT_NAME}.log"
fi
+4 -4
View File
@@ -696,10 +696,10 @@ final_log_kernel_vulns() {
local lCNT_PATHS_FOUND_WRONG_ARCH=0
if [[ -s "${TMP_DIR}/s25_counting.tmp" ]]; then
lCNT_PATHS_UNK=$(grep -c "lCNT_PATHS_UNK" "${TMP_DIR}/s25_counting.tmp")
lCNT_PATHS_NOT_FOUND=$(grep -c "lCNT_PATHS_NOT_FOUND" "${TMP_DIR}/s25_counting.tmp")
lCNT_PATHS_FOUND=$(grep -c "lCNT_PATHS_FOUND" "${TMP_DIR}/s25_counting.tmp")
lCNT_PATHS_FOUND_WRONG_ARCH=$(grep -c "lCNT_PATHS_FOUND_WRONG_ARCH" "${TMP_DIR}/s25_counting.tmp")
lCNT_PATHS_UNK=$(grep -c "lCNT_PATHS_UNK" "${TMP_DIR}/s25_counting.tmp" || true)
lCNT_PATHS_NOT_FOUND=$(grep -c "lCNT_PATHS_NOT_FOUND" "${TMP_DIR}/s25_counting.tmp" || true)
lCNT_PATHS_FOUND=$(grep -c "lCNT_PATHS_FOUND" "${TMP_DIR}/s25_counting.tmp" || true)
lCNT_PATHS_FOUND_WRONG_ARCH=$(grep -c "lCNT_PATHS_FOUND_WRONG_ARCH" "${TMP_DIR}/s25_counting.tmp" || true)
fi
print_output "[*] Found ${ORANGE}${lCNT_PATHS_UNK}${NC} advisories with missing vulnerable path details"
+1 -1
View File
@@ -55,7 +55,7 @@ S27_perl_check() {
max_pids_protection "${MAX_MOD_THREADS}" lWAIT_PIDS_S27
done
[[ "${THREADED}" -eq 1 ]] && wait_for_pid "${lWAIT_PIDS_S27[@]}"
wait_for_pid "${lWAIT_PIDS_S27[@]}"
[[ -d "${TMP_DIR}/s27" ]] && (rm -r "${TMP_DIR}"/s27 || true)
if [[ -f "${TMP_DIR}/S27_VULNS.tmp" ]]; then
+3 -2
View File
@@ -29,7 +29,8 @@ S28_java_check() {
local lJNAME=""
local lJAVA_VULNS_CNT=0
local lWAIT_PIDS_S28=()
local MAX_MOD_THREADS=$((MAX_MOD_THREADS/2))
# shadow the original MAX_MOD_THREADS as adjusted local variable
local MAX_MOD_THREADS=$(( MAX_MOD_THREADS > 1 ? MAX_MOD_THREADS / 2 : 1 ))
export JAVA_DECOMPILER="${EXT_DIR}/vineflower-1.11.2.jar"
@@ -90,7 +91,7 @@ S28_java_check() {
# AI integration
local lAI_ANCHOR=""
lAI_ANCHOR="$(openssl rand -hex 8)"
lAI_PRIO=4
local lAI_PRIO=4
write_csv_AI_tmp "${lJAVA_SOURCE_FILE}.log" "${lAI_ANCHOR}" "${lAI_PRIO}" "NA" "${lSEMGREP_RESULT}" "" ""
printf '%s\n\n' "" >>"${lJAVA_SOURCE_FILE}.log"
write_anchor_AI "${lAI_ANCHOR}" "${lJAVA_SOURCE_FILE}.log"
+21 -12
View File
@@ -1,6 +1,7 @@
# EMBA - EMBEDDED LINUX ANALYZER
#
# Copyright 2020-2026 Siemens Energy AG
# Copyright 2020-2025 Siemens Energy AG
# Copyright 2020-2023 Siemens AG
#
# EMBA comes with ABSOLUTELY NO WARRANTY. This is free software, and you are
# welcome to redistribute it under the terms of the GNU General Public License.
@@ -8,10 +9,10 @@
#
# EMBA is licensed under GPLv3
#
# Author(s): Benedikt Kuehne
# Contributor(s): Michael Messner
# Author(s): Michael Messner, Pascal Eckmann
#
# Description: Profile for AI usage
# Description: This is a default EMBA profile. You can Use it as a template for your own profiles
# or start emba with "-p default-scan.emba" to use it
export FORMAT_LOG=1
export THREADED=1
@@ -20,25 +21,34 @@ export HTML=1
# QEMULATION - for user-mode emulation to generate an optimised SBOM
export QEMULATION=1
# 0 -> off, 1-> unpaid GPT plan, 2 -> no GPT rate-limit, 3 -> LocalAI Server
export AI_OPTION=3
# disable yara tests (s110)
export YARA=0
# extended binary tests are now only testing non-linux binaries. With this mechanism we can enable
# it in the default profile.
export BINARY_EXTENDED=1
# we test only the first 10 binaries with cwe_checker and extended semgrep rules
export MAX_EXT_CHECK_BINS=15
# VEX metrics (check for KEV, Exploits, EPSS)
export VEX_METRICS=1
# disable long running modules - enable them if needed
export MODULE_BLACKLIST+=( "S10_binaries_basic_check" "S15_radare_decompile_checks" "S26_kernel_vuln_verifier" "S99_grepit" "S110_yara_check" "F20_vul_aggregator" )
# the following modules are long running modules which are disabled in the default profile
export MODULE_BLACKLIST+=( "S10_binaries_basic_check" "S15_radare_decompile_checks" "S99_grepit" "S110_yara_check" "F20_vul_aggregator" )
# enable silent mode and status bar
export DISABLE_STATUS_BAR=1
export SILENT=0
export DISABLE_STATUS_BAR=0
export SILENT=1
# enable AI connection:
# 0 -> off, 1-> unpaid GPT plan, 2 -> no GPT rate-limit, 3 -> LocalAI Server
export AI_OPTION=3
# enable EMBA binary map dependency generator
EMBA_MAP_GENERATOR=0
EMBA_MAP_GENERATOR=1
MAX_MAP_FILES=1000
SVG_BUILD_TIMEOUT="12h"
# we output the profile only at the beginning - outside the docker environment
if [[ $IN_DOCKER -ne 1 ]] ; then
@@ -46,7 +56,6 @@ if [[ $IN_DOCKER -ne 1 ]] ; then
print_output "$(indent "$(orange "Activate multi threading")")" "no_log"
print_output "$(indent "$(orange "Prints only relative paths")")" "no_log"
print_output "$(indent "$(orange "Activates web report creation in log path")")" "no_log"
print_output "$(indent "$(orange "Activates AI checks via OpenAI-API")")" "no_log"
if [[ "$USE_DOCKER" -ne 1 ]]; then
print_output "$(indent "$(orange "Enables automated qemu emulation tests (WARNING this module could harm your host!)")")" "no_log"
else
+61
View File
@@ -0,0 +1,61 @@
# EMBA - EMBEDDED LINUX ANALYZER
#
# Copyright 2020-2026 Siemens Energy AG
#
# EMBA comes with ABSOLUTELY NO WARRANTY. This is free software, and you are
# welcome to redistribute it under the terms of the GNU General Public License.
# See LICENSE file for usage of this software.
#
# EMBA is licensed under GPLv3
#
# Author(s): Benedikt Kuehne
# Contributor(s): Michael Messner
#
# Description: Profile for AI usage
export FORMAT_LOG=1
export THREADED=1
export SHORT_PATH=1
export HTML=1
# QEMULATION - for user-mode emulation to generate an optimised SBOM
export QEMULATION=1
# 0 -> off, 1-> unpaid GPT plan, 2 -> no GPT rate-limit, 3 -> LocalAI Server
export AI_OPTION=3
# extended binary tests are now only testing non-linux binaries. With this mechanism we can enable
# it in the default profile.
export BINARY_EXTENDED=1
# VEX metrics (check for KEV, Exploits, EPSS)
export VEX_METRICS=1
# disable long running modules - enable them if needed
export MODULE_BLACKLIST+=( "S10_binaries_basic_check" "S15_radare_decompile_checks" "S26_kernel_vuln_verifier" "S99_grepit" "S110_yara_check" "F20_vul_aggregator" )
# enable silent mode and status bar
export DISABLE_STATUS_BAR=1
export SILENT=0
# enable EMBA binary map dependency generator
EMBA_MAP_GENERATOR=0
# we output the profile only at the beginning - outside the docker environment
if [[ $IN_DOCKER -ne 1 ]] ; then
print_output "$(indent "$(orange "Adds ANSI color codes to log")")" "no_log"
print_output "$(indent "$(orange "Activate multi threading")")" "no_log"
print_output "$(indent "$(orange "Prints only relative paths")")" "no_log"
print_output "$(indent "$(orange "Activates web report creation in log path")")" "no_log"
print_output "$(indent "$(orange "Activates AI checks via OpenAI-API")")" "no_log"
if [[ "$USE_DOCKER" -ne 1 ]]; then
print_output "$(indent "$(orange "Enables automated qemu emulation tests (WARNING this module could harm your host!)")")" "no_log"
else
print_output "$(indent "$(orange "Enables automated qemu emulation tests")")" "no_log"
fi
print_output "$(indent "$(orange "Runs EMBA in docker container")")" "no_log"
print_output "$(indent "$(orange "Disable EMBA module via profile")")" "no_log"
for MODULE_ in "${MODULE_BLACKLIST[@]}"; do
print_output "$(indent "$(orange "Blacklisted module: $MODULE_")")" "no_log"
done
export USE_DOCKER=1
fi