From 7e5f34313804557ce29a8c2034ff855d9f2a8966 Mon Sep 17 00:00:00 2001 From: varunbhardwaj-gmail Date: Wed, 23 Jul 2025 22:09:33 +0530 Subject: [PATCH 01/11] Create download_filtered_sarifs.js --- download_filtered_sarifs.js | 150 ++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 download_filtered_sarifs.js diff --git a/download_filtered_sarifs.js b/download_filtered_sarifs.js new file mode 100644 index 0000000..5814efd --- /dev/null +++ b/download_filtered_sarifs.js @@ -0,0 +1,150 @@ +// download_filtered_sarifs.js + +const fs = require('node:fs'); +const path = require('node:path'); + +async function run(github, context, core) { + const repo = context.repo; + + // --- Input: Get the single excluded category from an environment variable --- + // This value will be passed from your GitHub Actions workflow step (see workflow example below). + const EXCLUDED_CATEGORY = process.env.EXCLUDED_CATEGORY_INPUT; + + if (!EXCLUDED_CATEGORY || EXCLUDED_CATEGORY.trim() === '') { + core.setFailed("Error: 'EXCLUDED_CATEGORY_INPUT' environment variable is required and cannot be empty."); + return; + } + + // --- Configuration: Adjust this to the Git reference (branch/tag/SHA) you want to download analyses from --- + // Examples: + // - 'refs/heads/main' (for your main branch) + // - 'refs/heads/develop' (for a development branch) + // - context.ref (for the current branch/ref the workflow is running on) + const targetRef = 'refs/heads/main'; // <--- ADJUST THIS IF NEEDED + + core.info(`Attempting to download SARIFs from '${targetRef}' for repo: ${repo.owner}/${repo.repo}`); + core.info(`EXCLUDING SARIFs with category: '${EXCLUDED_CATEGORY}'`); + + + const DOWNLOAD_DIR = 'sarif_downloads'; + if (!fs.existsSync(DOWNLOAD_DIR)) { + fs.mkdirSync(DOWNLOAD_DIR, { recursive: true }); // Ensure parent directories are created + core.info(`Created directory: ${DOWNLOAD_DIR}`); + } else { + core.info(`Directory already exists: ${DOWNLOAD_DIR}`); + } + + let allAnalyses = []; + let page = 1; + let hasNextPage = true; + + // --- 1. Fetch all recent Code Scanning analyses for the targetRef --- + try { + while (hasNextPage) { + const response = await github.rest.codeScanning.listRecentAnalyses({ + owner: repo.owner, + repo: repo.repo, + ref: targetRef, + per_page: 100, // Fetch 100 analyses per page + page: page, + tool_name: 'CodeQL' // Optionally filter by tool if you only want CodeQL SARIFs + }); + + allAnalyses = allAnalyses.concat(response.data); + + // Check if there are more pages to fetch + if (response.data.length < 100) { + hasNextPage = false; + } else { + page++; + } + } + core.info(`Found ${allAnalyses.length} total recent analyses for ref: '${targetRef}'`); + } catch (error) { + core.setFailed(`Failed to list recent analyses for '${targetRef}': ${error.message}`); + // Provide more detail for common errors like 404 (no analyses found) + if (error.status === 404) { + core.warning(`No CodeQL analyses found for ref: '${targetRef}'. Ensure analyses exist for this branch.`); + } + return; + } + + // --- 2. Filter out analyses based on the single EXCLUDED_CATEGORY --- + const analysesToDownload = []; + const categoriesSeen = new Set(); // To ensure we only download the most recent analysis for each unique category + + // Sort by created_at (most recent first) to ensure we get the latest if multiple analyses exist for a category + allAnalyses.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); + + for (const analysis of allAnalyses) { + // Check if the category matches our single excluded category + if (analysis.category === EXCLUDED_CATEGORY) { + core.info(`Skipping analysis for excluded category: '${analysis.category}' (ID: ${analysis.id})`); + continue; // Skip to the next analysis if it's the excluded one + } + + // Ensure we only download the most recent analysis for each unique category found after exclusion + if (!categoriesSeen.has(analysis.category)) { + categoriesSeen.add(analysis.category); + analysesToDownload.push(analysis); + } else { + core.debug(`Skipping older analysis for category '${analysis.category}' (ID: ${analysis.id})`); + } + } + + if (analysesToDownload.length === 0) { + core.info("No analyses found to download after filtering. Exiting."); + return; + } + + core.info(`Attempting to download ${analysesToDownload.length} SARIF files after filtering.`); + + // --- 3. Download the filtered SARIF files --- + // Use Promise.all to download files concurrently + await Promise.all(analysesToDownload.map(async (analysis) => { + try { + const sarifId = analysis.id; + const category = analysis.category; + // Generate a clean filename from the category + const fileName = category.replace(/[^a-z0-9_]/gi, '_').toLowerCase() + '.sarif'; + const filePath = path.join(DOWNLOAD_DIR, fileName); + + core.info(`Downloading SARIF for category '${category}' (ID: ${sarifId})`); + + // Make a direct API call to get the SARIF content + const sarifResponse = await github.rest.codeScanning.getAnalysis({ + owner: repo.owner, + repo: repo.repo, + analysis_id: sarifId, + headers: { + Accept: "application/sarif+json", // Crucial: Request SARIF JSON directly + }, + }); + + const sarifContent = sarifResponse.data; + + // Basic validation for received content + if (!sarifContent || (typeof sarifContent === 'object' && Object.keys(sarifContent).length === 0) || (typeof sarifContent === 'string' && sarifContent.trim().length === 0)) { + core.warning(`SARIF content received is empty or malformed for analysis ID ${analysis.id} (category: '${category}'). Skipping write.`); + core.debug(`Received SARIF content (raw): ${sarifContent}`); + return; + } + + // Write the SARIF content to a file + fs.writeFileSync(filePath, JSON.stringify(sarifContent, null, 2)); // Pretty print JSON for readability + core.info(`Successfully downloaded SARIF for '${category}' to '${filePath}'`); + + } catch (error) { + core.error(`Failed to download SARIF for analysis ID ${analysis.id} (category: '${analysis.category}'): ${error.message}`); + // Log the error but don't fail the whole job so other downloads can proceed + // If you want the job to fail on any download error, you'd re-throw or setFailed here. + } + })); + + core.info("All filtered SARIF files processed and downloaded."); +} + +// This is the entry point for actions/github-script +module.exports = (github, context, core) => { + run(github, context, core).then(() => {}); +}; From 9a00798f3fbc974b15fb20bfdd4b1141f2a2e090 Mon Sep 17 00:00:00 2001 From: varunbhardwaj-gmail Date: Wed, 23 Jul 2025 22:10:14 +0530 Subject: [PATCH 02/11] Delete download_filtered_sarifs.js --- download_filtered_sarifs.js | 150 ------------------------------------ 1 file changed, 150 deletions(-) delete mode 100644 download_filtered_sarifs.js diff --git a/download_filtered_sarifs.js b/download_filtered_sarifs.js deleted file mode 100644 index 5814efd..0000000 --- a/download_filtered_sarifs.js +++ /dev/null @@ -1,150 +0,0 @@ -// download_filtered_sarifs.js - -const fs = require('node:fs'); -const path = require('node:path'); - -async function run(github, context, core) { - const repo = context.repo; - - // --- Input: Get the single excluded category from an environment variable --- - // This value will be passed from your GitHub Actions workflow step (see workflow example below). - const EXCLUDED_CATEGORY = process.env.EXCLUDED_CATEGORY_INPUT; - - if (!EXCLUDED_CATEGORY || EXCLUDED_CATEGORY.trim() === '') { - core.setFailed("Error: 'EXCLUDED_CATEGORY_INPUT' environment variable is required and cannot be empty."); - return; - } - - // --- Configuration: Adjust this to the Git reference (branch/tag/SHA) you want to download analyses from --- - // Examples: - // - 'refs/heads/main' (for your main branch) - // - 'refs/heads/develop' (for a development branch) - // - context.ref (for the current branch/ref the workflow is running on) - const targetRef = 'refs/heads/main'; // <--- ADJUST THIS IF NEEDED - - core.info(`Attempting to download SARIFs from '${targetRef}' for repo: ${repo.owner}/${repo.repo}`); - core.info(`EXCLUDING SARIFs with category: '${EXCLUDED_CATEGORY}'`); - - - const DOWNLOAD_DIR = 'sarif_downloads'; - if (!fs.existsSync(DOWNLOAD_DIR)) { - fs.mkdirSync(DOWNLOAD_DIR, { recursive: true }); // Ensure parent directories are created - core.info(`Created directory: ${DOWNLOAD_DIR}`); - } else { - core.info(`Directory already exists: ${DOWNLOAD_DIR}`); - } - - let allAnalyses = []; - let page = 1; - let hasNextPage = true; - - // --- 1. Fetch all recent Code Scanning analyses for the targetRef --- - try { - while (hasNextPage) { - const response = await github.rest.codeScanning.listRecentAnalyses({ - owner: repo.owner, - repo: repo.repo, - ref: targetRef, - per_page: 100, // Fetch 100 analyses per page - page: page, - tool_name: 'CodeQL' // Optionally filter by tool if you only want CodeQL SARIFs - }); - - allAnalyses = allAnalyses.concat(response.data); - - // Check if there are more pages to fetch - if (response.data.length < 100) { - hasNextPage = false; - } else { - page++; - } - } - core.info(`Found ${allAnalyses.length} total recent analyses for ref: '${targetRef}'`); - } catch (error) { - core.setFailed(`Failed to list recent analyses for '${targetRef}': ${error.message}`); - // Provide more detail for common errors like 404 (no analyses found) - if (error.status === 404) { - core.warning(`No CodeQL analyses found for ref: '${targetRef}'. Ensure analyses exist for this branch.`); - } - return; - } - - // --- 2. Filter out analyses based on the single EXCLUDED_CATEGORY --- - const analysesToDownload = []; - const categoriesSeen = new Set(); // To ensure we only download the most recent analysis for each unique category - - // Sort by created_at (most recent first) to ensure we get the latest if multiple analyses exist for a category - allAnalyses.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); - - for (const analysis of allAnalyses) { - // Check if the category matches our single excluded category - if (analysis.category === EXCLUDED_CATEGORY) { - core.info(`Skipping analysis for excluded category: '${analysis.category}' (ID: ${analysis.id})`); - continue; // Skip to the next analysis if it's the excluded one - } - - // Ensure we only download the most recent analysis for each unique category found after exclusion - if (!categoriesSeen.has(analysis.category)) { - categoriesSeen.add(analysis.category); - analysesToDownload.push(analysis); - } else { - core.debug(`Skipping older analysis for category '${analysis.category}' (ID: ${analysis.id})`); - } - } - - if (analysesToDownload.length === 0) { - core.info("No analyses found to download after filtering. Exiting."); - return; - } - - core.info(`Attempting to download ${analysesToDownload.length} SARIF files after filtering.`); - - // --- 3. Download the filtered SARIF files --- - // Use Promise.all to download files concurrently - await Promise.all(analysesToDownload.map(async (analysis) => { - try { - const sarifId = analysis.id; - const category = analysis.category; - // Generate a clean filename from the category - const fileName = category.replace(/[^a-z0-9_]/gi, '_').toLowerCase() + '.sarif'; - const filePath = path.join(DOWNLOAD_DIR, fileName); - - core.info(`Downloading SARIF for category '${category}' (ID: ${sarifId})`); - - // Make a direct API call to get the SARIF content - const sarifResponse = await github.rest.codeScanning.getAnalysis({ - owner: repo.owner, - repo: repo.repo, - analysis_id: sarifId, - headers: { - Accept: "application/sarif+json", // Crucial: Request SARIF JSON directly - }, - }); - - const sarifContent = sarifResponse.data; - - // Basic validation for received content - if (!sarifContent || (typeof sarifContent === 'object' && Object.keys(sarifContent).length === 0) || (typeof sarifContent === 'string' && sarifContent.trim().length === 0)) { - core.warning(`SARIF content received is empty or malformed for analysis ID ${analysis.id} (category: '${category}'). Skipping write.`); - core.debug(`Received SARIF content (raw): ${sarifContent}`); - return; - } - - // Write the SARIF content to a file - fs.writeFileSync(filePath, JSON.stringify(sarifContent, null, 2)); // Pretty print JSON for readability - core.info(`Successfully downloaded SARIF for '${category}' to '${filePath}'`); - - } catch (error) { - core.error(`Failed to download SARIF for analysis ID ${analysis.id} (category: '${analysis.category}'): ${error.message}`); - // Log the error but don't fail the whole job so other downloads can proceed - // If you want the job to fail on any download error, you'd re-throw or setFailed here. - } - })); - - core.info("All filtered SARIF files processed and downloaded."); -} - -// This is the entry point for actions/github-script -module.exports = (github, context, core) => { - run(github, context, core).then(() => {}); -}; From 04eccec12433fb24391b734bd49d7034c79868fc Mon Sep 17 00:00:00 2001 From: varunbhardwaj-gmail Date: Wed, 23 Jul 2025 22:11:03 +0530 Subject: [PATCH 03/11] Create action.yml --- republish-filtered-sarifs/action.yml | 117 +++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 republish-filtered-sarifs/action.yml diff --git a/republish-filtered-sarifs/action.yml b/republish-filtered-sarifs/action.yml new file mode 100644 index 0000000..111df9d --- /dev/null +++ b/republish-filtered-sarifs/action.yml @@ -0,0 +1,117 @@ +# .github/actions/republish-filtered-sarifs/action.yml + +name: Download & Republish Filtered SARIFs +description: 'Downloads Code Scanning SARIF files, excludes a specified category, uploads them as an artifact, and then republishes the remaining to the PR/target branch.' +inputs: + excluded-category: + description: 'The single CodeQL category string to exclude from the download and republish (e.g., /language:java;project:backend-service).' + required: true + type: string + target-ref: + description: 'The Git reference (branch, tag, or SHA) from which to download analyses (e.g., refs/heads/main).' + required: false + type: string + default: 'refs/heads/main' # Default to main branch if not specified + +runs: + using: 'composite' + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Run SARIF Download Script + uses: actions/github-script@v7 + env: + EXCLUDED_CATEGORY_INPUT: ${{ inputs.excluded-category }} + TARGET_REF_INPUT: ${{ inputs.target-ref }} + with: + script: | + const scriptPath = `${process.env.GITHUB_ACTION_PATH}/download_filtered_sarifs.js`; + const script = require(scriptPath); + script(github, context, core); + github-token: ${{ github.token }} + + - name: Upload Downloaded SARIFs as Artifact + uses: actions/upload-artifact@v4 + with: + name: filtered-sarif-downloads + path: sarif_downloads/ + if-no-files-found: ignore + + # Scenario 1: PR mode - upload SARIF files via GitHub API to the PR itself + - name: Upload Filtered SARIF files to PR Code Scanning + if: github.event_name == 'pull_request' && github.event.pull_request.merged != true && hashFiles('sarif_downloads/*.sarif') != '' + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + set -x # Keep debugging enabled for now, can remove after successful run + + echo "Uploading filtered SARIF files to PR via GitHub Code Scanning API" + + SARIF_COUNT=$(find sarif_downloads -name "*.sarif" | wc -l) + echo "Found $SARIF_COUNT SARIF files to upload" + + REPO_OWNER="${GITHUB_REPOSITORY%/*}" + REPO_NAME="${GITHUB_REPOSITORY#*/}" + + for SARIF_FILE in sarif_downloads/*.sarif; do + echo "Processing $SARIF_FILE" + + # 1. Gzip and base64 encode the SARIF file content into a temporary file. + # This is the safest way to handle potentially very large data. + TEMP_BASE64_FILE=$(mktemp) + gzip -c "$SARIF_FILE" | base64 -w0 > "$TEMP_BASE64_FILE" + + # 2. Reconstruct the category name from the filename (for logging only, not payload) + CATEGORY_NAME=$(basename "$SARIF_FILE" .sarif | sed 's/_/\//g' | sed 's/\.yml:/yml:/g' | sed 's/^analyze\//\/analyze\//g') + + echo "Uploading $(basename "$SARIF_FILE") with category '$CATEGORY_NAME' to PR" + + if [ ! -s "$TEMP_BASE64_FILE" ]; then # -s checks if file exists and is not empty + echo "Error: SARIF content for $SARIF_FILE is empty after base64 encoding or file creation failed." + rm -f "$TEMP_BASE64_FILE" + exit 1 + fi + + # 3. Construct the full JSON payload using jq + # We use `--rawfile sarif_data "$TEMP_BASE64_FILE"` to read the file content + # directly into the 'sarif_data' variable as a raw string. + JSON_PAYLOAD=$(jq -n \ + --arg ref_val "refs/pull/${{ github.event.pull_request.number }}/merge" \ + --arg commit_val "${{ github.sha }}" \ + --rawfile sarif_data "$TEMP_BASE64_FILE" \ + '{sarif: $sarif_data, ref: $ref_val, commit_sha: $commit_val}') + + # Clean up the temporary file immediately after jq has used it + rm -f "$TEMP_BASE64_FILE" + + echo "DEBUG: JSON_PAYLOAD (sarif content redacted):" + # Pretty-print the JSON_PAYLOAD but replace the 'sarif' field's value with "REDACTED" + echo "$JSON_PAYLOAD" | jq 'if .sarif then .sarif = "REDACTED" else . end' + + if [ -z "$JSON_PAYLOAD" ]; then + echo "Error: JSON_PAYLOAD is empty. jq command failed to produce output." + exit 1 + fi + + # 4. Pipe the JSON payload to gh api --input - + printf "%s" "$JSON_PAYLOAD" | gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /repos/$REPO_OWNER/$REPO_NAME/code-scanning/sarifs \ + --input - \ + --jq '.id' + + if [ $? -eq 0 ]; then + echo "✓ Successfully uploaded $(basename "$SARIF_FILE")" + else + echo "✗ Failed to upload $(basename "$SARIF_FILE")" + exit 1 + fi + + sleep 1 + done + set +x + From 27c5968b4c6a115d1ccc4469e5e91030a21db9d2 Mon Sep 17 00:00:00 2001 From: varunbhardwaj-gmail Date: Wed, 23 Jul 2025 22:11:24 +0530 Subject: [PATCH 04/11] Create download_filtered_sarifs.js --- .../download_filtered_sarifs.js | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 republish-filtered-sarifs/download_filtered_sarifs.js diff --git a/republish-filtered-sarifs/download_filtered_sarifs.js b/republish-filtered-sarifs/download_filtered_sarifs.js new file mode 100644 index 0000000..5814efd --- /dev/null +++ b/republish-filtered-sarifs/download_filtered_sarifs.js @@ -0,0 +1,150 @@ +// download_filtered_sarifs.js + +const fs = require('node:fs'); +const path = require('node:path'); + +async function run(github, context, core) { + const repo = context.repo; + + // --- Input: Get the single excluded category from an environment variable --- + // This value will be passed from your GitHub Actions workflow step (see workflow example below). + const EXCLUDED_CATEGORY = process.env.EXCLUDED_CATEGORY_INPUT; + + if (!EXCLUDED_CATEGORY || EXCLUDED_CATEGORY.trim() === '') { + core.setFailed("Error: 'EXCLUDED_CATEGORY_INPUT' environment variable is required and cannot be empty."); + return; + } + + // --- Configuration: Adjust this to the Git reference (branch/tag/SHA) you want to download analyses from --- + // Examples: + // - 'refs/heads/main' (for your main branch) + // - 'refs/heads/develop' (for a development branch) + // - context.ref (for the current branch/ref the workflow is running on) + const targetRef = 'refs/heads/main'; // <--- ADJUST THIS IF NEEDED + + core.info(`Attempting to download SARIFs from '${targetRef}' for repo: ${repo.owner}/${repo.repo}`); + core.info(`EXCLUDING SARIFs with category: '${EXCLUDED_CATEGORY}'`); + + + const DOWNLOAD_DIR = 'sarif_downloads'; + if (!fs.existsSync(DOWNLOAD_DIR)) { + fs.mkdirSync(DOWNLOAD_DIR, { recursive: true }); // Ensure parent directories are created + core.info(`Created directory: ${DOWNLOAD_DIR}`); + } else { + core.info(`Directory already exists: ${DOWNLOAD_DIR}`); + } + + let allAnalyses = []; + let page = 1; + let hasNextPage = true; + + // --- 1. Fetch all recent Code Scanning analyses for the targetRef --- + try { + while (hasNextPage) { + const response = await github.rest.codeScanning.listRecentAnalyses({ + owner: repo.owner, + repo: repo.repo, + ref: targetRef, + per_page: 100, // Fetch 100 analyses per page + page: page, + tool_name: 'CodeQL' // Optionally filter by tool if you only want CodeQL SARIFs + }); + + allAnalyses = allAnalyses.concat(response.data); + + // Check if there are more pages to fetch + if (response.data.length < 100) { + hasNextPage = false; + } else { + page++; + } + } + core.info(`Found ${allAnalyses.length} total recent analyses for ref: '${targetRef}'`); + } catch (error) { + core.setFailed(`Failed to list recent analyses for '${targetRef}': ${error.message}`); + // Provide more detail for common errors like 404 (no analyses found) + if (error.status === 404) { + core.warning(`No CodeQL analyses found for ref: '${targetRef}'. Ensure analyses exist for this branch.`); + } + return; + } + + // --- 2. Filter out analyses based on the single EXCLUDED_CATEGORY --- + const analysesToDownload = []; + const categoriesSeen = new Set(); // To ensure we only download the most recent analysis for each unique category + + // Sort by created_at (most recent first) to ensure we get the latest if multiple analyses exist for a category + allAnalyses.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); + + for (const analysis of allAnalyses) { + // Check if the category matches our single excluded category + if (analysis.category === EXCLUDED_CATEGORY) { + core.info(`Skipping analysis for excluded category: '${analysis.category}' (ID: ${analysis.id})`); + continue; // Skip to the next analysis if it's the excluded one + } + + // Ensure we only download the most recent analysis for each unique category found after exclusion + if (!categoriesSeen.has(analysis.category)) { + categoriesSeen.add(analysis.category); + analysesToDownload.push(analysis); + } else { + core.debug(`Skipping older analysis for category '${analysis.category}' (ID: ${analysis.id})`); + } + } + + if (analysesToDownload.length === 0) { + core.info("No analyses found to download after filtering. Exiting."); + return; + } + + core.info(`Attempting to download ${analysesToDownload.length} SARIF files after filtering.`); + + // --- 3. Download the filtered SARIF files --- + // Use Promise.all to download files concurrently + await Promise.all(analysesToDownload.map(async (analysis) => { + try { + const sarifId = analysis.id; + const category = analysis.category; + // Generate a clean filename from the category + const fileName = category.replace(/[^a-z0-9_]/gi, '_').toLowerCase() + '.sarif'; + const filePath = path.join(DOWNLOAD_DIR, fileName); + + core.info(`Downloading SARIF for category '${category}' (ID: ${sarifId})`); + + // Make a direct API call to get the SARIF content + const sarifResponse = await github.rest.codeScanning.getAnalysis({ + owner: repo.owner, + repo: repo.repo, + analysis_id: sarifId, + headers: { + Accept: "application/sarif+json", // Crucial: Request SARIF JSON directly + }, + }); + + const sarifContent = sarifResponse.data; + + // Basic validation for received content + if (!sarifContent || (typeof sarifContent === 'object' && Object.keys(sarifContent).length === 0) || (typeof sarifContent === 'string' && sarifContent.trim().length === 0)) { + core.warning(`SARIF content received is empty or malformed for analysis ID ${analysis.id} (category: '${category}'). Skipping write.`); + core.debug(`Received SARIF content (raw): ${sarifContent}`); + return; + } + + // Write the SARIF content to a file + fs.writeFileSync(filePath, JSON.stringify(sarifContent, null, 2)); // Pretty print JSON for readability + core.info(`Successfully downloaded SARIF for '${category}' to '${filePath}'`); + + } catch (error) { + core.error(`Failed to download SARIF for analysis ID ${analysis.id} (category: '${analysis.category}'): ${error.message}`); + // Log the error but don't fail the whole job so other downloads can proceed + // If you want the job to fail on any download error, you'd re-throw or setFailed here. + } + })); + + core.info("All filtered SARIF files processed and downloaded."); +} + +// This is the entry point for actions/github-script +module.exports = (github, context, core) => { + run(github, context, core).then(() => {}); +}; From 2e7080a1dfedd576399b6fe80b17894b1f04543c Mon Sep 17 00:00:00 2001 From: varunbhardwaj-gmail Date: Wed, 23 Jul 2025 22:22:36 +0530 Subject: [PATCH 05/11] Update README.md --- README.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/README.md b/README.md index c68422d..c20c247 100644 --- a/README.md +++ b/README.md @@ -261,6 +261,49 @@ To allow this, you need to give the workflow the `closed` type on the `pull_requ - synchronize - closed ``` +### Alternative SARIF Republishing: `republish-filtered-sarif` Action + +A new GitHub Action, `republish-filtered-sarif`, is now available to streamline Code Scanning results presentation on Pull Requests within a monorepo. + +**Problem Addressed:** +In monorepo PR workflows, Code Scanning checks for unscanned projects may appear incomplete. While the existing `republish-sarif` action addresses this, it often requires maintaining a `projects.json` file to define all projects for republishing. This can be an overhead for users. + +**Solution (`republish-filtered-sarif` Action):** +This composite action provides a quick, easy, and **`projects.json`-agnostic** way to ensure a complete Code Scanning overview on PRs. It works by: + +1. **Dynamically Discovering Analyses:** It queries the GitHub Code Scanning API to find all recent CodeQL analyses published to the specified `target-ref` (e.g., your `main` branch). +2. **Intelligent Exclusion:** It takes an `excluded-category` input, which should be the exact category string used in the CodeQL `analyze` step for the project currently being scanned in the PR. This allows the action to *exclude* that specific project's SARIF from being downloaded and re-uploaded. +3. **Latest SARIF Selection:** For all *other* projects/categories found on the `target-ref`, it selects and downloads only the *most recent* SARIF. +4. **Direct API Republishing:** These filtered SARIFs are then directly uploaded to the current Pull Request's commit SHA via the GitHub Code Scanning API. + +**Key Benefit:** +This action **simplifies the republishing process by removing the need for a `projects.json` file** for this step. Users provide the CodeQL `category` value of the currently scanned project, and the action automatically handles the rest, offering a streamlined, category-based approach for comprehensive PR security insights. + +**Example Usage:** + +```yaml +# In your monorepo workflow (e.g., for a 'backend' project) + +jobs: + analyze: + steps: + # ... (checkout, setup, CodeQL init, build steps) ... + + - name: Perform CodeQL Analysis (Backend) + uses: github/codeql-action/analyze@v3 + with: + category: 'backend' # <--- This unique category string is key! + output: ${{ runner.temp }}/pr-analyze-sarif + + # ... (other steps, like dependency submission) ... + + - name: Republish other SARIFs for PR + if: github.event_name == 'pull_request' + uses: your-org/your-repo/.github/actions/republish-filtered-sarif@main # Update this path + with: + excluded-category: 'backend' # Pass the category of the project just scanned + target-ref: 'refs/heads/main' # Typically your default branch + ## Limitations From 46517c6f9f5d694cf5ffc1b5fa5afee28f273956 Mon Sep 17 00:00:00 2001 From: varunbhardwaj-gmail Date: Wed, 23 Jul 2025 22:24:16 +0530 Subject: [PATCH 06/11] Update README.md --- README.md | 43 ------------------------------------------- 1 file changed, 43 deletions(-) diff --git a/README.md b/README.md index c20c247..c68422d 100644 --- a/README.md +++ b/README.md @@ -261,49 +261,6 @@ To allow this, you need to give the workflow the `closed` type on the `pull_requ - synchronize - closed ``` -### Alternative SARIF Republishing: `republish-filtered-sarif` Action - -A new GitHub Action, `republish-filtered-sarif`, is now available to streamline Code Scanning results presentation on Pull Requests within a monorepo. - -**Problem Addressed:** -In monorepo PR workflows, Code Scanning checks for unscanned projects may appear incomplete. While the existing `republish-sarif` action addresses this, it often requires maintaining a `projects.json` file to define all projects for republishing. This can be an overhead for users. - -**Solution (`republish-filtered-sarif` Action):** -This composite action provides a quick, easy, and **`projects.json`-agnostic** way to ensure a complete Code Scanning overview on PRs. It works by: - -1. **Dynamically Discovering Analyses:** It queries the GitHub Code Scanning API to find all recent CodeQL analyses published to the specified `target-ref` (e.g., your `main` branch). -2. **Intelligent Exclusion:** It takes an `excluded-category` input, which should be the exact category string used in the CodeQL `analyze` step for the project currently being scanned in the PR. This allows the action to *exclude* that specific project's SARIF from being downloaded and re-uploaded. -3. **Latest SARIF Selection:** For all *other* projects/categories found on the `target-ref`, it selects and downloads only the *most recent* SARIF. -4. **Direct API Republishing:** These filtered SARIFs are then directly uploaded to the current Pull Request's commit SHA via the GitHub Code Scanning API. - -**Key Benefit:** -This action **simplifies the republishing process by removing the need for a `projects.json` file** for this step. Users provide the CodeQL `category` value of the currently scanned project, and the action automatically handles the rest, offering a streamlined, category-based approach for comprehensive PR security insights. - -**Example Usage:** - -```yaml -# In your monorepo workflow (e.g., for a 'backend' project) - -jobs: - analyze: - steps: - # ... (checkout, setup, CodeQL init, build steps) ... - - - name: Perform CodeQL Analysis (Backend) - uses: github/codeql-action/analyze@v3 - with: - category: 'backend' # <--- This unique category string is key! - output: ${{ runner.temp }}/pr-analyze-sarif - - # ... (other steps, like dependency submission) ... - - - name: Republish other SARIFs for PR - if: github.event_name == 'pull_request' - uses: your-org/your-repo/.github/actions/republish-filtered-sarif@main # Update this path - with: - excluded-category: 'backend' # Pass the category of the project just scanned - target-ref: 'refs/heads/main' # Typically your default branch - ## Limitations From 51a758fad2f69665aa4101acd71c5d642bbd41a2 Mon Sep 17 00:00:00 2001 From: varunbhardwaj-gmail Date: Wed, 23 Jul 2025 22:27:17 +0530 Subject: [PATCH 07/11] Update README.md --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/README.md b/README.md index c68422d..7a31b39 100644 --- a/README.md +++ b/README.md @@ -262,6 +262,50 @@ To allow this, you need to give the workflow the `closed` type on the `pull_requ - closed ``` +### Alternative SARIF Republishing: `republish-filtered-sarif` Action + +A new GitHub Action, `republish-filtered-sarif`, is now available to streamline Code Scanning results presentation on Pull Requests within a monorepo. + +**Problem Addressed:** +In monorepo PR workflows, Code Scanning checks for unscanned projects may appear incomplete. While the existing `republish-sarif` action addresses this, it often requires maintaining a `projects.json` file to define all projects for republishing. This can be an overhead for users. + +**Solution (`republish-filtered-sarif` Action):** +This composite action provides a quick, easy, and **`projects.json`-agnostic** way to ensure a complete Code Scanning overview on PRs. It works by: + +1. **Dynamically Discovering Analyses:** It queries the GitHub Code Scanning API to find all recent CodeQL analyses published to the specified `target-ref` (e.g., your `main` branch). +2. **Intelligent Exclusion:** It takes an `excluded-category` input, which should be the exact category string used in the CodeQL `analyze` step for the project currently being scanned in the PR. This allows the action to *exclude* that specific project's SARIF from being downloaded and re-uploaded. +3. **Latest SARIF Selection:** For all *other* projects/categories found on the `target-ref`, it selects and downloads only the *most recent* SARIF. +4. **Direct API Republishing:** These filtered SARIFs are then directly uploaded to the current Pull Request's commit SHA via the GitHub Code Scanning API. + +**Key Benefit:** +This action **simplifies the republishing process by removing the need for a `projects.json` file** for this step. Users provide the CodeQL `category` value of the currently scanned project, and the action automatically handles the rest, offering a streamlined, category-based approach for comprehensive PR security insights. + +**Example Usage:** + +```yaml +# In your monorepo workflow (e.g., for a 'backend' project) + +jobs: + analyze: + steps: + # ... (checkout, setup, CodeQL init, build steps) ... + + - name: Perform CodeQL Analysis (Backend) + uses: github/codeql-action/analyze@v3 + with: + category: 'backend' # <--- This unique category string is key! + output: ${{ runner.temp }}/pr-analyze-sarif + + # ... (other steps, like dependency submission) ... + + - name: Republish other SARIFs for PR + if: github.event_name == 'pull_request' + uses: your-org/your-repo/.github/actions/republish-filtered-sarif@main # Update this path + with: + excluded-category: 'backend' # Pass the category of the project just scanned + target-ref: 'refs/heads/main' # Typically your default branch + + ## Limitations Actions can create matrix jobs with a maximum of 256 targets. This means that monorepos with more than 256 projects must be divided up into more than one workflow, until something is done to deal with this in this Action (if that is possible). From ccce901132b1d3ceda703e84cd5c4de9d608b73c Mon Sep 17 00:00:00 2001 From: varunbhardwaj-gmail Date: Wed, 23 Jul 2025 22:30:16 +0530 Subject: [PATCH 08/11] Update README.md --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7a31b39..df4117f 100644 --- a/README.md +++ b/README.md @@ -272,9 +272,9 @@ In monorepo PR workflows, Code Scanning checks for unscanned projects may appear **Solution (`republish-filtered-sarif` Action):** This composite action provides a quick, easy, and **`projects.json`-agnostic** way to ensure a complete Code Scanning overview on PRs. It works by: -1. **Dynamically Discovering Analyses:** It queries the GitHub Code Scanning API to find all recent CodeQL analyses published to the specified `target-ref` (e.g., your `main` branch). +1. **Dynamically Discovering Analyses:** It queries the GitHub Code Scanning API to find all recent CodeQL analyses published to the `main` branch. 2. **Intelligent Exclusion:** It takes an `excluded-category` input, which should be the exact category string used in the CodeQL `analyze` step for the project currently being scanned in the PR. This allows the action to *exclude* that specific project's SARIF from being downloaded and re-uploaded. -3. **Latest SARIF Selection:** For all *other* projects/categories found on the `target-ref`, it selects and downloads only the *most recent* SARIF. +3. **Latest SARIF Selection:** For all *other* projects/categories found on the `main` branch, it selects and downloads only the *most recent* SARIF. 4. **Direct API Republishing:** These filtered SARIFs are then directly uploaded to the current Pull Request's commit SHA via the GitHub Code Scanning API. **Key Benefit:** @@ -293,8 +293,7 @@ jobs: - name: Perform CodeQL Analysis (Backend) uses: github/codeql-action/analyze@v3 with: - category: 'backend' # <--- This unique category string is key! - output: ${{ runner.temp }}/pr-analyze-sarif + category: 'backend' # <--- This unique category string is key! # ... (other steps, like dependency submission) ... @@ -303,8 +302,6 @@ jobs: uses: your-org/your-repo/.github/actions/republish-filtered-sarif@main # Update this path with: excluded-category: 'backend' # Pass the category of the project just scanned - target-ref: 'refs/heads/main' # Typically your default branch - ## Limitations From 708eca9b224f6729b01b0c0693a440406466b15d Mon Sep 17 00:00:00 2001 From: varunbhardwaj-gmail Date: Wed, 23 Jul 2025 22:31:11 +0530 Subject: [PATCH 09/11] Update README.md --- README.md | 82 +++++++++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index df4117f..4043c33 100644 --- a/README.md +++ b/README.md @@ -262,47 +262,6 @@ To allow this, you need to give the workflow the `closed` type on the `pull_requ - closed ``` -### Alternative SARIF Republishing: `republish-filtered-sarif` Action - -A new GitHub Action, `republish-filtered-sarif`, is now available to streamline Code Scanning results presentation on Pull Requests within a monorepo. - -**Problem Addressed:** -In monorepo PR workflows, Code Scanning checks for unscanned projects may appear incomplete. While the existing `republish-sarif` action addresses this, it often requires maintaining a `projects.json` file to define all projects for republishing. This can be an overhead for users. - -**Solution (`republish-filtered-sarif` Action):** -This composite action provides a quick, easy, and **`projects.json`-agnostic** way to ensure a complete Code Scanning overview on PRs. It works by: - -1. **Dynamically Discovering Analyses:** It queries the GitHub Code Scanning API to find all recent CodeQL analyses published to the `main` branch. -2. **Intelligent Exclusion:** It takes an `excluded-category` input, which should be the exact category string used in the CodeQL `analyze` step for the project currently being scanned in the PR. This allows the action to *exclude* that specific project's SARIF from being downloaded and re-uploaded. -3. **Latest SARIF Selection:** For all *other* projects/categories found on the `main` branch, it selects and downloads only the *most recent* SARIF. -4. **Direct API Republishing:** These filtered SARIFs are then directly uploaded to the current Pull Request's commit SHA via the GitHub Code Scanning API. - -**Key Benefit:** -This action **simplifies the republishing process by removing the need for a `projects.json` file** for this step. Users provide the CodeQL `category` value of the currently scanned project, and the action automatically handles the rest, offering a streamlined, category-based approach for comprehensive PR security insights. - -**Example Usage:** - -```yaml -# In your monorepo workflow (e.g., for a 'backend' project) - -jobs: - analyze: - steps: - # ... (checkout, setup, CodeQL init, build steps) ... - - - name: Perform CodeQL Analysis (Backend) - uses: github/codeql-action/analyze@v3 - with: - category: 'backend' # <--- This unique category string is key! - - # ... (other steps, like dependency submission) ... - - - name: Republish other SARIFs for PR - if: github.event_name == 'pull_request' - uses: your-org/your-repo/.github/actions/republish-filtered-sarif@main # Update this path - with: - excluded-category: 'backend' # Pass the category of the project just scanned - ## Limitations Actions can create matrix jobs with a maximum of 256 targets. This means that monorepos with more than 256 projects must be divided up into more than one workflow, until something is done to deal with this in this Action (if that is possible). @@ -353,3 +312,44 @@ You can inject CodeQL into the build tool as such as custom command; for compile It will be necessary to keep track of the CodeQL category assigned to each build target, to avoid clashes. See the GitHub documentation on [Using code scanning with your existing CI system](https://docs.github.com/en/enterprise-cloud@latest/code-security/code-scanning/integrating-with-code-scanning/using-code-scanning-with-your-existing-ci-system) + +### Alternative SARIF Republishing: `republish-filtered-sarif` Action + +A new GitHub Action, `republish-filtered-sarif`, is now available to streamline Code Scanning results presentation on Pull Requests within a monorepo. + +**Problem Addressed:** +In monorepo PR workflows, Code Scanning checks for unscanned projects may appear incomplete. While the existing `republish-sarif` action addresses this, it often requires maintaining a `projects.json` file to define all projects for republishing. This can be an overhead for users. + +**Solution (`republish-filtered-sarif` Action):** +This composite action provides a quick, easy, and **`projects.json`-agnostic** way to ensure a complete Code Scanning overview on PRs. It works by: + +1. **Dynamically Discovering Analyses:** It queries the GitHub Code Scanning API to find all recent CodeQL analyses published to the `main` branch. +2. **Intelligent Exclusion:** It takes an `excluded-category` input, which should be the exact category string used in the CodeQL `analyze` step for the project currently being scanned in the PR. This allows the action to *exclude* that specific project's SARIF from being downloaded and re-uploaded. +3. **Latest SARIF Selection:** For all *other* projects/categories found on the `main` branch, it selects and downloads only the *most recent* SARIF. +4. **Direct API Republishing:** These filtered SARIFs are then directly uploaded to the current Pull Request's commit SHA via the GitHub Code Scanning API. + +**Key Benefit:** +This action **simplifies the republishing process by removing the need for a `projects.json` file** for this step. Users provide the CodeQL `category` value of the currently scanned project, and the action automatically handles the rest, offering a streamlined, category-based approach for comprehensive PR security insights. + +**Example Usage:** + +```yaml +# In your monorepo workflow (e.g., for a 'backend' project) + +jobs: + analyze: + steps: + # ... (checkout, setup, CodeQL init, build steps) ... + + - name: Perform CodeQL Analysis (Backend) + uses: github/codeql-action/analyze@v3 + with: + category: 'backend' # <--- This unique category string is key! + + # ... (other steps, like dependency submission) ... + + - name: Republish other SARIFs for PR + if: github.event_name == 'pull_request' + uses: your-org/your-repo/.github/actions/republish-filtered-sarif@main # Update this path + with: + excluded-category: 'backend' # Pass the category of the project just scanned From 0e4f6eb3e36482ebee1fee8fe61799fedc3e1dd2 Mon Sep 17 00:00:00 2001 From: varunbhardwaj-gmail Date: Wed, 30 Jul 2025 16:45:06 +0530 Subject: [PATCH 10/11] Update download_filtered_sarifs.js --- .../download_filtered_sarifs.js | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/republish-filtered-sarifs/download_filtered_sarifs.js b/republish-filtered-sarifs/download_filtered_sarifs.js index 5814efd..c9c25cb 100644 --- a/republish-filtered-sarifs/download_filtered_sarifs.js +++ b/republish-filtered-sarifs/download_filtered_sarifs.js @@ -15,14 +15,20 @@ async function run(github, context, core) { return; } - // --- Configuration: Adjust this to the Git reference (branch/tag/SHA) you want to download analyses from --- - // Examples: - // - 'refs/heads/main' (for your main branch) - // - 'refs/heads/develop' (for a development branch) - // - context.ref (for the current branch/ref the workflow is running on) - const targetRef = 'refs/heads/main'; // <--- ADJUST THIS IF NEEDED - - core.info(`Attempting to download SARIFs from '${targetRef}' for repo: ${repo.owner}/${repo.repo}`); + // --- Dynamically determine the default branch for downloading analyses --- + let targetRef; + try { + const { data: repoData } = await github.rest.repos.get({ + owner: repo.owner, + repo: repo.repo, + }); + targetRef = `refs/heads/${repoData.default_branch}`; + core.info(`Dynamically determined default branch: '${repoData.default_branch}'. Will download analyses from '${targetRef}'.`); + } catch (error) { + core.setFailed(`Failed to get default branch for ${repo.owner}/${repo.repo}: ${error.message}`); + return; + } + core.info(`EXCLUDING SARIFs with category: '${EXCLUDED_CATEGORY}'`); From 846103c989761801385d3021747ab5a0454d3a58 Mon Sep 17 00:00:00 2001 From: varunbhardwaj-gmail Date: Wed, 30 Jul 2025 16:45:35 +0530 Subject: [PATCH 11/11] Update action.yml --- republish-filtered-sarifs/action.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/republish-filtered-sarifs/action.yml b/republish-filtered-sarifs/action.yml index 111df9d..5cf664e 100644 --- a/republish-filtered-sarifs/action.yml +++ b/republish-filtered-sarifs/action.yml @@ -1,4 +1,4 @@ -# .github/actions/republish-filtered-sarifs/action.yml +# .github/actions/republish-sarifs/action.yml name: Download & Republish Filtered SARIFs description: 'Downloads Code Scanning SARIF files, excludes a specified category, uploads them as an artifact, and then republishes the remaining to the PR/target branch.' @@ -6,12 +6,7 @@ inputs: excluded-category: description: 'The single CodeQL category string to exclude from the download and republish (e.g., /language:java;project:backend-service).' required: true - type: string - target-ref: - description: 'The Git reference (branch, tag, or SHA) from which to download analyses (e.g., refs/heads/main).' - required: false - type: string - default: 'refs/heads/main' # Default to main branch if not specified + type: string runs: using: 'composite' @@ -22,8 +17,7 @@ runs: - name: Run SARIF Download Script uses: actions/github-script@v7 env: - EXCLUDED_CATEGORY_INPUT: ${{ inputs.excluded-category }} - TARGET_REF_INPUT: ${{ inputs.target-ref }} + EXCLUDED_CATEGORY_INPUT: ${{ inputs.excluded-category }} with: script: | const scriptPath = `${process.env.GITHUB_ACTION_PATH}/download_filtered_sarifs.js`;