From 2810665aabe5198fb1b7895f92e76c18ef5843af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <323649642+oc-tmueller@users.noreply.github.com> Date: Mon, 26 Oct 2026 14:33:38 +0100 Subject: [PATCH 1/3] ci: add zizmor workflow audit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Runs zizmor over this repository's workflows and Dependabot config on every pull request, on pushes to the default branch and weekly, through owncloud/reusable-workflows' zizmor.yml. Any finding fails the check. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Thomas Müller <323649642+oc-tmueller@users.noreply.github.com> --- .github/workflows/zizmor.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/zizmor.yml diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 0000000..f9918c4 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,30 @@ +name: Zizmor + +on: + push: + branches: + - main + pull_request: + types: + - opened + - synchronize + - reopened + schedule: + # Weekly: online audits (known-vulnerable actions, impostor commits) can + # start failing without a code change. + - cron: '0 4 * * 1' + workflow_dispatch: + +permissions: + contents: read + +# Grouped per event, so a push to the default branch can never cancel or +# displace the weekly run; only pull request runs cancel superseded ones. +concurrency: + group: zizmor-${{ github.event_name }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + zizmor: + name: Zizmor + uses: owncloud/reusable-workflows/.github/workflows/zizmor.yml@main From a6994be498d9e400be90eab5e5d86dc10090147f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <323649642+oc-tmueller@users.noreply.github.com> Date: Mon, 26 Oct 2026 14:33:38 +0100 Subject: [PATCH 2/3] fix(ci): resolve zizmor findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - template-injection: workflow_dispatch inputs (from_version, to_version), reusable-workflow inputs (ssh host/user/key, oCIS URL, APK version and artifact name) and job outputs were expanded straight into run: scripts, so a crafted value became shell code. They are now passed through env: and quoted where used as paths. - secrets-inherit: sign_latest and sign_master passed every repository secret to sign-apk.yml; they now pass only the three it declares (TEST_KS_B64, TEST_KS_ALIAS, TEST_KS_KEY). - self-repository: local reusable workflows and the ansible-setup action are called as $/... instead of ./... . - artipacked: checkouts do not persist credentials. No job runs a git operation after checkout other than reading history. - dependabot-cooldown: gradle updates wait 7 days too, as github-actions already does. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Thomas Müller <323649642+oc-tmueller@users.noreply.github.com> --- .github/dependabot.yml | 2 ++ .github/workflows/ansible-setup/action.yml | 12 +++++-- .github/workflows/cleanup.yml | 4 ++- .github/workflows/compile.yml | 2 ++ .github/workflows/ocisbackend.yml | 8 +++-- .github/workflows/sign-apk.yml | 12 ++++--- .github/workflows/update.yml | 37 +++++++++++++++------- 7 files changed, 55 insertions(+), 22 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index fcb425c..8419471 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -23,6 +23,8 @@ updates: day: sunday time: '22:00' timezone: Etc/UTC + cooldown: + default-days: 7 open-pull-requests-limit: 5 groups: minor-and-patch: diff --git a/.github/workflows/ansible-setup/action.yml b/.github/workflows/ansible-setup/action.yml index 8c6b37d..b24d74a 100644 --- a/.github/workflows/ansible-setup/action.yml +++ b/.github/workflows/ansible-setup/action.yml @@ -23,12 +23,18 @@ runs: shell: bash run: | mkdir -p $HOME/.ssh - echo "${{ inputs.ssh_private_key }}" > $HOME/.ssh/id_ed25519 + echo "${INPUTS_SSH_PRIVATE_KEY}" > $HOME/.ssh/id_ed25519 chmod 600 $HOME/.ssh/id_ed25519 - ssh-keyscan -H ${{ inputs.ssh_host }} >> $HOME/.ssh/known_hosts + ssh-keyscan -H ${INPUTS_SSH_HOST} >> $HOME/.ssh/known_hosts + env: + INPUTS_SSH_PRIVATE_KEY: ${{ inputs.ssh_private_key }} + INPUTS_SSH_HOST: ${{ inputs.ssh_host }} - name: Create Ansible inventory shell: bash run: | echo "[ocis_server]" > ./server/ansible/hosts.ini - echo "${{ inputs.ssh_host }} ansible_user=${{ inputs.ssh_user }} ansible_ssh_private_key_file=$HOME/.ssh/id_ed25519" >> ./server/ansible/hosts.ini + echo "${INPUTS_SSH_HOST} ansible_user=${INPUTS_SSH_USER} ansible_ssh_private_key_file=$HOME/.ssh/id_ed25519" >> ./server/ansible/hosts.ini + env: + INPUTS_SSH_HOST: ${{ inputs.ssh_host }} + INPUTS_SSH_USER: ${{ inputs.ssh_user }} diff --git a/.github/workflows/cleanup.yml b/.github/workflows/cleanup.yml index a778b47..a22469a 100644 --- a/.github/workflows/cleanup.yml +++ b/.github/workflows/cleanup.yml @@ -26,9 +26,11 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false - name: Setup Ansible and SSH - uses: ./.github/workflows/ansible-setup + uses: $/.github/workflows/ansible-setup with: ssh_private_key: ${{ env.SSH_PRIVATE_KEY }} ssh_host: ${{ env.SSH_HOST }} diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index a7bc82d..15fd4a2 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -14,6 +14,8 @@ jobs: - name: Checkout repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Set up JDK 17 uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 #v6.0.1 diff --git a/.github/workflows/ocisbackend.yml b/.github/workflows/ocisbackend.yml index bd74b4c..03b8cab 100644 --- a/.github/workflows/ocisbackend.yml +++ b/.github/workflows/ocisbackend.yml @@ -54,6 +54,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false # If workflow_call secret exists, use it; otherwise use dispatch input - name: Set BASE_URL @@ -61,11 +63,13 @@ jobs: if [ -n "${{ secrets.OCIS_URL }}" ]; then echo "BASE_URL=${{ secrets.OCIS_URL }}" >> $GITHUB_ENV else - echo "BASE_URL=${{ inputs.ocis_url }}" >> $GITHUB_ENV + echo "BASE_URL=${INPUTS_OCIS_URL}" >> $GITHUB_ENV fi + env: + INPUTS_OCIS_URL: ${{ inputs.ocis_url }} - name: Setup Ansible and SSH - uses: ./.github/workflows/ansible-setup + uses: $/.github/workflows/ansible-setup with: ssh_private_key: ${{ env.SSH_PRIVATE_KEY }} ssh_host: ${{ env.SSH_HOST }} diff --git a/.github/workflows/sign-apk.yml b/.github/workflows/sign-apk.yml index ab1065b..1282323 100644 --- a/.github/workflows/sign-apk.yml +++ b/.github/workflows/sign-apk.yml @@ -34,7 +34,10 @@ jobs: path: ./ - name: Move APK to root directory - run: cp ./${{ inputs.unsigned_artifact_name }} ./owncloud-${{ inputs.version }}.apk + run: cp ./${INPUTS_UNSIGNED_ARTIFACT_NAME} ./owncloud-${INPUTS_VERSION}.apk + env: + INPUTS_UNSIGNED_ARTIFACT_NAME: ${{ inputs.unsigned_artifact_name }} + INPUTS_VERSION: ${{ inputs.version }} - name: Restore keystore run: | @@ -45,10 +48,11 @@ jobs: env: KEY_ALIAS: ${{ secrets.TEST_KS_ALIAS }} KEY_PASSWORD: ${{ secrets.TEST_KS_KEY }} + INPUTS_VERSION: ${{ inputs.version }} run: | - APK_INPUT="owncloud-${{ inputs.version }}.apk" - APK_ALIGNED="owncloud-${{ inputs.version }}-aligned.apk" - APK_SIGNED="owncloudSigned-${{ inputs.version }}.apk" + APK_INPUT="owncloud-${INPUTS_VERSION}.apk" + APK_ALIGNED="owncloud-${INPUTS_VERSION}-aligned.apk" + APK_SIGNED="owncloudSigned-${INPUTS_VERSION}.apk" KEYSTORE="./test.keystore" echo "Aligning APK..." diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index 09cb47f..dc94c67 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -43,6 +43,7 @@ jobs: ref: master fetch-depth: 0 path: android-repo + persist-credentials: false - name: Check commits from previous day id: check @@ -76,7 +77,7 @@ jobs: needs: - check_repo_changes if: github.event_name == 'workflow_dispatch' || needs.check_repo_changes.outputs.has_changes == 'true' - uses: ./.github/workflows/ocisbackend.yml + uses: $/.github/workflows/ocisbackend.yml with: ocis_version: ${{ vars.OCIS_VERSION }} secrets: @@ -113,22 +114,28 @@ jobs: name: Sign latest APK needs: - build_apk_latest - uses: ./.github/workflows/sign-apk.yml + uses: $/.github/workflows/sign-apk.yml with: version: ${{ inputs.from_version || 'latest' }} unsigned_artifact_name: ${{ needs.build_apk_latest.outputs.artifact_name }} - secrets: inherit + secrets: + TEST_KS_B64: ${{ secrets.TEST_KS_B64 }} + TEST_KS_ALIAS: ${{ secrets.TEST_KS_ALIAS }} + TEST_KS_KEY: ${{ secrets.TEST_KS_KEY }} # Signs build from master sign_master: name: Sign master APK needs: - build_apk_master - uses: ./.github/workflows/sign-apk.yml + uses: $/.github/workflows/sign-apk.yml with: version: ${{ inputs.to_version || 'master' }} unsigned_artifact_name: ${{ needs.build_apk_master.outputs.artifact_name }} - secrets: inherit + secrets: + TEST_KS_B64: ${{ secrets.TEST_KS_B64 }} + TEST_KS_ALIAS: ${{ secrets.TEST_KS_ALIAS }} + TEST_KS_KEY: ${{ secrets.TEST_KS_KEY }} # Launches the test update_test: @@ -148,6 +155,8 @@ jobs: # Checkout repo - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false - name: Show current branch and commit run: | @@ -172,10 +181,13 @@ jobs: # Move apk to resources folder - name: Move and rename APKs to resources directory + env: + FROM_VERSION: ${{ inputs.from_version || 'latest' }} + TO_VERSION: ${{ inputs.to_version || 'master' }} run: | ls -al . - mv ./owncloudSigned-${{ inputs.from_version || 'latest' }}.apk ./src/test/resources/owncloudSignedLatest.apk - mv ./owncloudSigned-${{ inputs.to_version || 'master' }}.apk ./src/test/resources/owncloudSignedMaster.apk + mv "./owncloudSigned-${FROM_VERSION}.apk" ./src/test/resources/owncloudSignedLatest.apk + mv "./owncloudSigned-${TO_VERSION}.apk" ./src/test/resources/owncloudSignedMaster.apk ls -al ./src/test/resources # Start Appium server @@ -196,19 +208,20 @@ jobs: - name: Shorten the built commit hash shell: bash run: | - COMMIT_HASH="${{ needs.build_apk_master.outputs.commit_hash }}" + COMMIT_HASH="${NEEDS_BUILD_APK_MASTER_OUTPUTS_COMMIT_HASH}" SHORT_HASH=${COMMIT_HASH:0:7} echo Hash shortened: $SHORT_HASH echo "SHORT_HASH=$SHORT_HASH" >> $GITHUB_ENV + env: + NEEDS_BUILD_APK_MASTER_OUTPUTS_COMMIT_HASH: ${{ needs.build_apk_master.outputs.commit_hash }} - name: Generate version summary env: FROM_COMMIT: ${{ needs.build_apk_latest.outputs.commit_hash }} TO_COMMIT: ${{ needs.build_apk_master.outputs.commit_hash }} + FROM_VERSION: ${{ inputs.from_version || 'latest' }} + TO_VERSION: ${{ inputs.to_version || 'master' }} run: | - FROM_VERSION="${{ inputs.from_version || 'latest' }}" - TO_VERSION="${{ inputs.to_version || 'master' }}" - { echo "From version: $FROM_VERSION" echo "From commit: $FROM_COMMIT" @@ -303,7 +316,7 @@ jobs: - create_ocis - check_repo_changes if: always() && (github.event_name == 'workflow_dispatch' || needs.check_repo_changes.outputs.has_changes == 'true') - uses: ./.github/workflows/cleanup.yml + uses: $/.github/workflows/cleanup.yml secrets: ssh_host: ${{ secrets.SSH_HOST }} ssh_user: ${{ secrets.SSH_USER }} From ce70cdd0880ba0b52e401157d20978b3900e63f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= <323649642+oc-tmueller@users.noreply.github.com> Date: Mon, 26 Oct 2026 14:44:37 +0100 Subject: [PATCH 3/3] fix(ci): address review of the zizmor rollout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only the zizmor caller's concurrency comment changes: GitHub keeps one queued run per group, so for non-pull-request events a newer run still replaces a queued one; the comment now says so instead of claiming only pull request runs are superseded. sign-apk's cp and ansible-setup's ssh-keyscan now quote the values that came from inputs, as the previous commit message already claimed. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Thomas Müller <323649642+oc-tmueller@users.noreply.github.com> --- .github/workflows/ansible-setup/action.yml | 2 +- .github/workflows/sign-apk.yml | 2 +- .github/workflows/zizmor.yml | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ansible-setup/action.yml b/.github/workflows/ansible-setup/action.yml index b24d74a..a17ac46 100644 --- a/.github/workflows/ansible-setup/action.yml +++ b/.github/workflows/ansible-setup/action.yml @@ -25,7 +25,7 @@ runs: mkdir -p $HOME/.ssh echo "${INPUTS_SSH_PRIVATE_KEY}" > $HOME/.ssh/id_ed25519 chmod 600 $HOME/.ssh/id_ed25519 - ssh-keyscan -H ${INPUTS_SSH_HOST} >> $HOME/.ssh/known_hosts + ssh-keyscan -H "${INPUTS_SSH_HOST}" >> $HOME/.ssh/known_hosts env: INPUTS_SSH_PRIVATE_KEY: ${{ inputs.ssh_private_key }} INPUTS_SSH_HOST: ${{ inputs.ssh_host }} diff --git a/.github/workflows/sign-apk.yml b/.github/workflows/sign-apk.yml index 1282323..99132ad 100644 --- a/.github/workflows/sign-apk.yml +++ b/.github/workflows/sign-apk.yml @@ -34,7 +34,7 @@ jobs: path: ./ - name: Move APK to root directory - run: cp ./${INPUTS_UNSIGNED_ARTIFACT_NAME} ./owncloud-${INPUTS_VERSION}.apk + run: cp "./${INPUTS_UNSIGNED_ARTIFACT_NAME}" "./owncloud-${INPUTS_VERSION}.apk" env: INPUTS_UNSIGNED_ARTIFACT_NAME: ${{ inputs.unsigned_artifact_name }} INPUTS_VERSION: ${{ inputs.version }} diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index f9918c4..8b59595 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -19,7 +19,8 @@ permissions: contents: read # Grouped per event, so a push to the default branch can never cancel or -# displace the weekly run; only pull request runs cancel superseded ones. +# displace the weekly run. Running pull request runs are cancelled by newer +# ones; for other events GitHub still replaces a queued run with a newer one. concurrency: group: zizmor-${{ github.event_name }}-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }}