diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..79797393 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +/pubspec.yaml @uxcam/maintainers +/.github/ @uxcam/maintainers @uxcam/security-team +/.github/workflows/ @uxcam/maintainers @uxcam/security-team diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index fcd81e53..f7a8fde6 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -1,106 +1,153 @@ name: Publish Flutter Package + on: workflow_dispatch: inputs: publish: - description: Publish flutter package + description: Publish flutter package to pub.dev type: boolean default: false required: true +permissions: {} + +concurrency: + group: publish-${{ github.ref }} + cancel-in-progress: false + jobs: - job_release: - runs-on: ubuntu-latest - name: 'Release a new version' + validate: + name: Validate package + runs-on: ubuntu-24.04 + timeout-minutes: 10 + permissions: + contents: read + outputs: + version: ${{ steps.version.outputs.version }} steps: - - name: Check out current commit (${{github.sha}}) - uses: actions/checkout@v3 + - name: Harden runner + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 with: - token: ${{ secrets.GH_RELEASE_PAT }} - fetch-depth: 0 - - name: Set git user credentials - run: | - git config user.email "uxcam-mobile@uxcam.com" - git config user.name "UXCam" - - name: Get version name - id: get-version - run: | - version=$(grep '^version:' pubspec.yaml | awk '{ print $2 }') - echo $version - echo "version=$version" >> $GITHUB_OUTPUT - # Install dependencies + egress-policy: audit + disable-sudo: true + + - name: Check out ${{ github.sha }} + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - name: Install Flutter 🎯 - uses: subosito/flutter-action@v2 + uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0 with: - channel: 'stable' + channel: stable - name: Install dependencies 📚 - run: | - flutter pub get + run: flutter pub get - - name: Verify version - run: | - dart pub publish --dry-run - - name: Publish package - if: ${{ inputs.publish }} + - name: Read package version + id: version run: | - mkdir -p $HOME/.config/dart - CREDENTIALS="${{ secrets.PUB_CREDENTIALS }}" - CREDENTIALS=$( echo -n $CREDENTIALS | base64 --decode) - cat < $HOME/.config/dart/pub-credentials.json - $CREDENTIALS - EOF - dart pub publish -f - - name: Add new tag to github - if: ${{ inputs.publish }} - run: | - version="v${{ steps.get-version.outputs.version }}" - git tag -a $version -m "Release: $version" - git push origin --tags - - name: Generate Changelogs - id: changelogs + version="$(grep '^version:' pubspec.yaml | awk '{ print $2 }')" + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "Resolved version: v$version" + + - name: Verify package (dry run) + run: dart pub publish --dry-run + + publish: + name: Publish to pub.dev + needs: validate + if: ${{ inputs.publish }} + runs-on: ubuntu-24.04 + timeout-minutes: 20 + environment: "pub.dev" + permissions: + contents: write # create tag + GitHub Release + id-token: write # pub.dev OIDC publishing + steps: + - name: Harden runner + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + disable-sudo: true + + - name: Check out ${{ github.sha }} + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Install Flutter 🎯 + uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0 + with: + channel: stable + + - name: Install dependencies 📚 + run: flutter pub get + + - name: Publish to pub.dev 🚀 + run: dart pub publish --force + + - name: Generate changelog + id: changelog run: | - if ${{ inputs.publish }}; then - current_tag=$(git describe --tags --abbrev=0) - else - current_tag=HEAD - fi - - previous_tag=$(git describe --tags --abbrev=0 ${current_tag}^) - logs=$(git log ${current_tag}...${previous_tag} --pretty=format:"%s" -i --grep="^feat.*:" --grep="^fix.*:" --no-merges) - - #santize commit logs - logs=$(awk -F ":" '{ print $2 }' <<< $logs | sed -e 's/^ *//g' | sed -e 's/^./\u&/g') - - #prepend line number - logs=$(awk '{print NR, "-", $0}' <<< $logs) - - # Remove new line in multiline change logs as sed doesn't support it - logs=$((sed -e '$ ! s/$/\\n/g' | tr -d '\n') <<< $logs) - echo "logs=${logs}" >> $GITHUB_OUTPUT - echo $logs - #save logs to new file - touch changelog.txt - echo "$logs" > changelog.txt - - - name: Generate Slack notification payload - id: slack-payload + previous_tag="$(git describe --tags --abbrev=0 2>/dev/null || echo '')" + range="HEAD" + [ -n "$previous_tag" ] && range="${previous_tag}..HEAD" + + logs="$(git log "$range" --no-merges --pretty=format:'%s' -i \ + --grep='^feat.*:' --grep='^fix.*:' \ + | awk -F ':' '{ sub(/^ */, "", $2); print "- " $2 }')" + + { + echo "logs<> "$GITHUB_OUTPUT" + echo "$logs" + + - name: Create tag and GitHub Release + id: release + env: + VERSION: ${{ needs.validate.outputs.version }} + BODY: ${{ steps.changelog.outputs.logs }} + GH_TOKEN: ${{ github.token }} run: | - DATE=$(date +'%d/%m/%Y') - ESCAPED_DATE=$(echo "$DATE" | sed -e 's/[\/&]/\\&/g') - payload_path='./.github/workflows/slack-payload.json' - payload=$(cat $payload_path) - logs="${{ steps.changelogs.outputs.logs }}" - logs=$(sed -e 's/\\n/\\\\n/g' <<< $logs) - echo $logs - - echo "$payload" | sed -e "s/__FLUTTER_VERSION__/${{ steps.get-version.outputs.version }}/g" -e "s/__RELEASE_DATE__/${ESCAPED_DATE}/g" -e "s/__CHANGELOG__/$logs/g" > $payload_path - cat $payload_path - - name: Send notification to Slack - id: slack - uses: slackapi/slack-github-action@v1.18.0 - with: - payload-file-path: './.github/workflows/slack-payload.json' + url="$(gh release create "v$VERSION" \ + --target "$GITHUB_SHA" \ + --title "v$VERSION" \ + --notes "$BODY" \ + --latest)" + echo "url=$url" >> "$GITHUB_OUTPUT" + + - name: Notify Slack + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + PKG_VERSION: v${{ needs.validate.outputs.version }} + CHANGELOG: ${{ steps.changelog.outputs.logs }} + RELEASE_URL: ${{ steps.release.outputs.url }} + with: + webhook: ${{ secrets.SLACK_WEBHOOK_URL }} + webhook-type: incoming-webhook + payload-templated: true + payload: | + text: ":rocket: UXCam Flutter Plugin {{ env.PKG_VERSION }} released :rocket:" + blocks: + - type: section + text: + type: mrkdwn + text: " UXCam Flutter Plugin *{{ env.PKG_VERSION }}* has been released. :rocket:" + - type: section + fields: + - type: mrkdwn + text: "*SDK*\nFlutter Plugin" + - type: mrkdwn + text: "*Version*\n{{ env.PKG_VERSION }}" + - type: section + text: + type: mrkdwn + text: "*CHANGELOG (Internal):*\n{{ env.CHANGELOG }}" + - type: section + text: + type: mrkdwn + text: "<{{ env.RELEASE_URL }}|View release notes>" diff --git a/.github/workflows/slack-payload.json b/.github/workflows/slack-payload.json deleted file mode 100644 index 087a2151..00000000 --- a/.github/workflows/slack-payload.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "text": ":rocket: UXCam Flutter Plugin Released :rocket:", - "blocks": [ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": " UXCam Flutter Plugin __FLUTTER_VERSION__ has been released.\n:rocket: UXCam Flutter Plugin __FLUTTER_VERSION__ :rocket:\n:date: __RELEASE_DATE__" - } - }, - { - "type": "section", - "fields": [ - { - "type": "mrkdwn", - "text": "*CHANGELOG (Internal):*\n__CHANGELOG__" - } - ] - }, - { - "type": "section", - "fields": [ - { - "type": "mrkdwn", - "text": "*SDK*\nFlutter Plugin" - }, - { - "type": "mrkdwn", - "text": "*Version*\n__FLUTTER_VERSION__" - } - ] - } - ] -} \ No newline at end of file diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 00000000..944c67c4 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,45 @@ +name: GitHub Actions security audit (zizmor) + +on: + push: + branches: [main] + paths: + - ".github/workflows/**" + - ".github/actions/**" + pull_request: + paths: + - ".github/workflows/**" + - ".github/actions/**" + workflow_dispatch: {} + +permissions: {} + +concurrency: + group: zizmor-${{ github.ref }} + cancel-in-progress: true + +jobs: + zizmor: + name: Audit workflows + runs-on: ubuntu-24.04 + timeout-minutes: 10 + permissions: + contents: read + security-events: write + actions: read + steps: + - name: Harden runner + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + disable-sudo: true + + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + + - name: Run zizmor + uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 + with: + advanced-security: true diff --git a/LICENSE b/LICENSE index a1b20c6b..a48dbd39 100644 --- a/LICENSE +++ b/LICENSE @@ -1,25 +1,28 @@ -Copyright (c) 2019-2024, UXCam. All rights reserved. +BSD 3-Clause License -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: +Copyright (c) UXCam - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..2f2e167f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +We welcome reports from security researchers, and we'll work with you to confirm and fix any issue you find. + +## Reporting a vulnerability + +Please don't report security issues through public GitHub issues, pull requests or discussions. Anyone can see them, and we don't want a security issue to become public before we have fixed it. Please use one of the private channels below instead. + +Our preferred channel is the bug bounty program at https://uxcam.com/bug-bounty, where you'll find our scope, rules, and how to submit a report. + +If you'd rather not use the program, you're welcome to email us at security@uxcam.com. For sensitive details, you can encrypt your report with our PGP key: https://github.com/uxcamsec.gpg + +Reports submitted through the bug bounty program are eligible for a reward and a place in our Hall of Fame: https://uxcam.com/bug-bounty-hall-of-fame