diff --git a/.github/workflows/org-codeql.yml b/.github/workflows/org-codeql.yml new file mode 100644 index 00000000..10833edf --- /dev/null +++ b/.github/workflows/org-codeql.yml @@ -0,0 +1,23 @@ +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 + - uses: ./actions/codeql diff --git a/actions/codeql/action.yml b/actions/codeql/action.yml new file mode 100644 index 00000000..ad097c6a --- /dev/null +++ b/actions/codeql/action.yml @@ -0,0 +1,93 @@ +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: ${{ 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'; + if (debug) { + console.log("Initializing CodeQL Action"); + console.log(${{ toJSON(github) }}); + } + + const { exists } = await import('fs/promises'); + 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, + codeql_config_file: await exists('.github/codeql/codeql-config.yml') ? + '.github/codeql/codeql-config.yml' : + await exists('${{ github.action_path }}/../../.github/codeql/codeql-config.yml') ? + '${{ github.action_path }}/../../.github/codeql/codeql-config.yml' : + undefined, + }, config, properties, inputs); + + return options; + - if: ${{ 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: ${{ steps.cfg.outputs.result.codeql_enabled == 'true' }} + name: Autobuild + uses: github/codeql-action/autobuild@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 + - if: ${{ 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