FROM python:3.11-slim

ARG TARGETARCH

ENV PROJECT_PATH=/opt/deploy
ENV LOG_PATH=/var/log/intel_owl/malware_tools_analyzers
ENV USER=malware_tools_analyzers-user
# this is required for if/else statements
SHELL ["/bin/bash", "-c"]

# update and install packages
# line 3: ClamAV deps
# line 4: Box-JS deps
RUN DEBIAN_FRONTEND=noninteractive apt-get update -qq \
    && apt-get install -y --no-install-recommends wget git libssl3 swig g++ make libssl-dev libmagic1 vim unzip gosu \
    clamav clamdscan clamav-daemon clamav-freshclam \
    nodejs npm gcc m4

# Add a new low-privileged user
RUN useradd -ms /bin/bash ${USER} \
    && mkdir ${PROJECT_PATH} ${PROJECT_PATH}/qiling ${PROJECT_PATH}/stringsifter ${PROJECT_PATH}/peframe ${PROJECT_PATH}/apkid

WORKDIR ${PROJECT_PATH}

# Install Box-js
RUN npm install box-js@1.9.29 --global --production \
    && mkdir -p /tmp/boxjs \
    && chown -R ${USER}:${USER} /tmp/boxjs

# Install Mandiant's GoReSym
WORKDIR ${PROJECT_PATH}/goresym
RUN if [[ $TARGETARCH == "amd64" ]]; \
    then export GORESYM_ARCH="linux"; \
    else export GORESYM_ARCH="mac"; fi \
    && wget -q "https://github.com/mandiant/GoReSym/releases/download/v3.4/GoReSym-$GORESYM_ARCH.zip" \
    && unzip "GoReSym-$GORESYM_ARCH.zip" \
    && chmod +x GoReSym \
    && ln -s ${PROJECT_PATH}/goresym/GoReSym /usr/local/bin/goresym

# Build Mandiant's Stringsifter
WORKDIR ${PROJECT_PATH}/stringsifter
COPY requirements/stringsifter-requirements.txt stringsifter/wrapper.py ./
# Installed at system level because virtualenv does not work as expected
# ModuleNotFoundError: No module named 'pybind11' while trying to build
RUN pip3 install --no-cache-dir --upgrade pip \
    && pip3 install --no-cache-dir -r stringsifter-requirements.txt \
    && chmod +x wrapper.py

# Build Qiling
WORKDIR ${PROJECT_PATH}/qiling
COPY requirements/qiling-requirements.txt qiling/analyze.py ./
# keystone-engine does not compile for ARM
RUN if [[ $TARGETARCH == "amd64" ]]; then \
    python3 -m venv venv \
    && . venv/bin/activate \
    && pip3 install --no-cache-dir --upgrade pip \
    && pip3 install --no-cache-dir -r qiling-requirements.txt; fi

# Then Build every possible other Python application inside its virtual environment
# Build guelfo's PEFrame
WORKDIR ${PROJECT_PATH}/peframe
COPY requirements/peframe-requirements.txt ./
# peframe-ds 6.1.0 uses array.tostring() which was removed in Python 3.9+, patch it after install
RUN python3 -m venv venv \
    && . venv/bin/activate \
    && pip3 install --no-cache-dir --upgrade pip \
    && pip3 install --no-cache-dir -r peframe-requirements.txt --no-cache-dir \
    && sed -i 's/\.tostring()/\.tobytes()/g' venv/lib/python3.*/site-packages/peframe/modules/features.py

# Install guelfo's artifacts
# there is no version management on this project so we just pull the most recent changes
WORKDIR ${PROJECT_PATH}/artifacts
RUN python3 -m venv venv \
    && . venv/bin/activate \
    && pip3 install --no-cache-dir --upgrade pip \
    && git clone https://github.com/guelfoweb/artifacts.git \
    && cd artifacts \
    && pip install --no-cache-dir -r requirements.txt \
    && chmod +x artifacts.py

# Build APKiD
WORKDIR ${PROJECT_PATH}/apkid
COPY requirements/apkid-requirements.txt ./
RUN python3 -m venv venv \
    && . venv/bin/activate \
    && pip3 install --no-cache-dir --upgrade pip \
    && pip3 install --no-cache-dir -r apkid-requirements.txt
    
# Install DroidLysis
WORKDIR "${PROJECT_PATH}/droidlysis"
COPY requirements/droidlysis-requirements.txt ./
RUN python3 -m venv venv \
 && . venv/bin/activate \
 && pip3 install --no-cache-dir --upgrade pip \
 && pip3 install --no-cache-dir -r droidlysis-requirements.txt --no-cache-dir \
 && mkdir -p ~/softs \
 && cd ~/softs \
 && wget -q https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.9.3.jar \
 && wget -q https://bitbucket.org/JesusFreke/smali/downloads/baksmali-2.5.2.jar \
 && wget -q https://github.com/pxb1988/dex2jar/releases/download/v2.4/dex-tools-v2.4.zip \
 && unzip dex-tools-v2.4.zip \
 && rm -f dex-tools-v2.4.zip \
 && apt-get -y install --no-install-recommends openjdk-21-jdk \
 && mkdir -p "${PROJECT_PATH}/droidlysis/out" "${PROJECT_PATH}/droidlysis/conf" /root/.cache/droidlysis \
 && chown -R ${USER}:${USER} /root/.cache/droidlysis \
 && chmod 711 /root/ \
 && DROIDLYSIS_PATH="$(pip3 show droidlysis | grep Location | cut -d ' ' -f 2)" \
 && cp "${DROIDLYSIS_PATH}/conf/"*.conf "${PROJECT_PATH}/droidlysis/conf/" \
 && rm -f "${PROJECT_PATH}/droidlysis/conf/general.conf"
COPY ./droidlysis/general.conf ${PROJECT_PATH}/droidlysis/conf/general.conf

# MobSF
WORKDIR ${PROJECT_PATH}/mobsf
COPY requirements/mobsf-requirements.txt ./
RUN python3 -m venv venv \
    && . venv/bin/activate \
    && pip3 install --no-cache-dir --upgrade pip \
    && pip3 install --no-cache-dir -r mobsf-requirements.txt \
    && mkdir -p /root/.semgrep/ \
    && chown -R ${USER}:${USER} /root/.semgrep \
    && chmod 711 /root

# prepare fangfrisch installation
COPY crontab /etc/cron.d/crontab
RUN mkdir -m 0770 -p /var/lib/fangfrisch \
    && chgrp ${USER} /var/lib/fangfrisch \
    && touch /var/log/cron.log \
    && chmod 0644 /etc/cron.d/crontab /var/log/cron.log

# Build Flask REST API
WORKDIR ${PROJECT_PATH}/flask
COPY app.py requirements/flask-requirements.txt entrypoint.sh ./
RUN python3 -m venv venv \
    && . venv/bin/activate \
    && pip3 install --no-cache-dir --upgrade pip \
    && pip3 install --no-cache-dir -r flask-requirements.txt \
    && chmod +x entrypoint.sh

# Cleanup
RUN chown -R ${USER}:${USER} ${PROJECT_PATH} \
    && apt-get remove --purge -y wget git gcc \
    && apt-get clean \
    && apt-get autoclean \
    && apt-get autoremove -y \
    && rm -rf /var/lib/apt/lists/* /usr/share/doc/* /usr/share/man/* > /dev/null 2>&1

# adding ClamAV config files and sigs to respective volumes
# these copies are required in case a Docker Volume is used instead of a Bind volume (Swarm deployments)
COPY clamav/etc/* /etc/clamav/
COPY clamav/sigs/* /var/lib/clamav/
# Permission juggling for ClamAV Analyzer
RUN mkdir -p /var/run/clamav ${LOG_PATH} ${LOG_PATH}/clamav && \
    touch ${LOG_PATH}/gunicorn_access.log ${LOG_PATH}/gunicorn_errors.log && \
    chmod 755 /var/run/clamav && \
    chown -R ${USER}:${USER} /var/run/clamav ${LOG_PATH}

# Serve Flask application using gunicorn
EXPOSE 4002
ENTRYPOINT ["./entrypoint.sh"]