mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-09-12 05:08:43 -04:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89eb68add9 | ||
|
|
faeea05d28 | ||
|
|
b9f926818e | ||
|
|
ebfadde688 | ||
|
|
4c029dd9d3 | ||
|
|
5351b53276 | ||
|
|
ec52eed3ed | ||
|
|
195b4c6d30 | ||
|
|
bd48f0751b | ||
|
|
dcef974c7c |
No files matched your search
@@ -1,5 +1,9 @@
|
||||
name: Lint GitHub Actions workflows
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# Static-check the workflow YAML with rhysd/actionlint. Catches missing
|
||||
# secrets, bad expressions, expression-type errors, unsupported runner
|
||||
# images, and (via embedded shellcheck) common pitfalls in `run:` scripts.
|
||||
@@ -14,7 +18,7 @@ on:
|
||||
- '.github/actionlint.yaml'
|
||||
- '.github/actionlint.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- '.github/workflows/*.yml'
|
||||
- '.github/actionlint.yaml'
|
||||
@@ -25,10 +29,8 @@ permissions:
|
||||
|
||||
jobs:
|
||||
actionlint:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
name: actionlint
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
name: Test rsync on AlmaLinux 8
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# Older-LTS coverage on the Fedora/RHEL family to help with backporting
|
||||
# security fixes. AlmaLinux 8 is the RHEL 8 rebuild and is the oldest
|
||||
# active LTS in this family (RHEL 8 full support runs to 2029).
|
||||
@@ -13,19 +17,20 @@ on:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/almalinux-8-build.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/almalinux-8-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
container:
|
||||
image: almalinux:8
|
||||
name: Test rsync on AlmaLinux 8
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
name: Build static rsync for Android
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# Cross-compiles statically-linked rsync binaries with the Android NDK,
|
||||
# suitable for dropping onto a phone (adb push / Termux) with no shared
|
||||
# libraries. arm64-v8a covers all modern phones; armeabi-v7a covers older
|
||||
@@ -16,7 +20,7 @@ on:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/android-static-build.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/android-static-build.yml'
|
||||
@@ -24,6 +28,9 @@ on:
|
||||
- cron: '42 8 * * 1'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
# Minimum supported API level. 24 (Android 7.0) runs on every modern
|
||||
# phone while keeping broad reach; bump if you need newer Bionic APIs.
|
||||
@@ -31,10 +38,8 @@ env:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
name: ${{ matrix.abi }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
name: rsync ASan+UBSan (clang)
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
@@ -7,7 +11,7 @@ on:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/asan-build.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/asan-build.yml'
|
||||
@@ -19,12 +23,13 @@ on:
|
||||
- cron: '42 9 * * 1'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
asan:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
name: rsync ASan+UBSan (clang)
|
||||
env:
|
||||
# rsync intentionally leaks small allocations at process exit, so leak
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
name: Coverage (Ubuntu)
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# Coverage duplicates the Ubuntu suite to measure it rather than protect a
|
||||
# distinct PR failure mode. Keep that cost scheduled and available on demand.
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/coverage.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/coverage.yml'
|
||||
schedule:
|
||||
- cron: '42 9 * * 1'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
coverage:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
name: gcov coverage
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
name: Test rsync on Cygwin
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
@@ -7,30 +11,69 @@ on:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/cygwin-build.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/cygwin-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: windows-2022
|
||||
timeout-minutes: 60
|
||||
name: Test rsync on Cygwin
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: cygwin
|
||||
run: choco install -y --no-progress cygwin cyg-get
|
||||
- name: prep
|
||||
run: |
|
||||
cyg-get make autoconf automake gcc-core attr libattr-devel python39 python39-pip libzstd-devel liblz4-devel libssl-devel libxxhash0 libxxhash-devel
|
||||
echo "C:/tools/cygwin/bin" >>$Env:GITHUB_PATH
|
||||
$setup = Join-Path $Env:RUNNER_TEMP 'setup-x86_64.exe'
|
||||
$sums = Join-Path $Env:RUNNER_TEMP 'cygwin-sha512.sum'
|
||||
$packages = 'make,autoconf,automake,gcc-core,attr,libattr-devel,python39,python39-pip,libzstd-devel,liblz4-devel,libssl-devel,libxxhash0,libxxhash-devel'
|
||||
|
||||
Invoke-WebRequest https://cygwin.com/setup-x86_64.exe -OutFile $setup
|
||||
Invoke-WebRequest https://cygwin.com/sha512.sum -OutFile $sums
|
||||
|
||||
$sum = Select-String -LiteralPath $sums -Pattern '^[0-9a-fA-F]{128}\s+\*?setup-x86_64\.exe$' | Select-Object -First 1
|
||||
if (-not $sum) {
|
||||
throw 'setup-x86_64.exe is missing from Cygwin sha512.sum'
|
||||
}
|
||||
$expected = ($sum.Line -split '\s+')[0]
|
||||
$actual = (Get-FileHash -LiteralPath $setup -Algorithm SHA512).Hash
|
||||
if ($actual -ine $expected) {
|
||||
throw 'Cygwin setup SHA-512 mismatch'
|
||||
}
|
||||
|
||||
$arguments = @(
|
||||
'--quiet-mode',
|
||||
'--no-desktop',
|
||||
'--no-startmenu',
|
||||
'--no-shortcuts',
|
||||
'--root', 'C:\tools\cygwin',
|
||||
'--local-package-dir', (Join-Path $Env:RUNNER_TEMP 'cygwin-packages'),
|
||||
'--site', 'https://mirrors.kernel.org/sourceware/cygwin/',
|
||||
'--packages', $packages
|
||||
)
|
||||
$install = Start-Process -FilePath $setup -ArgumentList $arguments -Wait -PassThru -NoNewWindow
|
||||
if ($install.ExitCode -ne 0) {
|
||||
exit $install.ExitCode
|
||||
}
|
||||
|
||||
$bash = 'C:\tools\cygwin\bin\bash.exe'
|
||||
if (-not (Test-Path -LiteralPath $bash)) {
|
||||
throw 'Cygwin setup did not install bash'
|
||||
}
|
||||
& $bash -lc 'command -v make aclocal gcc python3 >/dev/null'
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw 'Cygwin setup did not install all required build tools'
|
||||
}
|
||||
|
||||
echo 'C:/tools/cygwin/bin' >>$Env:GITHUB_PATH
|
||||
- name: commonmark
|
||||
run: bash -c 'python3 -mpip install --user commonmark'
|
||||
- name: configure
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
name: Test fleettest harness
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# Bitrot check for testsuite/fleettest.py (the developer fleet CI harness).
|
||||
# fleettest is meant to be run by developers on a modern Ubuntu box, so this
|
||||
# job runs only on ubuntu-latest: it stands up a one-host "fleet" of two
|
||||
@@ -16,7 +20,7 @@ on:
|
||||
- 'testsuite/skiplist/**'
|
||||
- 'testsuite/skiplist-spec_test.py'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- 'testsuite/fleettest.py'
|
||||
- '.github/workflows/fleettest.yml'
|
||||
@@ -27,12 +31,13 @@ on:
|
||||
schedule:
|
||||
- cron: '17 7 * * 1'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
fleettest:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
name: fleettest against localhost
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
name: Test rsync on FreeBSD
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
@@ -7,19 +11,20 @@ on:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/freebsd-build.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/freebsd-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * 1'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
name: Test rsync on FreeBSD
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
name: Test rsync on macOS
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
@@ -7,19 +11,20 @@ on:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/macos-build.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/macos-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 45
|
||||
name: Test rsync on macOS
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
name: Test rsync on NetBSD
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
@@ -7,19 +11,20 @@ on:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/netbsd-build.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/netbsd-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * 1'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
name: Test rsync on NetBSD
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
name: Test rsync on OpenBSD
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
@@ -7,19 +11,20 @@ on:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/openbsd-build.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/openbsd-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * 1'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
name: Test rsync on OpenBSD
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
name: rsync scan-build (clang analyzer)
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
@@ -7,12 +11,15 @@ on:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/scan-build.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/scan-build.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# GATING run: pinned clang-18 on a pinned runner so the checker set -- and
|
||||
# thus the expected zero -- is deterministic. The tree is kept clean for
|
||||
@@ -20,10 +27,8 @@ jobs:
|
||||
# when a new finding appears. Pin both the analyzer (clang-18/clang-tools-18)
|
||||
# and the runner (ubuntu-24.04, whose apt repos carry those packages).
|
||||
gate-clang18:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 45
|
||||
name: scan-build gate (clang-18, pinned)
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -64,10 +69,8 @@ jobs:
|
||||
# gate bump -- without blocking merges. continue-on-error keeps a noisy or
|
||||
# broken run from affecting the workflow's required status.
|
||||
informational-latest:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
name: scan-build (latest clang, informational)
|
||||
continue-on-error: true
|
||||
steps:
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
name: Test rsync on Solaris
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
@@ -7,19 +11,20 @@ on:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/solaris-build.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/solaris-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * 1'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
name: Test rsync on Solaris
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
name: Test rsync on Ubuntu 22.04
|
||||
|
||||
# Older-LTS coverage to help with backporting security fixes. ubuntu-22.04
|
||||
# is currently the oldest GitHub Actions runner image (20.04 was retired
|
||||
# in April 2025).
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-22.04-build.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-22.04-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-22.04
|
||||
name: Test rsync on Ubuntu 22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: prep
|
||||
run: |
|
||||
sudo apt-get install acl libacl1-dev attr libattr1-dev liblz4-dev libzstd-dev libxxhash-dev python3-cmarkgfm openssl
|
||||
echo "/usr/local/bin" >>"$GITHUB_PATH"
|
||||
- name: configure
|
||||
run: ./configure --with-rrsync
|
||||
- name: make
|
||||
run: make
|
||||
- name: install
|
||||
run: sudo make install
|
||||
- name: info
|
||||
run: rsync --version
|
||||
- name: check
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=@testsuite/skiplist/common.txt,@testsuite/skiplist/linux.txt make check
|
||||
- name: check30
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=@testsuite/skiplist/common.txt,@testsuite/skiplist/linux.txt make check30
|
||||
- name: check29
|
||||
run: sudo RSYNC_EXPECT_SKIPPED=@testsuite/skiplist/common.txt,@testsuite/skiplist/linux.txt,@testsuite/skiplist/proto29.txt make check29
|
||||
- name: check (TCP daemon transport)
|
||||
# Second run with daemon tests over a real loopback rsyncd; the default
|
||||
# 'make check' above uses the secure stdio-pipe transport.
|
||||
run: sudo ./runtests.py --rsync-bin="$PWD/rsync" --use-tcp -j 8
|
||||
- name: ssl file list
|
||||
run: rsync-ssl --no-motd download.samba.org::rsyncftp/ || true
|
||||
- name: save artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
retention-days: 45
|
||||
name: ubuntu-22.04-bin
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
rsync.1
|
||||
rsync-ssl.1
|
||||
rsyncd.conf.5
|
||||
rrsync.1
|
||||
rrsync
|
||||
@@ -1,5 +1,9 @@
|
||||
name: Test rsync on Ubuntu
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
@@ -7,20 +11,33 @@ on:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-build.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-build.yml'
|
||||
schedule:
|
||||
- cron: '42 8 * * *'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-latest
|
||||
name: Test rsync on Ubuntu
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- runner: ubuntu-latest
|
||||
name: Test rsync on Ubuntu latest
|
||||
artifact: ubuntu-bin
|
||||
nonroot: true
|
||||
- runner: ubuntu-22.04
|
||||
name: Test rsync on Ubuntu 22.04
|
||||
artifact: ubuntu-22.04-bin
|
||||
nonroot: false
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 45
|
||||
name: ${{ matrix.name }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -50,6 +67,7 @@ jobs:
|
||||
# is env-dependent here (chroot-acl), so leave RSYNC_EXPECT_SKIPPED unset.
|
||||
run: sudo ./runtests.py --rsync-bin="$PWD/rsync" --use-tcp -j 8
|
||||
- name: check (non-root, targeted)
|
||||
if: matrix.nonroot
|
||||
# Every run above is root (sudo), so privilege-sensitive tests never hit
|
||||
# their non-root path. Run those here as the unprivileged 'runner' user
|
||||
# (NO sudo). Explicit test names make runtests.py full_run False, so
|
||||
@@ -71,7 +89,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
retention-days: 45
|
||||
name: ubuntu-bin
|
||||
name: ${{ matrix.artifact }}
|
||||
path: |
|
||||
rsync
|
||||
rsync-ssl
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
name: Test rsync version mixing on Ubuntu
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# Runs the CURRENT test suite with two different rsync binaries: the freshly
|
||||
# built ./rsync as the client/driver, and a committed OLD static binary
|
||||
# (old_versions/rsync_<ver>) as the daemon / remote-shell peer. This exercises
|
||||
@@ -28,19 +32,20 @@ on:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-version-mix.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
types: [opened, synchronize, reopened]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/ubuntu-version-mix.yml'
|
||||
schedule:
|
||||
- cron: '52 8 * * 1'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
version-mix:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
name: rsync version-mix
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
name: Valgrind memcheck
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# ASan+UBSan remains the per-PR memory-safety gate. Run the four slower
|
||||
# Valgrind combinations daily and on demand instead of occupying PR runners.
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/valgrind.yml'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
paths-ignore:
|
||||
- '.github/workflows/*.yml'
|
||||
- '!.github/workflows/valgrind.yml'
|
||||
schedule:
|
||||
- cron: '17 4 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
memcheck:
|
||||
# temporary gate: PR CI runs only for PRs labeled 'run-ci', to save CI
|
||||
# minutes; labels need triage access, so fork PRs can't self-enable.
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-ci')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
||||
+10
-1
@@ -1,3 +1,12 @@
|
||||
REGARDING OPENSSL AND XXHASH
|
||||
|
||||
In addition, as a special exception, the copyright holders give
|
||||
permission to dynamically link rsync with the OpenSSL and xxhash
|
||||
libraries when those libraries are being distributed in compliance
|
||||
with their license terms, and to distribute a dynamically linked
|
||||
combination of rsync and these libraries. This is also considered
|
||||
to be covered under the GPL's System Libraries exception.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
@@ -671,4 +680,4 @@ into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<<a href="https://www.gnu.org/philosophy/why-not-lgpl.html">https://www.gnu.org/philosophy/why-not-lgpl.html</a>>.
|
||||
<<a href="https://www.gnu.org/licenses/why-not-lgpl.html">https://www.gnu.org/licenses/why-not-lgpl.html</a>>.
|
||||
@@ -2610,6 +2610,12 @@ sign) if you want the local shell to expand it.
|
||||
options are parsed (e.g. [`-a`](#opt) works the same before or after
|
||||
`--files-from`, as does `--no-R` and all other options).
|
||||
|
||||
Listing individual files with `--files-from` does not make unlisted
|
||||
siblings eligible for deletion. The [`--delete`](#opt) option only removes
|
||||
entries from directories whose complete contents are being synchronised,
|
||||
so list the directory itself and enable recursion if that deletion scope is
|
||||
intended.
|
||||
|
||||
The filenames that are read from the FILE are all relative to the source
|
||||
directory: any leading slash is removed, and ".." components are resolved away so
|
||||
an entry cannot rise above the source directory -- e.g. "../foo" is taken as "foo"
|
||||
|
||||
@@ -57,7 +57,14 @@ def main():
|
||||
for fn in files:
|
||||
if args.prefix:
|
||||
fn = args.prefix + fn
|
||||
mtime = os.lstat(fn).st_mtime
|
||||
try:
|
||||
mtime = os.lstat(fn).st_mtime
|
||||
except FileNotFoundError:
|
||||
# Tracked in git but absent from the tree we are stamping.
|
||||
# export-ignore in .gitattributes keeps rsync-web/ and the
|
||||
# old_versions/ binaries out of "git archive", so they have
|
||||
# no file here to give a commit time to.
|
||||
continue
|
||||
if args.list:
|
||||
print_line(fn, mtime, commit_time)
|
||||
elif mtime != commit_time:
|
||||
|
||||
@@ -16,30 +16,16 @@
|
||||
# *symlink* components. The fix sanitizes the wire xname itself (for basis
|
||||
# types only, leaving the hard-link "=> target" xname alone).
|
||||
#
|
||||
# Test: build an instrumented daemon-sender (env-gated sender.c edit that, when
|
||||
# Test: build an instrumented rsync (env-gated sender.c edit that, when
|
||||
# RSYNC_MAL_XNAME is set, injects ITEM_XNAME_FOLLOWS|ITEM_BASIS_TYPE_FOLLOWS +
|
||||
# fnamecmp_type=FNAMECMP_FUZZY+1 (== basis_dir[0]) + xname onto each transfer),
|
||||
# run it via RSYNC_CONNECT_PROG with the production rsync as the receiver pulling
|
||||
# with --link-dest, and observe where the receiver opens the basis.
|
||||
#
|
||||
# Two FIFOs, each with a helper blocked in open(O_WRONLY) that drops a flag when
|
||||
# some reader opens it, tell RED from GREEN without hanging the receiver (it
|
||||
# reads EOF and finishes):
|
||||
# * ESCAPE base/secret reached only by an unsanitized "../secret"
|
||||
# * DECOY linkdest/secret where the SANITIZED "secret" lands
|
||||
# Injected xname is "../secret":
|
||||
# - vulnerable receiver opens ESCAPE -> escape flag -> FAIL (traversal)
|
||||
# - fixed receiver sanitizes to "secret", opens DECOY -> decoy flag -> PASS
|
||||
# (the decoy flag also proves the crafted xname actually crossed the wire,
|
||||
# so a stale/failed injection build can't false-PASS as "confined")
|
||||
# - neither flag -> the injection never took effect -> FAIL (vacuous)
|
||||
# fnamecmp_type=FNAMECMP_FUZZY+1 (== basis_dir[0]) + xname onto each transfer).
|
||||
# An env-gated receiver.c edit records the exact basedir and relpath passed to
|
||||
# secure_basis_open(). This observes the security decision directly without
|
||||
# relying on timing-sensitive FIFO rendezvous behaviour across operating systems.
|
||||
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
from rsyncfns import (
|
||||
SCRATCHDIR, build_patched_rsync, forced_protocol, makepath, rmtree,
|
||||
@@ -53,11 +39,7 @@ from rsyncfns import (
|
||||
_proto = forced_protocol()
|
||||
if _proto is not None and _proto < 29:
|
||||
test_skipped("basis-xname-traversal: xname/item flags need protocol >= 29")
|
||||
if not hasattr(os, 'mkfifo'):
|
||||
test_skipped("basis-xname-traversal: os.mkfifo unavailable on this platform")
|
||||
|
||||
|
||||
# -- Build the instrumented sender (shared helper: Cygwin skip, CCACHE_DISABLE,
|
||||
# -- Build the instrumented peer (shared helper: Cygwin skip, CCACHE_DISABLE,
|
||||
# forced rebuild of the patched unit) -------------------------------------
|
||||
PATCH_OLD = ("\t\twrite_ndx_and_attrs(f_out, ndx, iflags, fname, file, fnamecmp_type, xname, xlen);\n"
|
||||
"\t\twrite_sum_head(f_xfer, s);")
|
||||
@@ -68,14 +50,32 @@ PATCH_NEW = ("\t\tif (getenv(\"RSYNC_MAL_XNAME\")) { /* basis-xname-traversal Po
|
||||
"\t\t}\n"
|
||||
"\t\twrite_ndx_and_attrs(f_out, ndx, iflags, fname, file, fnamecmp_type, xname, xlen);\n"
|
||||
"\t\twrite_sum_head(f_xfer, s);")
|
||||
mal_rsync = build_patched_rsync('mal-xname-rsync', [('sender.c', PATCH_OLD, PATCH_NEW)])
|
||||
TRACE_OLD = ("static int secure_basis_open(const char *basedir, const char *relpath, int flags, mode_t mode)\n"
|
||||
"{\n"
|
||||
"\textern int am_daemon, am_chrooted;")
|
||||
TRACE_NEW = ("static int secure_basis_open(const char *basedir, const char *relpath, int flags, mode_t mode)\n"
|
||||
"{\n"
|
||||
"\tconst char *trace_path = getenv(\"RSYNC_BASIS_TRACE\");\n"
|
||||
"\tif (trace_path) {\n"
|
||||
"\t\tFILE *trace = fopen(trace_path, \"a\");\n"
|
||||
"\t\tif (trace) {\n"
|
||||
"\t\t\tfprintf(trace, \"%s\\t%s\\n\", basedir ? basedir : \"\", relpath);\n"
|
||||
"\t\t\tfclose(trace);\n"
|
||||
"\t\t}\n"
|
||||
"\t}\n"
|
||||
"\textern int am_daemon, am_chrooted;")
|
||||
mal_rsync = build_patched_rsync(
|
||||
'mal-xname-rsync',
|
||||
[('sender.c', PATCH_OLD, PATCH_NEW),
|
||||
('receiver.c', TRACE_OLD, TRACE_NEW)],
|
||||
)
|
||||
|
||||
|
||||
# -- Workspace ----------------------------------------------------------------
|
||||
# base/serversrc/file the file the instrumented daemon offers
|
||||
# base/linkdest/ the client's --link-dest (basis_dir[0])
|
||||
# base/linkdest/secret DECOY fifo -- where a sanitized "secret" resolves
|
||||
# base/secret ESCAPE fifo -- where an unsanitized "../secret" lands
|
||||
# base/linkdest/secret where a sanitized "secret" resolves
|
||||
# base/secret where an unsanitized "../secret" resolves
|
||||
# base/dest/ the client's destination
|
||||
base = SCRATCHDIR / 'xname-race'
|
||||
rmtree(base)
|
||||
@@ -84,133 +84,57 @@ linkdest = base / 'linkdest'
|
||||
dest = base / 'dest'
|
||||
escape = base / 'secret' # linkdest/../secret
|
||||
decoy = linkdest / 'secret' # linkdest/secret
|
||||
esc_flag = base / 'escape.flag'
|
||||
dec_flag = base / 'decoy.flag'
|
||||
trace_file = base / 'basis.trace'
|
||||
makepath(serversrc)
|
||||
makepath(linkdest)
|
||||
makepath(dest)
|
||||
(serversrc / 'file').write_text("from the server\n")
|
||||
escape.write_text("escaped basis\n")
|
||||
decoy.write_text("confined basis\n")
|
||||
|
||||
|
||||
# A helper that blocks in open(fifo, O_WRONLY) until some reader opens the FIFO,
|
||||
# then records the flag. Terminated below if no reader ever appears.
|
||||
WRITER = ("import os,sys\n"
|
||||
"open(sys.argv[3],'w').close()\n" # ready: about to block in open()
|
||||
"fd=os.open(sys.argv[1],os.O_WRONLY)\n"
|
||||
"open(sys.argv[2],'w').close()\n"
|
||||
"os.close(fd)\n")
|
||||
|
||||
|
||||
def spawn(fifo, flag):
|
||||
ready = Path(str(flag) + '.ready')
|
||||
if ready.exists():
|
||||
ready.unlink()
|
||||
proc = subprocess.Popen(
|
||||
[sys.executable, '-c', WRITER, str(fifo), str(flag), str(ready)])
|
||||
# Wait until the helper is actually at its blocking open(). Starting the
|
||||
# transfer before that lets the receiver come and go while nothing is
|
||||
# watching the FIFO, and the run reports a vacuous result -- which is what
|
||||
# made this test flaky on the slower fleet VMs.
|
||||
deadline = time.time() + 30
|
||||
while not ready.exists() and proc.poll() is None and time.time() < deadline:
|
||||
time.sleep(0.02)
|
||||
return proc
|
||||
|
||||
|
||||
def settle(w):
|
||||
"""Give a rendezvoused helper a bounded chance to record its flag; a still-
|
||||
blocked one just times out. (Closes the terminate-before-flag race.)"""
|
||||
try:
|
||||
w.wait(timeout=15)
|
||||
except subprocess.TimeoutExpired:
|
||||
pass
|
||||
|
||||
|
||||
def reap(w):
|
||||
if w.poll() is None:
|
||||
w.terminate()
|
||||
try:
|
||||
w.wait(timeout=10)
|
||||
except subprocess.TimeoutExpired:
|
||||
w.kill()
|
||||
w.wait()
|
||||
|
||||
|
||||
def attempt():
|
||||
"""One injection run. Returns the receiver's CompletedProcess.
|
||||
|
||||
Re-creates the FIFOs and flags each time so a retry starts clean.
|
||||
"""
|
||||
for f in (escape, decoy, esc_flag, dec_flag):
|
||||
if os.path.lexists(f):
|
||||
os.unlink(f)
|
||||
rmtree(dest)
|
||||
makepath(dest)
|
||||
os.mkfifo(escape)
|
||||
os.mkfifo(decoy)
|
||||
esc_w = spawn(escape, esc_flag)
|
||||
dec_w = spawn(decoy, dec_flag)
|
||||
proc = None
|
||||
try:
|
||||
conf = write_daemon_conf(
|
||||
[('m', {'path': str(serversrc), 'read only': 'yes', 'use chroot': 'no'})],
|
||||
name='mal-xname-rsyncd.conf')
|
||||
os.environ['RSYNC_CONNECT_PROG'] = f'{shlex.quote(str(mal_rsync))} --config={shlex.quote(str(conf))} --daemon'
|
||||
os.environ['RSYNC_MAL_XNAME'] = '../secret' # from basis_dir[0] == linkdest
|
||||
proc = subprocess.run(
|
||||
rsync_argv('-a', f'--link-dest={linkdest}',
|
||||
'rsync://localhost/m/file', str(dest) + '/'),
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, timeout=120)
|
||||
settle(esc_w)
|
||||
settle(dec_w)
|
||||
finally:
|
||||
os.environ.pop('RSYNC_MAL_XNAME', None)
|
||||
os.environ.pop('RSYNC_CONNECT_PROG', None)
|
||||
reap(esc_w)
|
||||
reap(dec_w)
|
||||
for f in (escape, decoy):
|
||||
if os.path.lexists(f):
|
||||
os.unlink(f)
|
||||
return proc
|
||||
|
||||
|
||||
# A run where NEITHER fifo was opened proves nothing: the injection did not
|
||||
# take effect, so there was no traversal attempt to confine. That is a setup
|
||||
# failure, not a security signal, and on the slower fleet VMs it happens often
|
||||
# enough to make the test unusable -- so retry it. An ESCAPE is never retried:
|
||||
# the loop stops the moment the escape flag appears.
|
||||
attempts = 0
|
||||
for _try in range(6):
|
||||
attempts += 1
|
||||
proc = attempt()
|
||||
if esc_flag.is_file() or dec_flag.is_file():
|
||||
break
|
||||
conf = write_daemon_conf(
|
||||
[('m', {'path': str(serversrc), 'read only': 'yes', 'use chroot': 'no'})],
|
||||
name='mal-xname-rsyncd.conf')
|
||||
os.environ['RSYNC_CONNECT_PROG'] = f'{shlex.quote(str(mal_rsync))} --config={shlex.quote(str(conf))} --daemon'
|
||||
os.environ['RSYNC_MAL_XNAME'] = '../secret' # from basis_dir[0] == linkdest
|
||||
os.environ['RSYNC_BASIS_TRACE'] = str(trace_file)
|
||||
try:
|
||||
argv = rsync_argv('-a', f'--link-dest={linkdest}',
|
||||
'rsync://localhost/m/file', str(dest) + '/')
|
||||
argv[0] = str(mal_rsync)
|
||||
proc = subprocess.run(
|
||||
argv,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, timeout=120)
|
||||
finally:
|
||||
os.environ.pop('RSYNC_BASIS_TRACE', None)
|
||||
os.environ.pop('RSYNC_MAL_XNAME', None)
|
||||
os.environ.pop('RSYNC_CONNECT_PROG', None)
|
||||
|
||||
|
||||
# -- Oracle -------------------------------------------------------------------
|
||||
out_tail = '\n'.join((proc.stdout if proc else '').splitlines()[-20:])
|
||||
out_tail = '\n'.join(proc.stdout.splitlines()[-20:])
|
||||
trace = trace_file.read_text().splitlines() if trace_file.is_file() else []
|
||||
escaped = f'{linkdest}\t../secret'
|
||||
confined = f'{linkdest}\tsecret'
|
||||
|
||||
if esc_flag.is_file():
|
||||
if escaped in trace:
|
||||
test_fail(
|
||||
"malicious server traversed the client's filesystem via the alt-dest "
|
||||
f"xname: the receiver opened {escape} (one level above the --link-dest "
|
||||
f"xname: the receiver attempted {escape} (one level above the --link-dest "
|
||||
"dir) as the delta basis. A server-supplied xname of '../secret' was "
|
||||
"not sanitized on the client (sanitize_paths==0 off-daemon). Fix: "
|
||||
"sanitize a basis-type xname in read_ndx_and_attrs(). Receiver output "
|
||||
f"tail:\n{out_tail}")
|
||||
|
||||
# The decoy flag proves the crafted xname reached the receiver AND was confined
|
||||
# to the basedir (sanitized "../secret" -> "secret" -> linkdest/secret). Its
|
||||
# absence means the injection never took effect (e.g. a stale patched build),
|
||||
# so a clear escape flag alone would be a vacuous pass.
|
||||
if not dec_flag.is_file():
|
||||
# The trace proves the crafted xname reached the receiver and was confined to
|
||||
# the basedir (sanitized "../secret" -> "secret" -> linkdest/secret). Its
|
||||
# absence means the injection never took effect (e.g. a stale patched build).
|
||||
if confined not in trace:
|
||||
test_fail(
|
||||
"the crafted xname never reached the receiver's basis open (neither the "
|
||||
"escape nor the decoy FIFO was opened) -- the instrumented-sender "
|
||||
f"injection did not take effect, so this run is vacuous after "
|
||||
f"{attempts} attempt(s). This is a harness failure, NOT a traversal: "
|
||||
"an escape is reported separately and is never retried. Receiver rc="
|
||||
f"{proc.returncode if proc else 'n/a'}. Output tail:\n{out_tail}")
|
||||
"the crafted xname never reached the receiver's confined basis open; "
|
||||
"the instrumented injection did not take effect, so this run is "
|
||||
f"vacuous. Trace={trace!r}. Receiver rc={proc.returncode}. "
|
||||
f"Output tail:\n{out_tail}")
|
||||
|
||||
if proc.returncode != 0:
|
||||
test_fail(
|
||||
|
||||
@@ -91,10 +91,10 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"_comment": "Ubuntu 18.04 older-LTS backport coverage on a root@ box; no 18.04 runner image exists so it mirrors the 22.04 workflow.",
|
||||
"_comment": "Ubuntu 18.04 older-LTS backport coverage on a root@ box; no 18.04 runner image exists so it mirrors the 22.04 matrix lane.",
|
||||
"name": "ubuntu-1804",
|
||||
"ssh_host": "root@ubuntu-1804",
|
||||
"workflow": "ubuntu-22.04-build.yml",
|
||||
"workflow": "ubuntu-build.yml",
|
||||
"_python_comment": "18.04's default python3 is 3.6, but runtests.py uses subprocess capture_output (3.7+); run the suite under the 3.10 that's also installed.",
|
||||
"python": "python3.10",
|
||||
"_configure_flags_comment": "18.04's libzstd 1.3.3 lacks ZSTD_minCLevel (configure aborts), so disable zstd; also disable lz4 so the default -z compressor is zlib -- with lz4 as the default, compress-options' --compress-level=9 check fails since lz4 has no levels. xxhash is fine.",
|
||||
@@ -110,10 +110,10 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"_comment": "Ubuntu 20.04 older-LTS backport coverage on a root@ box; no 20.04 runner image exists so it mirrors the 22.04 workflow.",
|
||||
"_comment": "Ubuntu 20.04 older-LTS backport coverage on a root@ box; no 20.04 runner image exists so it mirrors the 22.04 matrix lane.",
|
||||
"name": "ubuntu-2004",
|
||||
"ssh_host": "root@ubuntu-2004",
|
||||
"workflow": "ubuntu-22.04-build.yml",
|
||||
"workflow": "ubuntu-build.yml",
|
||||
"configure_flags": [
|
||||
"--with-rrsync"
|
||||
],
|
||||
@@ -126,7 +126,7 @@
|
||||
"_comment": "Builds unprivileged (like a CI runner) and runs the suite via sudo; the nonroot pass reruns the privilege-sensitive tests as the ssh user. protocols: [30, 29] runs the check30/check29 passes on this NO-xattrat kernel: the CI's proto-29 step runs here, but the only other proto-29 fleet box (ubuntu-2604) is xattrat, so without this the (no-xattrat x proto-29) cell is uncovered.",
|
||||
"name": "ubuntu-2204",
|
||||
"ssh_host": "runner@ubuntu-2204",
|
||||
"workflow": "ubuntu-22.04-build.yml",
|
||||
"workflow": "ubuntu-build.yml",
|
||||
"privilege": "sudo",
|
||||
"nonroot": true,
|
||||
"protocols": [
|
||||
|
||||
@@ -6,10 +6,10 @@ Builds the committed HEAD of an rsync checkout on a fleet of remote machines
|
||||
--use-tcp) in parallel, and prints one report of only the UNEXPECTED results --
|
||||
a fast local pre-flight for the GitHub CI matrix.
|
||||
|
||||
Each target maps 1:1 to a .github/workflows/*.yml job: the per-target configure
|
||||
flags mirror that workflow, and the pipe-run RSYNC_EXPECT_SKIPPED list is PARSED
|
||||
from the workflow (not hardcoded). The --use-tcp run never sets an expected-skip
|
||||
list (matching the workflows), so only test FAILs matter there.
|
||||
Each target maps to a .github/workflows/*.yml job or matrix lane: the per-target
|
||||
configure flags mirror that lane, and the pipe-run RSYNC_EXPECT_SKIPPED list is
|
||||
PARSED from the workflow (not hardcoded). The --use-tcp run never sets an
|
||||
expected-skip list (matching the workflows), so only test FAILs matter there.
|
||||
|
||||
The tcp pass runs only the tests that can reach the daemon transport, because it
|
||||
follows a full pipe pass over the very same build: --use-tcp is observable only
|
||||
@@ -167,7 +167,7 @@ PUSH_EXCLUDES = [
|
||||
class Target:
|
||||
name: str
|
||||
ssh_host: str | None # null in JSON => run locally
|
||||
workflow: str # filename under .github/workflows
|
||||
workflow: str # workflow containing the matching job or matrix lane
|
||||
configure_flags: list[str]
|
||||
make: str = "make" # e.g. "gmake" on the BSDs/Solaris
|
||||
env_prefix: str = "" # exported before configure AND make (e.g. PATH)
|
||||
|
||||
@@ -48,6 +48,10 @@ try:
|
||||
os.utime(inside_file, (st.st_atime, st.st_mtime))
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
except PermissionError:
|
||||
# Cygwin can report EACCES while the flipper swaps this path.
|
||||
if not _CYGWIN:
|
||||
raise
|
||||
subprocess.run(
|
||||
rsync_argv('-a', '--remove-source-files', f'{url}src/real/file', str(dest) + '/'),
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, text=True)
|
||||
|
||||
Reference in new issue
Block a user