Skip to content

Instantly share code, notes, and snippets.

@ekristen
Last active September 18, 2018 04:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ekristen/f5b6081f80320c36cb5e16256926dbf5 to your computer and use it in GitHub Desktop.
Save ekristen/f5b6081f80320c36cb5e16256926dbf5 to your computer and use it in GitHub Desktop.
NowSecure Auto CI Script
#!/bin/bash
# NowSecure Auto CI Script
#
# Uploads a Binary to Auto, performs preflight checks,
# then performs an assessment, and compares summary score
# and exits non-zero if score is lower than score threshold
#
# ---------------------------------------------------------------
#
# Basic Usage
#
# export TOKEN=(API_TOKEN)
# bash auto-ci.sh /path/to/binary/of/android/or/ios
#
# -OR-
#
# TOKEN=(API_TOKEN) bash auto-ci.sh /path/to/binary/of/android/or/ios
#
# ---------------------------------------------------------------
#
# API Call Steps
# Step 1 - POST /binary/
# Step 2 - GET /binary/:digest/analysis
# Step 3 - POST /app/:platform/:package/assessment/
# Step 4 - GET /app/:platform/:package/assessment/:task/report
#
# ---------------------------------------------------------------
#
# This script require only a single environment variable by default
# called TOKEN, this is the API Token used to authenticate to the API
# You can optionally provide a SCORE_THRESHOLD which is set at 50 by
# default. If the app scores below this threshold, the script exits
# non-zero as the value of the score.
#
# If you know preflight has already been completed you can skip it by
# setting SKIP_PREFLIGHT to any value.
#
# If you know analysis was already started, you can skip starting analysis
# again by passing ANALYSIS_TASK set to the appropriate ID from a previous
# run.
#
# To debug the script set DEBUG to any value
#
if [ "x${DEBUG}" != "x" ]; then
set -x
fi
BASEURL=${BASEURL:-"https://lab-api.nowsecure.com"}
TOKEN=${TOKEN:-""}
SCORE_THRESHOLD=${SCORE_THRESHOLD:-50}
if [ "x${BASEURL}" == "x" ]; then
echo "BASEURL env var missing or empty."
exit 101
fi
if [ "x${TOKEN}" == "x" ]; then
echo "TOKEN env var missing or empty."
exit 102
fi
if [ $# -ne 1 ]; then
echo "Usage: $0 <android_or_ios_binary>"
exit 103
fi
if [ ! -f ${1} ]; then
echo "ERROR: File does not exist."
exit 104
fi
BINARY_FILE=${1}
OUTPUT_UPLOAD=""
OUTPUT_ANALYSIS=""
OUTPUT_SUMMARY=""
APP_DIGEST=""
APP_PLATFORM=""
APP_PACKAGE=""
ANALYSIS_TASK=${ANALYSIS_TASK:-""}
info () {
echo " [INFO] ${1}"
}
warning () {
echo " [WARN] ${1} - Details: ${2}"
}
fatal () {
echo ""
echo "[FATAL] ${1}"
echo ""
exit 10
}
retry() {
local retry_max=$1
shift
local count=$retry_max
while [ $count -gt 0 ]; do
"$@" && break
count=$(($count - 1))
sleep $(echo "1.2" "$count" | awk '{printf "%4.3f\n",$1*$2}')
done
[ $count -eq 0 ] && {
warning "Retry failed [$retry_max]: $@" >&2
return 1
}
return 0
}
download_jq () {
if [ "x$(which jq)" != "x" ]; then
info "jq already present"
return 0
fi
info "Downloading jq ... "
wget https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64
}
upload_binary () {
OUTPUT_UPLOAD=$(curl -s -H "Authorization: Bearer ${TOKEN}" ${BASEURL}/binary/ --data-binary @${BINARY_FILE})
if [ $? -ne 0 ]; then
echo $OUTPUT
return 1
fi
APP_DIGEST=$(echo $OUTPUT_UPLOAD | jq -rM '.digest')
APP_PLATFORM=$(echo $OUTPUT_UPLOAD | jq -rM '.platform')
APP_PACKAGE=$(echo $OUTPUT_UPLOAD | jq -rM '.package')
info "Upload Successful!"
return 0
}
run_preflight () {
if [ "x${SKIP_PREFLIGHT}" != "x" ]; then
warn "Skipping Preflight"
return 0
fi
OUTPUT_PREFLIGHT=$(curl -s -H "Authorization: Bearer ${TOKEN}" -XGET ${BASEURL}/binary/${APP_DIGEST}/analysis)
if [ $? -ne 0 ]; then
warning "Unable to trigger preflight" $OUTPUT_PREFLIGHT
return 1
fi
info "Preflight ran successfully"
return 0
}
run_analysis () {
if [ "x${ANALYSIS_TASK}" != "x" ]; then
info "Skipping Starting Analysis already have Task ID ..."
return 0
fi
OUTPUT_ANALYSIS=$(curl -s -H "Authorization: Bearer ${TOKEN}" -XPOST ${BASEURL}/app/${APP_PLATFORM}/${APP_PACKAGE}/assessment/)
ANALYSIS_TASK=$(echo "${OUTPUT_ANALYSIS}" | jq -rM '.task')
if [ "x${ANALYSIS_TASK}" == "x" ]; then
warning "Unable to obtain TASK ID for the Analysis"
return 1
fi
info "Queued App for Analysis, Task ID: ${ANALYSIS_TASK}"
return 0
}
poll_summary () {
OUTPUT_SUMMARY=$(curl -s -H "Authorization: Bearer ${TOKEN}" -XGET ${BASEURL}/assessment/${ANALYSIS_TASK}/summary${GROUP_URL})
if [ $? -ne 0 ]; then
warning "Unable to poll for analysis results"
return 1
fi
if [ "x${OUTPUT_SUMMARY}" == "x" ]; then
info "Polling for Summary ..."
return 1
fi
return 0
}
parse_results () {
SCORE=$(echo "${OUTPUT_SUMMARY}" | jq -rM '.score' | awk -F'.' '{ print $1 }')
if [ $SCORE -lt $SCORE_THRESHOLD ]; then
warning "Application Scored Lower Than Threshold (Score: ${SCORE}, Threshold: ${SCORE_THRESHOLD})"
exit $SCORE
fi
info "Score: ${SCORE}"
info "Analysis Complete"
info "Full Results"
echo $OUTPUT_SUMMARY | jq .
exit 0
}
retry 3 download_jq || fatal "Download jq"
retry 3 upload_binary || fatal "Uploading Binary"
retry 3 run_preflight || fatal "Running Preflight Checks"
retry 3 run_analysis || fatal "Running Analysis"
retry 60 poll_summary || fatal "Waiting for Analysis to Complete"
retry 3 parse_results || fatal "Unable to Parse Results"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment