diff --git a/.github/workflows/composer-audit.yml b/.github/workflows/composer-audit.yml index 0dcb345..a1457c0 100644 --- a/.github/workflows/composer-audit.yml +++ b/.github/workflows/composer-audit.yml @@ -1,4 +1,13 @@ -name: D10 - Security Review +name: Drupal - Security Review +# Required Secrets (same across projects): +# EMAIL_USERNAME: Gmail address for sending notifications (i.e. security-bot@palantir.net) +# EMAIL_PASSWORD: Gmail App Password (no spaces) +# COMPOSER_GITHUB_TOKEN: (Conditional) GitHub Personal Access Token ("PAT") with 'repo' scope - only needed if composer.json has private VCS dependencies. A classic PAT is recommended. More info here: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens +# **Details are stored in the 1password vault named "Production".** +# +# Required Variables (unique per project): +# PALANTIR_NOTIFICATION_EMAIL: Email address to receive alerts; group email address recommended +# CLIENT_WEBSITE_URL: (Optional) Client's website URL on: schedule: - cron: '0 21 * * 3' # Runs every Wednesday at 8:30PM UTC / 5:00 EDT / 4:00 EST. @@ -11,6 +20,25 @@ jobs: - name: "Checkout code" uses: actions/checkout@v4 + - name: Check if private composer dependencies exist + id: check_private_deps + run: | + if grep -q '"type"[[:space:]]*:[[:space:]]*"vcs"' composer.json; then + echo "has_private_deps=true" >> $GITHUB_OUTPUT + echo "⚠️ This project uses private VCS dependencies" + else + echo "has_private_deps=false" >> $GITHUB_OUTPUT + echo "✅ This project uses only public dependencies" + fi + + - name: Validate composer authentication + if: steps.check_private_deps.outputs.has_private_deps == 'true' && !secrets.COMPOSER_GITHUB_TOKEN + run: | + echo "::error::This project requires private composer dependencies but COMPOSER_GITHUB_TOKEN secret is not set." + echo "::error::Please add COMPOSER_GITHUB_TOKEN secret with a GitHub Personal Access Token that has 'repo' scope." + echo "::error::Generate at: https://github.com/settings/tokens" + exit 1 + - name: Install PHP with extensions uses: shivammathur/setup-php@2.35.4 with: @@ -19,6 +47,8 @@ jobs: tools: composer:v2 - name: "Composer install" + env: + COMPOSER_AUTH: ${{ secrets.COMPOSER_GITHUB_TOKEN && format('{{"github-oauth":{{"github.com":"{0}"}}}}', secrets.COMPOSER_GITHUB_TOKEN) || '{}' }} uses: "ramsey/composer-install@2.2.0" with: composer-options: "--prefer-dist" @@ -57,7 +87,7 @@ jobs: echo "drupal_count=$DRUPAL_COUNT" >> $GITHUB_OUTPUT echo "non_drupal_count=$NON_DRUPAL_COUNT" >> $GITHUB_OUTPUT - # Function to create a table from JSON advisories + # Function to create a table from JSON advisories (for GitHub step summary) create_table() { local json_file=$1 local output_file=$2 @@ -73,7 +103,17 @@ jobs: fi } - # Create tables for Drupal and non-Drupal vulnerabilities + # Function to create simplified format for email + create_simple_format() { + local json_file=$1 + local output_file=$2 + + if [ -f "$json_file" ] && [ "$(jq length "$json_file")" -gt 0 ]; then + jq -r '.[] | "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nPackage: " + .packageName + "\nSeverity: " + (.severity // "") + (if .cve then " | " + .cve else "" end) + "\nTitle: " + .title + "\nURL: " + .link + "\nAffected: " + .affectedVersions + "\nReported: " + .reportedAt + "\n"' "$json_file" > "$output_file" + fi + } + + # Create tables for Drupal and non-Drupal vulnerabilities (for GitHub) if [ "$HAS_DRUPAL" = "true" ]; then create_table drupal_advisories.json drupal_table.txt fi @@ -82,6 +122,15 @@ jobs: create_table non_drupal_advisories.json non_drupal_table.txt fi + # Create simplified format for email + if [ "$HAS_DRUPAL" = "true" ]; then + create_simple_format drupal_advisories.json drupal_simple.txt + fi + + if [ "$HAS_NON_DRUPAL" = "true" ]; then + create_simple_format non_drupal_advisories.json non_drupal_simple.txt + fi + # Show Drupal vulnerabilities if any if [ "$HAS_DRUPAL" = "true" ]; then echo "⚠️ **Security vulnerabilities detected in Drupal packages!**" >> $GITHUB_STEP_SUMMARY @@ -94,9 +143,9 @@ jobs: # Save Drupal vulnerabilities for email if [ "$HAS_DRUPAL" = "true" ]; then - DRUPAL_DETAILS=$(cat drupal_table.txt) - echo "drupal_details<> $GITHUB_OUTPUT - echo "$DRUPAL_DETAILS" >> $GITHUB_OUTPUT + DRUPAL_SIMPLE=$(cat drupal_simple.txt) + echo "drupal_simple<> $GITHUB_OUTPUT + echo "$DRUPAL_SIMPLE" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT fi @@ -117,9 +166,9 @@ jobs: # Save non-Drupal vulnerabilities for email if [ "$HAS_NON_DRUPAL" = "true" ]; then - NON_DRUPAL_DETAILS=$(cat non_drupal_table.txt) - echo "non_drupal_details<> $GITHUB_OUTPUT - echo "$NON_DRUPAL_DETAILS" >> $GITHUB_OUTPUT + NON_DRUPAL_SIMPLE=$(cat non_drupal_simple.txt) + echo "non_drupal_simple<> $GITHUB_OUTPUT + echo "$NON_DRUPAL_SIMPLE" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT fi @@ -171,15 +220,9 @@ jobs: Non-Drupal Vulnerabilities: ${{ steps.audit.outputs.non_drupal_count }} - ${{ steps.audit.outputs.drupal_count > 0 && '

🎯 Drupal Package Vulnerabilities (PRIORITY)

' || '' }} - ${{ steps.audit.outputs.drupal_count > 0 && '
' || '' }} - ${{ steps.audit.outputs.drupal_details }} - ${{ steps.audit.outputs.drupal_count > 0 && '
' || '' }} + ${{ steps.audit.outputs.drupal_count > 0 && '

🎯 Drupal Package Vulnerabilities (PRIORITY)

' || '' }}${{ steps.audit.outputs.drupal_simple }}${{ steps.audit.outputs.drupal_count > 0 && '
' || '' }} - ${{ steps.audit.outputs.non_drupal_count > 0 && '

ℹ️ Non-Drupal Package Vulnerabilities

' || '' }} - ${{ steps.audit.outputs.non_drupal_count > 0 && '
' || '' }} - ${{ steps.audit.outputs.non_drupal_details }} - ${{ steps.audit.outputs.non_drupal_count > 0 && '
' || '' }} + ${{ steps.audit.outputs.non_drupal_count > 0 && '

ℹ️ Non-Drupal Package Vulnerabilities

' || '' }}${{ steps.audit.outputs.non_drupal_simple }}${{ steps.audit.outputs.non_drupal_count > 0 && '
' || '' }}
Next Steps: