diff --git a/.github/workflows/titanshield-scan.yml b/.github/workflows/titanshield-scan.yml index 9f8cfab..87548de 100644 --- a/.github/workflows/titanshield-scan.yml +++ b/.github/workflows/titanshield-scan.yml @@ -1,8 +1,12 @@ -# TitanShield Security Scanner — Real E2E GitHub Actions workflow -# Scans APK/IPA via TitanShield API, polls for results, downloads SARIF, uploads to GitHub Security tab +# TitanShield Security Scanner — GitHub Actions +# Scans APK/IPA on push/PR, uploads SARIF, comments on PR, security gate +# +# Works with ALL repos (public + private): +# - Public repos: SARIF uploaded to Security tab +# - Private repos: Results in PR comment + downloadable artifacts # # Required secrets: -# TITANSHIELD_API_TOKEN — API token from https://titanshield.tech/profile +# TITANSHIELD_API_TOKEN — from https://titanshield.tech/profile name: TitanShield Security Scan @@ -15,7 +19,8 @@ on: permissions: contents: read - security-events: write # Required for SARIF upload to GitHub Security tab + security-events: write + pull-requests: write actions: read env: @@ -87,16 +92,18 @@ jobs: CRITICAL=$(echo "$STATUS" | jq -r '.severity_counts.Critical // 0') HIGH=$(echo "$STATUS" | jq -r '.severity_counts.High // 0') MEDIUM=$(echo "$STATUS" | jq -r '.severity_counts.Medium // 0') + LOW=$(echo "$STATUS" | jq -r '.severity_counts.Low // 0') VULNS=$(echo "$STATUS" | jq -r '.vulnerabilities_count // 0') echo "critical=$CRITICAL" >> $GITHUB_OUTPUT echo "high=$HIGH" >> $GITHUB_OUTPUT echo "medium=$MEDIUM" >> $GITHUB_OUTPUT + echo "low=$LOW" >> $GITHUB_OUTPUT echo "total=$VULNS" >> $GITHUB_OUTPUT echo "completed=true" >> $GITHUB_OUTPUT echo "" - echo "Scan complete: $VULNS vulnerabilities (C:$CRITICAL H:$HIGH M:$MEDIUM)" + echo "Scan complete: $VULNS vulnerabilities (C:$CRITICAL H:$HIGH M:$MEDIUM L:$LOW)" break fi @@ -107,33 +114,32 @@ jobs: fi done - - name: Download SARIF Report + - name: Download Reports if: steps.poll.outputs.completed == 'true' run: | AID=${{ steps.scan.outputs.analysis_id }} + + echo "Downloading SARIF..." curl -sL "$TITANSHIELD_URL/api/analysis/$AID/download/sarif" \ -H "X-API-Token: ${{ secrets.TITANSHIELD_API_TOKEN }}" \ -o titanshield-results.sarif - echo "SARIF downloaded:" - cat titanshield-results.sarif | jq '{schema: ."$schema", tool: .runs[0].tool.driver.name, results: (.runs[0].results | length)}' 2>/dev/null || echo "SARIF parse check failed" + echo "Downloading PDF..." + curl -sL "$TITANSHIELD_URL/api/analysis/$AID/download/pdf" \ + -H "X-API-Token: ${{ secrets.TITANSHIELD_API_TOKEN }}" \ + -o titanshield-report.pdf + + echo "SARIF: $(wc -c < titanshield-results.sarif) bytes" + echo "PDF: $(wc -c < titanshield-report.pdf) bytes" - name: Upload SARIF to GitHub Security if: steps.poll.outputs.completed == 'true' + continue-on-error: true uses: github/codeql-action/upload-sarif@v3 with: sarif_file: titanshield-results.sarif category: titanshield-android-sast - - name: Download PDF Report - if: steps.poll.outputs.completed == 'true' - run: | - AID=${{ steps.scan.outputs.analysis_id }} - curl -sL "$TITANSHIELD_URL/api/analysis/$AID/download/pdf" \ - -H "X-API-Token: ${{ secrets.TITANSHIELD_API_TOKEN }}" \ - -o titanshield-report.pdf - echo "PDF: $(du -h titanshield-report.pdf | cut -f1)" - - name: Upload Artifacts if: always() && steps.poll.outputs.completed == 'true' uses: actions/upload-artifact@v4 @@ -142,29 +148,70 @@ jobs: path: | titanshield-results.sarif titanshield-report.pdf - retention-days: 30 + retention-days: 90 + + - name: Comment on PR + if: github.event_name == 'pull_request' && steps.poll.outputs.completed == 'true' + uses: actions/github-script@v7 + with: + script: | + const critical = parseInt('${{ steps.poll.outputs.critical }}') || 0; + const high = parseInt('${{ steps.poll.outputs.high }}') || 0; + const medium = parseInt('${{ steps.poll.outputs.medium }}') || 0; + const low = parseInt('${{ steps.poll.outputs.low }}') || 0; + const total = parseInt('${{ steps.poll.outputs.total }}') || 0; + const aid = '${{ steps.scan.outputs.analysis_id }}'; + + const status = critical > 0 ? ':red_circle: FAILED' : ':green_circle: PASSED'; + const runUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`; + + const body = `## TitanShield Security Scan ${status} + + | Severity | Count | + |----------|-------| + | Critical | ${critical} | + | High | ${high} | + | Medium | ${medium} | + | Low | ${low} | + | **Total** | **${total}** | + + **Reports:** [Download from workflow artifacts](${runUrl}) + **Dashboard:** [View in TitanShield](https://titanshield.tech/analysis/s/${aid}) + + --- + Powered by [TitanShield](https://titanshield.tech) — Mobile & Web Security Testing`; + + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: body + }); - name: Security Gate if: steps.poll.outputs.completed == 'true' run: | CRITICAL=${{ steps.poll.outputs.critical }} HIGH=${{ steps.poll.outputs.high }} + TOTAL=${{ steps.poll.outputs.total }} - echo "## Security Gate" >> $GITHUB_STEP_SUMMARY + echo "## TitanShield Security Gate" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY echo "| Severity | Count |" >> $GITHUB_STEP_SUMMARY echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY echo "| Critical | $CRITICAL |" >> $GITHUB_STEP_SUMMARY echo "| High | $HIGH |" >> $GITHUB_STEP_SUMMARY echo "| Medium | ${{ steps.poll.outputs.medium }} |" >> $GITHUB_STEP_SUMMARY - echo "| **Total** | **${{ steps.poll.outputs.total }}** |" >> $GITHUB_STEP_SUMMARY + echo "| Low | ${{ steps.poll.outputs.low }} |" >> $GITHUB_STEP_SUMMARY + echo "| **Total** | **$TOTAL** |" >> $GITHUB_STEP_SUMMARY if [ "$CRITICAL" -gt 0 ]; then echo "" >> $GITHUB_STEP_SUMMARY - echo "> **FAILED** — $CRITICAL critical vulnerabilities found" >> $GITHUB_STEP_SUMMARY - echo "::error::Security gate failed: $CRITICAL critical vulnerabilities" + echo "> :red_circle: **FAILED** — $CRITICAL critical vulnerabilities found" >> $GITHUB_STEP_SUMMARY + echo "::error::Security gate FAILED: $CRITICAL critical vulnerabilities" exit 1 fi echo "" >> $GITHUB_STEP_SUMMARY - echo "> **PASSED** — No critical vulnerabilities" >> $GITHUB_STEP_SUMMARY - echo "Security gate passed" + echo "> :green_circle: **PASSED** — No critical vulnerabilities" >> $GITHUB_STEP_SUMMARY + echo "Security gate PASSED ($TOTAL findings, 0 critical)"