Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 60 additions & 17 deletions .github/workflows/composer-audit.yml
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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:
Expand All @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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 '.[] | "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n<strong>Package:</strong> " + .packageName + "\n<strong>Severity:</strong> " + (.severity // "") + (if .cve then " | " + .cve else "" end) + "\n<strong>Title:</strong> " + .title + "\n<strong>URL:</strong> <a href=\"" + .link + "\" style=\"color: #0366d6;\">" + .link + "</a>\n<strong>Affected:</strong> " + .affectedVersions + "\n<strong>Reported:</strong> " + .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
Expand All @@ -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
Expand All @@ -94,9 +143,9 @@ jobs:

# Save Drupal vulnerabilities for email
if [ "$HAS_DRUPAL" = "true" ]; then
DRUPAL_DETAILS=$(cat drupal_table.txt)
echo "drupal_details<<EOF" >> $GITHUB_OUTPUT
echo "$DRUPAL_DETAILS" >> $GITHUB_OUTPUT
DRUPAL_SIMPLE=$(cat drupal_simple.txt)
echo "drupal_simple<<EOF" >> $GITHUB_OUTPUT
echo "$DRUPAL_SIMPLE" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi

Expand All @@ -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<<EOF" >> $GITHUB_OUTPUT
echo "$NON_DRUPAL_DETAILS" >> $GITHUB_OUTPUT
NON_DRUPAL_SIMPLE=$(cat non_drupal_simple.txt)
echo "non_drupal_simple<<EOF" >> $GITHUB_OUTPUT
echo "$NON_DRUPAL_SIMPLE" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi

Expand Down Expand Up @@ -171,15 +220,9 @@ jobs:
<strong>Non-Drupal Vulnerabilities:</strong> ${{ steps.audit.outputs.non_drupal_count }}
</div>

${{ steps.audit.outputs.drupal_count > 0 && '<h3 style="color: #d73a49;">🎯 Drupal Package Vulnerabilities (PRIORITY)</h3>' || '' }}
${{ steps.audit.outputs.drupal_count > 0 && '<div style="background-color: #f6f8fa; padding: 15px; border-radius: 5px; font-family: monospace; font-size: 14px; line-height: 1.4;">' || '' }}
${{ steps.audit.outputs.drupal_details }}
${{ steps.audit.outputs.drupal_count > 0 && '</div>' || '' }}
${{ steps.audit.outputs.drupal_count > 0 && '<h3 style="color: #d73a49;">🎯 Drupal Package Vulnerabilities (PRIORITY)</h3><pre style="background-color: #f6f8fa; padding: 15px; border-radius: 5px; font-family: monospace; font-size: 13px; line-height: 1.6; white-space: pre-wrap; word-wrap: break-word; margin: 0;">' || '' }}${{ steps.audit.outputs.drupal_simple }}${{ steps.audit.outputs.drupal_count > 0 && '</pre>' || '' }}

${{ steps.audit.outputs.non_drupal_count > 0 && '<h3 style="color: #f66a0a;">ℹ️ Non-Drupal Package Vulnerabilities</h3>' || '' }}
${{ steps.audit.outputs.non_drupal_count > 0 && '<div style="background-color: #f6f8fa; padding: 15px; border-radius: 5px; font-family: monospace; font-size: 14px; line-height: 1.4;">' || '' }}
${{ steps.audit.outputs.non_drupal_details }}
${{ steps.audit.outputs.non_drupal_count > 0 && '</div>' || '' }}
${{ steps.audit.outputs.non_drupal_count > 0 && '<h3 style="color: #f66a0a;">ℹ️ Non-Drupal Package Vulnerabilities</h3><pre style="background-color: #f6f8fa; padding: 15px; border-radius: 5px; font-family: monospace; font-size: 13px; line-height: 1.6; white-space: pre-wrap; word-wrap: break-word; margin: 0;">' || '' }}${{ steps.audit.outputs.non_drupal_simple }}${{ steps.audit.outputs.non_drupal_count > 0 && '</pre>' || '' }}

<div style="margin-top: 30px; padding: 15px; background-color: #e3f2fd; border-radius: 5px;">
<strong>Next Steps:</strong>
Expand Down