diff --git a/.github/workflows/add-good-first-issue-labels.yml b/.github/workflows/add-good-first-issue-labels.yml index 05d05ca..5ba7a6a 100644 --- a/.github/workflows/add-good-first-issue-labels.yml +++ b/.github/workflows/add-good-first-issue-labels.yml @@ -9,15 +9,20 @@ on: types: - created +permissions: {} + jobs: add-labels: + name: Add 'Good First Issue' and 'area/*' labels if: ${{(!github.event.issue.pull_request && github.event.issue.state != 'closed' && github.actor != 'asyncapi-bot') && (contains(github.event.comment.body, '/good-first-issue') || contains(github.event.comment.body, '/gfi' ))}} runs-on: ubuntu-latest + permissions: + issues: write # This is needed to add labels to issues. steps: - name: Add label uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_TOKEN }} + github-token: ${{ github.token }} script: | const areas = ['javascript', 'typescript', 'java' , 'go', 'docs', 'ci-cd', 'design']; const words = context.payload.comment.body.trim().split(" "); diff --git a/.github/workflows/automerge-for-humans-add-ready-to-merge-or-do-not-merge-label.yml b/.github/workflows/automerge-for-humans-add-ready-to-merge-or-do-not-merge-label.yml index 2db55d8..99078d3 100644 --- a/.github/workflows/automerge-for-humans-add-ready-to-merge-or-do-not-merge-label.yml +++ b/.github/workflows/automerge-for-humans-add-ready-to-merge-or-do-not-merge-label.yml @@ -12,8 +12,15 @@ on: types: - created +permissions: {} + jobs: add-ready-to-merge-label: + name: Add ready-to-merge label + permissions: + issues: write # required to add labels and post comments on PR issues + pull-requests: write # required to read PR metadata from the issue pull_request URL + contents: read # required to compare PR branch commits against base if: > github.event.issue.pull_request && github.event.issue.state != 'closed' && @@ -30,7 +37,7 @@ jobs: env: GITHUB_ACTOR: ${{ github.actor }} with: - github-token: ${{ secrets.GH_TOKEN }} + github-token: ${{ github.token }} script: | const prDetailsUrl = context.payload.issue.pull_request.url; const { data: pull } = await github.request(prDetailsUrl); @@ -69,6 +76,10 @@ jobs: } add-do-not-merge-label: + name: Add do-not-merge label + permissions: + issues: write # required to add labels on PR issues + pull-requests: write # required to read PR metadata from the issue pull_request URL if: > github.event.issue.pull_request && github.event.issue.state != 'closed' && @@ -82,7 +93,7 @@ jobs: - name: Add do-not-merge label uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_TOKEN }} + github-token: ${{ github.token }} script: | github.rest.issues.addLabels({ issue_number: context.issue.number, @@ -91,6 +102,10 @@ jobs: labels: ['do-not-merge'] }) add-autoupdate-label: + name: Add autoupdate label + permissions: + issues: write # required to add labels on PR issues + pull-requests: write # required to read PR metadata from the issue pull_request URL if: > github.event.issue.pull_request && github.event.issue.state != 'closed' && @@ -104,7 +119,7 @@ jobs: - name: Add autoupdate label uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_TOKEN }} + github-token: ${{ github.token }} script: | github.rest.issues.addLabels({ issue_number: context.issue.number, diff --git a/.github/workflows/automerge-for-humans-merging.yml b/.github/workflows/automerge-for-humans-merging.yml index 482c83d..b47a551 100644 --- a/.github/workflows/automerge-for-humans-merging.yml +++ b/.github/workflows/automerge-for-humans-merging.yml @@ -14,16 +14,20 @@ on: - edited - ready_for_review - reopened - - unlocked + - unlocked # zizmor: ignore[dangerous-triggers] needed if we want author to be our bot + +permissions: {} jobs: automerge-for-humans: + name: Automerge PRs labeled with ready-to-merge + permissions: + contents: read # required for PR commit metadata reads + pull-requests: read # required to read pull request details in github-script steps # it runs only if PR actor is not a bot, at least not a bot that we know if: | github.event.pull_request.draft == false && - (github.event.pull_request.user.login != 'asyncapi-bot' || - github.event.pull_request.user.login != 'dependabot[bot]' || - github.event.pull_request.user.login != 'dependabot-preview[bot]') + !contains(fromJSON('["asyncapi-bot","dependabot[bot]","dependabot-preview[bot]"]'), github.event.pull_request.user.login) runs-on: ubuntu-latest steps: - name: Get PR authors @@ -68,9 +72,11 @@ jobs: - name: Create commit message id: create-commit-message uses: actions/github-script@v7 + env: + AUTHORS_JSON: ${{ steps.authors.outputs.result }} with: script: | - const authors = ${{ steps.authors.outputs.result }}; + const authors = JSON.parse(process.env.AUTHORS_JSON); if (Object.keys(authors).length === 0) { core.setFailed('No authors found in the PR'); diff --git a/.github/workflows/automerge-for-humans-remove-ready-to-merge-label-on-edit.yml b/.github/workflows/automerge-for-humans-remove-ready-to-merge-label-on-edit.yml index d5a290d..d31ed82 100644 --- a/.github/workflows/automerge-for-humans-remove-ready-to-merge-label-on-edit.yml +++ b/.github/workflows/automerge-for-humans-remove-ready-to-merge-label-on-edit.yml @@ -6,19 +6,24 @@ name: Remove ready-to-merge label on: - pull_request_target: + pull_request: types: - synchronize - edited +permissions: {} + jobs: remove-ready-label: + name: Remove ready-to-merge label runs-on: ubuntu-latest + permissions: + pull-requests: write # required to remove labels and post comments on PR issues steps: - name: Remove label uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_TOKEN }} + github-token: ${{ github.token }} script: | const labelToRemove = 'ready-to-merge'; const labels = context.payload.pull_request.labels; diff --git a/.github/workflows/automerge-orphans.yml b/.github/workflows/automerge-orphans.yml index 23a0b00..cda0740 100644 --- a/.github/workflows/automerge-orphans.yml +++ b/.github/workflows/automerge-orphans.yml @@ -7,19 +7,26 @@ on: schedule: - cron: "0 0 * * *" +permissions: {} + jobs: identify-orphans: if: startsWith(github.repository, 'asyncapi/') name: Find orphans and notify + permissions: + contents: read # required by checkout and repository metadata reads + pull-requests: read # required to list open pull requests runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Get list of orphans uses: actions/github-script@v7 id: orphans with: - github-token: ${{ secrets.GITHUB_TOKEN }} + github-token: ${{ github.token }} script: | const query = `query($owner:String!, $name:String!) { repository(owner:$owner, name:$name){ diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 9faa78b..e81c003 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -4,10 +4,12 @@ name: Automerge PRs from bots on: - pull_request_target: + pull_request_target: # Needed as GH_TOKEN_BOT_EVE needed for approval. types: - opened - - synchronize + - synchronize # zizmor: ignore[dangerous-triggers] + +permissions: {} jobs: autoapprove-for-bot: diff --git a/.github/workflows/autoupdate.yml b/.github/workflows/autoupdate.yml index eeb77a4..30ff788 100644 --- a/.github/workflows/autoupdate.yml +++ b/.github/workflows/autoupdate.yml @@ -18,6 +18,8 @@ on: - 'bot/**' - 'all-contributors/**' +permissions: {} + jobs: autoupdate-for-bot: if: startsWith(github.repository, 'asyncapi/') @@ -25,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Autoupdating - uses: docker://chinthakagodawita/autoupdate-action:v1 + uses: chinthakagodawita/autoupdate@0707656cd062a3b0cf8fa9b2cda1d1404d74437e env: GITHUB_TOKEN: '${{ secrets.GH_TOKEN_BOT_EVE }}' PR_FILTER: "labelled" diff --git a/.github/workflows/bounty-program-commands.yml b/.github/workflows/bounty-program-commands.yml index c42e300..3447c2e 100644 --- a/.github/workflows/bounty-program-commands.yml +++ b/.github/workflows/bounty-program-commands.yml @@ -20,10 +20,16 @@ env: {"name": "bounty", "color": "0e8a16", "description": "Participation in the Bounty Program"} ] +permissions: {} + jobs: guard-against-unauthorized-use: + name: Guard against unauthorized use + permissions: + issues: write # required to post a comment on the issue/PR + pull-requests: write # required to post a comment on the issue/PR if it's a PR if: > - github.actor != ('aeworxet' || 'thulieblack') && + !contains(fromJSON('["aeworxet","thulieblack"]'), github.actor) && ( startsWith(github.event.comment.body, '/bounty' ) ) @@ -36,7 +42,7 @@ jobs: env: ACTOR: ${{ github.actor }} with: - github-token: ${{ secrets.GH_TOKEN }} + github-token: ${{ github.token }} script: | const commentText = `❌ @${process.env.ACTOR} is not authorized to use the Bounty Program's commands. These commands can only be used by members of the [Bounty Team](https://github.com/orgs/asyncapi/teams/bounty_team).`; @@ -50,19 +56,22 @@ jobs: }) add-label-bounty: + name: Add bounty label + permissions: + issues: write # required to read/create labels and add labels on the issue/PR + pull-requests: write # required to read/create labels and add labels on the issue/PR if: > - github.actor == ('aeworxet' || 'thulieblack') && + contains(fromJSON('["aeworxet","thulieblack"]'), github.actor) && ( startsWith(github.event.comment.body, '/bounty' ) ) runs-on: ubuntu-latest - steps: - name: Add label `bounty` uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_TOKEN }} + github-token: ${{ github.token }} script: | const BOUNTY_PROGRAM_LABELS = JSON.parse(process.env.BOUNTY_PROGRAM_LABELS_JSON); let LIST_OF_LABELS_FOR_REPO = await github.rest.issues.listLabelsForRepo({ @@ -91,19 +100,21 @@ jobs: }) remove-label-bounty: + name: Remove bounty label + permissions: + issues: write # required to read/remove labels on the issue/PR + pull-requests: write # required to read/remove labels on the issue/PR if it's a PR if: > - github.actor == ('aeworxet' || 'thulieblack') && + contains(fromJSON('["aeworxet","thulieblack"]'), github.actor) && ( startsWith(github.event.comment.body, '/unbounty' ) ) - runs-on: ubuntu-latest - steps: - name: Remove label `bounty` uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_TOKEN }} + github-token: ${{ github.token }} script: | const BOUNTY_PROGRAM_LABELS = JSON.parse(process.env.BOUNTY_PROGRAM_LABELS_JSON); let LIST_OF_LABELS_FOR_ISSUE = await github.rest.issues.listLabelsOnIssue({ diff --git a/.github/workflows/help-command.yml b/.github/workflows/help-command.yml index 6228c56..6fe1a13 100644 --- a/.github/workflows/help-command.yml +++ b/.github/workflows/help-command.yml @@ -4,21 +4,26 @@ name: Create help comment on: - issue_comment: - types: - - created + issue_comment: + types: + - created + +permissions: {} jobs: create_help_comment_pr: + name: Help Comment in PR if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/help') && github.actor != 'asyncapi-bot' }} runs-on: ubuntu-latest + permissions: + pull-requests: write # To comment on Pull requests steps: - name: Add comment to PR uses: actions/github-script@v7 env: ACTOR: ${{ github.actor }} with: - github-token: ${{ secrets.GH_TOKEN }} + github-token: ${{ github.token }} script: | //Yes to add comment to PR the same endpoint is use that we use to create a comment in issue //For more details http://developer.github.com/v3/issues/comments/ @@ -41,15 +46,18 @@ jobs: }) create_help_comment_issue: + name: Help Comment in Issue if: ${{ !github.event.issue.pull_request && startsWith(github.event.comment.body, '/help') && github.actor != 'asyncapi-bot' }} runs-on: ubuntu-latest + permissions: + issues: write # To comment on Issues steps: - name: Add comment to Issue uses: actions/github-script@v7 env: ACTOR: ${{ github.actor }} with: - github-token: ${{ secrets.GH_TOKEN }} + github-token: ${{ github.token }} script: | github.rest.issues.createComment({ issue_number: context.issue.number, diff --git a/.github/workflows/if-nodejs-pr-testing.yml b/.github/workflows/if-nodejs-pr-testing.yml index 2bab0a7..cef2b77 100644 --- a/.github/workflows/if-nodejs-pr-testing.yml +++ b/.github/workflows/if-nodejs-pr-testing.yml @@ -8,6 +8,9 @@ on: pull_request: types: [opened, reopened, synchronize, ready_for_review] +permissions: + contents: read + jobs: test-nodejs-pr: name: Test NodeJS PR - ${{ matrix.os }} @@ -18,21 +21,21 @@ jobs: steps: - if: > !github.event.pull_request.draft && !( - (github.actor == 'asyncapi-bot' && ( + (github.event.pull_request.user.login == 'asyncapi-bot' && ( startsWith(github.event.pull_request.title, 'ci: update of files from global .github repo') || startsWith(github.event.pull_request.title, 'chore(release):') )) || - (github.actor == 'asyncapi-bot-eve' && ( + (github.event.pull_request.user.login == 'asyncapi-bot-eve' && ( startsWith(github.event.pull_request.title, 'ci: update of files from global .github repo') || startsWith(github.event.pull_request.title, 'chore(release):') )) || - (github.actor == 'allcontributors[bot]' && + (github.event.pull_request.user.login == 'allcontributors[bot]' && startsWith(github.event.pull_request.title, 'docs: add') ) ) id: should_run name: Should Run - run: echo "shouldrun=true" >> $GITHUB_OUTPUT + run: echo "shouldrun=true" >> "$GITHUB_OUTPUT" shell: bash - if: steps.should_run.outputs.shouldrun == 'true' name: Set git to use LF #to once and for all finish neverending fight between Unix and Windows @@ -43,10 +46,12 @@ jobs: - if: steps.should_run.outputs.shouldrun == 'true' name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - if: steps.should_run.outputs.shouldrun == 'true' name: Check if Node.js project and has package.json id: packagejson - run: test -e ./package.json && echo "exists=true" >> $GITHUB_OUTPUT || echo "exists=false" >> $GITHUB_OUTPUT + run: test -e ./package.json && echo "exists=true" >> "$GITHUB_OUTPUT" || echo "exists=false" >> "$GITHUB_OUTPUT" shell: bash - if: steps.packagejson.outputs.exists == 'true' name: Determine what node version to use diff --git a/.github/workflows/issues-prs-notifications.yml b/.github/workflows/issues-prs-notifications.yml index ce13628..1e3b616 100644 --- a/.github/workflows/issues-prs-notifications.yml +++ b/.github/workflows/issues-prs-notifications.yml @@ -9,11 +9,13 @@ on: types: [opened, reopened] pull_request_target: - types: [opened, reopened, ready_for_review] + types: [opened, reopened, ready_for_review] # zizmor: ignore[dangerous-triggers] discussion: types: [created] +permissions: {} + jobs: issue: if: github.event_name == 'issues' && github.actor != 'asyncapi-bot' && github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]' diff --git a/.github/workflows/lint-pr-title.yml b/.github/workflows/lint-pr-title.yml index 77aa1c6..106f316 100644 --- a/.github/workflows/lint-pr-title.yml +++ b/.github/workflows/lint-pr-title.yml @@ -4,32 +4,37 @@ name: Lint PR title on: - pull_request_target: + pull_request: types: [opened, reopened, synchronize, edited, ready_for_review] +permissions: {} + jobs: lint-pr-title: name: Lint PR title runs-on: ubuntu-latest + permissions: + contents: read # To checkout code and read PR information + pull-requests: write # To comment on PR if the title is not valid steps: # Since this workflow is REQUIRED for a PR to be mergable, we have to have this 'if' statement in step level instead of job level. - - if: ${{ !contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]", "allcontributors[bot]"]'), github.actor) }} + - if: ${{ !contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]", "allcontributors[bot]"]'), github.actor) }} # zizmor: ignore[obfuscation] uses: amannn/action-semantic-pull-request@c3cd5d1ea3580753008872425915e343e351ab54 #version 5.2.0 https://github.com/amannn/action-semantic-pull-request/releases/tag/v5.2.0 id: lint_pr_title env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN}} + GITHUB_TOKEN: ${{ github.token }} with: subjectPattern: ^(?![A-Z]).+$ subjectPatternError: | The subject "{subject}" found in the pull request title "{title}" should start with a lowercase character. # Comments the error message from the above lint_pr_title action - - if: ${{ always() && steps.lint_pr_title.outputs.error_message != null && !contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]", "allcontributors[bot]"]'), github.actor)}} + - if: ${{ always() && steps.lint_pr_title.outputs.error_message != null && !contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]", "allcontributors[bot]"]'), github.actor)}} # zizmor: ignore[obfuscation] name: Comment on PR uses: marocchino/sticky-pull-request-comment@3d60a5b2dae89d44e0c6ddc69dd7536aec2071cd #use 2.5.0 https://github.com/marocchino/sticky-pull-request-comment/releases/tag/v2.5.0 with: header: pr-title-lint-error - GITHUB_TOKEN: ${{ secrets.GH_TOKEN}} + GITHUB_TOKEN: ${{ github.token }} message: | We require all PRs to follow [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/). @@ -44,4 +49,4 @@ jobs: with: header: pr-title-lint-error delete: true - GITHUB_TOKEN: ${{ secrets.GH_TOKEN}} + GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/notify-tsc-members-mention.yml b/.github/workflows/notify-tsc-members-mention.yml index ffa39bb..7048d69 100644 --- a/.github/workflows/notify-tsc-members-mention.yml +++ b/.github/workflows/notify-tsc-members-mention.yml @@ -17,14 +17,16 @@ on: types: - opened - pull_request_target: + pull_request_target: # Needed to access secrets. The checkout is done on base branch so script cannot be malicious. types: - - opened - + - opened # zizmor: ignore[dangerous-triggers] discussion: types: - created +permissions: + contents: read # To checkout repository + jobs: issue: if: github.event_name == 'issues' && contains(github.event.issue.body, '@asyncapi/tsc_members') @@ -33,6 +35,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Node.js uses: actions/setup-node@v4 with: @@ -68,10 +72,11 @@ jobs: CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} TITLE: ${{ github.event.issue.title }} + HTML_URL: ${{ github.event.issue.html_url }} with: script: | const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); - sendEmail('${{github.event.issue.html_url}}', process.env.TITLE); + sendEmail(process.env.HTML_URL, process.env.TITLE); pull_request: if: github.event_name == 'pull_request_target' && contains(github.event.pull_request.body, '@asyncapi/tsc_members') @@ -80,6 +85,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Node.js uses: actions/setup-node@v4 with: @@ -115,10 +122,11 @@ jobs: CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} TITLE: ${{ github.event.pull_request.title }} + HTML_URL: ${{ github.event.pull_request.html_url }} with: script: | const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); - sendEmail('${{github.event.pull_request.html_url}}', process.env.TITLE); + sendEmail(process.env.HTML_URL, process.env.TITLE); discussion: if: github.event_name == 'discussion' && contains(github.event.discussion.body, '@asyncapi/tsc_members') @@ -127,6 +135,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Node.js uses: actions/setup-node@v4 with: @@ -162,10 +172,11 @@ jobs: CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} TITLE: ${{ github.event.discussion.title }} + HTML_URL: ${{ github.event.discussion.html_url }} with: script: | const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); - sendEmail('${{github.event.discussion.html_url}}', process.env.TITLE); + sendEmail(process.env.HTML_URL, process.env.TITLE); issue_comment: if: ${{ github.event_name == 'issue_comment' && !github.event.issue.pull_request && contains(github.event.comment.body, '@asyncapi/tsc_members') }} @@ -174,6 +185,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Node.js uses: actions/setup-node@v4 with: @@ -209,10 +222,11 @@ jobs: CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} TITLE: ${{ github.event.issue.title }} + HTML_URL: ${{ github.event.comment.html_url }} with: script: | const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); - sendEmail('${{github.event.comment.html_url}}', process.env.TITLE); + sendEmail(process.env.HTML_URL, process.env.TITLE); pr_comment: if: github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '@asyncapi/tsc_members') @@ -221,6 +235,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Node.js uses: actions/setup-node@v4 with: @@ -256,10 +272,11 @@ jobs: CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} TITLE: ${{ github.event.issue.title }} + HTML_URL: ${{ github.event.comment.html_url }} with: script: | const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); - sendEmail('${{github.event.comment.html_url}}', process.env.TITLE); + sendEmail(process.env.HTML_URL, process.env.TITLE); discussion_comment: if: github.event_name == 'discussion_comment' && contains(github.event.comment.body, '@asyncapi/tsc_members') @@ -268,6 +285,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Node.js uses: actions/setup-node@v4 with: @@ -303,7 +322,8 @@ jobs: CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} TITLE: ${{ github.event.discussion.title }} + HTML_URL: ${{ github.event.comment.html_url }} with: script: | const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); - sendEmail('${{github.event.comment.html_url}}', process.env.TITLE); + sendEmail(process.env.HTML_URL, process.env.TITLE); diff --git a/.github/workflows/please-take-a-look-command.yml b/.github/workflows/please-take-a-look-command.yml index 84b5a26..30a6938 100644 --- a/.github/workflows/please-take-a-look-command.yml +++ b/.github/workflows/please-take-a-look-command.yml @@ -11,6 +11,8 @@ on: issue_comment: types: [created] +permissions: {} + jobs: ping-for-attention: if: > @@ -22,6 +24,7 @@ jobs: contains(github.event.comment.body, '/ptal') || contains(github.event.comment.body, '/PTAL') ) + name: Ping code owners for attention runs-on: ubuntu-latest steps: - name: Check for Please Take a Look Command @@ -31,7 +34,7 @@ jobs: script: | const prDetailsUrl = context.payload.issue.pull_request.url; const { data: pull } = await github.request(prDetailsUrl); - const reviewers = pull.requested_reviewers.map(reviewer => reviewer.login); + const reviewers = (pull.requested_reviewers || []).map(reviewer => reviewer.login); const { data: reviews } = await github.rest.pulls.listReviews({ owner: context.repo.owner, diff --git a/.github/workflows/release-announcements.yml b/.github/workflows/release-announcements.yml index 311b701..b521d39 100644 --- a/.github/workflows/release-announcements.yml +++ b/.github/workflows/release-announcements.yml @@ -8,6 +8,9 @@ on: types: - published +permissions: + contents: read # To checkout code and read release information + jobs: slack-announce: @@ -16,6 +19,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Convert markdown to slack markdown for issue # This workflow is from our own org repo and safe to reference by 'master'. uses: asyncapi/.github/.github/actions/slackify-markdown@master # //NOSONAR @@ -42,11 +47,13 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v4 + with: + persist-credentials: false - name: Get version of last and previous release uses: actions/github-script@v7 id: versions with: - github-token: ${{ secrets.GITHUB_TOKEN }} + github-token: ${{ github.token }} script: | const query = `query($owner:String!, $name:String!) { repository(owner:$owner, name:$name){ @@ -72,12 +79,12 @@ jobs: env: PREV_VERSION: ${{steps.versions.outputs.previousver}} LAST_VERSION: ${{steps.versions.outputs.lastver}} - run: echo "type=$(npx -q -p semver-diff-cli semver-diff "$PREV_VERSION" "$LAST_VERSION")" >> $GITHUB_OUTPUT + run: echo "type=$(npx -q -p semver-diff-cli semver-diff "$PREV_VERSION" "$LAST_VERSION")" >> "$GITHUB_OUTPUT" - name: Get name of the person that is behind the newly released version id: author run: | AUTHOR_NAME=$(git log -1 --pretty=format:'%an') - printf 'name=%s\n' "$AUTHOR_NAME" >> $GITHUB_OUTPUT + printf 'name=%s\n' "$AUTHOR_NAME" >> "$GITHUB_OUTPUT" - name: Publish information about the release to Twitter # tweet only if detected version change is not a patch # tweet goes out even if the type is not major or minor but "You need provide version number to compare." # it is ok, it just means we did not identify previous version as we are tweeting out information about the release for the first time diff --git a/.github/workflows/scripts/mailchimp/htmlContent.js b/.github/workflows/scripts/mailchimp/htmlContent.js index d132c72..e0c8ef6 100644 --- a/.github/workflows/scripts/mailchimp/htmlContent.js +++ b/.github/workflows/scripts/mailchimp/htmlContent.js @@ -2,7 +2,24 @@ * This code is centrally managed in https://github.com/asyncapi/.github/ * Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo */ + +/** + * Escape HTML special characters to prevent XSS + */ +function escapeHtml(text) { + if (!text) return ''; + return text + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + module.exports = (link, title) => { + // Sanitize inputs to prevent XSS + const safeLink = escapeHtml(link); + const safeTitle = escapeHtml(title); return ` @@ -386,7 +403,7 @@ There is a new topic at AsyncAPI Initiative that requires Technical Steering Com
Please have a look if it is just something you need to be aware of, or maybe your vote is needed.
-Topic: ${ title }. +Topic: ${ safeTitle }. diff --git a/.github/workflows/scripts/mailchimp/index.js b/.github/workflows/scripts/mailchimp/index.js index 4a200c6..b5147bf 100644 --- a/.github/workflows/scripts/mailchimp/index.js +++ b/.github/workflows/scripts/mailchimp/index.js @@ -6,6 +6,12 @@ const mailchimp = require('@mailchimp/mailchimp_marketing'); const core = require('@actions/core'); const htmlContent = require('./htmlContent.js'); +function formatMailchimpError(error) { + const status = error?.status || error?.response?.status; + const message = error?.message || error?.response?.body?.title || 'Unknown error'; + return status ? `${ message } (status: ${ status })` : message; +} + /** * Sending API request to mailchimp to schedule email to subscribers * Input is the URL to issue/discussion or other resource @@ -14,6 +20,28 @@ module.exports = async (link, title) => { let newCampaign; + // Validate inputs to prevent injection attacks + if (!link || typeof link !== 'string' || link.length > 2000) { + return core.setFailed('Invalid link parameter'); + } + if (!title || typeof title !== 'string' || title.length > 500) { + return core.setFailed('Invalid title parameter'); + } + + let parsedLink; + try { + parsedLink = new URL(link); + } catch (error) { + return core.setFailed('Invalid link parameter'); + } + + if (parsedLink.protocol !== 'https:') { + return core.setFailed('Link must use https protocol'); + } + + // Sanitize title by removing control characters and limiting length + const sanitizedTitle = title.replace(/[\x00-\x1F\x7F]/g, '').substring(0, 250); + mailchimp.setConfig({ apiKey: process.env.MAILCHIMP_API_KEY, server: 'us12' @@ -38,7 +66,7 @@ module.exports = async (link, title) => { } }, settings: { - subject_line: `TSC attention required: ${ title }`, + subject_line: `TSC attention required: ${ sanitizedTitle }`, preview_text: 'Check out the latest topic that TSC members have to be aware of', title: `New topic info - ${ new Date(Date.now()).toUTCString()}`, from_name: 'AsyncAPI Initiative', @@ -46,16 +74,16 @@ module.exports = async (link, title) => { } }); } catch (error) { - return core.setFailed(`Failed creating campaign: ${ JSON.stringify(error) }`); + return core.setFailed(`Failed creating campaign: ${ formatMailchimpError(error) }`); } /* * Content of the email is added separately after campaign creation */ try { - await mailchimp.campaigns.setContent(newCampaign.id, { html: htmlContent(link, title) }); + await mailchimp.campaigns.setContent(newCampaign.id, { html: htmlContent(parsedLink.toString(), sanitizedTitle) }); } catch (error) { - return core.setFailed(`Failed adding content to campaign: ${ JSON.stringify(error) }`); + return core.setFailed(`Failed adding content to campaign: ${ formatMailchimpError(error) }`); } /* @@ -72,7 +100,7 @@ module.exports = async (link, title) => { schedule_time: scheduleDate.toISOString(), }); } catch (error) { - return core.setFailed(`Failed scheduling email: ${ JSON.stringify(error) }`); + return core.setFailed(`Failed scheduling email: ${ formatMailchimpError(error) }`); } core.info(`New email campaign created`); diff --git a/.github/workflows/stale-issues-prs.yml b/.github/workflows/stale-issues-prs.yml index 25bee82..e76a36b 100644 --- a/.github/workflows/stale-issues-prs.yml +++ b/.github/workflows/stale-issues-prs.yml @@ -7,15 +7,21 @@ on: schedule: - cron: "0 0 * * *" +permissions: {} + jobs: stale: if: startsWith(github.repository, 'asyncapi/') name: Mark issue or PR as stale runs-on: ubuntu-latest + permissions: + contents: read # As delete-branch is not being used + issues: write # To add comments and labels to issues + pull-requests: write # To add comments and labels to PRs steps: - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 #v9.1.0 but pointing to commit for security reasons with: - repo-token: ${{ secrets.GITHUB_TOKEN }} + repo-token: ${{ github.token }} stale-issue-message: | This issue has been automatically marked as stale because it has not had recent activity :sleeping: diff --git a/.github/workflows/transfer-issue.yml b/.github/workflows/transfer-issue.yml deleted file mode 100644 index dbe84ca..0000000 --- a/.github/workflows/transfer-issue.yml +++ /dev/null @@ -1,62 +0,0 @@ -# This action is centrally managed in https://github.com/asyncapi/.github/ -# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo - -name: Transfer Issues between repositories - -on: - issue_comment: - types: - - created - -permissions: - issues: write - -jobs: - transfer: - if: ${{(!github.event.issue.pull_request && github.event.issue.state != 'closed' && github.actor != 'asyncapi-bot') && (startsWith(github.event.comment.body, '/transfer-issue') || startsWith(github.event.comment.body, '/ti'))}} - runs-on: ubuntu-latest - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - name: Extract Input - id: extract_step - env: - COMMENT: "${{ github.event.comment.body }}" - run: | - REPO=$(echo "$COMMENT" | awk '{print $2}') - echo "repo=$REPO" >> $GITHUB_OUTPUT - - name: Check Repo - uses: actions/github-script@v7 - with: - github-token: ${{secrets.GH_TOKEN}} - script: | - const r = "${{github.repository}}" - const [owner, repo] = r.split('/') - const repoToMove = process.env.REPO_TO_MOVE - const issue_number = context.issue.number - try { - const {data} = await github.rest.repos.get({ - owner, - repo: repoToMove - }) - }catch (e) { - const body = `${repoToMove} is not a repo under ${owner}. You can only transfer issue to repos that belong to the same organization.` - await github.rest.issues.createComment({ - owner, - repo, - issue_number, - body - }) - process.exit(1) - } - env: - REPO_TO_MOVE: ${{steps.extract_step.outputs.repo}} - - name: Transfer Issue - id: transferIssue - working-directory: ./ - run: | - gh issue transfer "$ISSUE_NUMBER" "asyncapi/$REPO_NAME" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ISSUE_NUMBER: ${{ github.event.issue.number }} - REPO_NAME: ${{ steps.extract_step.outputs.repo }} diff --git a/.github/workflows/update-maintainers-trigger.yaml b/.github/workflows/update-maintainers-trigger.yaml index 12fc4ab..0535e39 100644 --- a/.github/workflows/update-maintainers-trigger.yaml +++ b/.github/workflows/update-maintainers-trigger.yaml @@ -13,6 +13,9 @@ on: - '.github/CODEOWNERS' - '.docs/CODEOWNERS' +permissions: + contents: read # Just to limit GITHUB_TOKEN as we use GH_TOKEN only + jobs: trigger-maintainers-update: name: Trigger updating MAINTAINERS.yaml because of CODEOWNERS change diff --git a/.github/workflows/update-pr.yml b/.github/workflows/update-pr.yml index 2fa19b0..285c02d 100644 --- a/.github/workflows/update-pr.yml +++ b/.github/workflows/update-pr.yml @@ -9,12 +9,16 @@ name: Update PR branches from fork +permissions: + contents: read + on: issue_comment: types: [created] jobs: update-pr: + name: Update the fork PR with upstream changes if: > startsWith(github.repository, 'asyncapi/') && github.event.issue.pull_request && @@ -23,12 +27,16 @@ jobs: contains(github.event.comment.body, '/u') ) runs-on: ubuntu-latest + permissions: + issues: write # Required to read PR details and post comments on the PR + pull-requests: write # Required to update the PR branch + contents: read steps: - name: Get Pull Request Details id: pr uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + github-token: ${{ secrets.GH_TOKEN || github.token }} previews: 'merge-info-preview' # https://docs.github.com/en/graphql/overview/schema-previews#merge-info-preview-more-detailed-information-about-a-pull-requests-merge-state-preview script: | const prNumber = context.payload.issue.number; @@ -40,7 +48,7 @@ jobs: }); // If the PR has conflicts, we don't want to update it - const updateable = ['behind', 'blocked', 'unknown', 'draft', 'clean'].includes(pr.mergeable_state); + const updateable = ['behind', 'blocked', 'unknown', 'draft', 'clean', 'unstable'].includes(pr.mergeable_state); console.log(`PR #${prNumber} is ${pr.mergeable_state} and is ${updateable ? 'updateable' : 'not updateable'}`); core.setOutput('updateable', updateable); @@ -54,8 +62,10 @@ jobs: - name: Update the Pull Request if: steps.pr.outputs.updateable == 'true' uses: actions/github-script@v7 + env: + PR_DETAILS: ${{ steps.pr.outputs.result }} with: - github-token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + github-token: ${{ secrets.GH_TOKEN || github.token }} script: | const mutation = `mutation update($input: UpdatePullRequestBranchInput!) { updatePullRequestBranch(input: $input) { @@ -65,7 +75,7 @@ jobs: } }`; - const pr_details = ${{ steps.pr.outputs.result }}; + const pr_details = JSON.parse(process.env.PR_DETAILS); try { const { data } = await github.graphql(mutation, { diff --git a/.github/workflows/welcome-first-time-contrib.yml b/.github/workflows/welcome-first-time-contrib.yml index 2614d8d..49c761b 100644 --- a/.github/workflows/welcome-first-time-contrib.yml +++ b/.github/workflows/welcome-first-time-contrib.yml @@ -4,22 +4,30 @@ name: Welcome first time contributors on: - pull_request_target: + pull_request: types: - opened issues: types: - opened +permissions: + issues: read # Required to check if the issue is the user's first contribution + pull-requests: read # Required to check if the pull request is the user's first contribution + jobs: welcome: name: Post welcome message - if: ${{ !contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]", "allcontributors[bot]"]'), github.actor) }} + if: ${{ !contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]", "allcontributors[bot]"]'), github.actor) }} # zizmor: ignore[obfuscation] runs-on: ubuntu-latest + permissions: + contents: read # Required to read repository data for checking if it's the user's first contribution + issues: write # Required to post welcome message on issues + pull-requests: write # Required to post welcome message on pull requests steps: - uses: actions/github-script@v7 with: - github-token: ${{ secrets.GITHUB_TOKEN }} + github-token: ${{ github.token }} script: | const issueMessage = `Welcome to AsyncAPI. Thanks a lot for reporting your first issue. Please check out our [contributors guide](https://github.com/asyncapi/community/blob/master/CONTRIBUTING.md) and the instructions about a [basic recommended setup](https://github.com/asyncapi/community/blob/master/git-workflow.md) useful for opening a pull request.
Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out [this issue](https://github.com/asyncapi/asyncapi/issues/115).`; const prMessage = `Welcome to AsyncAPI. Thanks a lot for creating your first pull request. Please check out our [contributors guide](https://github.com/asyncapi/community/blob/master/CONTRIBUTING.md) useful for opening a pull request.
Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out [this issue](https://github.com/asyncapi/asyncapi/issues/115).`;