fix(security): mitigate polynomial ReDoS in formatter and parser #252
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release Please | |
| on: | |
| push: | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| inputs: | |
| tag_name: | |
| description: 'Release tag name (e.g. v1.26.0)' | |
| required: true | |
| concurrency: | |
| group: release-please | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| pages: write | |
| packages: write | |
| jobs: | |
| release-please: | |
| runs-on: macos-latest | |
| outputs: | |
| tag_name: ${{ steps.release.outputs['apps/web--tag_name'] || github.event.inputs.tag_name }} | |
| release_created: ${{ steps.release.outputs['apps/web--release_created'] || (github.event_name == 'workflow_dispatch' && 'true') }} | |
| steps: | |
| - uses: googleapis/release-please-action@v4 | |
| id: release | |
| if: ${{ github.event_name == 'push' }} | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| # Update screenshots when a new release is created | |
| update-screenshots: | |
| needs: release-please | |
| if: ${{ needs.release-please.outputs.release_created == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.release-please.outputs.tag_name }} | |
| - uses: pnpm/action-setup@v5.0.0 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| cache: pnpm | |
| node-version: latest | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Install Playwright browsers | |
| run: npx playwright install chromium | |
| - name: Generate screenshots | |
| run: pnpm screenshot | |
| - uses: peter-evans/create-pull-request@v8 | |
| with: | |
| commit-message: 'chore: update screenshots for ${{ needs.release-please.outputs.tag_name }} [skip ci]' | |
| title: 'chore: update screenshots for ${{ needs.release-please.outputs.tag_name }} [skip ci]' | |
| body: | | |
| Auto-generated screenshots update for ${{ needs.release-please.outputs.tag_name }} | |
| ## Desktop Screenshots | |
| | Setup | Orchestrate | | |
| |-------|-------------| | |
| |  |  | | |
| ## Mobile Screenshots | |
| | Setup | Orchestrate | | |
| |-------|-------------| | |
| | <img src="https://raw.githubusercontent.com/${{ github.repository }}/chore/update-screenshots/docs/screenshots/mobile/setup.png" width="300" /> | <img src="https://raw.githubusercontent.com/${{ github.repository }}/chore/update-screenshots/docs/screenshots/mobile/orchestrate.png" width="300" /> | | |
| base: main | |
| branch: chore/update-screenshots | |
| delete-branch: true | |
| add-paths: docs/screenshots/ | |
| checkout-full-src: | |
| needs: release-please | |
| if: ${{ needs.release-please.outputs.release_created == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| submodules: recursive | |
| ref: ${{ needs.release-please.outputs.tag_name }} | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.24' | |
| cache: false | |
| - run: go version | |
| - name: Download wing vendor | |
| run: | | |
| git submodule update --init --recursive | |
| export GOMODCACHE="${PWD}"/go-mod | |
| go mod download -modcacherw | |
| cd dae-core && go mod download -modcacherw && cd .. | |
| find "$GOMODCACHE" -maxdepth 1 ! -name "cache" ! -name "go-mod" -exec rm -rf {} \; | |
| sed -i 's/#export GOMODCACHE=$(PWD)\/go-mod/export GOMODCACHE=$(PWD)\/go-mod/' Makefile | |
| working-directory: wing | |
| - name: Create full source ZIP archive and Signature | |
| run: | | |
| zip -9vr daed-full-src.zip . -x .git/\* | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: daed-full-src.zip | |
| path: daed-full-src.zip | |
| build-web: | |
| needs: release-please | |
| if: ${{ needs.release-please.outputs.release_created == 'true' }} | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.release-please.outputs.tag_name }} | |
| - uses: pnpm/action-setup@v5.0.0 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| cache: pnpm | |
| node-version: latest | |
| - name: Build | |
| env: | |
| NODE_OPTIONS: --max-old-space-size=4096 | |
| run: | | |
| pnpm install | |
| pnpm build --filter daed | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: web | |
| path: apps/web/dist | |
| # Publish GitHub Pages when a new release is created | |
| publish-gh-pages: | |
| needs: [release-please, build-web] | |
| if: ${{ needs.release-please.outputs.release_created == 'true' }} | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| name: web | |
| path: dist/ | |
| - uses: peaceiris/actions-gh-pages@v4 | |
| with: | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| publish_dir: ./dist | |
| build-bundle: | |
| needs: [release-please, build-web] | |
| if: ${{ needs.release-please.outputs.release_created == 'true' }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| goos: [linux] | |
| goarch: [386, riscv64] | |
| include: | |
| # BEGIN Linux ARM64 | |
| - goos: linux | |
| goarch: arm64 | |
| # END Linux ARM64 | |
| # BEGIN Linux AMD64 v1 v2 v3 | |
| - goos: linux | |
| goarch: amd64 | |
| goamd64: v1 | |
| - goos: linux | |
| goarch: amd64 | |
| goamd64: v2 | |
| - goos: linux | |
| goarch: amd64 | |
| goamd64: v3 | |
| # END Linux AMD64 v1 v2 v3 | |
| # BEGIN Linux mips | |
| - goos: linux | |
| goarch: mips64 | |
| cgo_enabled: 1 | |
| cc: mips64-linux-gnuabi64-gcc | |
| - goos: linux | |
| goarch: mips64le | |
| cgo_enabled: 1 | |
| cc: mips64el-linux-gnuabi64-gcc | |
| - goos: linux | |
| goarch: mipsle | |
| cgo_enabled: 1 | |
| cc: mipsel-linux-gnu-gcc | |
| - goos: linux | |
| goarch: mips | |
| cgo_enabled: 1 | |
| cc: mips-linux-gnu-gcc | |
| # END Linux mips | |
| fail-fast: false | |
| env: | |
| GOOS: ${{ matrix.goos }} | |
| GOARCH: ${{ matrix.goarch }} | |
| GOAMD64: ${{ matrix.goamd64 }} | |
| CGO_ENABLED: ${{ matrix.cgo_enabled || 0 }} | |
| CC: ${{ matrix.cc }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.release-please.outputs.tag_name }} | |
| fetch-depth: 0 | |
| submodules: recursive | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: '1.24' | |
| cache: false | |
| - run: go version | |
| - name: Get the version | |
| id: get_version | |
| env: | |
| REF: ${{ needs.release-please.outputs.tag_name }} | |
| run: | | |
| tag=${REF} | |
| version=${tag} | |
| package_version="${tag:1}" | |
| echo "VERSION=$version" >> $GITHUB_OUTPUT | |
| echo "VERSION=$version" >> $GITHUB_ENV | |
| echo "PACKAGE_VERSION=$package_version" >> $GITHUB_OUTPUT | |
| echo "PACKAGE_VERSION=$package_version" >> $GITHUB_ENV | |
| - name: Get the filename | |
| id: get_filename | |
| run: | | |
| export _NAME=$(jq ".[\"$GOOS-$GOARCH$GOAMD64\"].friendlyName" -r < install/friendly-filenames.json) | |
| echo "GOOS: $GOOS, GOARCH: $GOARCH, RELEASE_NAME: $_NAME" | |
| echo "BUNDLE_NAME=daed-$_NAME" >> $GITHUB_OUTPUT | |
| - name: Install mips build dependencies | |
| if: ${{ startsWith(matrix.goarch, 'mips') }} | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc-mips64-linux-gnuabi64 gcc-mips64el-linux-gnuabi64 gcc-mips-linux-gnu gcc-mipsel-linux-gnu | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| name: web | |
| path: dist/ | |
| - name: make | |
| run: | | |
| mkdir -p ./bundled/ | |
| export VERSION=${{ steps.get_version.outputs.VERSION }} | |
| export GOFLAGS="-trimpath -modcacherw" | |
| export OUTPUT=bundled/${{ steps.get_filename.outputs.BUNDLE_NAME }} | |
| make | |
| cp ./install/daed.service ./bundled/ | |
| cp ./install/daed.desktop ./bundled/ | |
| cp -r ./install/icons/ ./bundled/ | |
| curl -L -o ./bundled/geoip.dat https://github.com/v2rayA/dist-v2ray-rules-dat/raw/master/geoip.dat | |
| curl -L -o ./bundled/geosite.dat https://github.com/v2rayA/dist-v2ray-rules-dat/raw/master/geosite.dat | |
| - name: Upload artifact - bundle | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ${{ steps.get_filename.outputs.BUNDLE_NAME }} | |
| path: bundled/* | |
| - name: Smoking test | |
| if: matrix.goarch == 'amd64' && matrix.goamd64 == 'v1' | |
| run: ./bundled/${{ steps.get_filename.outputs.BUNDLE_NAME }} --version | |
| - name: Build Linux packages | |
| run: | | |
| mkdir -p ./output_packages/ | |
| sudo apt install -y libarchive-tools rpm | |
| sudo gem install fpm -v 1.15.1 | |
| pushd bundled || exit 1 | |
| if [ "$GOARCH" == 'mips64' ] || [ "$GOARCH" == 'mips64le' ] || [ "$GOARCH" == 'mipsle' ] || [ "$GOARCH" == 'mips' ];then | |
| echo "Skip pacman package build for Arch Linux has never been port to MIPS family, Debian package and RPM package are enough." | |
| ARCH_PACMAN='' | |
| else | |
| ARCH_PACMAN='pacman' | |
| fi | |
| for package_manager in deb rpm $ARCH_PACMAN; do | |
| if [ "$package_manager" == 'pacman' ];then | |
| if [ "$GOARCH" == 'arm64' ];then | |
| package_arch='aarch64' | |
| else | |
| package_arch="$GOARCH" | |
| fi | |
| elif [ "$package_manager" == 'rpm' ];then | |
| if [ "$GOARCH" == 'arm64' ];then | |
| package_arch='aarch64' | |
| else | |
| package_arch="$GOARCH" | |
| fi | |
| elif [ "$package_manager" == 'deb' ];then | |
| package_arch="$GOARCH" | |
| fi | |
| fpm -s dir -t "$package_manager" -a $package_arch --version "$PACKAGE_VERSION" \ | |
| --url 'https://github.com/daeuniverse/daed' --description "daed, A Modern Dashboard For dae." \ | |
| --maintainer "daed@dae.v2raya.org" --name daed --license 'MIT AGPL' \ | |
| --package daed-linux-$GOARCH$GOAMD64.$package_manager \ | |
| --after-install ../install/package_after_install.sh \ | |
| --after-remove ../install/package_after_remove.sh \ | |
| ./geoip.dat=/usr/share/daed/geoip.dat ./geosite.dat=/usr/share/daed/geosite.dat \ | |
| ./daed.service=/usr/lib/systemd/system/daed.service \ | |
| ./daed.desktop=/usr/share/applications/daed.desktop \ | |
| ./icons/16x16.png=/usr/share/icons/hicolor/16x16/apps/daed.png \ | |
| ./icons/32x32.png=/usr/share/icons/hicolor/32x32/apps/daed.png \ | |
| ./icons/64x64.png=/usr/share/icons/hicolor/64x64/apps/daed.png \ | |
| ./icons/128x128.png=/usr/share/icons/hicolor/128x128/apps/daed.png \ | |
| ./icons/256x256.png=/usr/share/icons/hicolor/256x256/apps/daed.png \ | |
| ./icons/512x512.png=/usr/share/icons/hicolor/512x512/apps/daed.png \ | |
| ./icons/1024x1024.png=/usr/share/icons/hicolor/1024x1024/apps/daed.png \ | |
| ./${{ steps.get_filename.outputs.BUNDLE_NAME }}=/usr/bin/daed | |
| done | |
| popd || exit 1 | |
| cp bundled/daed-linux-$GOARCH$GOAMD64.deb ./output_packages/installer-${{ steps.get_filename.outputs.BUNDLE_NAME }}.deb | |
| cp bundled/daed-linux-$GOARCH$GOAMD64.rpm ./output_packages/installer-${{ steps.get_filename.outputs.BUNDLE_NAME }}.rpm | |
| if [ "$ARCH_PACMAN" == 'pacman' ];then | |
| cp bundled/daed-linux-$GOARCH$GOAMD64.pacman ./output_packages/installer-${{ steps.get_filename.outputs.BUNDLE_NAME }}.pkg.tar.zst | |
| fi | |
| ls ./output_packages/ | grep -E ".deb|.rpm|.pkg.tar.zst" | |
| - if: ${{ env.GOARCH != 'mips' && env.GOARCH != 'mipsle' && env.GOARCH != 'mips64' && env.GOARCH != 'mips64le' }} | |
| uses: nanoufo/action-upload-artifacts-and-release-assets@v2.0 | |
| with: | |
| path: | | |
| output_packages/* | |
| upload-release: | |
| needs: [release-please, checkout-full-src, build-bundle] | |
| if: ${{ needs.release-please.outputs.release_created == 'true' }} | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.release-please.outputs.tag_name }} | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| path: release/ | |
| - name: Prepare files for upload | |
| run: | | |
| set -euo pipefail | |
| # Parallel execution helper function | |
| # Usage: parallel_exec <function_name> <input_list> | |
| parallel_exec() { | |
| local func_name="$1" | |
| shift | |
| export -f "$func_name" | |
| echo "$@" | tr ' ' '\n' | xargs -P 8 -I {} bash -c 'set -e; '"$func_name"' "$@"' _ {} | |
| } | |
| cp release/*/*.deb ./ | |
| cp release/*/*.rpm ./ | |
| cp release/*/*.pkg.tar.zst ./ | |
| cp release/*/*.zip ./ | |
| # Create zip archives in parallel | |
| create_zip() { | |
| local dir="$1" | |
| zip -9r "../${dir}.zip" "$dir" && echo "✓ Created ${dir}.zip" | |
| } | |
| cd release | |
| dirs=$(ls -d daed-linux* web) | |
| parallel_exec create_zip $dirs | |
| cd .. | |
| # Verify all zip files | |
| verify_zip() { | |
| local dir="$1" | |
| if [[ ! -f "${dir}.zip" ]]; then | |
| echo "✗ Failed to create ${dir}.zip" && exit 1 | |
| fi | |
| unzip -t "${dir}.zip" > /dev/null 2>&1 || { echo "✗ ${dir}.zip is corrupted"; exit 1; } | |
| echo "✓ Verified ${dir}.zip" | |
| } | |
| parallel_exec verify_zip $dirs | |
| # Generate checksums in parallel | |
| generate_dgst() { | |
| local package="$1" | |
| echo "$(md5 -q "$package") $package md5" >> "$package.dgst" | |
| echo "$(shasum -a 1 "$package") sha1" >> "$package.dgst" | |
| echo "$(shasum -a 256 "$package") sha256" >> "$package.dgst" | |
| echo "$(shasum -a 512 "$package") sha512" >> "$package.dgst" | |
| echo "✓ Generated checksums for $package" | |
| } | |
| packages=$(ls | grep -E "\.deb$|\.pkg\.tar\.zst$|\.rpm$|\.zip$") | |
| parallel_exec generate_dgst $packages | |
| echo "Show files are going to upload..." | |
| ls -lh | grep -E ".deb|.pkg.tar.zst|.rpm|.zip" | |
| - name: Upload Release | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| TAG_NAME: ${{ needs.release-please.outputs.tag_name }} | |
| run: | | |
| set -euo pipefail | |
| # Parallel execution helper function | |
| parallel_exec() { | |
| local func_name="$1" | |
| shift | |
| export -f "$func_name" | |
| echo "$@" | tr ' ' '\n' | xargs -P 8 -I {} bash -c 'set -e; '"$func_name"' "$@"' _ {} | |
| } | |
| upload_file() { | |
| local file="$1" | |
| gh release upload "$TAG_NAME" "$file" --clobber | |
| echo "✓ Uploaded $file" | |
| } | |
| export TAG_NAME | |
| files=$(ls | grep -E "\.deb$|\.pkg\.tar\.zst$|\.rpm$|\.zip$|\.dgst$") | |
| parallel_exec upload_file $files | |
| # Publish container image when release is created | |
| publish-container-image: | |
| needs: [release-please, build-web] | |
| if: ${{ needs.release-please.outputs.release_created == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| submodules: recursive | |
| ref: ${{ needs.release-please.outputs.tag_name }} | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| name: web | |
| path: apps/web/dist/ | |
| - name: Prepare Tag | |
| id: prep | |
| env: | |
| REF: ${{ needs.release-please.outputs.tag_name }} | |
| run: | | |
| tag=${REF:1} | |
| echo "IMAGE=daeuniverse/daed" >> $GITHUB_OUTPUT | |
| echo "TAG=$tag" >> $GITHUB_OUTPUT | |
| - uses: docker/setup-qemu-action@v4 | |
| - uses: docker/setup-docker-action@v5 | |
| with: | |
| daemon-config: | | |
| { | |
| "debug": true, | |
| "features": { | |
| "containerd-snapshotter": true | |
| } | |
| } | |
| - uses: docker/setup-buildx-action@v4 | |
| id: buildx | |
| - uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: docker/login-action@v4 | |
| with: | |
| registry: quay.io | |
| username: ${{ github.repository_owner }} | |
| password: ${{ secrets.QUAY_PASS }} | |
| - uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| build-args: DAED_VERSION=${{ steps.prep.outputs.TAG }} | |
| builder: ${{ steps.buildx.outputs.name }} | |
| file: publish.Dockerfile | |
| target: prod | |
| platforms: linux/386,linux/amd64,linux/arm64 | |
| push: true | |
| load: true | |
| tags: | | |
| ${{ github.repository }}:latest | |
| ${{ github.repository }}:${{ steps.prep.outputs.TAG }} | |
| ghcr.io/${{ github.repository }}:latest | |
| ghcr.io/${{ github.repository }}:${{ steps.prep.outputs.TAG }} | |
| quay.io/${{ github.repository }}:latest | |
| quay.io/${{ github.repository }}:${{ steps.prep.outputs.TAG }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max |