Files
emba/modules/S28_java_check.sh
2026-07-17 12:29:56 +02:00

142 lines
5.9 KiB
Bash
Executable File

#!/bin/bash -p
# EMBA - EMBEDDED LINUX ANALYZER
#
# Copyright 2025-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): Michael Messner
# Description: Decompiles Java files
# In the future it should also perform security analysis on the extracted sources
# This module was inspired by the crass java checkers here:
# https://github.com/floyd-fuh/crass/blob/master/java-decompile.sh
S28_java_check() {
module_log_init "${FUNCNAME[0]}"
module_title "Decompiles Java files and performs security tests"
pre_module_reporter "${FUNCNAME[0]}"
local lS28_JAVA_SCRIPTS=0
local lJAVA_BINS_ARR=()
local lJAVA_BINARY=""
local lJNAME=""
local lJAVA_VULNS_CNT=0
local lWAIT_PIDS_S28=()
# 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"
write_csv_log "Script path" "Java issues detected" "common linux file" "vuln title" "vuln line nr" "vuln note"
mapfile -t lJAVA_BINS_ARR < <(grep "Java\ archive\|\.jar;\|\.war;\|\.java;\|\.class;" "${P99_CSV_LOG}" | sort -u || true)
if [[ "${#lJAVA_BINS_ARR[@]}" -eq 0 ]]; then
module_end_log "${FUNCNAME[0]}" 0
return
fi
for lJAVA_BINARY in "${lJAVA_BINS_ARR[@]}"; do
if [[ -f "${BASE_LINUX_FILES}" && "${FULL_TEST}" -eq 0 ]]; then
# if we have the base linux config file we only test non known Linux binaries
# with this we do not waste too much time on open source Linux stuff
lJNAME=$(basename "$(cut -d ';' -f2 <<<"${lJAVA_BINARY}")" 2>/dev/null) # field 2
if grep -E -q "^${lJNAME}$" "${BASE_LINUX_FILES}" 2>/dev/null; then
continue
fi
fi
((lS28_JAVA_SCRIPTS += 1))
s28_java_decompile "$(cut -d ';' -f2 <<<"${lJAVA_BINARY}")" & # field 2
local lTMP_PID="$!"
lWAIT_PIDS_S28+=("${lTMP_PID}")
max_pids_protection "${MAX_MOD_THREADS}" lWAIT_PIDS_S28
done
wait_for_pid "${lWAIT_PIDS_S28[@]}"
sub_module_title "Java security analysis"
local lJAVA_BIN_DIR=""
mapfile -t lSEMGREP_SOURCES_ARR < <(find "${LOG_PATH_MODULE}/java_decompile/" -mindepth 1 -maxdepth 1 -type d)
for lJAVA_BIN_DIR in "${lSEMGREP_SOURCES_ARR[@]}"; do
s28_java_semgrep "${lJAVA_BIN_DIR}" &
local lTMP_PID="$!"
lWAIT_PIDS_S28+=("${lTMP_PID}")
max_pids_protection "${MAX_MOD_THREADS}" lWAIT_PIDS_S28
done
wait_for_pid "${lWAIT_PIDS_S28[@]}"
if [[ -d "${LOG_PATH_MODULE}/java_semgrep/" ]]; then
local lSEMGREP_RESULTS_ARR=()
local lSEMGREP_RESULT=""
mapfile -t lSEMGREP_RESULTS_ARR < <(find "${LOG_PATH_MODULE}/java_semgrep/" -name "semgrep_*.json")
for lSEMGREP_RESULT in "${lSEMGREP_RESULTS_ARR[@]}"; do
if grep -q '\"results\":' "${lSEMGREP_RESULT}"; then
if jq '.results | length >0' "${lSEMGREP_RESULT}"; then
local lJAVA_SOURCE_FILE_ARR=()
mapfile -t lJAVA_SOURCE_FILE_ARR < <(jq -r '.results[].path' "${lSEMGREP_RESULT}" | sort -u)
lJAVA_VULNS_CNT=$((lJAVA_VULNS_CNT + "$(jq -r '.results[].check_id' "${lSEMGREP_RESULT}" | wc -l)"))
local lJAVA_SOURCE_FILE=""
for lJAVA_SOURCE_FILE in "${lJAVA_SOURCE_FILE_ARR[@]}"; do
print_output "[+] Semgrep security scanning results for ${ORANGE}$(basename "${lJAVA_SOURCE_FILE}")${NC}" "" "${lSEMGREP_RESULT}"
# we log ".log" for the web reporter
mv "${lJAVA_SOURCE_FILE}" "${lJAVA_SOURCE_FILE}.log" || true
print_output "$(indent "$(orange "Decompiled Java sources for ${ORANGE}$(basename "${lJAVA_SOURCE_FILE}")${NC}")")" "" "${lJAVA_SOURCE_FILE}.log"
# AI integration
local lAI_ANCHOR=""
lAI_ANCHOR="$(openssl rand -hex 8)"
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"
done
# Todo: color the affected lines of code
fi
fi
done
fi
local lJAVA_DECOMPILED=0
if [[ -d "${LOG_PATH_MODULE}/java_decompile/" ]]; then
lJAVA_DECOMPILED=$(find "${LOG_PATH_MODULE}/java_decompile/" -type f | wc -l)
fi
print_ln ""
print_output "[+] Found ${ORANGE}${lJAVA_VULNS_CNT} vulnerabilities${GREEN} in ${ORANGE}${lJAVA_DECOMPILED}${GREEN} decompiled Java binaries.${NC}"
write_log ""
write_log "[*] Statistics:${#lJAVA_BINS_ARR[@]}:${lJAVA_DECOMPILED}:${lJAVA_VULNS_CNT}"
module_end_log "${FUNCNAME[0]}" "${lJAVA_DECOMPILED}"
}
s28_java_semgrep() {
local lJAVA_BIN_DIR="${1:-}"
print_output "[*] Analysing ${ORANGE}${lJAVA_BIN_DIR}${NC} with semgrep" "no_log"
local lJ_ANALYSE_DIR="${LOG_PATH_MODULE}/java_semgrep/"
[[ ! -d "${lJ_ANALYSE_DIR}" ]] && mkdir -p "${lJ_ANALYSE_DIR}"
lJNAME="$(basename "${lJAVA_BIN_DIR}" 2>/dev/null)"
local lJ_ANALYSE_RESULTS="${lJ_ANALYSE_DIR}/semgrep_${lJNAME}.json"
semgrep --disable-version-check --metrics=off --severity ERROR --severity WARNING --json --config "${EXT_DIR}"/semgrep-rules/java "${lJAVA_BIN_DIR}" >"${lJ_ANALYSE_RESULTS}" || true
}
s28_java_decompile() {
local lJAVA_BINARY="${1:-}"
local lJNAME=""
lJNAME=$(basename "${lJAVA_BINARY}" 2>/dev/null | sed -e 's/:/_/g')
lJNAME=$(iconv -f UTF-8 -t ASCII//TRANSLIT <<<"${lJNAME}" || true)
local lJ_DECOMPILE_DIR="${LOG_PATH_MODULE}/java_decompile/${lJNAME}_${RANDOM}"
print_output "[*] Decompiling Java binary ${ORANGE}${lJNAME}${NC} to ${lJ_DECOMPILE_DIR}" "no_log"
[[ ! -d "${lJ_DECOMPILE_DIR}" ]] && mkdir -p "${lJ_DECOMPILE_DIR}"
java -jar "${JAVA_DECOMPILER}" --silent "${lJAVA_BINARY}" "${lJ_DECOMPILE_DIR}" || true
}