From 08759dcdcab02c38873abb19e87cb5829d036e3e Mon Sep 17 00:00:00 2001 From: Rob DeVita <104449361+robertjdevita@users.noreply.github.com> Date: Wed, 22 Oct 2025 13:00:54 -0400 Subject: [PATCH 1/2] Add comments to top of file Add comments to the top of the YAML with details about the required secrets and variables. --- .github/workflows/composer-audit.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/composer-audit.yml b/.github/workflows/composer-audit.yml index 0dcb345..ac1ae0e 100644 --- a/.github/workflows/composer-audit.yml +++ b/.github/workflows/composer-audit.yml @@ -1,4 +1,12 @@ name: D10 - 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) +# **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. From 9d72cf52ff817383d06883b25ddef0c9c81c62ed Mon Sep 17 00:00:00 2001 From: Rob DeVita <104449361+robertjdevita@users.noreply.github.com> Date: Mon, 10 Nov 2025 09:36:00 -0500 Subject: [PATCH 2/2] Add support for VCS-type dependencies Made the following changes: 1. Checks composer.json for VCS-type repos before composer install 2. Validates token presence if private deps are found 3. Fails with clear error if token is needed but missing 4. Uses token conditionally - only if it exists 5. Updated documentation in comments about when COMPOSER_GITHUB_TOKEN is needed --- .github/workflows/composer-audit.yml | 69 +++++++++++++++++++++------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/.github/workflows/composer-audit.yml b/.github/workflows/composer-audit.yml index ac1ae0e..a1457c0 100644 --- a/.github/workflows/composer-audit.yml +++ b/.github/workflows/composer-audit.yml @@ -1,7 +1,8 @@ -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): @@ -19,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: @@ -27,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" @@ -65,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 @@ -81,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 @@ -90,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 @@ -102,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 @@ -125,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 @@ -179,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: