mirror of
https://github.com/HDFGroup/hdf5.git
synced 2026-09-25 04:09:44 +03:00
Harden S3 publish workflows against zizmor findings (#6543)
Address GitHub Actions static-analysis (zizmor/CodeQL) findings in the
S3 publishing workflows:
- publish-release.yml: hoist every inputs/secrets/vars value used inside
run blocks into job-level env vars and reference plain shell variables
($USE_TAG, $FILE_NAME, $TARGET_DIR, $S3_BUCKET, $TARGET_PATH, $DRY_RUN),
eliminating 41 template-injection findings. Add persist-credentials:
false to the checkout (artipacked).
- publish-branch.yml: add persist-credentials: false to the checkout
(artipacked); its S3 sync already used env vars.
No behavioral change: the same values are used, only via the shell
environment instead of direct ${{ }} expansion into the script body.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
61fbe139e8
commit
fe17c9b1cd
@@ -26,6 +26,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: '${{ github.head_ref || github.ref_name }}'
|
||||
persist-credentials: false
|
||||
|
||||
- name: List files for the space
|
||||
run: |
|
||||
|
||||
@@ -28,21 +28,33 @@ jobs:
|
||||
publish-tag:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
|
||||
|
||||
# Expose the templated inputs/secrets/vars as environment variables so the
|
||||
# run steps below reference plain shell variables ($USE_TAG, ...) instead of
|
||||
# interpolating ${{ ... }} directly into the script body, which avoids
|
||||
# template-injection (see zizmor/CodeQL findings).
|
||||
env:
|
||||
USE_TAG: ${{ inputs.use_tag }}
|
||||
FILE_NAME: ${{ inputs.file_name }}
|
||||
TARGET_DIR: ${{ inputs.target_dir }}
|
||||
DRY_RUN: ${{ inputs.dry_run }}
|
||||
S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
|
||||
TARGET_PATH: ${{ vars.TARGET_PATH }}
|
||||
|
||||
steps:
|
||||
- name: Validate Inputs
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "Validating inputs..."
|
||||
if [[ ! "${{ inputs.use_tag }}" =~ ^hdf5[_-][0-9]+[._][0-9]+[._][0-9]+([._][0-9]+)?(-.*)?$ ]]; then
|
||||
if [[ ! "$USE_TAG" =~ ^hdf5[_-][0-9]+[._][0-9]+[._][0-9]+([._][0-9]+)?(-.*)?$ ]]; then
|
||||
echo "❌ Invalid tag format. Expected: hdf5_X.Y.Z, hdf5-X_Y_Z, or hdf5_X.Y.Z.W"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${{ inputs.target_dir }}" == *".."* ]] || [[ "${{ inputs.target_dir }}" == /* ]]; then
|
||||
if [[ "$TARGET_DIR" == *".."* ]] || [[ "$TARGET_DIR" == /* ]]; then
|
||||
echo "❌ Invalid target_dir. Cannot contain '..' or start with '/'"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${{ inputs.file_name }}" == *".."* ]] || [[ "${{ inputs.file_name }}" == *"/"* ]] || [[ "${{ inputs.file_name }}" =~ [^a-zA-Z0-9._-] ]]; then
|
||||
if [[ "$FILE_NAME" == *".."* ]] || [[ "$FILE_NAME" == *"/"* ]] || [[ "$FILE_NAME" =~ [^a-zA-Z0-9._-] ]]; then
|
||||
echo "❌ Invalid file_name. Can only contain alphanumeric, dots, underscores, and hyphens"
|
||||
exit 1
|
||||
fi
|
||||
@@ -54,6 +66,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: '${{ github.head_ref || github.ref_name }}'
|
||||
persist-credentials: false
|
||||
|
||||
- name: Create download directory
|
||||
run: mkdir -p HDF5
|
||||
@@ -72,9 +85,9 @@ jobs:
|
||||
set -euo pipefail
|
||||
echo "📁 Downloaded files:"
|
||||
ls -la HDF5/
|
||||
|
||||
|
||||
# Check for expected files
|
||||
EXPECTED_FILES=("${{ inputs.file_name }}.doxygen.zip" "${{ inputs.file_name }}.html.abi.reports.tar.gz")
|
||||
EXPECTED_FILES=("${FILE_NAME}.doxygen.zip" "${FILE_NAME}.html.abi.reports.tar.gz")
|
||||
for file in "${EXPECTED_FILES[@]}"; do
|
||||
if [ ! -f "HDF5/$file" ]; then
|
||||
echo "⚠️ Warning: Expected file not found: $file"
|
||||
@@ -98,8 +111,8 @@ jobs:
|
||||
chmod +x .github/scripts/generate-index-html.sh
|
||||
.github/scripts/generate-index-html.sh \
|
||||
"./HDF5" \
|
||||
"HDF5 ${{ inputs.use_tag }} - Downloads" \
|
||||
"Release binaries, source code, and documentation packages for HDF5 ${{ inputs.use_tag }}" \
|
||||
"HDF5 ${USE_TAG} - Downloads" \
|
||||
"Release binaries, source code, and documentation packages for HDF5 ${USE_TAG}" \
|
||||
"../"
|
||||
echo "✅ Downloads index.html generated"
|
||||
|
||||
@@ -108,7 +121,7 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "🚀 Syncing release files to S3..."
|
||||
aws s3 sync ./HDF5 s3://${{ secrets.AWS_S3_BUCKET }}/${{ vars.TARGET_PATH }}/${{ inputs.target_dir }}/downloads \
|
||||
aws s3 sync ./HDF5 "s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/downloads" \
|
||||
--delete \
|
||||
--exclude="*" \
|
||||
--include="*.tar.gz" \
|
||||
@@ -122,7 +135,7 @@ jobs:
|
||||
|
||||
# Upload index.html with proper content type
|
||||
aws s3 cp ./HDF5/index.html \
|
||||
s3://${{ secrets.AWS_S3_BUCKET }}/${{ vars.TARGET_PATH }}/${{ inputs.target_dir }}/downloads/index.html \
|
||||
"s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/downloads/index.html" \
|
||||
--content-type "text/html" \
|
||||
--metadata-directive REPLACE
|
||||
echo "✅ Release files sync completed"
|
||||
@@ -156,12 +169,12 @@ jobs:
|
||||
- name: Generate index.html for documentation directory
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -d "${{ inputs.file_name }}.doxygen" ]; then
|
||||
if [ -d "${FILE_NAME}.doxygen" ]; then
|
||||
echo "📄 Generating index.html for documentation directory..."
|
||||
.github/scripts/generate-index-html.sh \
|
||||
"./${{ inputs.file_name }}.doxygen" \
|
||||
"HDF5 ${{ inputs.use_tag }} - Documentation" \
|
||||
"Doxygen API documentation for HDF5 ${{ inputs.use_tag }}" \
|
||||
"./${FILE_NAME}.doxygen" \
|
||||
"HDF5 ${USE_TAG} - Documentation" \
|
||||
"Doxygen API documentation for HDF5 ${USE_TAG}" \
|
||||
"../../"
|
||||
echo "✅ Documentation index.html generated"
|
||||
else
|
||||
@@ -172,10 +185,10 @@ jobs:
|
||||
if: ${{ !inputs.dry_run }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -d "${{ inputs.file_name }}.doxygen" ]; then
|
||||
if [ -d "${FILE_NAME}.doxygen" ]; then
|
||||
echo "📚 Syncing documentation to S3..."
|
||||
aws s3 sync ./${{ inputs.file_name }}.doxygen \
|
||||
s3://${{ secrets.AWS_S3_BUCKET }}/${{ vars.TARGET_PATH }}/${{ inputs.target_dir }}/documentation/doxygen \
|
||||
aws s3 sync "./${FILE_NAME}.doxygen" \
|
||||
"s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/documentation/doxygen" \
|
||||
--delete \
|
||||
--content-type "text/html" \
|
||||
--metadata-directive REPLACE
|
||||
@@ -187,7 +200,7 @@ jobs:
|
||||
- name: Process compatibility reports
|
||||
run: |
|
||||
set -euo pipefail
|
||||
COMPAT_FILE="HDF5/${{ inputs.file_name }}.html.abi.reports.tar.gz"
|
||||
COMPAT_FILE="HDF5/${FILE_NAME}.html.abi.reports.tar.gz"
|
||||
if [ -f "$COMPAT_FILE" ]; then
|
||||
echo "📊 Processing compatibility reports..."
|
||||
tar -xzf "$COMPAT_FILE"
|
||||
@@ -208,8 +221,8 @@ jobs:
|
||||
echo "📄 Generating index.html for compatibility reports directory..."
|
||||
.github/scripts/generate-index-html.sh \
|
||||
"./hdf5" \
|
||||
"HDF5 ${{ inputs.use_tag }} - Compatibility Reports" \
|
||||
"ABI/API compatibility reports for HDF5 ${{ inputs.use_tag }}" \
|
||||
"HDF5 ${USE_TAG} - Compatibility Reports" \
|
||||
"ABI/API compatibility reports for HDF5 ${USE_TAG}" \
|
||||
"../"
|
||||
echo "✅ Compatibility reports index.html generated"
|
||||
else
|
||||
@@ -223,7 +236,7 @@ jobs:
|
||||
if [ -d "hdf5" ]; then
|
||||
echo "📊 Syncing compatibility reports to S3..."
|
||||
aws s3 sync ./hdf5 \
|
||||
s3://${{ secrets.AWS_S3_BUCKET }}/${{ vars.TARGET_PATH }}/${{ inputs.target_dir }}/compat_report \
|
||||
"s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/compat_report" \
|
||||
--delete \
|
||||
--content-type "text/html" \
|
||||
--metadata-directive REPLACE
|
||||
@@ -238,18 +251,18 @@ jobs:
|
||||
echo "📄 Generating main release directory index.html..."
|
||||
|
||||
# Create a temporary directory structure to mimic the S3 layout
|
||||
mkdir -p release_root/${{ inputs.target_dir }}/{downloads,documentation,compat_report}
|
||||
mkdir -p "release_root/${TARGET_DIR}"/{downloads,documentation,compat_report}
|
||||
|
||||
# Create placeholder files so the script can list them
|
||||
touch "release_root/${{ inputs.target_dir }}/downloads/.placeholder"
|
||||
touch "release_root/${{ inputs.target_dir }}/documentation/.placeholder"
|
||||
touch "release_root/${{ inputs.target_dir }}/compat_report/.placeholder"
|
||||
touch "release_root/${TARGET_DIR}/downloads/.placeholder"
|
||||
touch "release_root/${TARGET_DIR}/documentation/.placeholder"
|
||||
touch "release_root/${TARGET_DIR}/compat_report/.placeholder"
|
||||
|
||||
# Generate index for the release directory
|
||||
.github/scripts/generate-index-html.sh \
|
||||
"release_root/${{ inputs.target_dir }}" \
|
||||
"HDF5 ${{ inputs.use_tag }}" \
|
||||
"Release files, documentation, and compatibility reports for HDF5 ${{ inputs.use_tag }}" \
|
||||
"release_root/${TARGET_DIR}" \
|
||||
"HDF5 ${USE_TAG}" \
|
||||
"Release files, documentation, and compatibility reports for HDF5 ${USE_TAG}" \
|
||||
"../"
|
||||
|
||||
echo "✅ Main release index.html generated"
|
||||
@@ -259,8 +272,8 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "📤 Uploading main release directory index.html..."
|
||||
aws s3 cp "release_root/${{ inputs.target_dir }}/index.html" \
|
||||
s3://${{ secrets.AWS_S3_BUCKET }}/${{ vars.TARGET_PATH }}/${{ inputs.target_dir }}/index.html \
|
||||
aws s3 cp "release_root/${TARGET_DIR}/index.html" \
|
||||
"s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/index.html" \
|
||||
--content-type "text/html" \
|
||||
--metadata-directive REPLACE
|
||||
echo "✅ Main index.html uploaded"
|
||||
@@ -270,17 +283,17 @@ jobs:
|
||||
set -euo pipefail
|
||||
echo "🎉 HDF5 Release Publication Summary"
|
||||
echo "=================================="
|
||||
echo "🏷️ Tag: ${{ inputs.use_tag }}"
|
||||
echo "📁 File Base: ${{ inputs.file_name }}"
|
||||
echo "🎯 Target Directory: ${{ inputs.target_dir }}"
|
||||
echo "🌐 Dry Run: ${{ inputs.dry_run }}"
|
||||
echo "🏷️ Tag: ${USE_TAG}"
|
||||
echo "📁 File Base: ${FILE_NAME}"
|
||||
echo "🎯 Target Directory: ${TARGET_DIR}"
|
||||
echo "🌐 Dry Run: ${DRY_RUN}"
|
||||
echo ""
|
||||
if [ "${{ inputs.dry_run }}" == "true" ]; then
|
||||
if [ "$DRY_RUN" == "true" ]; then
|
||||
echo "ℹ️ This was a dry run - no files were uploaded to S3"
|
||||
else
|
||||
echo "✅ Release published successfully!"
|
||||
echo "📍 Main page: s3://${{ secrets.AWS_S3_BUCKET }}/${{ vars.TARGET_PATH }}/${{ inputs.target_dir }}/index.html"
|
||||
echo "📍 Downloads: s3://${{ secrets.AWS_S3_BUCKET }}/${{ vars.TARGET_PATH }}/${{ inputs.target_dir }}/downloads"
|
||||
echo "📖 Documentation: s3://${{ secrets.AWS_S3_BUCKET }}/${{ vars.TARGET_PATH }}/${{ inputs.target_dir }}/documentation/doxygen"
|
||||
echo "📊 Reports: s3://${{ secrets.AWS_S3_BUCKET }}/${{ vars.TARGET_PATH }}/${{ inputs.target_dir }}/compat_report"
|
||||
echo "📍 Main page: s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/index.html"
|
||||
echo "📍 Downloads: s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/downloads"
|
||||
echo "📖 Documentation: s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/documentation/doxygen"
|
||||
echo "📊 Reports: s3://${S3_BUCKET}/${TARGET_PATH}/${TARGET_DIR}/compat_report"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user