44 workflow_dispatch :
55
66permissions :
7- contents : write
8- id-token : write # Required for SLSA provenance signing
9- actions : read # Required for SLSA provenance
7+ contents : read # default; jobs that need more grant it at job level
108
119jobs :
1210 build :
1311 runs-on : ubuntu-latest
12+ permissions :
13+ contents : read
1414 env :
1515 ANDROID_STORE_PASSWORD : ${{ secrets.ANDROID_STORE_PASSWORD }}
1616 ANDROID_KEY_PASSWORD : ${{ secrets.ANDROID_KEY_PASSWORD }}
7979 virustotal :
8080 needs : build
8181 runs-on : ubuntu-latest
82+ permissions : {}
8283 outputs :
8384 default_report : ${{ steps.scan.outputs.default_report }}
8485 armv7_report : ${{ steps.scan.outputs.armv7_report }}
@@ -96,10 +97,11 @@ jobs:
9697 VT_API_KEY : ${{ secrets.VIRUSTOTAL_API_KEY }}
9798 VERSION : ${{ needs.build.outputs.version_name }}
9899 run : |
99- RESULT_URL=""
100- RESULT_STATUS=""
100+ # Scans both APKs in parallel; results written to temp files to avoid
101+ # race conditions between background subshells.
101102 upload_and_poll() {
102103 local file="$1"
104+ local outprefix="$2"
103105 local sha256
104106 sha256=$(sha256sum "$file" | awk '{print $1}')
105107 local size_bytes
@@ -111,8 +113,8 @@ jobs:
111113 --url https://www.virustotal.com/api/v3/files/upload_url \
112114 --header "x-apikey: $VT_API_KEY" | jq -r '.data')
113115 if [ -z "$upload_url" ] || [ "$upload_url" = "null" ]; then
114- RESULT_URL= "https://www.virustotal.com/gui/file/$sha256/detection"
115- RESULT_STATUS= "⬜ N/A"
116+ printf '%s' "https://www.virustotal.com/gui/file/$sha256/detection" > "${outprefix}.url "
117+ printf '%s' "⬜ N/A" > "${outprefix}.status "
116118 return
117119 fi
118120 fi
@@ -122,8 +124,8 @@ jobs:
122124 --header "x-apikey: $VT_API_KEY" \
123125 --form "file=@$file" | jq -r '.data.id')
124126 if [ -z "$analysis_id" ] || [ "$analysis_id" = "null" ]; then
125- RESULT_URL= "https://www.virustotal.com/gui/file/$sha256/detection"
126- RESULT_STATUS= "⬜ N/A"
127+ printf '%s' "https://www.virustotal.com/gui/file/$sha256/detection" > "${outprefix}.url "
128+ printf '%s' "⬜ N/A" > "${outprefix}.status "
127129 return
128130 fi
129131 local last_response=""
@@ -143,19 +145,27 @@ jobs:
143145 harmless=$(echo "$last_response" | jq -r '.data.attributes.stats.harmless // 0')
144146 total=$((malicious + suspicious + undetected + harmless))
145147 detected=$((malicious + suspicious))
148+ local result_status
146149 if [ "$detected" -eq 0 ]; then
147- RESULT_STATUS ="✅ ${detected}/${total} Clean"
150+ result_status ="✅ ${detected}/${total} Clean"
148151 else
149- RESULT_STATUS ="⚠️ ${detected}/${total} Detected"
152+ result_status ="⚠️ ${detected}/${total} Detected"
150153 fi
151- RESULT_URL="https://www.virustotal.com/gui/file/$sha256/detection"
154+ printf '%s' "https://www.virustotal.com/gui/file/$sha256/detection" > "${outprefix}.url"
155+ printf '%s' "$result_status" > "${outprefix}.status"
152156 }
153- upload_and_poll "PlainApp-${VERSION}-default.apk"
154- DEFAULT_REPORT="$RESULT_URL"
155- DEFAULT_STATUS="$RESULT_STATUS"
156- upload_and_poll "PlainApp-${VERSION}-armeabi-v7a.apk"
157- ARMV7_REPORT="$RESULT_URL"
158- ARMV7_STATUS="$RESULT_STATUS"
157+ # Launch both scans in parallel
158+ upload_and_poll "PlainApp-${VERSION}-default.apk" /tmp/vt_default &
159+ PID_DEFAULT=$!
160+ upload_and_poll "PlainApp-${VERSION}-armeabi-v7a.apk" /tmp/vt_armv7 &
161+ PID_ARMV7=$!
162+ # Wait for both to finish
163+ wait $PID_DEFAULT
164+ wait $PID_ARMV7
165+ DEFAULT_REPORT=$(cat /tmp/vt_default.url)
166+ DEFAULT_STATUS=$(cat /tmp/vt_default.status)
167+ ARMV7_REPORT=$(cat /tmp/vt_armv7.url)
168+ ARMV7_STATUS=$(cat /tmp/vt_armv7.status)
159169 {
160170 echo "default_report=$DEFAULT_REPORT"
161171 echo "armv7_report=$ARMV7_REPORT"
0 commit comments