diff --git a/.github/workflows/aquasec_repository_scan.yml b/.github/workflows/aquasec_repository_scan.yml new file mode 100644 index 0000000..b17def9 --- /dev/null +++ b/.github/workflows/aquasec_repository_scan.yml @@ -0,0 +1,80 @@ +name: AquaSec Full Repository Scan + +on: + workflow_dispatch: + pull_request: + types: [ opened, synchronize ] + +permissions: + contents: read + issues: write + pull-requests: write + security-events: write + +jobs: + aquasec: + name: AquaSec Full Repository Scan + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Retrieve AquaSec Scan Results + env: + AQUA_KEY: ${{ secrets.AQUA_KEY }} + AQUA_SECRET: ${{ secrets.AQUA_SECRET }} + run: | + echo "=== Authenticating with AquaSec ===" + + TIMESTAMP=$(date -u +%s) + AUTH_ENDPOINT="https://eu-1.api.cloudsploit.com" + METHOD="POST" + POST_BODY='{"validity":240,"allowed_endpoints":["GET","POST"]}' + STRING_TO_SIGN="${TIMESTAMP}${METHOD}/v2/tokens${POST_BODY}" + SIGNATURE=$(echo -n "$STRING_TO_SIGN" | openssl dgst -sha256 -hmac "$AQUA_SECRET" -hex | sed 's/.*= //g') + + AUTH_RESPONSE=$(curl -s -X "$METHOD" "$AUTH_ENDPOINT" \ + -H "Content-Type: application/json" \ + -H "X-API-Key: $AQUA_KEY" \ + -H "X-Timestamp: $TIMESTAMP" \ + -H "X-Signature: $SIGNATURE" \ + -d "$POST_BODY") + + RESPONSE_STATUS=$(echo "$AUTH_RESPONSE" | jq -r '.status') + + if [ "$RESPONSE_STATUS" = "200" ]; then + echo "Login successful." + BEARER_TOKEN=$(echo "$AUTH_RESPONSE" | jq -r '.data') + echo "::add-mask::$BEARER_TOKEN" + else + echo "Login failed" + exit 1 + fi + + echo "=== Getting Repository ID from GitHub ===" + + REPO_ID=$(curl -s "https://api.github.com/repos/${{ github.repository }}" | jq -r '.id') + + if [ -z "$REPO_ID" ] || [ "$REPO_ID" = "null" ]; then + echo "Failed to get repository ID from GitHub" + exit 1 + fi + + echo "=== Receiving AquaSec Scan Results ===" + + SCAN_RESULTS_ENDPOINT="https://eu-central-1.edge.cloud.aquasec.com/codesec/api/v1/scans/results" + SCAN_RESULTS=$(curl -s -X GET \ + "$SCAN_RESULTS_ENDPOINT?repositoryIds=$REPO_ID" \ + -H "Authorization: Bearer $BEARER_TOKEN" \ + -H "Accept: application/json") + + if [ -z "$SCAN_RESULTS" ]; then + echo "Failed to retrieve scan results" + exit 1 + fi + + echo "=== Scan Results ===" + echo "$SCAN_RESULTS" | jq '.' \ No newline at end of file diff --git a/.github/workflows/trivy_repository_scan.yml b/.github/workflows/trivy_repository_scan.yml deleted file mode 100644 index dfe3a26..0000000 --- a/.github/workflows/trivy_repository_scan.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Trivy Full Repository Scan - -on: - workflow_dispatch: - pull_request: - types: [ opened, synchronize ] - -permissions: - contents: read - issues: write - pull-requests: write - security-events: write - -jobs: - trivy: - name: Trivy Full Repository Scan - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v5 - with: - persist-credentials: false - fetch-depth: 0 - - - name: Setup Trivy - uses: aquasecurity/setup-trivy@v0.2.4 - - - name: Run Trivy filesystem scan - run: | - trivy fs . \ - --format sarif \ - --scanners vuln,secret,misconfig,license \ - --output trivy_repository_report.sarif - - - name: Upload SARIF to GitHub Security Hub - uses: github/codeql-action/upload-sarif@v4 - with: - sarif_file: trivy_repository_report.sarif - - - name: Create scan summary table - id: scan_summary_table - run: | - python <<'PY' - import os - import json - import sys - from collections import defaultdict, Counter - - SARIF_PATH = "trivy_repository_report.sarif" - SEVERITIES = ["CRITICAL", "HIGH", "MEDIUM", "LOW"] - CATEGORIES = ["vulnerability", "secret", "misconfiguration", "license"] - - try: - # Parse results from SARIF - with open(SARIF_PATH, "r", encoding="utf-8") as f: - sarif = json.load(f) - - # Validate SARIF structure - if "runs" not in sarif or not sarif["runs"]: - raise ValueError("SARIF file contains no runs") - - run = sarif["runs"][0] - if "tool" not in run or "driver" not in run["tool"]: - raise ValueError("SARIF structure missing expected tool/driver keys") - - rules = run["tool"]["driver"].get("rules", []) - results = run.get("results", []) - category_severity_counts = defaultdict(Counter) - - except (IOError, json.JSONDecodeError, KeyError, ValueError) as e: - print(f"Error processing SARIF file: {e}", file=sys.stderr) - sys.exit(1) - - # Count results by category and severity - for result in results: - try: - rule_idx = result.get("ruleIndex") - if rule_idx is None or rule_idx >= len(rules): - continue - rule = rules[rule_idx] - tags = rule.get("properties", {}).get("tags", []) - # Find category and severity - category = next((c for c in CATEGORIES if c in tags), None) - severity = next((s for s in SEVERITIES if s in tags), None) - if category and severity: - category_severity_counts[category][severity] += 1 - except (KeyError, IndexError, TypeError) as e: - print(f"Warning: Error processing result: {e}", file=sys.stderr) - continue - - # Build Markdown summary table - headers = ["TRIVY"] + SEVERITIES + ["TOTAL"] - summary_table = "| " + " | ".join(headers) + " |\n" - summary_table += "|---|---|---|---|---|---|\n" - - # Rows with counts for each category - total_severity = Counter() - total_all = 0 - for category in CATEGORIES: - row = [category] - category_total = 0 - for severity in SEVERITIES: - count = category_severity_counts[category][severity] - row.append(str(count)) - total_severity[severity] += count - category_total += count - row.append(f"**{category_total}**") - total_all += category_total - summary_table += "| " + " | ".join(row) + " |\n" - - total_row = ["**➡️ Total**"] + [f"**{total_severity[sev]}**" for sev in SEVERITIES] + [f"**{total_all}**"] - summary_table += "| " + " | ".join(total_row) + " |" - - # Set summary table output - try: - if "GITHUB_OUTPUT" in os.environ: - with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f: - f.write("table<