From d52e2e70e2af6d5771e8928eca82ef00ca38f2c8 Mon Sep 17 00:00:00 2001 From: Andrea Brancaleoni Date: Fri, 19 Jan 2024 00:26:31 +0100 Subject: [PATCH] *: new workflow org-codeql --- .github/workflows/org-codeql.yml | 22 ++++++++ action.yml | 68 +---------------------- actions/codeql/action.yml | 95 ++++++++++++++++++++++++++++++++ src/getConfig.js | 21 +++++++ src/getProperties.js | 25 +++++++++ 5 files changed, 166 insertions(+), 65 deletions(-) create mode 100644 .github/workflows/org-codeql.yml create mode 100644 actions/codeql/action.yml create mode 100644 src/getConfig.js create mode 100644 src/getProperties.js diff --git a/.github/workflows/org-codeql.yml b/.github/workflows/org-codeql.yml new file mode 100644 index 00000000..1e1a7fd5 --- /dev/null +++ b/.github/workflows/org-codeql.yml @@ -0,0 +1,22 @@ +name: CodeQL +on: + workflow_dispatch: + push: + branches: [main, master, staging, development, devel, dev, prod] + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + branches: [main, master, staging, development, devel, dev] + merge_group: +permissions: + contents: read + issues: read + pull-requests: read +jobs: + codeql: + name: codeql + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: brave/security-action/actions/codeql@main \ No newline at end of file diff --git a/action.yml b/action.yml index 184e7bd7..efd03ee5 100644 --- a/action.yml +++ b/action.yml @@ -36,9 +36,6 @@ inputs: debug: description: enables debug output for this action required: false - codeql_config: - description: CodeQL configuration config config - default: ./.github/codeql/codeql-config.yml enabled: type: boolean description: may disable the whole action, big red button for emergency cases @@ -87,14 +84,14 @@ runs: with: separator: '\0' - name: Store reviewdog enabled for PR - if: ${{ inputs.enabled == 'true' && inputs.baseline_scan_only != 'false' && steps.changed-files.outputs.all_changed_files != '' && github.event_name == 'pull_request' && github.event.pull_request.draft == false && github.actor != 'dependabot[bot]' && (!matrix.language || matrix.language == 'generic') }} + if: ${{ inputs.enabled == 'true' && inputs.baseline_scan_only != 'false' && steps.changed-files.outputs.all_changed_files != '' && github.event_name == 'pull_request' && github.event.pull_request.draft == false && github.actor != 'dependabot[bot]' }} id: reviewdog-enabled-pr shell: bash run: | set -x echo "result=true" >> $GITHUB_OUTPUT - name: Store reviewdog enabled for full repo manual run - if: ${{ inputs.enabled == 'true' && !(steps.reviewdog-enabled-pr.outputs.result == 'true') && (inputs.baseline_scan_only == 'false' || github.event_name == 'workflow_dispatch') && (!matrix.language || matrix.language == 'generic') }} + if: ${{ inputs.enabled == 'true' && !(steps.reviewdog-enabled-pr.outputs.result == 'true') && (inputs.baseline_scan_only == 'false' || github.event_name == 'workflow_dispatch') }} id: reviewdog-enabled-full shell: bash run: | @@ -107,41 +104,6 @@ runs: run: | set -x echo "result=true" >> $GITHUB_OUTPUT - - name: Store if CodeQL should be enabled - id: codeql-enabled - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - env: - LANGUAGE: ${{ matrix.language }} - ENABLED: ${{ inputs.enabled }} - with: - script: | - const query = ` - query($owner: String!, $name: String!) { - repository(owner: $owner, name: $name) { - isPrivate - } - } - `; - const variables = { - owner: context.repo.owner, - name: context.repo.repo, - }; - const result = await github.graphql(query, variables); - - const isPrivate = result.repository.isPrivate; - const isDraft = context.payload.pull_request?.draft; - const isDependabot = context.actor === 'dependabot[bot]'; - const isNotGeneric = process.env.LANGUAGE !== "" && process.env.LANGUAGE !== 'generic'; - const isEnabled = process.env.ENABLED === 'true'; - - return isEnabled && !isDraft && !isDependabot && isNotGeneric && !isPrivate; - - name: CodeQL Sanity Check - if: ${{ steps.codeql-enabled.outputs.result != 'true' && steps.codeql-enabled.outputs.result != 'false' }} - shell: bash - run: | - set -x - echo "CodeQL enabled is not a boolean, aborting" - exit 1 # REVIEWDOG Steps # REVIEWDOG Setup - name: Write changed files to file @@ -219,7 +181,7 @@ runs: console.log("Comments: %d", commentsNumber); return commentsNumber; - id: unverified-commits - if: ${{ github.event_name == 'pull_request' && (!matrix.language || matrix.language == 'generic') }} + if: ${{ github.event_name == 'pull_request' }} uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | @@ -628,27 +590,3 @@ runs: channel: secops-hotspots color: green verbose: true -# CodeQL Steps - - if: ${{ steps.codeql-enabled.outputs.result == 'true' && hashFiles(inputs.codeql_config) }} - name: Initialize CodeQL - uses: github/codeql-action/init@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 - with: - languages: ${{ matrix.language }} - config-file: ${{ inputs.codeql_config }} - - if: ${{ steps.codeql-enabled.outputs.result == 'true' && !hashFiles(inputs.codeql_config)}} - name: Initialize CodeQL (without config) - uses: github/codeql-action/init@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 - with: - languages: ${{ matrix.language }} - - if: ${{ steps.codeql-enabled.outputs.result == 'true' }} - name: Autobuild - uses: github/codeql-action/autobuild@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 - - if: ${{ steps.codeql-enabled.outputs.result == 'true' }} - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 -# - run: echo ${{ inputs.in-name }} -# shell: bash -# - id: output-step-id -# run: echo "output-name=antani" >> $GITHUB_OUTPUT -# shell bash - diff --git a/actions/codeql/action.yml b/actions/codeql/action.yml new file mode 100644 index 00000000..03cb2f42 --- /dev/null +++ b/actions/codeql/action.yml @@ -0,0 +1,95 @@ +name: "CodeQL action" +description: "CodeQL" +inputs: + debug: + description: enables debug output for this action + required: false + enabled: + description: enables this action + required: false +runs: + using: "composite" + steps: + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@ae82ed4ae04587b665efad2f206578aa6f0e8539 # v42.0.0 + with: + separator: '\0' + files: | + **/*.{cpp,c++,hpp,hh,h++,hxx,c,cc,h} + **/*.{sln,csproj,cs,cshtml,xaml} + **/*.go + **/*.java + **/*.kt + **/*.{js,jsx,mjs,es,es6,htm,html,xhtm,xhtml,vue,hbs,ejs,njk,json,yaml,yml,raml,xml} + **/*.py + **/*.{rb,erb,gemspec} + **/Gemfile + **/*.swift + **/*.{ts,tsx,mts,cts} + - name: Store configurations + id: cfg + env: + DEBUG: ${{ (inputs.debug == 'true' || runner.debug) && 'true' || 'false'}} + FILES: ${{ steps.changed-files.outputs.all_changed_and_modified_files }} + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + var debug = process.env.DEBUG == 'true'; + console.log(debug); + if (debug) { + console.log("Initializing CodeQL Action"); + } + + const { existsSync } = await import('fs'); + const { default: getConfig } = await import('${{ github.action_path }}/../../src/getConfig.js'); + const { default: getProperties } = await import('${{ github.action_path }}/../../src/getProperties.js'); + + const query = ` + query($owner: String!, $name: String!) { + repository(owner: $owner, name: $name) { + isPrivate + } + } + `; + const variables = { + owner: context.repo.owner, + name: context.repo.repo, + }; + const result = await github.graphql(query, variables); + + const isPrivate = result.repository.isPrivate; + const isDraft = context.payload.pull_request?.draft; + const isBot = context.actor.endsWith('[bot]'); + const isEmptyFiles = process.env.FILES.trim() === ''; + + const inputs = ${{ toJson(inputs) }}; + // delete if empty string in inputs value + Object.keys(inputs).forEach(key => inputs[key] === '' && delete inputs[key]); + + const config = await getConfig({owner: context.repo.owner, repo: context.repo.repo, path: '.github/codeql.json', debug, github}); + const properties = await getProperties({owner: context.repo.owner, repo: context.repo.repo, debug, github}); + + const options = Object.assign({ + codeql_enabled: (!isDraft && !isBot && !isPrivate && !isEmptyFiles).toString(), + codeql_config_file: existsSync('.github/codeql/codeql-config.yml') ? + '.github/codeql/codeql-config.yml' : + existsSync('${{ github.action_path }}/../../.github/codeql/codeql-config.yml') ? + '${{ github.action_path }}/../../.github/codeql/codeql-config.yml' : + undefined, + }, config, properties, inputs); + + if (debug) console.log(`options: ${JSON.stringify(options, null, 2)}`); + + return options; + - if: ${{ fromJson(steps.cfg.outputs.result).codeql_enabled == 'true' }} + name: Initialize CodeQL + uses: github/codeql-action/init@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 + with: + config-file: ${{ steps.cfg.outputs.result.codeql_config_file }} + - if: ${{ fromJson(steps.cfg.outputs.result).codeql_enabled == 'true' }} + name: Autobuild + uses: github/codeql-action/autobuild@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 + - if: ${{ fromJson(steps.cfg.outputs.result).codeql_enabled == 'true' }} + name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 diff --git a/src/getConfig.js b/src/getConfig.js new file mode 100644 index 00000000..f5d3913a --- /dev/null +++ b/src/getConfig.js @@ -0,0 +1,21 @@ +export default async function getConfig({ owner, repo, path, github, githubToken, debug = false }) { + if (!github && githubToken) { + const { Octokit } = await import("@octokit/core"); + + github = new Octokit({ auth: githubToken }) + } + + try { + const { data } = await github.rest.repos.getContent({ + owner, + repo, + path, + }); + const fileContent = Buffer.from(data.content, 'base64').toString('utf8'); + if (debug) console.log(fileContent); + return JSON.parse(fileContent); + } catch (err) { + if (debug) console.log(err); + return {}; + } +} \ No newline at end of file diff --git a/src/getProperties.js b/src/getProperties.js new file mode 100644 index 00000000..8dd0a885 --- /dev/null +++ b/src/getProperties.js @@ -0,0 +1,25 @@ +export default async function getProperties({ owner, repo, github, githubToken, debug = false }) { + if (!github && githubToken) { + const { Octokit } = await import("@octokit/core"); + + github = new Octokit({ auth: githubToken }) + } + + try { + let properties = await github.request('GET /repos/{owner}/{repo}/properties/values', { + owner: owner, + repo: repo, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }); + if (debug) console.log(properties); + return properties.data.reduce((acc, cur) => { + acc[cur.property_name] = cur.value; + return acc; + }, {}); + } catch (err) { + console.log(err); + return {}; + } +} \ No newline at end of file