[Fortify] FPR Report 스크립트

#!/bin/bash
#####################################################################
#
# Fortify SCA FPR파일 분석 스크립트
#
# 이 스크립트는 FPR파일을 분석하여 아래의 파일을 출력한다.
# 1. CSV포맷의 요약,상세 리포트
# 2. TXT포맷의 Suppressed된 취약점의 IID 리스트
#
# 윈도우에서 실행 시:
# bash -c "./fpr_report.sh webgoat.fpr"
#
# by 이존석(hasu0707@esvali.com)
#
#####################################################################
DEBUG_ON=1
VERSION=0.2
FILTER_SET="Security Auditor View"
#FILTER_SET="Quick View"
WIN_MINGW_DIR="/cygdrive/c/PortableApps/cmd_cygwin_x86_64"
FPR_TMP_DIR=./fpr_tmp
REPORT_TEMPLATE=./my_report_template.xml
SUPPRESSED_LIST_SUFFIX=suppressed_list.txt
TMP_FILE1=./tmpfile.txt

#####################################################################
#
# 사용방법 출력
#
#####################################################################
func_usage() {
  echo "$0 ver.${VERSION}"
  echo
  echo "usage: $0 <FPR file>"
}

#####################################################################
#
# 초기화
# $1 : FPR 파일명
#
#####################################################################
func_init() {
  if [ ${DEBUG_ON} -eq 1 ]
  then
    echo ">> func_init()"
  fi

  if [ -z "${WINDIR}" ]; then
    IS_WINDOWS=0
  else
    IS_WINDOWS=1
  fi

  if [ ${IS_WINDOWS} -eq 1 ]; then
    DEVNULL=null.dev
    WINCMD="/cygdrive/c/Windows/System32/cmd.exe /C"
    FORTIFY_HOME="/cygdrive/c/Program Files/Fortify/Fortify_SCA_and_Apps_18.10"
    REPORT_GENERATOR="ReportGenerator.bat"
    PATH=${FORTIFY_HOME}/bin:${WIN_MINGW_DIR}/bin:${PATH}
  else
    DEVNULL=/dev/null
    WINCMD=""
    FORTIFY_HOME="/opt/Fortify/Fortify_SCA_and_Apps_18.20"
    REPORT_GENERATOR="ReportGenerator"
    PATH=${FORTIFY_HOME}/bin:${PATH}
  fi

  BUILD_ID=$(basename $1 .fpr)
}

#####################################################################
#
# 필요한 유틸리티가 있는지 검사
#
#####################################################################
func_check_utils() {
  if [ ${DEBUG_ON} -eq 1 ]
  then
    echo ">> func_check_utils()"
  fi

  local IS_EXIT=0;
  UTILNAMES=( "xmllint" "unzip" "sed" "basename")

  # 윈도우 유틸리티 체킹
  if [ ${IS_WINDOWS} -eq 1 ]; then
    if [ ! -e ${WIN_MINGW_DIR}/bin/xmllint ] || [ ! -e ${WIN_MINGW_DIR}/bin/unzip ] || [ ! -e ${WIN_MINGW_DIR}/bin/sed ] || [ ! -e ${WIN_MINGW_DIR}/bin/basename ]; then
      echo "ERROR: ${WIN_MINGW_DIR} not found !"
      IS_EXIT=1
    fi

    if [ ${IS_EXIT} -ne 0 ]; then
      exit 1
    fi
    return
  fi

  # 리눅스 유틸리티 체킹
  for LOOP1 in "${UTILNAMES[@]}"
  do
    which ${LOOP1} > ${DEVNULL}
    if [ $? -ne 0 ]; then
      echo "ERROR: ${LOOP1} not found !"
      IS_EXIT=1
    fi
  done

  # ReportGenerator 체크
  if [ ! -e ${FORTIFY_HOME}/bin/${REPORT_GENERATOR} ]; then
    echo "ERROR: ${REPORT_GENERATOR} not found !"
    IS_EXIT=1
  fi

  # Fortify Report Template 체크
  if [ ! -e ${REPORT_TEMPLATE} ]; then
    echo "ERROR: ${REPORT_TEMPLATE} not found !"
    IS_EXIT=1
  fi

  # 없는 유틸리티가 있으면 스크립트 종료
  if [ ${IS_EXIT} -ne 0 ]; then
    exit 1
  fi

  unset UTILNAMES
}

#####################################################################
#
# 임시 및 불필요한 파일 삭제
#
#####################################################################
func_clean() {
  if [ ${DEBUG_ON} -eq 1 ]
  then
    echo ">> func_clean()"
  fi

  if [ ${IS_WINDOWS} -eq 1 ]; then
    rm -f ${DEVNULL}
  fi

  rm -rf ${FPR_TMP_DIR}
  rm -f ${TMP_FILE1}
  rm -f ${BUILD_ID}.xml
}

#####################################################################
#
# FPR파일 unzip
# $1 : fpr 파일명
#
#####################################################################
func_unzip() {
  if [ ${DEBUG_ON} -eq 1 ]
  then
    echo ">> func_unzip()"
  fi

  if [ -d ${FPR_TMP_DIR} ]
  then
    rm -rf ${FPR_TMP_DIR}
  fi
  mkdir ${FPR_TMP_DIR}
  unzip $1 -d ${FPR_TMP_DIR} audit.fvdl audit.xml filtertemplate.xml &> ${DEVNULL}
}

#####################################################################
#
# FPR파일에서 Suppressed된 취약점들의 IID를 뽑아서 저장한다.
# $1 : suppressed 목록이 기록될 파일명
#
#####################################################################
func_write_suppressed_list() {
  local S_COUNT=1
  local RET_VAL=0

  if [ ${DEBUG_ON} -eq 1 ]
  then
    echo ">> func_write_suppressed_list()"
  fi

  rm -f ${1}
  while [ ${RET_VAL} -eq 0 ]
  do
    xmllint \
--encode UTF-8 --nowarning --noblanks \
--xpath "//*[local-name()='Audit']/*[local-name()='IssueList']/*[local-name()='Issue'][${S_COUNT}]/@instanceId" \
./fpr_tmp/audit.xml \
>> ${1} 2> ${DEVNULL}
    RET_VAL=$?
    if [ ${RET_VAL} -eq 0 ]
    then
      S_COUNT=$((S_COUNT + 1))
    fi
    echo >> ${1}
  done

  # 불필요한 문자열 및 빈줄 삭제
  sed -i "s/ instanceId=\"//g" ${1}
  sed -i "s/\"$//g" ${1}
  sed -i "/^$/d" ${1}

  # Suppressed된 갯수 출력
  if [ ${DEBUG_ON} -eq 1 ]
  then
    echo ">> suppressed count: $((S_COUNT - 1))"
  fi

  return $((S_COUNT - 1))
}

#####################################################################
#
# ReportGenerator를 사용하여 XML 리포트를 뽑는다.
# $1 : 입력 FPR 파일명
# $2 : 출력 XML 파일명
#
#####################################################################
func_reportgenerator() {
  if [ ${DEBUG_ON} -eq 1 ]
  then
    echo ">> func_reportgenerator()"
  fi

  ${WINCMD} "${REPORT_GENERATOR}" \
-format xml \
-template ${REPORT_TEMPLATE} \
-filterSet "${FILTER_SET}" \
-showSuppressed -source $1 -f $2
}

#####################################################################
#
# ReportGenerator로 만든 XML을 파싱하여 상세 CSV 리포트를 만든다
# $1 : 입력 XML 파일명
# $2 : 출력 CSV 파일명
#
#####################################################################
func_xml_to_csv_detail() {
  if [ ${DEBUG_ON} -eq 1 ]
  then
    echo ">> func_xml_to_csv_detail()"
  fi

  xmllint --encode UTF-8 --xpath \
"/ReportDefinition/ReportSection[3]/SubSection[2]/IssueListing/Chart/GroupingSection/Issue/@iid|//Folder|//Kingdom|//Category|//Primary/FilePath|//Primary/LineStart" \
$1 > ${TMP_FILE1}

  sed -i \
-e "s/<\/LineStart>/\n/g" -e "s/<LineStart>//g" \
-e "s/<\/Category>/,/g" -e "s/<Category>//g" \
-e "s/<\/Folder>/,/g" -e "s/<Folder>//g" \
-e "s/<\/Kingdom>/,/g" -e "s/<Kingdom>//g" \
-e "s/<\/FilePath>/,/g" -e "s/<FilePath>//g" \
-e "s/ iid=\"//g" -e "s/<>//g" \
-e 's/\"/,/g' \
${TMP_FILE1}

  echo "IID,CATEGORY,FOLDER,KINGDOM,FILE_PATH,LINE_NO" > $2
  cat ${TMP_FILE1} >> $2
  rm -f ${TMP_FILE1}
}

#####################################################################
#
# ReportGenerator로 만든 XML을 파싱하여 요약 CSV 리포트를 만든다
# $1 : 입력 XML 파일명
# $2 : 출력 CSV 파일명
#
#####################################################################
func_xml_to_csv_summary() {
  if [ ${DEBUG_ON} -eq 1 ]
  then
    echo ">> func_xml_to_csv_summary()"
  fi

  xmllint --encode UTF-8 --xpath \
"/ReportDefinition/ReportSection[1]/SubSection[2]/IssueListing/Chart/GroupingSection/groupTitle" \
$1 > ${TMP_FILE1}
  echo >> ${TMP_FILE1}
  xmllint --encode UTF-8 --xpath \
"/ReportDefinition/ReportSection[1]/SubSection[2]/IssueListing/Chart/GroupingSection/@count" \
$1 >> ${TMP_FILE1}

  sed -i \
-e "s/<\/groupTitle>/,/g" -e "s/<groupTitle>//g" \
-e 's/ count=\"//g' -e 's/\"/,/g' -e "s/,$//g" \
${TMP_FILE1}

  cat ${TMP_FILE1} > $2
  rm -f ${TMP_FILE1}
}

#####################################################################
#
# main
#
#####################################################################

# 명령행 인수가 없으면 사용방법 출력하고 끝냄
if [ $# -lt 1 ]; then
  func_usage
  exit 3
fi

# FPR 파일 존재 여부 체크
if [ ! -e ${1} ]; then
  echo "ERROR: ${1} not found !"
  func_usage
  exit 2
fi

func_init ${1}
func_check_utils
func_unzip ${1}
func_write_suppressed_list ${BUILD_ID}_${SUPPRESSED_LIST_SUFFIX}
func_reportgenerator ${1} ${BUILD_ID}.xml
func_xml_to_csv_detail ${BUILD_ID}.xml ${BUILD_ID}_detail.csv
func_xml_to_csv_summary ${BUILD_ID}.xml ${BUILD_ID}_summary.csv
func_clean
위로 스크롤